summaryrefslogtreecommitdiffstats
path: root/wsutil
diff options
context:
space:
mode:
Diffstat (limited to 'wsutil')
-rw-r--r--wsutil/.editorconfig117
-rw-r--r--wsutil/802_11-utils.c113
-rw-r--r--wsutil/802_11-utils.h115
-rw-r--r--wsutil/CMakeLists.txt494
-rw-r--r--wsutil/adler32.c58
-rw-r--r--wsutil/adler32.h30
-rw-r--r--wsutil/base32.c68
-rw-r--r--wsutil/base32.h46
-rw-r--r--wsutil/bits_count_ones.h51
-rw-r--r--wsutil/bits_ctz.h92
-rw-r--r--wsutil/bitswap.c71
-rw-r--r--wsutil/bitswap.h26
-rw-r--r--wsutil/buffer.c206
-rw-r--r--wsutil/buffer.h68
-rw-r--r--wsutil/cfutils.c51
-rw-r--r--wsutil/cfutils.h31
-rw-r--r--wsutil/clopts_common.c108
-rw-r--r--wsutil/clopts_common.h56
-rw-r--r--wsutil/cmdarg_err.c59
-rw-r--r--wsutil/cmdarg_err.h51
-rw-r--r--wsutil/codecs.c194
-rw-r--r--wsutil/codecs.h149
-rw-r--r--wsutil/color.h57
-rw-r--r--wsutil/console_win32.c295
-rw-r--r--wsutil/console_win32.h73
-rw-r--r--wsutil/cpu_info.c491
-rw-r--r--wsutil/cpu_info.h26
-rw-r--r--wsutil/crash_info.c178
-rw-r--r--wsutil/crash_info.h29
-rw-r--r--wsutil/crc10.c83
-rw-r--r--wsutil/crc10.h18
-rw-r--r--wsutil/crc11.c80
-rw-r--r--wsutil/crc11.h41
-rw-r--r--wsutil/crc16-plain.c194
-rw-r--r--wsutil/crc16-plain.h121
-rw-r--r--wsutil/crc16.c483
-rw-r--r--wsutil/crc16.h113
-rw-r--r--wsutil/crc32.c461
-rw-r--r--wsutil/crc32.h106
-rw-r--r--wsutil/crc5.c67
-rw-r--r--wsutil/crc5.h40
-rw-r--r--wsutil/crc6.c82
-rw-r--r--wsutil/crc6.h17
-rw-r--r--wsutil/crc7.c84
-rw-r--r--wsutil/crc7.h76
-rw-r--r--wsutil/crc8.c204
-rw-r--r--wsutil/crc8.h53
-rw-r--r--wsutil/curve25519.c102
-rw-r--r--wsutil/curve25519.h34
-rw-r--r--wsutil/dot11decrypt_wep.c86
-rw-r--r--wsutil/eax.c249
-rw-r--r--wsutil/eax.h46
-rw-r--r--wsutil/epochs.h67
-rw-r--r--wsutil/exported_pdu_tlvs.h198
-rw-r--r--wsutil/feature_list.c56
-rw-r--r--wsutil/feature_list.h77
-rw-r--r--wsutil/file_util.c700
-rw-r--r--wsutil/file_util.h241
-rw-r--r--wsutil/filesystem.c2722
-rw-r--r--wsutil/filesystem.h426
-rw-r--r--wsutil/filter_files.c564
-rw-r--r--wsutil/filter_files.h98
-rw-r--r--wsutil/g711.c300
-rw-r--r--wsutil/g711.h30
-rw-r--r--wsutil/glib-compat.h46
-rw-r--r--wsutil/inet_addr.c102
-rw-r--r--wsutil/inet_addr.h75
-rw-r--r--wsutil/inet_ipv4.h57
-rw-r--r--wsutil/inet_ipv6.h103
-rw-r--r--wsutil/interface.c171
-rw-r--r--wsutil/interface.h36
-rw-r--r--wsutil/introspection.c27
-rw-r--r--wsutil/introspection.h29
-rw-r--r--wsutil/jsmn.c332
-rw-r--r--wsutil/jsmn.h99
-rw-r--r--wsutil/json_dumper.c689
-rw-r--r--wsutil/json_dumper.h140
-rw-r--r--wsutil/mpeg-audio.c122
-rw-r--r--wsutil/mpeg-audio.h95
-rw-r--r--wsutil/nstime.c653
-rw-r--r--wsutil/nstime.h186
-rw-r--r--wsutil/os_version_info.c806
-rw-r--r--wsutil/os_version_info.h29
-rw-r--r--wsutil/path_config.h.in10
-rw-r--r--wsutil/pint.h385
-rw-r--r--wsutil/please_report_bug.c33
-rw-r--r--wsutil/please_report_bug.h35
-rw-r--r--wsutil/plugins.c333
-rw-r--r--wsutil/plugins.h63
-rw-r--r--wsutil/pow2.h29
-rw-r--r--wsutil/privileges.c288
-rw-r--r--wsutil/privileges.h66
-rw-r--r--wsutil/processes.h56
-rw-r--r--wsutil/regex.c203
-rw-r--r--wsutil/regex.h63
-rw-r--r--wsutil/report_message.c162
-rw-r--r--wsutil/report_message.h120
-rw-r--r--wsutil/rsa.c376
-rw-r--r--wsutil/rsa.h44
-rw-r--r--wsutil/safe-math.h1064
-rw-r--r--wsutil/sign_ext.h71
-rw-r--r--wsutil/sober128.c488
-rw-r--r--wsutil/sober128.h48
-rw-r--r--wsutil/socket.c150
-rw-r--r--wsutil/socket.h97
-rw-r--r--wsutil/str_util.c1309
-rw-r--r--wsutil/str_util.h392
-rw-r--r--wsutil/strnatcmp.c189
-rw-r--r--wsutil/strnatcmp.h33
-rw-r--r--wsutil/strtoi.c306
-rw-r--r--wsutil/strtoi.h108
-rw-r--r--wsutil/tempfile.c153
-rw-r--r--wsutil/tempfile.h55
-rw-r--r--wsutil/test_wsutil.c881
-rw-r--r--wsutil/time_util.c339
-rw-r--r--wsutil/time_util.h81
-rw-r--r--wsutil/to_str.c989
-rw-r--r--wsutil/to_str.h314
-rw-r--r--wsutil/type_util.c68
-rw-r--r--wsutil/type_util.h44
-rw-r--r--wsutil/unicode-utils.c368
-rw-r--r--wsutil/unicode-utils.h138
-rw-r--r--wsutil/utf8_entities.h75
-rw-r--r--wsutil/version_info.c612
-rw-r--r--wsutil/version_info.h141
-rw-r--r--wsutil/win32-utils.c313
-rw-r--r--wsutil/win32-utils.h95
-rw-r--r--wsutil/wmem/wmem-int.h32
-rw-r--r--wsutil/wmem/wmem.h43
-rw-r--r--wsutil/wmem/wmem_allocator.h64
-rw-r--r--wsutil/wmem/wmem_allocator_block.c1125
-rw-r--r--wsutil/wmem/wmem_allocator_block.h46
-rw-r--r--wsutil/wmem/wmem_allocator_block_fast.c261
-rw-r--r--wsutil/wmem/wmem_allocator_block_fast.h41
-rw-r--r--wsutil/wmem/wmem_allocator_simple.c152
-rw-r--r--wsutil/wmem/wmem_allocator_simple.h42
-rw-r--r--wsutil/wmem/wmem_allocator_strict.c230
-rw-r--r--wsutil/wmem/wmem_allocator_strict.h45
-rw-r--r--wsutil/wmem/wmem_array.c196
-rw-r--r--wsutil/wmem/wmem_array.h121
-rw-r--r--wsutil/wmem/wmem_core.c240
-rw-r--r--wsutil/wmem/wmem_core.h252
-rw-r--r--wsutil/wmem/wmem_interval_tree.c190
-rw-r--r--wsutil/wmem/wmem_interval_tree.h101
-rw-r--r--wsutil/wmem/wmem_list.c276
-rw-r--r--wsutil/wmem/wmem_list.h128
-rw-r--r--wsutil/wmem/wmem_map.c515
-rw-r--r--wsutil/wmem/wmem_map.h246
-rw-r--r--wsutil/wmem/wmem_map_int.h41
-rw-r--r--wsutil/wmem/wmem_miscutl.c55
-rw-r--r--wsutil/wmem/wmem_miscutl.h73
-rw-r--r--wsutil/wmem/wmem_multimap.c185
-rw-r--r--wsutil/wmem/wmem_multimap.h195
-rw-r--r--wsutil/wmem/wmem_queue.h70
-rw-r--r--wsutil/wmem/wmem_stack.c57
-rw-r--r--wsutil/wmem/wmem_stack.h73
-rw-r--r--wsutil/wmem/wmem_strbuf.c470
-rw-r--r--wsutil/wmem/wmem_strbuf.h194
-rw-r--r--wsutil/wmem/wmem_strutl.c159
-rw-r--r--wsutil/wmem/wmem_strutl.h95
-rw-r--r--wsutil/wmem/wmem_test.c1496
-rw-r--r--wsutil/wmem/wmem_tree-int.h85
-rw-r--r--wsutil/wmem/wmem_tree.c869
-rw-r--r--wsutil/wmem/wmem_tree.h263
-rw-r--r--wsutil/wmem/wmem_user_cb.c104
-rw-r--r--wsutil/wmem/wmem_user_cb.h92
-rw-r--r--wsutil/wmem/wmem_user_cb_int.h45
-rw-r--r--wsutil/ws_assert.h129
-rw-r--r--wsutil/ws_cpuid.h141
-rw-r--r--wsutil/ws_getopt.c271
-rw-r--r--wsutil/ws_getopt.h60
-rw-r--r--wsutil/ws_mempbrk.c87
-rw-r--r--wsutil/ws_mempbrk.h37
-rw-r--r--wsutil/ws_mempbrk_int.h20
-rw-r--r--wsutil/ws_mempbrk_sse42.c173
-rw-r--r--wsutil/ws_pipe.c856
-rw-r--r--wsutil/ws_pipe.h105
-rw-r--r--wsutil/ws_roundup.h22
-rw-r--r--wsutil/ws_strptime.c906
-rw-r--r--wsutil/ws_strptime.h46
-rw-r--r--wsutil/wsgcrypt.c214
-rw-r--r--wsutil/wsgcrypt.h74
-rw-r--r--wsutil/wsjson.c312
-rw-r--r--wsutil/wsjson.h95
-rw-r--r--wsutil/wslog.c1442
-rw-r--r--wsutil/wslog.h458
-rw-r--r--wsutil/xtea.c70
-rw-r--r--wsutil/xtea.h39
188 files changed, 41230 insertions, 0 deletions
diff --git a/wsutil/.editorconfig b/wsutil/.editorconfig
new file mode 100644
index 00000000..78cac438
--- /dev/null
+++ b/wsutil/.editorconfig
@@ -0,0 +1,117 @@
+#
+# Editor configuration
+#
+# https://editorconfig.org/
+#
+
+[adler32.[ch]]
+indent_size = 2
+
+[aes.[ch]]
+indent_style = tab
+indent_size = tab
+
+[dot11decrypt_wep.[ch]]
+indent_style = tab
+indent_size = tab
+
+[base64.[ch]]
+indent_style = tab
+indent_size = tab
+
+[bitswap.[ch]]
+indent_size = 2
+
+[buffer.[ch]]
+indent_style = tab
+indent_size = tab
+
+[cfutils.[ch]]
+indent_style = tab
+indent_size = tab
+
+[clopts_common.[ch]]
+indent_size = 2
+
+[crash_info.[ch]]
+indent_style = tab
+indent_size = tab
+
+[crc10.[ch]]
+indent_style = tab
+indent_size = tab
+
+[crc32.[ch]]
+indent_style = tab
+indent_size = tab
+
+[des.[ch]]
+indent_style = tab
+indent_size = tab
+
+[g711.[ch]]
+indent_style = tab
+indent_size = tab
+
+[interface.[ch]]
+indent_style = tab
+indent_size = tab
+
+[jsmn.[ch]]
+indent_size = 8
+
+[md4.[ch]]
+indent_style = tab
+indent_size = tab
+
+[mpeg-audio.[ch]]
+indent_style = tab
+indent_size = tab
+
+[os_version_info.[ch]]
+indent_style = tab
+indent_size = tab
+
+[privileges.[ch]]
+indent_style = tab
+indent_size = tab
+
+[rc4.[ch]]
+indent_size = 2
+
+[report_err.[ch]]
+indent_style = tab
+indent_size = tab
+
+[tempfile.[ch]]
+indent_size = 2
+
+[time_util.[ch]]
+indent_style = tab
+indent_size = tab
+
+[to_str.[ch]]
+indent_style = tab
+indent_size = tab
+
+[type_util.[ch]]
+indent_size = 2
+
+[u3.[ch]]
+indent_size = 2
+
+[ws_getopt.[ch]]
+indent_style = tab
+indent_size = tab
+
+[ws_mempbrk.[ch]]
+indent_style = tab
+indent_size = tab
+
+[ws_mempbrk_sse42.[ch]]
+indent_size = 2
+
+[ws_strptime.[ch]]
+indent_style = tab
+indent_size = tab
+
diff --git a/wsutil/802_11-utils.c b/wsutil/802_11-utils.c
new file mode 100644
index 00000000..ee79f839
--- /dev/null
+++ b/wsutil/802_11-utils.c
@@ -0,0 +1,113 @@
+/* 802_11-utils.c
+ * 802.11 utility definitions
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2007 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include "802_11-utils.h"
+
+typedef struct freq_cvt_s {
+ unsigned fmin; /* Minimum frequency in MHz */
+ unsigned fmax; /* Maximum frequency in MHz */
+ int cmin; /* Minimum/base channel */
+ bool is_bg; /* B/G channel? */
+} freq_cvt_t;
+
+#define FREQ_STEP 5 /* MHz. This seems to be consistent, thankfully */
+
+/*
+ * XXX - Japanese channels 182 through 196 actually have center
+ * frequencies that are off by 2.5 MHz from these values, according
+ * to the IEEE standard, although the table in ARIB STD T-71 version 5.2:
+ *
+ * http://www.arib.or.jp/english/html/overview/doc/1-STD-T71v5_2.pdf
+ *
+ * section 5.3.8.3.3 doesn't show that.
+ *
+ * XXX - what about the U.S. public safety 4.9 GHz band?
+ *
+ * XXX - what about 802.11ad?
+ */
+static freq_cvt_t freq_cvt[] = {
+ { 2412, 2472, 1, true }, /* IEEE Std 802.11-2020: Section 15.4.4.3 and Annex E */
+ { 2484, 2484, 14, true }, /* IEEE Std 802.11-2020: Section 15.4.4.3 and Annex E */
+ { 5000, 5925, 0, false }, /* IEEE Std 802.11-2020: Annex E */
+ { 5950, 7125, 0, false }, /* IEEE Std 802.11ax-2021: Annex E */
+ { 4910, 4980, 182, false },
+};
+
+#define NUM_FREQ_CVT (sizeof(freq_cvt) / sizeof(freq_cvt_t))
+#define MAX_CHANNEL(fc) ( (int) ((fc.fmax - fc.fmin) / FREQ_STEP) + fc.cmin )
+
+/*
+ * Get channel number given a Frequency
+ */
+int
+ieee80211_mhz_to_chan(unsigned freq) {
+ unsigned i;
+
+ for (i = 0; i < NUM_FREQ_CVT; i++) {
+ if (freq >= freq_cvt[i].fmin && freq <= freq_cvt[i].fmax) {
+ return ((freq - freq_cvt[i].fmin) / FREQ_STEP) + freq_cvt[i].cmin;
+ }
+ }
+ return -1;
+}
+
+/*
+ * Get Frequency given a Channel number
+ *
+ * XXX - Because channel numbering schemes for 2.4 and 5 overlap with 6 GHz,
+ * this function may not return the correct channel. For example, the frequency
+ * for channel 1 in 2.4 GHz band is 2412 MHz, while the frequency for channel 1
+ * in the 6 GHz band is 5955 MHz. To resolve this problem, this function needs
+ * to take a starting frequency to convert channel to frequencies correctly.
+ * Unfortunately, this is not possible in some cases, so for now, the order on
+ * which frequency ranges are defined will favor 2.4 and 5 GHz over 6 GHz.
+ */
+unsigned
+ieee80211_chan_to_mhz(int chan, bool is_bg) {
+ unsigned i;
+
+ for (i = 0; i < NUM_FREQ_CVT; i++) {
+ if (is_bg == freq_cvt[i].is_bg &&
+ chan >= freq_cvt[i].cmin && chan <= MAX_CHANNEL(freq_cvt[i])) {
+ return ((chan - freq_cvt[i].cmin) * FREQ_STEP) + freq_cvt[i].fmin;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Get channel representation string given a Frequency
+ */
+char*
+ieee80211_mhz_to_str(unsigned freq){
+ int chan = ieee80211_mhz_to_chan(freq);
+ bool is_bg = FREQ_IS_BG(freq);
+
+ if (chan < 0) {
+ return ws_strdup_printf("%u", freq);
+ } else {
+ return ws_strdup_printf("%u [%s %u]", freq, is_bg ? "BG" : "A",
+ chan);
+ }
+}
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wsutil/802_11-utils.h b/wsutil/802_11-utils.h
new file mode 100644
index 00000000..887faddb
--- /dev/null
+++ b/wsutil/802_11-utils.h
@@ -0,0 +1,115 @@
+/* 802_11-utils.h
+ * 802.11 utility definitions
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2007 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __802_11_UTILS_H__
+#define __802_11_UTILS_H__
+
+#include <wireshark.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/** @file
+ * 802.11 utilities.
+ */
+
+/**
+ * Given a center frequency in MHz, return a channel number.
+ * @param freq Frequency in MHz.
+ * @return The equivalent channel or -1 if no match is found.
+ */
+WS_DLL_PUBLIC
+int
+ieee80211_mhz_to_chan(unsigned freq);
+
+/**
+ * Given an 802.11 channel number and a band type, return a center frequency.
+ * @param chan Channel number
+ * @param is_bg true if the channel is a b/g channel, false otherwise.
+ * @return The equivalent frequency or 0 if no match is found.
+ */
+WS_DLL_PUBLIC
+unsigned
+ieee80211_chan_to_mhz(int chan, bool is_bg);
+
+/**
+ * Given an 802.11 channel center frequency in MHz, return a string
+ * representation.
+ * @param freq Frequench in MHz.
+ * @return A string showing the frequency, channel number, and type.
+ * The string must be freed with g_free() after use.
+ */
+WS_DLL_PUBLIC
+char*
+ieee80211_mhz_to_str(unsigned freq);
+
+/* Should this be "(freq < 4920)", or something else? */
+#define FREQ_IS_BG(freq) ((freq) <= 2484)
+#define CHAN_IS_BG(chan) ((chan) <= 14)
+
+/*
+ * Test whether a data rate is an {HR}/DSSS (legacy DSSS/11b) data rate
+ * and whether it's an OFDM (11a/11g OFDM mode) data rate.
+ *
+ * rate is in units of 500 Kb/s.
+ *
+ * The 22 and 33 Mb/s rates for DSSS use Packet Binary Convolutional
+ * Coding (PBCC). That was provided by Texas Instruments as 11b+,
+ * and was in section 19.6 "ERP-PBCC operation specifications" of
+ * IEEE Std 802.11g-2003, and sections 18.4.6.6 "DSSS/PBCC data modulation
+ * and modulation rate (optional)" and 19.6 "ERP-PBCC operation
+ * specifications" of IEEE Std 802.11-2007, and sections 17.4.6.7 "DSSS/PBCC
+ * data modulation and modulation rate (optional)" and 19.6 "ERP-PBCC
+ * operation specifications" of IEEE Std 802.11-2012, marked as optional
+ * in both cases, but is not present in IEEE Std 802.11-2016.
+ *
+ * (Note: not to be confused with "peanut butter and chocolate chips":
+ *
+ * https://www.bigoven.com/recipe/peanut-butter-chocolate-chip-cookies-pbcc-cookies/186266
+ *
+ * :-))
+ */
+#define RATE_IS_DSSS(rate) \
+ ((rate) == 2 /* 1 Mb/s */ || \
+ (rate) == 4 /* 2 Mb/s */ || \
+ (rate) == 11 /* 5.5 Mb/s */ || \
+ (rate) == 22 /* 11 Mb/s */ || \
+ (rate) == 44 /* 22 Mb/s */ || \
+ (rate) == 66 /* 33 Mb/s */)
+
+#define RATE_IS_OFDM(rate) \
+ ((rate) == 12 /* 6 Mb/s */ || \
+ (rate) == 18 /* 9 Mb/s */ || \
+ (rate) == 24 /* 12 Mb/s */ || \
+ (rate) == 36 /* 18 Mb/s */ || \
+ (rate) == 48 /* 24 Mb/s */ || \
+ (rate) == 72 /* 36 Mb/s */ || \
+ (rate) == 96 /* 48 Mb/s */ || \
+ (rate) == 108 /* 54 Mb/s */)
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __802_11_UTILS_H__ */
+
+/*
+ * Editor modelines
+ *
+ * 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/wsutil/CMakeLists.txt b/wsutil/CMakeLists.txt
new file mode 100644
index 00000000..ba8633e1
--- /dev/null
+++ b/wsutil/CMakeLists.txt
@@ -0,0 +1,494 @@
+# CMakeLists.txt
+#
+# Wireshark - Network traffic analyzer
+# By Gerald Combs <gerald@wireshark.org>
+# Copyright 1998 Gerald Combs
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+
+file(TO_NATIVE_PATH "${CMAKE_INSTALL_PREFIX}" PATH_INSTALL_PREFIX)
+string(REPLACE "\\" "\\\\" PATH_INSTALL_PREFIX "${PATH_INSTALL_PREFIX}")
+file(TO_NATIVE_PATH "${CMAKE_INSTALL_DATADIR}" PATH_DATA_DIR)
+string(REPLACE "\\" "\\\\" PATH_DATA_DIR "${PATH_DATA_DIR}")
+file(TO_NATIVE_PATH "${CMAKE_INSTALL_DOCDIR}" PATH_DOC_DIR)
+string(REPLACE "\\" "\\\\" PATH_DOC_DIR "${PATH_DOC_DIR}")
+file(TO_NATIVE_PATH "${PLUGIN_INSTALL_LIBDIR}" PATH_PLUGIN_DIR)
+string(REPLACE "\\" "\\\\" PATH_PLUGIN_DIR "${PATH_PLUGIN_DIR}")
+file(TO_NATIVE_PATH "${EXTCAP_INSTALL_LIBDIR}" PATH_EXTCAP_DIR)
+string(REPLACE "\\" "\\\\" PATH_EXTCAP_DIR "${PATH_EXTCAP_DIR}")
+
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/path_config.h.in ${CMAKE_CURRENT_BINARY_DIR}/path_config.h)
+
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+set(WMEM_PUBLIC_HEADERS
+ wmem/wmem.h
+ wmem/wmem_array.h
+ wmem/wmem_core.h
+ wmem/wmem_list.h
+ wmem/wmem_map.h
+ wmem/wmem_miscutl.h
+ wmem/wmem_multimap.h
+ wmem/wmem_queue.h
+ wmem/wmem_stack.h
+ wmem/wmem_strbuf.h
+ wmem/wmem_strutl.h
+ wmem/wmem_tree.h
+ wmem/wmem_interval_tree.h
+ wmem/wmem_user_cb.h
+)
+
+set(WMEM_HEADER_FILES
+ ${WMEM_PUBLIC_HEADERS}
+ wmem/wmem_allocator.h
+ wmem/wmem_allocator_block.h
+ wmem/wmem_allocator_block_fast.h
+ wmem/wmem_allocator_simple.h
+ wmem/wmem_allocator_strict.h
+ wmem/wmem_interval_tree.h
+ wmem/wmem_map_int.h
+ wmem/wmem_tree-int.h
+ wmem/wmem_user_cb_int.h
+)
+
+set(WMEM_FILES
+ wmem/wmem_array.c
+ wmem/wmem_core.c
+ wmem/wmem_allocator_block.c
+ wmem/wmem_allocator_block_fast.c
+ wmem/wmem_allocator_simple.c
+ wmem/wmem_allocator_strict.c
+ wmem/wmem_interval_tree.c
+ wmem/wmem_list.c
+ wmem/wmem_map.c
+ wmem/wmem_miscutl.c
+ wmem/wmem_multimap.c
+ wmem/wmem_stack.c
+ wmem/wmem_strbuf.c
+ wmem/wmem_strutl.c
+ wmem/wmem_tree.c
+ wmem/wmem_user_cb.c
+)
+
+set(WSUTIL_PUBLIC_HEADERS
+ 802_11-utils.h
+ adler32.h
+ base32.h
+ bits_count_ones.h
+ bits_ctz.h
+ bitswap.h
+ buffer.h
+ clopts_common.h
+ cmdarg_err.h
+ codecs.h
+ color.h
+ cpu_info.h
+ crash_info.h
+ crc5.h
+ crc6.h
+ crc7.h
+ crc8.h
+ crc10.h
+ crc11.h
+ crc16.h
+ crc16-plain.h
+ crc32.h
+ curve25519.h
+ eax.h
+ epochs.h
+ exported_pdu_tlvs.h
+ feature_list.h
+ filesystem.h
+ g711.h
+ inet_addr.h
+ inet_ipv4.h
+ inet_ipv6.h
+ interface.h
+ introspection.h
+ jsmn.h
+ json_dumper.h
+ mpeg-audio.h
+ nstime.h
+ os_version_info.h
+ pint.h
+ please_report_bug.h
+ pow2.h
+ privileges.h
+ processes.h
+ regex.h
+ report_message.h
+ sign_ext.h
+ sober128.h
+ socket.h
+ str_util.h
+ strnatcmp.h
+ strtoi.h
+ tempfile.h
+ time_util.h
+ to_str.h
+ type_util.h
+ unicode-utils.h
+ utf8_entities.h
+ version_info.h
+ ws_assert.h
+ ws_cpuid.h
+ glib-compat.h
+ ws_getopt.h
+ ws_mempbrk.h
+ ws_mempbrk_int.h
+ ws_pipe.h
+ ws_roundup.h
+ ws_strptime.h
+ wsgcrypt.h
+ wsjson.h
+ wslog.h
+ xtea.h
+)
+
+set(WSUTIL_COMMON_FILES
+ 802_11-utils.c
+ adler32.c
+ base32.c
+ bitswap.c
+ buffer.c
+ clopts_common.c
+ cmdarg_err.c
+ codecs.c
+ crash_info.c
+ crc10.c
+ crc16.c
+ crc16-plain.c
+ crc32.c
+ crc5.c
+ crc6.c
+ crc7.c
+ crc8.c
+ crc11.c
+ curve25519.c
+ dot11decrypt_wep.c
+ eax.c
+ feature_list.c
+ filesystem.c
+ filter_files.c
+ g711.c
+ inet_addr.c
+ interface.c
+ introspection.c
+ jsmn.c
+ json_dumper.c
+ mpeg-audio.c
+ nstime.c
+ cpu_info.c
+ os_version_info.c
+ please_report_bug.c
+ privileges.c
+ regex.c
+ rsa.c
+ sober128.c
+ socket.c
+ strnatcmp.c
+ str_util.c
+ strtoi.c
+ report_message.c
+ tempfile.c
+ time_util.c
+ to_str.c
+ type_util.c
+ unicode-utils.c
+ version_info.c
+ ws_getopt.c
+ ws_mempbrk.c
+ ws_pipe.c
+ ws_strptime.c
+ wsgcrypt.c
+ wsjson.c
+ wslog.c
+ xtea.c
+)
+
+if(WIN32)
+ list(APPEND WSUTIL_COMMON_FILES
+ console_win32.c
+ )
+endif()
+
+if(ENABLE_PLUGINS)
+ list(APPEND WSUTIL_COMMON_FILES
+ plugins.c
+ )
+endif()
+
+set(WSUTIL_FILES
+ ${WMEM_FILES}
+ ${WSUTIL_COMMON_FILES}
+)
+
+if(WIN32)
+ list(APPEND WSUTIL_FILES
+ file_util.c
+ win32-utils.c
+ )
+endif(WIN32)
+
+
+if(HAVE_MACOS_FRAMEWORKS)
+ list(APPEND WSUTIL_FILES cfutils.c)
+endif()
+
+#
+# XXX - we're assuming MSVC doesn't require a flag to enable SSE 4.2
+# support, and that, if the compiler supports a flag for SSE 4.2
+# support, the intrinsics are supported iff we can include the
+# <nmmintrin.h> flag.
+#
+# We only check for the GCC-style -msse4.2 flag and the Sun C
+# -xarch=sse4_2 flag.
+#
+if(CMAKE_C_COMPILER_ID MATCHES "MSVC")
+ set(COMPILER_CAN_HANDLE_SSE4_2 TRUE)
+ set(SSE4_2_FLAG "")
+elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "i686|x86|x86_64|AMD64")
+ check_c_compiler_flag(-msse4.2 COMPILER_CAN_HANDLE_SSE4_2)
+ if(COMPILER_CAN_HANDLE_SSE4_2)
+ set(SSE4_2_FLAG "-msse4.2")
+ else()
+ check_c_compiler_flag(-xarch=sse4_2 COMPILER_CAN_HANDLE_SSE4_2)
+ if(COMPILER_CAN_HANDLE_SSE4_2)
+ set(SSE4_2_FLAG "-xarch=sse4_2")
+ endif()
+ endif()
+else()
+ set(COMPILE_CAN_HANDLE_SSE4_2 FALSE)
+ set(SSE4_2_FLAG "")
+endif()
+
+if(SSE4_2_FLAG)
+ message(STATUS "SSE4.2 compiler flag: ${SSE4_2_FLAG}")
+else()
+ message(STATUS "No SSE4.2 compiler flag enabled")
+endif()
+if(COMPILER_CAN_HANDLE_SSE4_2)
+ #
+ # Make sure we have the necessary headers for the SSE4.2 intrinsics
+ # and that we can use them.
+ #
+ # First, check whether we have emmintrin.h and can use it
+ # *without* the SSE 4.2 flag.
+ #
+ check_include_file("emmintrin.h" EMMINTRIN_H_WORKS)
+
+ #
+ # OK, if that works, see whether we have nmmintrin.h and
+ # can use it *with* the SSE 4.2 flag.
+ #
+ if(EMMINTRIN_H_WORKS)
+ #
+ # Does this add the SSE4.2 flags to the beginning of
+ # CFLAGS?
+ #
+ # Note that if there's a mix of "enable SSE 4.2" and
+ # "disable SSE 4.2" flags, this may not indicate that
+ # we can use the header. That's not a bug, that's a
+ # feature; the other flags may have been forced by
+ # the build process, e.g. in Gentoo Linux, and we want
+ # to check this with whatever flags will actually be
+ # used when building (see bug 10792).
+ #
+ cmake_push_check_state()
+ set(CMAKE_REQUIRED_FLAGS "${SSE4_2_FLAG}")
+ check_include_file("nmmintrin.h" HAVE_SSE4_2)
+ cmake_pop_check_state()
+ endif()
+endif()
+if(HAVE_SSE4_2)
+ list(APPEND WSUTIL_FILES ws_mempbrk_sse42.c)
+endif()
+
+if(APPLE)
+ #
+ # We assume that APPLE means macOS so that we have the macOS
+ # frameworks.
+ #
+ FIND_LIBRARY (APPLE_CORE_FOUNDATION_LIBRARY CoreFoundation)
+endif()
+
+set_source_files_properties(
+ ${WSUTIL_FILES}
+ PROPERTIES
+ COMPILE_FLAGS "${WERROR_COMMON_FLAGS}"
+)
+
+if (HAVE_SSE4_2)
+ # TODO with CMake 2.8.12, we could use COMPILE_OPTIONS and just append
+ # instead of this COMPILE_FLAGS duplication...
+ set_source_files_properties(
+ ws_mempbrk_sse42.c
+ PROPERTIES
+ COMPILE_FLAGS "${WERROR_COMMON_FLAGS} ${SSE4_2_FLAG}"
+ )
+endif()
+
+if (ENABLE_APPLICATION_BUNDLE)
+ set_source_files_properties(
+ filesystem.c
+ PROPERTIES
+ COMPILE_FLAGS "${WERROR_COMMON_FLAGS} -DENABLE_APPLICATION_BUNDLE"
+)
+endif()
+
+add_library(wsutil
+ ${WSUTIL_FILES}
+ ${CMAKE_BINARY_DIR}/resources/libwsutil.rc
+)
+
+if(NOT VCSVERSION_OVERRIDE)
+ add_dependencies(wsutil vcs_version)
+endif()
+
+target_compile_definitions(wsutil PRIVATE
+ WS_BUILD_DLL
+ BUILD_WSUTIL
+)
+
+set_target_properties(wsutil PROPERTIES
+ PREFIX "lib"
+ LINK_FLAGS "${WS_LINK_FLAGS}"
+ VERSION "15.0.0" SOVERSION 15
+ FOLDER "DLLs"
+ INSTALL_RPATH "${LIBRARY_INSTALL_RPATH}"
+)
+if(MSVC)
+ set_target_properties(wsutil PROPERTIES LINK_FLAGS_DEBUG "${WS_MSVC_DEBUG_LINK_FLAGS}")
+endif()
+
+target_link_libraries(wsutil
+ PUBLIC
+ ${GLIB2_LIBRARIES}
+ PRIVATE
+ ${GMODULE2_LIBRARIES}
+ ${APPLE_CORE_FOUNDATION_LIBRARY}
+ ${CMAKE_DL_LIBS}
+ ${GCRYPT_LIBRARIES}
+ ${GNUTLS_LIBRARIES}
+ ${ZLIB_LIBRARIES}
+ $<IF:$<CONFIG:Debug>,${PCRE2_DEBUG_LIBRARIES},${PCRE2_LIBRARIES}>
+ ${WIN_IPHLPAPI_LIBRARY}
+ ${WIN_WS2_32_LIBRARY}
+)
+
+target_include_directories(wsutil SYSTEM
+ PUBLIC
+ ${GLIB2_INCLUDE_DIRS}
+ ${GCRYPT_INCLUDE_DIRS}
+ ${GNUTLS_INCLUDE_DIRS}
+ PRIVATE
+ ${GMODULE2_INCLUDE_DIRS}
+ ${ZLIB_INCLUDE_DIRS}
+ ${PCRE2_INCLUDE_DIRS}
+)
+
+install(TARGETS wsutil
+ EXPORT WiresharkTargets
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+install(FILES ${WMEM_PUBLIC_HEADERS}
+ DESTINATION "${PROJECT_INSTALL_INCLUDEDIR}/wsutil/wmem"
+ COMPONENT "Development"
+ EXCLUDE_FROM_ALL
+)
+
+install(FILES ${WSUTIL_PUBLIC_HEADERS}
+ DESTINATION "${PROJECT_INSTALL_INCLUDEDIR}/wsutil"
+ COMPONENT "Development"
+ EXCLUDE_FROM_ALL
+)
+
+add_library(wsutil_static STATIC
+ ${WSUTIL_FILES}
+)
+
+target_compile_definitions(wsutil_static PRIVATE
+ ENABLE_STATIC
+ BUILD_WSUTIL
+)
+
+target_link_libraries(wsutil_static
+ PUBLIC
+ ${GLIB2_LIBRARIES}
+ PRIVATE
+ ${GMODULE2_LIBRARIES}
+ ${APPLE_CORE_FOUNDATION_LIBRARY}
+ ${CMAKE_DL_LIBS}
+ ${GCRYPT_LIBRARIES}
+ ${GNUTLS_LIBRARIES}
+ ${ZLIB_LIBRARIES}
+ $<IF:$<CONFIG:Debug>,${PCRE2_DEBUG_LIBRARIES},${PCRE2_LIBRARIES}>
+ ${WIN_IPHLPAPI_LIBRARY}
+ ${WIN_WS2_32_LIBRARY}
+)
+
+target_include_directories(wsutil_static SYSTEM
+ PUBLIC
+ ${GLIB2_INCLUDE_DIRS}
+ ${GCRYPT_INCLUDE_DIRS}
+ ${GNUTLS_INCLUDE_DIRS}
+ PRIVATE
+ ${GMODULE2_INCLUDE_DIRS}
+ ${ZLIB_INCLUDE_DIRS}
+ ${PCRE2_INCLUDE_DIRS}
+)
+
+if(NOT VCSVERSION_OVERRIDE)
+ add_dependencies(wsutil_static vcs_version)
+endif()
+
+add_executable(wmem_test EXCLUDE_FROM_ALL wmem/wmem_test.c ${WMEM_FILES})
+
+target_link_libraries(wmem_test wsutil)
+
+set_target_properties(wmem_test PROPERTIES
+ FOLDER "Tests"
+ EXCLUDE_FROM_DEFAULT_BUILD True
+ COMPILE_DEFINITIONS "WS_BUILD_DLL"
+ COMPILE_FLAGS "${WERROR_COMMON_FLAGS}"
+)
+
+add_executable(test_wsutil EXCLUDE_FROM_ALL
+ test_wsutil.c
+)
+
+target_link_libraries(test_wsutil ${GLIB2_LIBRARIES} wsutil)
+
+set_target_properties(test_wsutil PROPERTIES
+ FOLDER "Tests"
+ EXCLUDE_FROM_DEFAULT_BUILD True
+ COMPILE_FLAGS "${WERROR_COMMON_FLAGS}"
+)
+
+CHECKAPI(
+ NAME
+ wsutil
+ SWITCHES
+ SOURCES
+ ${WMEM_FILES}
+ ${WSUTIL_COMMON_FILES}
+)
+
+set_source_files_properties(jsmn.c PROPERTIES COMPILE_DEFINITIONS "JSMN_STRICT")
+
+#
+# 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:
+#
diff --git a/wsutil/adler32.c b/wsutil/adler32.c
new file mode 100644
index 00000000..2830eab4
--- /dev/null
+++ b/wsutil/adler32.c
@@ -0,0 +1,58 @@
+/* adler32.c
+ * Compute the Adler32 checksum (RFC 1950)
+ * 2003 Tomas Kukosa
+ * Based on code from RFC 1950 (Chapter 9. Appendix: Sample code)
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <wsutil/adler32.h>
+
+#include <string.h>
+
+#define BASE 65521 /* largest prime smaller than 65536 */
+
+/*--- update_adler32 --------------------------------------------------------*/
+uint32_t update_adler32(uint32_t adler, const uint8_t *buf, size_t len)
+{
+ uint32_t s1 = adler & 0xffff;
+ uint32_t s2 = (adler >> 16) & 0xffff;
+ size_t n;
+
+ for (n = 0; n < len; n++) {
+ s1 = (s1 + buf[n]) % BASE;
+ s2 = (s2 + s1) % BASE;
+ }
+ return (s2 << 16) + s1;
+}
+
+/*--- adler32 ---------------------------------------------------------------*/
+uint32_t adler32_bytes(const uint8_t *buf, size_t len)
+{
+ return update_adler32(1, buf, len);
+}
+
+/*--- adler32_str -----------------------------------------------------------*/
+uint32_t adler32_str(const char *buf)
+{
+ return update_adler32(1, (const uint8_t*)buf, strlen(buf));
+}
+
+/*---------------------------------------------------------------------------*/
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local Variables:
+ * c-basic-offset: 2
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=2 tabstop=8 expandtab:
+ * :indentSize=2:tabSize=8:noTabs=true:
+ */
diff --git a/wsutil/adler32.h b/wsutil/adler32.h
new file mode 100644
index 00000000..9ff7c649
--- /dev/null
+++ b/wsutil/adler32.h
@@ -0,0 +1,30 @@
+/** @file
+ * Compute the Adler32 checksum (RFC 1950)
+ * 2003 Tomas Kukosa
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef ADLER32_H
+#define ADLER32_H
+
+#include <wireshark.h>
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+WS_DLL_PUBLIC uint32_t update_adler32(uint32_t adler, const uint8_t *buf, size_t len);
+WS_DLL_PUBLIC uint32_t adler32_bytes(const uint8_t *buf, size_t len);
+WS_DLL_PUBLIC uint32_t adler32_str(const char *buf);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ADLER32_H */
+
diff --git a/wsutil/base32.c b/wsutil/base32.c
new file mode 100644
index 00000000..ad8b800c
--- /dev/null
+++ b/wsutil/base32.c
@@ -0,0 +1,68 @@
+/* base32.c
+ * Base-32 conversion
+ *
+ * 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 "base32.h"
+
+#include <string.h>
+
+/*
+ * Cjdns style base32 encoding
+ */
+
+/** Returned by ws_base32_encode() if the input is not valid base32. */
+#define Base32_BAD_INPUT -1
+/** Returned by ws_base32_encode() if the output buffer is too small. */
+#define Base32_TOO_BIG -2
+
+int ws_base32_decode(uint8_t* output, const uint32_t outputLength,
+ const uint8_t* in, const uint32_t inputLength)
+{
+ uint32_t outIndex = 0;
+ uint32_t inIndex = 0;
+ uint32_t work = 0;
+ uint32_t bits = 0;
+ static const uint8_t* kChars = (uint8_t*) "0123456789bcdfghjklmnpqrstuvwxyz";
+ while (inIndex < inputLength) {
+ work |= ((unsigned) in[inIndex++]) << bits;
+ bits += 8;
+ while (bits >= 5) {
+ if (outIndex >= outputLength) {
+ return Base32_TOO_BIG;
+ }
+ output[outIndex++] = kChars[work & 31];
+ bits -= 5;
+ work >>= 5;
+ }
+ }
+ if (bits) {
+ if (outIndex >= outputLength) {
+ return Base32_TOO_BIG;
+ }
+ output[outIndex++] = kChars[work & 31];
+ }
+ if (outIndex < outputLength) {
+ output[outIndex] = '\0';
+ }
+ return outIndex;
+}
+
+/*
+ * 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:
+ */
diff --git a/wsutil/base32.h b/wsutil/base32.h
new file mode 100644
index 00000000..ec1b244e
--- /dev/null
+++ b/wsutil/base32.h
@@ -0,0 +1,46 @@
+/** @file
+ * Base-32 conversion
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef __BASE32_H__
+#define __BASE32_H__
+
+#include <wireshark.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/** Returned by base32_decode() if the input is not valid base32. */
+#define Base32_BAD_INPUT -1
+/** Returned by base32_decode() if the output buffer is too small. */
+#define Base32_TOO_BIG -2
+
+/* Encoding of a base32 byte array */
+WS_DLL_PUBLIC
+int ws_base32_decode(uint8_t* output, const uint32_t outputLength,
+ const uint8_t* in, const uint32_t inputLength);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __BASE32_H__ */
+
+/*
+ * 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/wsutil/bits_count_ones.h b/wsutil/bits_count_ones.h
new file mode 100644
index 00000000..1b4f1d68
--- /dev/null
+++ b/wsutil/bits_count_ones.h
@@ -0,0 +1,51 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WSUTIL_BITS_COUNT_ONES_H__
+#define __WSUTIL_BITS_COUNT_ONES_H__
+
+#include <inttypes.h>
+
+/*
+ * The variable-precision SWAR algorithm is an interesting way to count
+ * the number of bits set in an integer:
+ *
+ * https://www.playingwithpointers.com/blog/swar.html
+ *
+ * See
+ *
+ * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=36041
+ * https://danluu.com/assembly-intrinsics/
+ *
+ * for discussions of various forms of population-counting code on x86.
+ *
+ * See
+ *
+ * https://docs.microsoft.com/en-us/cpp/intrinsics/popcnt16-popcnt-popcnt64
+ *
+ * for MSVC's population count intrinsics.
+ *
+ * Note that not all x86 processors support the POPCOUNT instruction.
+ *
+ * Other CPUs may have population count instructions as well.
+ */
+
+static inline int
+ws_count_ones(const uint64_t x)
+{
+ uint64_t bits = x;
+
+ bits = bits - ((bits >> 1) & G_GUINT64_CONSTANT(0x5555555555555555));
+ bits = (bits & G_GUINT64_CONSTANT(0x3333333333333333)) + ((bits >> 2) & G_GUINT64_CONSTANT(0x3333333333333333));
+ bits = (bits + (bits >> 4)) & G_GUINT64_CONSTANT(0x0F0F0F0F0F0F0F0F);
+
+ return (int)((bits * G_GUINT64_CONSTANT(0x0101010101010101)) >> 56);
+}
+
+#endif /* __WSUTIL_BITS_COUNT_ONES_H__ */
diff --git a/wsutil/bits_ctz.h b/wsutil/bits_ctz.h
new file mode 100644
index 00000000..b4c43e3f
--- /dev/null
+++ b/wsutil/bits_ctz.h
@@ -0,0 +1,92 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WSUTIL_BITS_CTZ_H__
+#define __WSUTIL_BITS_CTZ_H__
+
+#include <inttypes.h>
+
+/* ws_ctz == trailing zeros == position of lowest set bit [0..63] */
+/* ws_ilog2 == position of highest set bit == 63 - leading zeros [0..63] */
+
+/* The return value of both ws_ctz and ws_ilog2 is undefined for x == 0 */
+
+#if defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))
+
+static inline int
+ws_ctz(uint64_t x)
+{
+ return __builtin_ctzll(x);
+}
+
+static inline int
+ws_ilog2(uint64_t x)
+{
+ return 63 - __builtin_clzll(x);
+}
+
+#else
+
+static inline int
+__ws_ctz32(uint32_t x)
+{
+ /* From http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightMultLookup */
+ static const uint8_t table[32] = {
+ 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
+ 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
+ };
+
+ return table[((uint32_t)((x & -(int32_t)x) * 0x077CB531U)) >> 27];
+}
+
+static inline int
+ws_ctz(uint64_t x)
+{
+ uint32_t hi = x >> 32;
+ uint32_t lo = (uint32_t) x;
+
+ if (lo == 0)
+ return 32 + __ws_ctz32(hi);
+ else
+ return __ws_ctz32(lo);
+}
+
+static inline int
+__ws_ilog2_32(uint32_t x)
+{
+ /* From http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogDeBruijn */
+ static const uint8_t table[32] = {
+ 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
+ 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
+ };
+
+ x |= x >> 1;
+ x |= x >> 2;
+ x |= x >> 4;
+ x |= x >> 8;
+ x |= x >> 16;
+
+ return table[((uint32_t)(x * 0x07C4ACDDU)) >> 27];
+}
+
+static inline int
+ws_ilog2(uint64_t x)
+{
+ uint32_t hi = x >> 32;
+ uint32_t lo = (uint32_t) x;
+
+ if (hi == 0)
+ return __ws_ilog2_32(lo);
+ else
+ return 32 + __ws_ilog2_32(hi);
+}
+
+#endif
+
+#endif /* __WSUTIL_BITS_CTZ_H__ */
diff --git a/wsutil/bitswap.c b/wsutil/bitswap.c
new file mode 100644
index 00000000..33f78485
--- /dev/null
+++ b/wsutil/bitswap.c
@@ -0,0 +1,71 @@
+/* bitswap.c
+ * Table of bit-swapped values of bytes
+ *
+ * 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 "bitswap.h"
+
+/* "swaptab[i]" is the value of "i" with the bits reversed. */
+static const uint8_t swaptab[256] =
+{
+ 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
+ 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
+ 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+ 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
+ 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
+ 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+ 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
+ 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
+ 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+ 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
+ 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
+ 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+ 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
+ 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
+ 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+ 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
+ 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
+ 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+ 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
+ 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
+ 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
+ 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
+ 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
+ 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+ 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
+ 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
+ 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
+ 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
+ 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
+ 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+ 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
+ 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
+};
+
+void bitswap_buf_inplace(uint8_t *buf, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++)
+ buf[i] = swaptab[buf[i]];
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local Variables:
+ * c-basic-offset: 2
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=2 tabstop=8 expandtab:
+ * :indentSize=2:tabSize=8:noTabs=true:
+ */
diff --git a/wsutil/bitswap.h b/wsutil/bitswap.h
new file mode 100644
index 00000000..7e836609
--- /dev/null
+++ b/wsutil/bitswap.h
@@ -0,0 +1,26 @@
+/** @file
+ * Macro to bitswap a byte by looking it up in a table
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __BITSWAP_H__
+#define __BITSWAP_H__
+
+#include <wireshark.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+WS_DLL_PUBLIC void bitswap_buf_inplace(uint8_t *buf, size_t len);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* bitswap.h */
diff --git a/wsutil/buffer.c b/wsutil/buffer.c
new file mode 100644
index 00000000..95330086
--- /dev/null
+++ b/wsutil/buffer.c
@@ -0,0 +1,206 @@
+/* buffer.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_WSUTIL
+#include "buffer.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <wsutil/ws_assert.h>
+#include <wsutil/wslog.h>
+
+#define SMALL_BUFFER_SIZE (2 * 1024) /* Everyone still uses 1500 byte frames, right? */
+static GPtrArray *small_buffers = NULL; /* Guaranteed to be at least SMALL_BUFFER_SIZE */
+/* XXX - Add medium and large buffers? */
+
+/* Initializes a buffer with a certain amount of allocated space */
+void
+ws_buffer_init(Buffer* buffer, size_t space)
+{
+ ws_assert(buffer);
+ if (G_UNLIKELY(!small_buffers)) small_buffers = g_ptr_array_sized_new(1024);
+
+ if (space <= SMALL_BUFFER_SIZE) {
+ if (small_buffers->len > 0) {
+ buffer->data = (uint8_t*) g_ptr_array_remove_index(small_buffers, small_buffers->len - 1);
+ ws_assert(buffer->data);
+ } else {
+ buffer->data = (uint8_t*)g_malloc(SMALL_BUFFER_SIZE);
+ }
+ buffer->allocated = SMALL_BUFFER_SIZE;
+ } else {
+ buffer->data = (uint8_t*)g_malloc(space);
+ buffer->allocated = space;
+ }
+ buffer->start = 0;
+ buffer->first_free = 0;
+}
+
+/* Frees the memory used by a buffer */
+void
+ws_buffer_free(Buffer* buffer)
+{
+ ws_assert(buffer);
+ if (buffer->allocated == SMALL_BUFFER_SIZE) {
+ ws_assert(buffer->data);
+ g_ptr_array_add(small_buffers, buffer->data);
+ } else {
+ g_free(buffer->data);
+ }
+ buffer->allocated = 0;
+ buffer->data = NULL;
+}
+
+/* Assures that there are 'space' bytes at the end of the used space
+ so that another routine can copy directly into the buffer space. After
+ doing that, the routine will also want to run
+ ws_buffer_increase_length(). */
+void
+ws_buffer_assure_space(Buffer* buffer, size_t space)
+{
+ ws_assert(buffer);
+ size_t available_at_end = buffer->allocated - buffer->first_free;
+ size_t space_used;
+ bool space_at_beginning;
+
+ /* If we've got the space already, good! */
+ if (space <= available_at_end) {
+ return;
+ }
+
+ /* Maybe we don't have the space available at the end, but we would
+ if we moved the used space back to the beginning of the
+ allocation. The buffer could have become fragmented through lots
+ of calls to ws_buffer_remove_start(). I'm using buffer->start as the
+ same as 'available_at_start' in this comparison. */
+
+ /* or maybe there's just no more room. */
+
+ space_at_beginning = buffer->start >= space;
+ if (space_at_beginning || buffer->start > 0) {
+ space_used = buffer->first_free - buffer->start;
+ /* this memory copy better be safe for overlapping memory regions! */
+ memmove(buffer->data, buffer->data + buffer->start, space_used);
+ buffer->start = 0;
+ buffer->first_free = space_used;
+ }
+ /*if (buffer->start >= space) {*/
+ if (space_at_beginning) {
+ return;
+ }
+
+ /* We'll allocate more space */
+ buffer->allocated += space + 1024;
+ buffer->data = (uint8_t*)g_realloc(buffer->data, buffer->allocated);
+}
+
+void
+ws_buffer_append(Buffer* buffer, uint8_t *from, size_t bytes)
+{
+ ws_assert(buffer);
+ ws_buffer_assure_space(buffer, bytes);
+ memcpy(buffer->data + buffer->first_free, from, bytes);
+ buffer->first_free += bytes;
+}
+
+void
+ws_buffer_remove_start(Buffer* buffer, size_t bytes)
+{
+ ws_assert(buffer);
+ if (buffer->start + bytes > buffer->first_free) {
+ ws_error("ws_buffer_remove_start trying to remove %" PRIu64 " bytes. s=%" PRIu64 " ff=%" PRIu64 "!\n",
+ (uint64_t)bytes, (uint64_t)buffer->start,
+ (uint64_t)buffer->first_free);
+ /** ws_error() does an abort() and thus never returns **/
+ }
+ buffer->start += bytes;
+
+ if (buffer->start == buffer->first_free) {
+ buffer->start = 0;
+ buffer->first_free = 0;
+ }
+}
+
+
+#ifndef SOME_FUNCTIONS_ARE_DEFINES
+void
+ws_buffer_clean(Buffer* buffer)
+{
+ ws_assert(buffer);
+ ws_buffer_remove_start(buffer, ws_buffer_length(buffer));
+}
+#endif
+
+#ifndef SOME_FUNCTIONS_ARE_DEFINES
+void
+ws_buffer_increase_length(Buffer* buffer, size_t bytes)
+{
+ ws_assert(buffer);
+ buffer->first_free += bytes;
+}
+#endif
+
+#ifndef SOME_FUNCTIONS_ARE_DEFINES
+size_t
+ws_buffer_length(Buffer* buffer)
+{
+ ws_assert(buffer);
+ return buffer->first_free - buffer->start;
+}
+#endif
+
+#ifndef SOME_FUNCTIONS_ARE_DEFINES
+uint8_t *
+ws_buffer_start_ptr(Buffer* buffer)
+{
+ ws_assert(buffer);
+ return buffer->data + buffer->start;
+}
+#endif
+
+#ifndef SOME_FUNCTIONS_ARE_DEFINES
+uint8_t *
+ws_buffer_end_ptr(Buffer* buffer)
+{
+ ws_assert(buffer);
+ return buffer->data + buffer->first_free;
+}
+#endif
+
+#ifndef SOME_FUNCTIONS_ARE_DEFINES
+void
+ws_buffer_append_buffer(Buffer* buffer, Buffer* src_buffer)
+{
+ ws_assert(buffer);
+ ws_buffer_append(buffer, ws_buffer_start_ptr(src_buffer), ws_buffer_length(src_buffer));
+}
+#endif
+
+void
+ws_buffer_cleanup(void)
+{
+ if (small_buffers) {
+ g_ptr_array_set_free_func(small_buffers, g_free);
+ g_ptr_array_free(small_buffers, true);
+ small_buffers = NULL;
+ }
+}
+
+/*
+ * 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:
+ */
diff --git a/wsutil/buffer.h b/wsutil/buffer.h
new file mode 100644
index 00000000..628d324c
--- /dev/null
+++ b/wsutil/buffer.h
@@ -0,0 +1,68 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __W_BUFFER_H__
+#define __W_BUFFER_H__
+
+#include <inttypes.h>
+#include <stddef.h>
+#include "ws_symbol_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define SOME_FUNCTIONS_ARE_DEFINES
+
+typedef struct Buffer {
+ uint8_t *data;
+ size_t allocated;
+ size_t start;
+ size_t first_free;
+} Buffer;
+
+WS_DLL_PUBLIC
+void ws_buffer_init(Buffer* buffer, size_t space);
+WS_DLL_PUBLIC
+void ws_buffer_free(Buffer* buffer);
+WS_DLL_PUBLIC
+void ws_buffer_assure_space(Buffer* buffer, size_t space);
+WS_DLL_PUBLIC
+void ws_buffer_append(Buffer* buffer, uint8_t *from, size_t bytes);
+WS_DLL_PUBLIC
+void ws_buffer_remove_start(Buffer* buffer, size_t bytes);
+WS_DLL_PUBLIC
+void ws_buffer_cleanup(void);
+
+#ifdef SOME_FUNCTIONS_ARE_DEFINES
+# define ws_buffer_clean(buffer) ws_buffer_remove_start((buffer), ws_buffer_length(buffer))
+# define ws_buffer_increase_length(buffer,bytes) (buffer)->first_free += (bytes)
+# define ws_buffer_length(buffer) ((buffer)->first_free - (buffer)->start)
+# define ws_buffer_start_ptr(buffer) ((buffer)->data + (buffer)->start)
+# define ws_buffer_end_ptr(buffer) ((buffer)->data + (buffer)->first_free)
+# define ws_buffer_append_buffer(buffer,src_buffer) ws_buffer_append((buffer), ws_buffer_start_ptr(src_buffer), ws_buffer_length(src_buffer))
+#else
+ WS_DLL_PUBLIC
+ void ws_buffer_clean(Buffer* buffer);
+ WS_DLL_PUBLIC
+ void ws_buffer_increase_length(Buffer* buffer, size_t bytes);
+ WS_DLL_PUBLIC
+ size_t ws_buffer_length(Buffer* buffer);
+ WS_DLL_PUBLIC
+ uint8_t* ws_buffer_start_ptr(Buffer* buffer);
+ WS_DLL_PUBLIC
+ uint8_t* ws_buffer_end_ptr(Buffer* buffer);
+ WS_DLL_PUBLIC
+ void ws_buffer_append_buffer(Buffer* buffer, Buffer* src_buffer);
+#endif
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/wsutil/cfutils.c b/wsutil/cfutils.c
new file mode 100644
index 00000000..5693411a
--- /dev/null
+++ b/wsutil/cfutils.c
@@ -0,0 +1,51 @@
+/* cfutils.c
+ * Routines to work around deficiencies in Core Foundation, such as the
+ * lack of a routine to convert a CFString to a C string of arbitrary
+ * size.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2001 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <wsutil/cfutils.h>
+
+/*
+ * Convert a CFString to a UTF-8-encoded C string; the resulting string
+ * is allocated with g_malloc(). Returns NULL if the conversion fails.
+ */
+char *
+CFString_to_C_string(CFStringRef cfstring)
+{
+ CFIndex string_len;
+ char *string;
+
+ string_len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstring),
+ kCFStringEncodingUTF8);
+ string = (char *)g_malloc(string_len + 1);
+ if (!CFStringGetCString(cfstring, string, string_len + 1,
+ kCFStringEncodingUTF8)) {
+ g_free(string);
+ return NULL;
+ }
+ return string;
+}
+
+/*
+ * 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:
+ */
diff --git a/wsutil/cfutils.h b/wsutil/cfutils.h
new file mode 100644
index 00000000..12cbefef
--- /dev/null
+++ b/wsutil/cfutils.h
@@ -0,0 +1,31 @@
+/** @file
+ * Declarations of routines to work around deficiencies in Core Foundation,
+ * such as the lack of a routine to convert a CFString to a C string of
+ * arbitrary size.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2001 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WSUTIL_CFUTILS_H__
+#define __WSUTIL_CFUTILS_H__
+
+#include "ws_symbol_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * Convert a CFString to a g_malloc()ated C string.
+ */
+WS_DLL_PUBLIC char *CFString_to_C_string(CFStringRef cfstring);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WSUTIL_CFUTILS_H__ */
diff --git a/wsutil/clopts_common.c b/wsutil/clopts_common.c
new file mode 100644
index 00000000..fa17f8e3
--- /dev/null
+++ b/wsutil/clopts_common.c
@@ -0,0 +1,108 @@
+/* clopts_common.c
+ * Handle command-line arguments common to various programs
+ *
+ * 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 "clopts_common.h"
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include <wsutil/strtoi.h>
+#include <wsutil/cmdarg_err.h>
+
+int
+get_natural_int(const char *string, const char *name)
+{
+ int32_t number;
+
+ if (!ws_strtoi32(string, NULL, &number)) {
+ if (errno == EINVAL) {
+ cmdarg_err("The specified %s \"%s\" isn't a decimal number", name, string);
+ exit(1);
+ }
+ if (number < 0) {
+ cmdarg_err("The specified %s \"%s\" is a negative number", name, string);
+ exit(1);
+ }
+ cmdarg_err("The specified %s \"%s\" is too large (greater than %d)",
+ name, string, number);
+ exit(1);
+ }
+ if (number < 0) {
+ cmdarg_err("The specified %s \"%s\" is a negative number", name, string);
+ exit(1);
+ }
+ return (int)number;
+}
+
+int
+get_positive_int(const char *string, const char *name)
+{
+ int number;
+
+ number = get_natural_int(string, name);
+
+ if (number == 0) {
+ cmdarg_err("The specified %s is zero", name);
+ exit(1);
+ }
+
+ return number;
+}
+
+uint32_t
+get_guint32(const char *string, const char *name)
+{
+ uint32_t number;
+
+ if (!ws_strtou32(string, NULL, &number)) {
+ if (errno == EINVAL) {
+ cmdarg_err("The specified %s \"%s\" isn't a decimal number", name, string);
+ exit(1);
+ }
+ cmdarg_err("The specified %s \"%s\" is too large (greater than %d)",
+ name, string, number);
+ exit(1);
+ }
+ return number;
+}
+
+uint32_t
+get_nonzero_guint32(const char *string, const char *name)
+{
+ uint32_t number;
+
+ number = get_guint32(string, name);
+
+ if (number == 0) {
+ cmdarg_err("The specified %s is zero", name);
+ exit(1);
+ }
+
+ return number;
+}
+
+double
+get_positive_double(const char *string, const char *name)
+{
+ double number = g_ascii_strtod(string, NULL);
+
+ if (errno == EINVAL) {
+ cmdarg_err("The specified %s \"%s\" isn't a floating point number", name, string);
+ exit(1);
+ }
+ if (number < 0.0) {
+ cmdarg_err("The specified %s \"%s\" is a negative number", name, string);
+ exit(1);
+ }
+
+ return number;
+}
diff --git a/wsutil/clopts_common.h b/wsutil/clopts_common.h
new file mode 100644
index 00000000..3f8b7f71
--- /dev/null
+++ b/wsutil/clopts_common.h
@@ -0,0 +1,56 @@
+/** @file
+ *
+ * Handle command-line arguments common to various programs
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __CLOPTS_COMMON_H__
+#define __CLOPTS_COMMON_H__
+
+#include <wireshark.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * Long options.
+ * For long options with no corresponding short options, we define values
+ * outside the range of ASCII graphic characters, make that the last
+ * component of the entry for the long option, and have a case for that
+ * option in the switch statement.
+ */
+// Base value for capture related long options
+#define LONGOPT_BASE_CAPTURE 1000
+// Base value for dissector related long options
+#define LONGOPT_BASE_DISSECTOR 2000
+// Base value for application specific long options
+#define LONGOPT_BASE_APPLICATION 3000
+// Base value for GUI specific long options
+#define LONGOPT_BASE_GUI 4000
+
+WS_DLL_PUBLIC int
+get_natural_int(const char *string, const char *name);
+
+WS_DLL_PUBLIC int
+get_positive_int(const char *string, const char *name);
+
+WS_DLL_PUBLIC uint32_t
+get_guint32(const char *string, const char *name);
+
+WS_DLL_PUBLIC uint32_t
+get_nonzero_guint32(const char *string, const char *name);
+
+WS_DLL_PUBLIC double
+get_positive_double(const char *string, const char *name);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __CLOPTS_COMMON_H__ */
diff --git a/wsutil/cmdarg_err.c b/wsutil/cmdarg_err.c
new file mode 100644
index 00000000..85a4d8d4
--- /dev/null
+++ b/wsutil/cmdarg_err.c
@@ -0,0 +1,59 @@
+/* cmdarg_err.c
+ * Routines to report command-line argument errors.
+ *
+ * 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 "cmdarg_err.h"
+
+static void (*print_err)(const char *, va_list ap);
+static void (*print_err_cont)(const char *, va_list ap);
+
+/*
+ * Set the reporting functions for error messages.
+ */
+void
+cmdarg_err_init(void (*err)(const char *, va_list),
+ void (*err_cont)(const char *, va_list))
+{
+ print_err = err;
+ print_err_cont = err_cont;
+}
+
+/*
+ * Report an error in command-line arguments.
+ */
+void
+vcmdarg_err(const char *fmt, va_list ap)
+{
+ print_err(fmt, ap);
+}
+
+void
+cmdarg_err(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ print_err(fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * Report additional information for an error in command-line arguments.
+ */
+void
+cmdarg_err_cont(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ print_err_cont(fmt, ap);
+ va_end(ap);
+}
diff --git a/wsutil/cmdarg_err.h b/wsutil/cmdarg_err.h
new file mode 100644
index 00000000..daa50524
--- /dev/null
+++ b/wsutil/cmdarg_err.h
@@ -0,0 +1,51 @@
+/** @file
+ *
+ * Declarations of routines to report command-line argument errors.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __CMDARG_ERR_H__
+#define __CMDARG_ERR_H__
+
+#include <wireshark.h>
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * Set the reporting functions for error messages.
+ */
+WS_DLL_PUBLIC void
+cmdarg_err_init(void (*err)(const char *, va_list),
+ void (*err_cont)(const char *, va_list));
+
+/*
+ * Report an error in command-line arguments.
+ */
+WS_DLL_PUBLIC void
+vcmdarg_err(const char *fmt, va_list ap)
+ G_GNUC_PRINTF(1, 0);
+
+WS_DLL_PUBLIC void
+cmdarg_err(const char *fmt, ...)
+ G_GNUC_PRINTF(1, 2);
+
+/*
+ * Report additional information for an error in command-line arguments.
+ */
+WS_DLL_PUBLIC void
+cmdarg_err_cont(const char *fmt, ...)
+ G_GNUC_PRINTF(1, 2);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __CMDARG_ERR_H__ */
diff --git a/wsutil/codecs.c b/wsutil/codecs.c
new file mode 100644
index 00000000..857cd33b
--- /dev/null
+++ b/wsutil/codecs.c
@@ -0,0 +1,194 @@
+/* codecs.c
+ * codecs interface 2007 Tomas Kukosa
+ *
+ * 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 "codecs.h"
+
+#include <wsutil/wslog.h>
+#ifdef HAVE_PLUGINS
+#include <wsutil/plugins.h>
+#endif
+
+#ifdef HAVE_PLUGINS
+static plugins_t *libwscodecs_plugins = NULL;
+#endif
+
+static GSList *codecs_plugins = NULL;
+
+#ifdef HAVE_PLUGINS
+void
+codecs_register_plugin(const codecs_plugin *plug)
+{
+ codecs_plugins = g_slist_prepend(codecs_plugins, (codecs_plugin *)plug);
+}
+#else /* HAVE_PLUGINS */
+void
+codecs_register_plugin(const codecs_plugin *plug _U_)
+{
+ ws_warning("codecs_register_plugin: built without support for binary plugins");
+}
+#endif /* HAVE_PLUGINS */
+
+static void
+call_plugin_register_codec_module(void * data, void * user_data _U_)
+{
+ codecs_plugin *plug = (codecs_plugin *)data;
+
+ if (plug->register_codec_module) {
+ plug->register_codec_module();
+ }
+}
+
+
+/*
+ * For all codec plugins, call their register routines.
+ */
+void
+codecs_init(void)
+{
+#ifdef HAVE_PLUGINS
+ libwscodecs_plugins = plugins_init(WS_PLUGIN_CODEC);
+#endif
+ g_slist_foreach(codecs_plugins, call_plugin_register_codec_module, NULL);
+}
+
+void
+codecs_cleanup(void)
+{
+ g_slist_free(codecs_plugins);
+ codecs_plugins = NULL;
+#ifdef HAVE_PLUGINS
+ plugins_cleanup(libwscodecs_plugins);
+ libwscodecs_plugins = NULL;
+#endif
+}
+
+
+struct codec_handle {
+ const char *name;
+ codec_init_fn init_fn;
+ codec_release_fn release_fn;
+ codec_get_channels_fn channels_fn;
+ codec_get_frequency_fn frequency_fn;
+ codec_decode_fn decode_fn;
+};
+
+/*
+ * List of registered codecs.
+ */
+static GHashTable *registered_codecs = NULL;
+
+
+/* Find a registered codec by name. */
+codec_handle_t
+find_codec(const char *name)
+{
+ codec_handle_t ret;
+ char *key = g_ascii_strup(name, -1);
+
+ ret = (registered_codecs) ? (codec_handle_t)g_hash_table_lookup(registered_codecs, key) : NULL;
+ g_free(key);
+ return ret;
+}
+
+/* Register a codec by name. */
+bool
+register_codec(const char *name, codec_init_fn init_fn, codec_release_fn release_fn,
+ codec_get_channels_fn channels_fn, codec_get_frequency_fn frequency_fn,
+ codec_decode_fn decode_fn)
+{
+ struct codec_handle *handle;
+ char *key;
+
+ /* Create our hash table if it doesn't already exist */
+ if (registered_codecs == NULL)
+ registered_codecs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+
+ /* RFC 4855 3. Mapping to SDP Parameters "Note that the payload format
+ * (encoding) names... are commonly shown in upper case. These names
+ * are case-insensitive in both places."
+ */
+ key = g_ascii_strup(name, -1);
+
+ /* Make sure the registration is unique */
+ if (g_hash_table_lookup(registered_codecs, key) != NULL) {
+ g_free(key);
+ return false; /* report an error, or have our caller do it? */
+ }
+
+ handle = g_new(struct codec_handle, 1);
+ handle->name = name;
+ handle->init_fn = init_fn;
+ handle->release_fn = release_fn;
+ handle->channels_fn = channels_fn;
+ handle->frequency_fn = frequency_fn;
+ handle->decode_fn = decode_fn;
+
+ g_hash_table_insert(registered_codecs, (void *)key, (void *) handle);
+ return true;
+}
+
+/* Deregister a codec by name. */
+bool
+deregister_codec(const char *name)
+{
+ bool ret = false;
+ if (registered_codecs) {
+ char *key = g_ascii_strup(name, -1);
+
+ ret = g_hash_table_remove(registered_codecs, key);
+ g_free(key);
+ }
+ return ret;
+}
+
+void *codec_init(codec_handle_t codec, codec_context_t *context)
+{
+ if (!codec) return NULL;
+ return (codec->init_fn)(context);
+}
+
+void codec_release(codec_handle_t codec, codec_context_t *context)
+{
+ if (!codec) return;
+ (codec->release_fn)(context);
+}
+
+unsigned codec_get_channels(codec_handle_t codec, codec_context_t *context)
+{
+ if (!codec) return 0;
+ return (codec->channels_fn)(context);
+}
+
+unsigned codec_get_frequency(codec_handle_t codec, codec_context_t *context)
+{
+ if (!codec) return 0;
+ return (codec->frequency_fn)(context);
+}
+
+size_t codec_decode(codec_handle_t codec, codec_context_t *context, const void *input, size_t inputSizeBytes, void *output, size_t *outputSizeBytes)
+{
+ if (!codec) return 0;
+ return (codec->decode_fn)(context, input, inputSizeBytes, output, outputSizeBytes);
+}
+
+/*
+ * 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/wsutil/codecs.h b/wsutil/codecs.h
new file mode 100644
index 00000000..be340bc7
--- /dev/null
+++ b/wsutil/codecs.h
@@ -0,0 +1,149 @@
+/** @file
+ * codecs interface 2007 Tomas Kukosa
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef _CODECS_H_
+#define _CODECS_H_
+
+#include "ws_symbol_export.h"
+#include "ws_attributes.h"
+
+#include <stdbool.h>
+
+#include "wsutil/wmem/wmem_map.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct {
+ void (*register_codec_module)(void); /* routine to call to register a codec */
+} codecs_plugin;
+
+WS_DLL_PUBLIC void codecs_register_plugin(const codecs_plugin *plug);
+
+/**
+ * For all built-in codecs and codec plugins, call their register routines.
+ */
+WS_DLL_PUBLIC void codecs_init(void);
+
+WS_DLL_PUBLIC void codecs_cleanup(void);
+
+/**
+ * Get compile-time information for libraries used by libwscodecs.
+ */
+WS_DLL_PUBLIC void codec_get_compiled_version_info(GString *str);
+
+struct codec_handle;
+typedef struct codec_handle *codec_handle_t;
+
+typedef struct _codec_context_t {
+ unsigned sample_rate;
+ unsigned channels;
+ wmem_map_t *fmtp_map;
+ void *priv; /* Private state set by the decoder */
+} codec_context_t;
+
+/*****************************************************************************/
+/* Interface which must be implemented by a codec */
+/* Codec decodes bytes to samples. Sample is 2 bytes! Codec writer must
+ * be careful when API refers bytes and when samples and its counts.
+ */
+/*****************************************************************************/
+
+/** Initialize context of codec.
+ * Context can contain any information required by codec to pass between calls
+ * Note: There is just one codec context in runtime therefore no RTP stream
+ * related information should be stored in the context!
+ *
+ * @return Pointer to codec context
+ */
+typedef void *(*codec_init_fn)(codec_context_t *context);
+
+/** Destroy context of codec
+ *
+ * @param context Pointer to codec context
+ */
+typedef void (*codec_release_fn)(codec_context_t *context);
+
+/** Get count of channels provided by the codec
+ *
+ * @param context Pointer to codec context
+ *
+ * @return Count of channels (e.g. 1)
+ */
+typedef unsigned (*codec_get_channels_fn)(codec_context_t *context);
+
+/** Get frequency/rate provided by the codec
+ *
+ * @param context Pointer to codec context
+ *
+ * @return Frequency (e.g. 8000)
+ */
+typedef unsigned (*codec_get_frequency_fn)(codec_context_t *context);
+
+/** Decode one frame of payload
+ * Function is called twice, with different values of parameters:
+ * (1) To query size of required buffer in bytes for decoded samples
+ * pointed by inputBytes:
+ * outputSamples or outputSamplesSize must be set NULL
+ * (2) To decode samples:
+ * outputSamples points to allocated memory, outputSamplesSize is set to
+ * value returned in step (1)
+ *
+ * @param context Pointer to codec context
+ * @param inputBytes Pointer to input frame
+ * @param inputBytesSize Length of input frame in bytes
+ * (count of bytes to decode)
+ * @param outputSamples Pointer to output buffer with samples
+ * @param outputSamplesSize Length of output buffer in bytes (not samples!)
+ * Function can override this value. All codecs set it to same value as it returns in (2) when (2) is called.
+ *
+ * @return Count of reqired bytes (!not samples) to allocate in (1) or
+ * Count of decoded bytes (!not samples) in (2)
+ */
+typedef size_t (*codec_decode_fn)(codec_context_t *context,
+ const void *inputBytes, size_t inputBytesSize,
+ void *outputSamples, size_t *outputSamplesSize);
+
+/*****************************************************************************/
+/* Codec registering interface */
+/*****************************************************************************/
+
+WS_DLL_PUBLIC bool register_codec(const char *name, codec_init_fn init_fn,
+ codec_release_fn release_fn, codec_get_channels_fn channels_fn,
+ codec_get_frequency_fn frequency_fn, codec_decode_fn decode_fn);
+WS_DLL_PUBLIC bool deregister_codec(const char *name);
+WS_DLL_PUBLIC codec_handle_t find_codec(const char *name);
+WS_DLL_PUBLIC void *codec_init(codec_handle_t codec, codec_context_t *context);
+WS_DLL_PUBLIC void codec_release(codec_handle_t codec, codec_context_t *context);
+WS_DLL_PUBLIC unsigned codec_get_channels(codec_handle_t codec, codec_context_t *context);
+WS_DLL_PUBLIC unsigned codec_get_frequency(codec_handle_t codec, codec_context_t *context);
+WS_DLL_PUBLIC size_t codec_decode(codec_handle_t codec, codec_context_t *context,
+ const void *inputBytes, size_t inputBytesSize,
+ void *outputSamples, size_t *outputSamplesSize);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _CODECS_H_ */
+
+/*
+ * 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/wsutil/color.h b/wsutil/color.h
new file mode 100644
index 00000000..794ba054
--- /dev/null
+++ b/wsutil/color.h
@@ -0,0 +1,57 @@
+/** @file
+ *
+ * Definitions for colors
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef __COLOR_H__
+#define __COLOR_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include <inttypes.h>
+
+/*
+ * Data structure holding RGB value for a color, 16 bits per channel.
+ */
+typedef struct {
+ uint16_t red;
+ uint16_t green;
+ uint16_t blue;
+} color_t;
+
+/*
+ * Convert a color_t to a 24-bit RGB value, reducing each channel to
+ * 8 bits and combining them.
+ */
+inline static unsigned int
+color_t_to_rgb(const color_t *color) {
+ return (((color->red >> 8) << 16)
+ | ((color->green >> 8) << 8)
+ | (color->blue >> 8));
+}
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#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/wsutil/console_win32.c b/wsutil/console_win32.c
new file mode 100644
index 00000000..eed18c88
--- /dev/null
+++ b/wsutil/console_win32.c
@@ -0,0 +1,295 @@
+/* console_win32.c
+ * Console support for MSWindows
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2002, Jeffrey C. Foster <jfoste@woodward.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifdef _WIN32
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <wsutil/file_util.h>
+
+#include "console_win32.h"
+
+#include <fcntl.h>
+#include <conio.h>
+#include <windows.h>
+#include <tchar.h>
+
+static bool has_console; /* true if app has console */
+static bool console_wait; /* "Press any key..." */
+static bool stdin_capture = false; /* Don't grab stdin & stdout if true */
+
+/*
+ * Check whether a given standard handle needs to be redirected.
+ *
+ * If you run a Windows-subsystem program from cmd.exe on Windows XP,
+ * and you haven't redirected the handle in question, GetStdHandle()
+ * succeeds (so it doesn't return INVALID_HANDLE_VALUE or NULL), but
+ * GetFile_type fails on the results with ERROR_INVALID_HANDLE.
+ * In that case, redirection to a console is necessary.
+ *
+ * If you run it from the shell prompt in "mintty" in at least some
+ * versions of Cygwin on Windows XP, and you haven't redirected the
+ * handle in question, GetStdHandle() succeeds and returns a handle
+ * that's a pipe or socket; it appears mintty reads from it and outputs
+ * what it reads to the console.
+ */
+static bool
+needs_redirection(int std_handle)
+{
+ HANDLE fd;
+ DWORD handle_type;
+ DWORD error;
+
+ fd = GetStdHandle(std_handle);
+ if (fd == NULL) {
+ /*
+ * No standard handle. According to Microsoft's
+ * documentation for GetStdHandle(), one reason for
+ * this would be that the process is "a service on
+ * an interactive desktop"; I'm not sure whether
+ * such a process should be popping up a console.
+ *
+ * However, it also appears to be the case for
+ * the standard input and standard error, but
+ * *not* the standard output, for something run
+ * with a double-click in Windows Explorer,
+ * sow we'll say it needs redirection.
+ */
+ return true;
+ }
+ if (fd == INVALID_HANDLE_VALUE) {
+ /*
+ * OK, I'm not when this would happen; return
+ * "no redirection" for now.
+ */
+ return false;
+ }
+ handle_type = GetFileType(fd);
+ if (handle_type == FILE_TYPE_UNKNOWN) {
+ error = GetLastError();
+ if (error == ERROR_INVALID_HANDLE) {
+ /*
+ * OK, this appears to be the case where we're
+ * running something in a mode that needs a
+ * console.
+ */
+ return true;
+ }
+ }
+
+ /*
+ * Assume no redirection is needed for all other cases.
+ */
+ return false;
+}
+
+/*
+ * If this application has no console window to which its standard output
+ * would go, create one.
+ */
+void
+create_console(void)
+{
+ bool must_redirect_stdin;
+ bool must_redirect_stdout;
+ bool must_redirect_stderr;
+
+ if (stdin_capture) {
+ /* We've been handed "-i -". Don't mess with stdio. */
+ return;
+ }
+
+ if (has_console) {
+ return;
+ }
+
+ /* Are the standard input, output, and error invalid handles? */
+ must_redirect_stdin = needs_redirection(STD_INPUT_HANDLE);
+ must_redirect_stdout = needs_redirection(STD_OUTPUT_HANDLE);
+ must_redirect_stderr = needs_redirection(STD_ERROR_HANDLE);
+
+ /* If none of them are invalid, we don't need to do anything. */
+ if (!must_redirect_stdin && !must_redirect_stdout && !must_redirect_stderr)
+ return;
+
+ /* OK, at least one of them needs to be redirected to a console;
+ try to attach to the parent process's console and, if that fails,
+ try to create one. */
+ /*
+ * See if we have an existing console (i.e. we were run from a
+ * command prompt).
+ */
+ if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
+ /* Probably not, as we couldn't attach to the parent process's
+ console.
+ Try to create a console.
+
+ According to a comment on
+
+http://msdn.microsoft.com/en-us/library/windows/desktop/ms681952(v=vs.85).aspx
+
+ (which now redirects to a docs.microsoft.com page that is
+ devoid of comments, and which is not available on the
+ Wayback Machine)
+
+ and according to
+
+http://connect.microsoft.com/VisualStudio/feedback/details/689696/installing-security-update-kb2507938-prevents-console-allocation
+
+ (which has disappeared, and isn't available on the Wayback
+ Machine)
+
+ and
+
+https://answers.microsoft.com/en-us/windows/forum/windows_xp-windows_update/kb2567680-andor-kb2507938-breaks-attachconsole-api/e8191280-2d49-4be4-9918-18486fba0afa
+
+even a failed attempt to attach to another process's console
+will cause subsequent AllocConsole() calls to fail, possibly due
+to bugs introduced by a security patch. To work around this, we
+do a FreeConsole() first. */
+ FreeConsole();
+ if (AllocConsole()) {
+ /* That succeeded. */
+ console_wait = true;
+ SetConsoleTitle(_T("Wireshark Debug Console"));
+ } else {
+ /* On Windows XP, this still fails; FreeConsole() apparently
+ doesn't clear the state, as it does on Windows 7. */
+ return; /* couldn't create console */
+ }
+ }
+
+ if (must_redirect_stdin)
+ ws_freopen("CONIN$", "r", stdin);
+ if (must_redirect_stdout) {
+ ws_freopen("CONOUT$", "w", stdout);
+ fprintf(stdout, "\n");
+ }
+ if (must_redirect_stderr) {
+ ws_freopen("CONOUT$", "w", stderr);
+ fprintf(stderr, "\n");
+ }
+
+ /* Now register "destroy_console()" as a routine to be called just
+ before the application exits, so that we can destroy the console
+ after the user has typed a key (so that the console doesn't just
+ disappear out from under them, giving the user no chance to see
+ the message(s) we put in there). */
+ atexit(destroy_console);
+
+ /* Well, we have a console now. */
+ has_console = true;
+}
+
+void
+restore_pipes(void)
+{
+ bool must_redirect_stdin;
+ bool must_redirect_stdout;
+ bool must_redirect_stderr;
+
+ HANDLE fd;
+
+ if (stdin_capture) {
+ /* We've been handed "-i -". Don't mess with stdio. */
+ return;
+ }
+
+ if (has_console) {
+ return;
+ }
+
+ /* Are the standard input, output, and error invalid handles? */
+ must_redirect_stdin = needs_redirection(STD_INPUT_HANDLE);
+ must_redirect_stdout = needs_redirection(STD_OUTPUT_HANDLE);
+ must_redirect_stderr = needs_redirection(STD_ERROR_HANDLE);
+
+ /* If none of them are invalid, we don't need to do anything. */
+ if (!must_redirect_stdin && !must_redirect_stdout && !must_redirect_stderr)
+ return;
+
+ if (!must_redirect_stdin)
+ fd = GetStdHandle(STD_INPUT_HANDLE);
+
+ /* OK, at least one of them needs to be redirected to a console;
+ try to attach to the parent process's console and, if that fails,
+ cleanup and return. */
+ /*
+ * See if we have an existing console (i.e. we were run from a
+ * command prompt).
+ */
+ if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
+ FreeConsole();
+ return; /* No parent - cleanup and exit */
+ }
+
+ if (must_redirect_stdin) {
+ ws_freopen("CONIN$", "r", stdin);
+ } else {
+ SetStdHandle(STD_INPUT_HANDLE, fd);
+ }
+ if (must_redirect_stdout) {
+ ws_freopen("CONOUT$", "w", stdout);
+ fprintf(stdout, "\n");
+ }
+ if (must_redirect_stderr) {
+ ws_freopen("CONOUT$", "w", stderr);
+ fprintf(stderr, "\n");
+ }
+
+ /* Now register "destroy_console()" as a routine to be called just
+ before the application exits, so that we can destroy the console
+ after the user has typed a key (so that the console doesn't just
+ disappear out from under them, giving the user no chance to see
+ the message(s) we put in there). */
+ atexit(destroy_console);
+
+ /* Well, we have a console now. */
+ has_console = true;
+}
+
+void
+destroy_console(void)
+{
+ if (console_wait) {
+ printf("\n\nPress any key to exit\n");
+ _getch();
+ }
+ FreeConsole();
+}
+
+void
+set_console_wait(bool set_console_wait)
+{
+ console_wait = set_console_wait;
+}
+
+bool
+get_console_wait(void)
+{
+ return console_wait;
+}
+
+void
+set_stdin_capture(bool set_stdin_capture)
+{
+ stdin_capture = set_stdin_capture;
+}
+
+bool
+get_stdin_capture(void)
+{
+ return stdin_capture;
+}
+
+#endif /* _WIN32 */
diff --git a/wsutil/console_win32.h b/wsutil/console_win32.h
new file mode 100644
index 00000000..b1020c1a
--- /dev/null
+++ b/wsutil/console_win32.h
@@ -0,0 +1,73 @@
+/** @file
+ *
+ * Console support for MSWindows
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2002, Jeffrey C. Foster <jfoste@woodward.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __CONSOLE_WIN32_H__
+#define __CONSOLE_WIN32_H__
+
+#include <wireshark.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifdef _WIN32
+
+/** @file
+ * Win32 specific console.
+ */
+
+/** Create Windows console.
+ *
+ */
+WS_DLL_PUBLIC
+void create_console(void);
+
+/** Connect to stdio if available.
+ *
+ */
+WS_DLL_PUBLIC
+void restore_pipes(void);
+
+/** Destroy Windows console.
+ *
+ */
+WS_DLL_PUBLIC
+void destroy_console(void);
+
+/** Set console wait. GTK+ only.
+ * @param console_wait set/no set console wait
+ */
+WS_DLL_PUBLIC
+void set_console_wait(bool console_wait);
+/** get console wait
+ * @return set/no set console wait
+ */
+WS_DLL_PUBLIC
+bool get_console_wait(void);
+
+/** Set stdin capture.
+ * @param console_wait set/no stdin_capture
+ */
+WS_DLL_PUBLIC
+void set_stdin_capture(bool set_stdin_capture);
+
+/** get stdin caputre
+ * @return set/no set stdin_capture
+ */
+WS_DLL_PUBLIC
+bool get_stdin_capture(void);
+#endif/* _WIN32 */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __CONSOLE_WIN32_H__ */
diff --git a/wsutil/cpu_info.c b/wsutil/cpu_info.c
new file mode 100644
index 00000000..f9e6aa25
--- /dev/null
+++ b/wsutil/cpu_info.c
@@ -0,0 +1,491 @@
+/* cpu_info.c
+ * Routines to report CPU information
+ *
+ * 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 <wsutil/cpu_info.h>
+
+#include <string.h>
+
+#include <wsutil/ws_cpuid.h>
+#include <wsutil/file_util.h>
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
+ #define HAVE_SYSCTL
+#elif defined(sun) || defined(__sun)
+ #define HAVE_SYSINFO
+#endif
+
+#if defined(_WIN32)
+ #include <windows.h>
+#elif defined(HAVE_SYSCTL)
+ #include <sys/types.h>
+ #include <sys/sysctl.h>
+#elif defined(HAVE_SYSINFO)
+ #include <sys/systeminfo.h>
+#endif
+
+/*
+ * Functions used for the GTree we use to keep a list of *unique*
+ * model strings.
+ */
+static int
+compare_model_names(gconstpointer a, gconstpointer b, void * user_data _U_)
+{
+ return strcmp((const char *)a, (const char *)b);
+}
+
+struct string_info {
+ GString *str;
+ const char *sep;
+};
+
+static gboolean
+add_model_name_to_string(void * key, void * value _U_,
+ void * data)
+{
+ struct string_info *info = (struct string_info *)data;
+
+ /* Separate this from the previous entry, if necessary. */
+ if (info->sep != NULL)
+ g_string_append(info->str, info->sep);
+
+ /* Now add the model name. */
+ g_string_append(info->str, g_strstrip((char *)key));
+
+ /*
+ * There will *definitely* need to be a separator for any subsequent
+ * model string.
+ */
+ info->sep = ", ";
+
+ /* Keep going. */
+ return false;
+}
+
+/*
+ * Get the CPU info, and append it to the GString
+ *
+ * On at least some OSes, there's a call that will return this information
+ * for all CPU types for which the OS determines that information, not just
+ * x86 processors with CPUID and the brand string. On those OSes, we use
+ * that.
+ *
+ * On other OSes, we use ws_cpuid(), which will fail unconditionally on
+ * non-x86 CPUs.
+ */
+void
+get_cpu_info(GString *str)
+{
+ GTree *model_names = g_tree_new_full(compare_model_names, NULL, g_free, NULL);
+
+#if defined(__linux__)
+ /*
+ * We scan /proc/cpuinfo looking for lines that begins with
+ * "model name\t: ", and extract what comes after that prefix.
+ *
+ * /proc/cpuinfo can report information about multiple "CPU"s.
+ * A "CPU" appears to be a CPU core, so this treats a multi-core
+ * chip as multiple CPUs (which is arguably should), but doesn't
+ * appear to treat a multi-threaded core as multiple CPUs.
+ *
+ * So we accumulate a table of *multiple* CPU strings, saving
+ * one copy of each unique string, and glue them together at
+ * the end. We use a GTree for this.
+ *
+ * We test for Linux first, so that, even if you're on a Linux
+ * that supports sysctl(), we don't use it, we scan /proc/cpuinfo,
+ * as that's the right way to do this.
+ */
+ FILE *proc_cpuinfo;
+
+ proc_cpuinfo = ws_fopen("/proc/cpuinfo", "r");
+ if (proc_cpuinfo == NULL) {
+ /* Just give up. */
+ g_tree_destroy(model_names);
+ return;
+ }
+
+ char *line = NULL;
+ size_t linecap = 0;
+ static const char prefix[] = "model name\t: ";
+ #define PREFIX_STRLEN (sizeof prefix - 1)
+ ssize_t linelen;
+
+ /*
+ * Read lines from /proc/cpuinfo; stop when we either hit an EOF
+ * or get an error.
+ */
+ for (;;) {
+ linelen = getline(&line, &linecap, proc_cpuinfo);
+ if (linelen == -1) {
+ /* EOF or error; just stop. */
+ break;
+ }
+ /* Remove trailing newline. */
+ if (linelen != 0)
+ line[linelen - 1] = '\0';
+ if (strncmp(line, prefix, PREFIX_STRLEN) == 0) {
+ /* OK, we have a model name. */
+ char *model_name;
+
+ /* Get everything after the prefix. */
+ model_name = g_strdup(line + PREFIX_STRLEN);
+
+ /*
+ * Add an entry to the tree with the model name as key and
+ * a null value. There will only be one such entry in the
+ * tree; if there's already such an entry, it will be left
+ * alone, and model_name will be freed, otherwise a new
+ * node will be created using model_name as the key.
+ *
+ * Thus, we don't free model_name; either it will be freed
+ * for us, or it will be used in the tree and freed when we
+ * free the tree.
+ */
+ g_tree_insert(model_names, model_name, NULL);
+ }
+ }
+
+ fclose(proc_cpuinfo);
+#define xx_free free /* hack so checkAPIs doesn't complain */
+ xx_free(line); /* yes, free(), as getline() mallocates it */
+#elif defined(_WIN32)
+ /*
+ * They're in the Registry. (Isn't everything?)
+ */
+ HKEY processors_key;
+
+ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+ L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor",
+ 0, KEY_READ, &processors_key) != ERROR_SUCCESS) {
+ /* Just give up. */
+ g_tree_destroy(model_names);
+ return;
+ }
+
+ /*
+ * The processors appear under that key. Enumerate all the keys
+ * under it.
+ */
+ DWORD num_subkeys;
+ DWORD max_subkey_len;
+ wchar_t *subkey_buf;
+
+ /*
+ * How many subkeys are there, and what's the biggest subkey size?
+ *
+ * I assume that when the documentation says that some number is
+ * in units of "Unicode characters" they mean "units of elements
+ * of UTF-16 characters", i.e. "units of 2-octet items".
+ */
+ if (RegQueryInfoKeyW(processors_key, NULL, NULL, NULL, &num_subkeys,
+ &max_subkey_len, NULL, NULL, NULL, NULL, NULL,
+ NULL) != ERROR_SUCCESS) {
+ /* Just give up. */
+ g_tree_destroy(model_names);
+ return;
+ }
+
+ /*
+ * max_subkey_len does not count the trailing '\0'. Add it.
+ */
+ max_subkey_len++;
+
+ /*
+ * Allocate a buffer for the subkey.
+ */
+ subkey_buf = (wchar_t *)g_malloc(max_subkey_len * sizeof (wchar_t));
+ if (subkey_buf == NULL) {
+ /* Just give up. */
+ g_tree_destroy(model_names);
+ return;
+ }
+
+ for (DWORD processor_index = 0; processor_index < num_subkeys;
+ processor_index++) {
+ /*
+ * The documentation says that this is "in characters"; I'm
+ * assuming, for now, that they mean "Unicode characters",
+ * meaning "2-octet items".
+ */
+ DWORD subkey_bufsize = max_subkey_len;
+ if (RegEnumKeyExW(processors_key, processor_index, subkey_buf,
+ &subkey_bufsize, NULL, NULL, NULL,
+ NULL) != ERROR_SUCCESS) {
+ /* Just exit the loop. */
+ break;
+ }
+
+ /*
+ * Get the length of processor name string for this processor.
+ *
+ * That's the "ProcessorNameString" value for the subkey of
+ * processors_key with the name in subkey_buf.
+ *
+ * It's a string, so only allow REG_SZ values.
+ */
+ DWORD model_name_bufsize;
+
+ model_name_bufsize = 0;
+ if (RegGetValueW(processors_key, subkey_buf, L"ProcessorNameString",
+ RRF_RT_REG_SZ, NULL, NULL,
+ &model_name_bufsize) != ERROR_SUCCESS) {
+ /* Just exit the loop. */
+ break;
+ }
+
+ /*
+ * Allocate a buffer for the string, as UTF-16.
+ * The retrieved length includes the terminating '\0'.
+ */
+ wchar_t *model_name_wchar = g_malloc(model_name_bufsize);
+ if (RegGetValueW(processors_key, subkey_buf, L"ProcessorNameString",
+ RRF_RT_REG_SZ, NULL, model_name_wchar,
+ &model_name_bufsize) != ERROR_SUCCESS) {
+ /* Just exit the loop. */
+ g_free(model_name_wchar);
+ break;
+ }
+
+ /* Convert it to UTF-8. */
+ char *model_name = g_utf16_to_utf8(model_name_wchar, -1, NULL, NULL, NULL);
+ g_free(model_name_wchar);
+
+ /*
+ * Add an entry to the tree with the model name as key and
+ * a null value. There will only be one such entry in the
+ * tree; if there's already such an entry, it will be left
+ * alone, and model_name will be freed, otherwise a new
+ * node will be created using model_name as the key.
+ *
+ * Thus, we don't free model_name; either it will be freed
+ * for us, or it will be used in the tree and freed when we
+ * free the tree.
+ */
+ g_tree_insert(model_names, model_name, NULL);
+ }
+
+ g_free(subkey_buf);
+
+ /*
+ * Close the registry key.
+ */
+ RegCloseKey(processors_key);
+#elif defined(HAVE_SYSCTL)
+ /*
+ * Fetch the string using the appropriate sysctl.
+ */
+ size_t model_name_len;
+ char *model_name;
+ #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
+ /*
+ * Thanks, OpenBSD guys, for not having APIs to map MIB names to
+ * MIB values! Just consruct the MIB entry directly.
+ *
+ * We also do that for FreeBSD and DragonFly BSD, because we can.
+ *
+ * FreeBSD appears to support this for x86, PowerPC/Power ISA, and
+ * Arm. OpenBSD appears to support this for a number of
+ * architectures. DragonFly BSD appears to support it only for
+ * x86, but I think they only run on x86-64 now, and may never
+ * have run on anything non-x86.
+ */
+ int mib[2] = { CTL_HW, HW_MODEL };
+ size_t miblen = 2;
+ #else
+ /* These require a lookup, as they don't have #defines. */
+ #if defined(__APPLE__) /* Darwin */
+ /*
+ * The code seems to support this on both x86 and ARM.
+ */
+ #define BRAND_STRING_SYSCTL "machdep.cpu.brand_string"
+ #define MIB_DEPTH 3
+ #elif defined(__NetBSD__)
+ /*
+ * XXX - the "highly portable Unix-like Open Source operating
+ * system" that "is available for a wide range of platforms"
+ * doesn't seem to support this except on x86, and doesn't
+ * seem to support any other MIB for, for example, ARM64.
+ *
+ * Maybe someday, so use it anyway.
+ */
+ #define BRAND_STRING_SYSCTL "machdep.cpu_brand"
+ #define MIB_DEPTH 2
+ #endif
+ int mib[MIB_DEPTH];
+ size_t miblen = MIB_DEPTH;
+
+ /* Look up the sysctl name and get the MIB. */
+ if (sysctlnametomib(BRAND_STRING_SYSCTL, mib, &miblen) == -1) {
+ /*
+ * Either there's no such string or something else went wrong.
+ * Just give up.
+ */
+ g_tree_destroy(model_names);
+ return;
+ }
+ #endif
+ if (sysctl(mib, (u_int)miblen, NULL, &model_name_len, NULL, 0) == -1) {
+ /*
+ * Either there's no such string or something else went wrong.
+ * Just give up.
+ */
+ g_tree_destroy(model_names);
+ return;
+ }
+ model_name = g_malloc(model_name_len);
+ if (sysctl(mib, (u_int)miblen, model_name, &model_name_len, NULL, 0) == -1) {
+ /*
+ * Either there's no such string or something else went wrong.
+ * Just give up.
+ */
+ g_free(model_name);
+ g_tree_destroy(model_names);
+ return;
+ }
+ g_tree_insert(model_names, model_name, NULL);
+#elif defined(HAVE_SYSINFO) && defined(SI_CPUBRAND)
+ /*
+ * Solaris. Use sysinfo() with SI_CPUBRAND; the documentation
+ * indicates that it works on SPARC as well as x86.
+ *
+ * Unfortunately, SI_CPUBRAND seems to be a recent addition, so
+ * older versions of Solaris - dating back to some versions of
+ * 11.3 - don't have it.
+ */
+ int model_name_len;
+ char *model_name;
+
+ /* How big is the model name? */
+ model_name_len = sysinfo(SI_CPUBRAND, NULL, 0);
+ if (model_name_len == -1) {
+ g_tree_destroy(model_names);
+ return;
+ }
+ model_name = g_malloc(model_name_len);
+ if (sysinfo(SI_CPUBRAND, model_name, model_name_len) == -1) {
+ g_tree_destroy(model_names);
+ return;
+ }
+ g_tree_insert(model_names, model_name, NULL);
+#else
+ /*
+ * OS for which we don't support the "get the CPU type" call; we
+ * use ws_cpuid(), which uses CPUID on x86 and doesn't get any
+ * information for other instruction sets.
+ */
+ uint32_t CPUInfo[4];
+ char CPUBrandString[0x40];
+ unsigned nExIds;
+
+ /*
+ * Calling ws_cpuid with 0x80000000 as the selector argument, i.e.
+ * executing a cpuid instruction with EAX equal to 0x80000000 and
+ * ECX equal to 0, gets the number of valid extended IDs.
+ */
+ if (!ws_cpuid(CPUInfo, 0x80000000)) {
+ g_tree_destroy(model_names);
+ return;
+ }
+
+ nExIds = CPUInfo[0];
+
+ if (nExIds<0x80000005) {
+ g_tree_destroy(model_names);
+ return;
+ }
+
+ memset(CPUBrandString, 0, sizeof(CPUBrandString));
+
+ /* Interpret CPU brand string */
+ ws_cpuid(CPUInfo, 0x80000002);
+ memcpy(CPUBrandString, CPUInfo, sizeof(CPUInfo));
+ ws_cpuid(CPUInfo, 0x80000003);
+ memcpy(CPUBrandString + 16, CPUInfo, sizeof(CPUInfo));
+ ws_cpuid(CPUInfo, 0x80000004);
+ memcpy(CPUBrandString + 32, CPUInfo, sizeof(CPUInfo));
+
+ model_name = g_strdup(g_strstrip(CPUBrandString));
+ g_tree_insert(model_names, model_name, NULL);
+#endif
+
+ int num_model_names = g_tree_nnodes(model_names);
+
+ if (num_model_names > 0) {
+ /*
+ * We have at least one model name, so add the name(s) to
+ * the string.
+ *
+ * If the string is not empty, separate the name(s) from
+ * what precedes it.
+ */
+ if (str->len > 0)
+ g_string_append(str, ", with ");
+
+ if (num_model_names > 1) {
+ /*
+ * There's more than one, so put the list inside curly
+ * brackets.
+ */
+ g_string_append(str, "{ ");
+ }
+
+ /* Iterate over the tree, adding model names to the string. */
+ struct string_info info;
+ info.str = str;
+ info.sep = NULL;
+ g_tree_foreach(model_names, add_model_name_to_string, &info);
+
+ if (num_model_names > 1) {
+ /*
+ * There's more than one, so put the list inside curly
+ * brackets.
+ */
+ g_string_append(str, " }");
+ }
+ }
+
+ /* We're done; get rid of the tree. */
+ g_tree_destroy(model_names);
+
+ /*
+ * We do this on all OSes and instruction sets, so that we don't
+ * have to figure out how to dredge the "do we have SSE 4.2?"
+ * information from whatever source provides it in the OS on
+ * x86 processors. We already have ws_cpuid_sse42() (which we
+ * use to determine whether to use SSE 4.2 code to scan buffers
+ * for strings), so use that; it always returns "false" on non-x86
+ * processors.
+ *
+ * If you have multiple CPUs, some of which support it and some
+ * of which don't, I'm not sure we can guarantee that buffer
+ * scanning will work if, for example, the scanning code gets
+ * preempted while running on an SSE-4.2-capable CPU and, when
+ * it gets rescheduled, gets rescheduled on a non-SSE-4.2-capable
+ * CPU and tries to continue the SSE 4.2-based scan. So we don't
+ * worry about that case; constructing a CPU string is the *least*
+ * of our worries in that case.
+ */
+ if (ws_cpuid_sse42())
+ g_string_append(str, " (with SSE4.2)");
+}
+
+/*
+ * 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/wsutil/cpu_info.h b/wsutil/cpu_info.h
new file mode 100644
index 00000000..e2e011a2
--- /dev/null
+++ b/wsutil/cpu_info.h
@@ -0,0 +1,26 @@
+/** @file
+ * Declarations of routines to report CPU information
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WSUTIL_CPU_INFO_H__
+#define __WSUTIL_CPU_INFO_H__
+
+#include <wireshark.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+WS_DLL_PUBLIC void get_cpu_info(GString *str);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WSUTIL_CPU_INFO_H__ */
diff --git a/wsutil/crash_info.c b/wsutil/crash_info.c
new file mode 100644
index 00000000..bf1fc70a
--- /dev/null
+++ b/wsutil/crash_info.c
@@ -0,0 +1,178 @@
+/* crash_info.c
+ * Routines to try to provide more useful information in crash dumps.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2006 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include "crash_info.h"
+
+#ifdef __APPLE__
+/*
+ * Copyright 2005-2012 Apple Inc. All rights reserved.
+ *
+ * IMPORTANT: This Apple software is supplied to you by Apple Computer,
+ * Inc. ("Apple") in consideration of your agreement to the following
+ * terms, and your use, installation, modification or redistribution of
+ * this Apple software constitutes acceptance of these terms. If you do
+ * not agree with these terms, please do not use, install, modify or
+ * redistribute this Apple software.
+ *
+ * In consideration of your agreement to abide by the following terms, and
+ * subject to these terms, Apple grants you a personal, non-exclusive
+ * license, under Apple's copyrights in this original Apple software (the
+ * "Apple Software"), to use, reproduce, modify and redistribute the Apple
+ * Software, with or without modifications, in source and/or binary forms;
+ * provided that if you redistribute the Apple Software in its entirety and
+ * without modifications, you must retain this notice and the following
+ * text and disclaimers in all such redistributions of the Apple Software.
+ * Neither the name, trademarks, service marks or logos of Apple Computer,
+ * Inc. may be used to endorse or promote products derived from the Apple
+ * Software without specific prior written permission from Apple. Except
+ * as expressly stated in this notice, no other rights or licenses, express
+ * or implied, are granted by Apple herein, including but not limited to
+ * any patent rights that may be infringed by your derivative works or by
+ * other works in which the Apple Software may be incorporated.
+ *
+ * The Apple Software is provided by Apple on an "AS IS" basis. APPLE
+ * MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
+ * THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
+ * OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+ *
+ * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
+ * MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
+ * AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
+ * STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+/*
+ * This used to be the way to add an application-specific string to
+ * crash dumps; see
+ *
+ * http://www.allocinit.net/blog/2008/01/04/application-specific-information-in-leopard-crash-reports/
+ *
+ * It still appears to work as of OS X 10.8 (Mountain Lion).
+ */
+__private_extern__ char *__crashreporter_info__ = NULL;
+
+#if 0
+/*
+ * And this appears to be the new way to do it, as of Lion.
+ * However, if we do both, we get the message twice, so we're
+ * not doing this one, for now.
+ *
+ * This code was lifted from SVN trunk CUPS.
+ */
+#define _crc_make_getter(attr, type) (type)(gCRAnnotations.attr)
+#define _crc_make_setter(attr, arg) (gCRAnnotations.attr = (uint64_t)(unsigned long)(arg))
+#define CRASH_REPORTER_CLIENT_HIDDEN __attribute__((visibility("hidden")))
+#define CRASHREPORTER_ANNOTATIONS_VERSION 4
+#define CRASHREPORTER_ANNOTATIONS_SECTION "__crash_info"
+
+/*
+ * Yes, these are all 64-bit, even on 32-bit platforms.
+ *
+ * version is presumably the version of this structure.
+ *
+ * message and message2 are reported, one after the other,
+ * under "Application Specific Information".
+ *
+ * signature_string is reported under "Application Specific
+ * Signatures".
+ *
+ * backtrace is reported under "Application Specific Backtrace".
+ *
+ * Dunno which versions are supported by which versions of macOS.
+ */
+struct crashreporter_annotations_t {
+ uint64_t version; /* unsigned long */
+ uint64_t message; /* char * */
+ uint64_t signature_string; /* char * */
+ uint64_t backtrace; /* char * */
+ uint64_t message2; /* char * */
+ uint64_t thread; /* uint64_t */
+ uint64_t dialog_mode; /* unsigned int */
+};
+
+CRASH_REPORTER_CLIENT_HIDDEN
+struct crashreporter_annotations_t gCRAnnotations
+ __attribute__((section("__DATA," CRASHREPORTER_ANNOTATIONS_SECTION))) = {
+ CRASHREPORTER_ANNOTATIONS_VERSION, /* version */
+ 0, /* message */
+ 0, /* signature_string */
+ 0, /* backtrace */
+ 0, /* message2 */
+ 0, /* thread */
+ 0 /* dialog_mode */
+};
+
+#define CRSetCrashLogMessage(m) _crc_make_setter(message, m)
+#endif /* 0 */
+
+void
+ws_vadd_crash_info(const char *fmt, va_list ap)
+{
+ char *m, *old_info, *new_info;
+
+ m = ws_strdup_vprintf(fmt, ap);
+ if (__crashreporter_info__ == NULL)
+ __crashreporter_info__ = m;
+ else {
+ old_info = __crashreporter_info__;
+ new_info = ws_strdup_printf("%s\n%s", old_info, m);
+ g_free(m);
+ __crashreporter_info__ = new_info;
+ g_free(old_info);
+ }
+}
+
+#else /* __APPLE__ */
+/*
+ * Perhaps Google Breakpad (http://code.google.com/p/google-breakpad/) or
+ * other options listed at
+ * http://stackoverflow.com/questions/7631908/library-for-logging-call-stack-at-runtime-windows-linux
+ * ?
+ */
+void
+ws_vadd_crash_info(const char *fmt _U_, va_list ap _U_)
+{
+}
+#endif /* __APPLE__ */
+
+void
+ws_add_crash_info(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ ws_vadd_crash_info(fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * 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:
+ */
diff --git a/wsutil/crash_info.h b/wsutil/crash_info.h
new file mode 100644
index 00000000..37840067
--- /dev/null
+++ b/wsutil/crash_info.h
@@ -0,0 +1,29 @@
+/** @file
+ * Routines to try to provide more useful information in crash dumps.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2006 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __CRASH_INFO_H__
+#define __CRASH_INFO_H__
+
+#include <wireshark.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+WS_DLL_PUBLIC void ws_vadd_crash_info(const char *fmt, va_list ap);
+
+WS_DLL_PUBLIC void ws_add_crash_info(const char *fmt, ...)
+ G_GNUC_PRINTF(1,2);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __CRASH_INFO_H__ */
diff --git a/wsutil/crc10.c b/wsutil/crc10.c
new file mode 100644
index 00000000..ecbf5f23
--- /dev/null
+++ b/wsutil/crc10.c
@@ -0,0 +1,83 @@
+/*
+ * crc10.c
+ * 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 "crc10.h"
+
+/*
+ * Charles Michael Heard's CRC-10 code, from
+ *
+ * http://web.archive.org/web/20061005231950/http://cell-relay.indiana.edu/cell-relay/publications/software/CRC/crc10.html
+ *
+ * with the CRC table initialized with values computed by
+ * his "gen_byte_crc10_table()" routine, rather than by calling that
+ * routine at run time, and with various data type cleanups.
+ */
+static const uint16_t byte_crc10_table[256] = {
+ 0x0000, 0x0233, 0x0255, 0x0066, 0x0299, 0x00aa, 0x00cc, 0x02ff,
+ 0x0301, 0x0132, 0x0154, 0x0367, 0x0198, 0x03ab, 0x03cd, 0x01fe,
+ 0x0031, 0x0202, 0x0264, 0x0057, 0x02a8, 0x009b, 0x00fd, 0x02ce,
+ 0x0330, 0x0103, 0x0165, 0x0356, 0x01a9, 0x039a, 0x03fc, 0x01cf,
+ 0x0062, 0x0251, 0x0237, 0x0004, 0x02fb, 0x00c8, 0x00ae, 0x029d,
+ 0x0363, 0x0150, 0x0136, 0x0305, 0x01fa, 0x03c9, 0x03af, 0x019c,
+ 0x0053, 0x0260, 0x0206, 0x0035, 0x02ca, 0x00f9, 0x009f, 0x02ac,
+ 0x0352, 0x0161, 0x0107, 0x0334, 0x01cb, 0x03f8, 0x039e, 0x01ad,
+ 0x00c4, 0x02f7, 0x0291, 0x00a2, 0x025d, 0x006e, 0x0008, 0x023b,
+ 0x03c5, 0x01f6, 0x0190, 0x03a3, 0x015c, 0x036f, 0x0309, 0x013a,
+ 0x00f5, 0x02c6, 0x02a0, 0x0093, 0x026c, 0x005f, 0x0039, 0x020a,
+ 0x03f4, 0x01c7, 0x01a1, 0x0392, 0x016d, 0x035e, 0x0338, 0x010b,
+ 0x00a6, 0x0295, 0x02f3, 0x00c0, 0x023f, 0x000c, 0x006a, 0x0259,
+ 0x03a7, 0x0194, 0x01f2, 0x03c1, 0x013e, 0x030d, 0x036b, 0x0158,
+ 0x0097, 0x02a4, 0x02c2, 0x00f1, 0x020e, 0x003d, 0x005b, 0x0268,
+ 0x0396, 0x01a5, 0x01c3, 0x03f0, 0x010f, 0x033c, 0x035a, 0x0169,
+ 0x0188, 0x03bb, 0x03dd, 0x01ee, 0x0311, 0x0122, 0x0144, 0x0377,
+ 0x0289, 0x00ba, 0x00dc, 0x02ef, 0x0010, 0x0223, 0x0245, 0x0076,
+ 0x01b9, 0x038a, 0x03ec, 0x01df, 0x0320, 0x0113, 0x0175, 0x0346,
+ 0x02b8, 0x008b, 0x00ed, 0x02de, 0x0021, 0x0212, 0x0274, 0x0047,
+ 0x01ea, 0x03d9, 0x03bf, 0x018c, 0x0373, 0x0140, 0x0126, 0x0315,
+ 0x02eb, 0x00d8, 0x00be, 0x028d, 0x0072, 0x0241, 0x0227, 0x0014,
+ 0x01db, 0x03e8, 0x038e, 0x01bd, 0x0342, 0x0171, 0x0117, 0x0324,
+ 0x02da, 0x00e9, 0x008f, 0x02bc, 0x0043, 0x0270, 0x0216, 0x0025,
+ 0x014c, 0x037f, 0x0319, 0x012a, 0x03d5, 0x01e6, 0x0180, 0x03b3,
+ 0x024d, 0x007e, 0x0018, 0x022b, 0x00d4, 0x02e7, 0x0281, 0x00b2,
+ 0x017d, 0x034e, 0x0328, 0x011b, 0x03e4, 0x01d7, 0x01b1, 0x0382,
+ 0x027c, 0x004f, 0x0029, 0x021a, 0x00e5, 0x02d6, 0x02b0, 0x0083,
+ 0x012e, 0x031d, 0x037b, 0x0148, 0x03b7, 0x0184, 0x01e2, 0x03d1,
+ 0x022f, 0x001c, 0x007a, 0x0249, 0x00b6, 0x0285, 0x02e3, 0x00d0,
+ 0x011f, 0x032c, 0x034a, 0x0179, 0x0386, 0x01b5, 0x01d3, 0x03e0,
+ 0x021e, 0x002d, 0x004b, 0x0278, 0x0087, 0x02b4, 0x02d2, 0x00e1
+};
+
+/* Update the data block's CRC-10 remainder one byte at a time */
+uint16_t
+update_crc10_by_bytes(uint16_t crc10_accum, const uint8_t *data_blk_ptr,
+ int data_blk_size)
+{
+ register int i;
+
+ for (i = 0; i < data_blk_size; i++) {
+ crc10_accum = ((crc10_accum << 8) & 0x3ff)
+ ^ byte_crc10_table[( crc10_accum >> 2) & 0xff]
+ ^ *data_blk_ptr++;
+ }
+ return crc10_accum;
+}
+
+/*
+ * 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:
+ */
diff --git a/wsutil/crc10.h b/wsutil/crc10.h
new file mode 100644
index 00000000..9ff5322a
--- /dev/null
+++ b/wsutil/crc10.h
@@ -0,0 +1,18 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __CRC10_H__
+#define __CRC10_H__
+
+#include <wireshark.h>
+
+/* Update the data block's CRC-10 remainder one byte at a time */
+WS_DLL_PUBLIC uint16_t update_crc10_by_bytes(uint16_t crc10, const uint8_t *data_blk_ptr, int data_blk_size);
+
+#endif /* __CRC10_H__ */
diff --git a/wsutil/crc11.c b/wsutil/crc11.c
new file mode 100644
index 00000000..d7ebb1cb
--- /dev/null
+++ b/wsutil/crc11.c
@@ -0,0 +1,80 @@
+/*
+ * crc11.c
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+
+#include <wsutil/crc11.h>
+
+/**
+ * Functions and types for CRC checks.
+ *
+ * Generated on Tue Aug 7 15:45:57 2012,
+ * by pycrc v0.7.10, http://www.tty1.net/pycrc/
+ * using the configuration:
+ * Width = 11
+ * Poly = 0x307
+ * XorIn = 0x000
+ * ReflectIn = False
+ * XorOut = 0x000
+ * ReflectOut = False
+ * Algorithm = table-driven
+ *****************************************************************************/
+/**
+ * Static table used for the table_driven implementation.
+ *****************************************************************************/
+static const uint16_t crc11_table_307_noreflect_noxor[256] = {
+ 0x000, 0x307, 0x60e, 0x509, 0x71b, 0x41c, 0x115, 0x212, 0x531, 0x636, 0x33f, 0x038, 0x22a, 0x12d, 0x424, 0x723,
+ 0x165, 0x262, 0x76b, 0x46c, 0x67e, 0x579, 0x070, 0x377, 0x454, 0x753, 0x25a, 0x15d, 0x34f, 0x048, 0x541, 0x646,
+ 0x2ca, 0x1cd, 0x4c4, 0x7c3, 0x5d1, 0x6d6, 0x3df, 0x0d8, 0x7fb, 0x4fc, 0x1f5, 0x2f2, 0x0e0, 0x3e7, 0x6ee, 0x5e9,
+ 0x3af, 0x0a8, 0x5a1, 0x6a6, 0x4b4, 0x7b3, 0x2ba, 0x1bd, 0x69e, 0x599, 0x090, 0x397, 0x185, 0x282, 0x78b, 0x48c,
+ 0x594, 0x693, 0x39a, 0x09d, 0x28f, 0x188, 0x481, 0x786, 0x0a5, 0x3a2, 0x6ab, 0x5ac, 0x7be, 0x4b9, 0x1b0, 0x2b7,
+ 0x4f1, 0x7f6, 0x2ff, 0x1f8, 0x3ea, 0x0ed, 0x5e4, 0x6e3, 0x1c0, 0x2c7, 0x7ce, 0x4c9, 0x6db, 0x5dc, 0x0d5, 0x3d2,
+ 0x75e, 0x459, 0x150, 0x257, 0x045, 0x342, 0x64b, 0x54c, 0x26f, 0x168, 0x461, 0x766, 0x574, 0x673, 0x37a, 0x07d,
+ 0x63b, 0x53c, 0x035, 0x332, 0x120, 0x227, 0x72e, 0x429, 0x30a, 0x00d, 0x504, 0x603, 0x411, 0x716, 0x21f, 0x118,
+ 0x02f, 0x328, 0x621, 0x526, 0x734, 0x433, 0x13a, 0x23d, 0x51e, 0x619, 0x310, 0x017, 0x205, 0x102, 0x40b, 0x70c,
+ 0x14a, 0x24d, 0x744, 0x443, 0x651, 0x556, 0x05f, 0x358, 0x47b, 0x77c, 0x275, 0x172, 0x360, 0x067, 0x56e, 0x669,
+ 0x2e5, 0x1e2, 0x4eb, 0x7ec, 0x5fe, 0x6f9, 0x3f0, 0x0f7, 0x7d4, 0x4d3, 0x1da, 0x2dd, 0x0cf, 0x3c8, 0x6c1, 0x5c6,
+ 0x380, 0x087, 0x58e, 0x689, 0x49b, 0x79c, 0x295, 0x192, 0x6b1, 0x5b6, 0x0bf, 0x3b8, 0x1aa, 0x2ad, 0x7a4, 0x4a3,
+ 0x5bb, 0x6bc, 0x3b5, 0x0b2, 0x2a0, 0x1a7, 0x4ae, 0x7a9, 0x08a, 0x38d, 0x684, 0x583, 0x791, 0x496, 0x19f, 0x298,
+ 0x4de, 0x7d9, 0x2d0, 0x1d7, 0x3c5, 0x0c2, 0x5cb, 0x6cc, 0x1ef, 0x2e8, 0x7e1, 0x4e6, 0x6f4, 0x5f3, 0x0fa, 0x3fd,
+ 0x771, 0x476, 0x17f, 0x278, 0x06a, 0x36d, 0x664, 0x563, 0x240, 0x147, 0x44e, 0x749, 0x55b, 0x65c, 0x355, 0x052,
+ 0x614, 0x513, 0x01a, 0x31d, 0x10f, 0x208, 0x701, 0x406, 0x325, 0x022, 0x52b, 0x62c, 0x43e, 0x739, 0x230, 0x137
+};
+/**
+ * Update the crc value with new data.
+ *
+ * \param data Pointer to a buffer of \a data_len bytes.
+ * \param data_len Number of bytes in the \a data buffer.
+ * \return The updated crc value.
+ *****************************************************************************/
+uint16_t crc11_307_noreflect_noxor(const uint8_t *data, uint64_t data_len)
+{
+ uint16_t crc = 0;
+ unsigned tbl_idx;
+
+ while (data_len--) {
+ tbl_idx = ((crc >> 3) ^ *data) & 0xff;
+ crc = (crc11_table_307_noreflect_noxor[tbl_idx] ^ (crc << 8)) & 0x7ff;
+
+ data++;
+ }
+ return crc & 0x7ff;
+}
+
+/*
+ * 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/wsutil/crc11.h b/wsutil/crc11.h
new file mode 100644
index 00000000..3c48dd5c
--- /dev/null
+++ b/wsutil/crc11.h
@@ -0,0 +1,41 @@
+/** @file
+ * http://www.tty1.net/pycrc/faq_en.html#code-ownership
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __CRC11_____H__
+
+#include <stdint.h>
+
+#include "ws_symbol_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/**
+ * Functions and types for CRC checks.
+ *
+ * Generated on Tue Aug 7 15:45:57 2012,
+ * by pycrc v0.7.10, http://www.tty1.net/pycrc/
+ * using the configuration:
+ * Width = 11
+ * Poly = 0x307
+ * XorIn = 0x000
+ * ReflectIn = False
+ * XorOut = 0x000
+ * ReflectOut = False
+ * Algorithm = table-driven
+ *****************************************************************************/
+WS_DLL_PUBLIC
+uint16_t crc11_307_noreflect_noxor(const uint8_t *data, uint64_t data_len);
+
+#ifdef __cplusplus
+} /* closing brace for extern "C" */
+#endif
+
+#endif /*__CRC11_____H__*/
diff --git a/wsutil/crc16-plain.c b/wsutil/crc16-plain.c
new file mode 100644
index 00000000..732f636a
--- /dev/null
+++ b/wsutil/crc16-plain.c
@@ -0,0 +1,194 @@
+/*
+ * crc16-plain.c
+ * http://www.tty1.net/pycrc/faq_en.html#code-ownership
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/**
+ * \file crc16-plain.c
+ * Functions and types for CRC checks.
+ *
+ * Generated on Wed Mar 18 14:12:09 2009,
+ * by pycrc v0.7, http://www.tty1.net/pycrc/
+ * using the configuration:
+ * Width = 16
+ * Poly = 0x8005
+ * XorIn = 0x0000
+ * ReflectIn = True
+ * XorOut = 0x0000
+ * ReflectOut = True
+ * Algorithm = table-driven
+ * Direct = True
+ *
+ * Modified 2009-03-16 not to include <stdint.h> as our Win32 environment
+ * appears not to have it; we're using GLib types, instead.
+ * Modified 2023-06-28 to use C99 types.
+ *****************************************************************************/
+#include "wsutil/crc16-plain.h"
+
+/**
+ * Static table used for the table_driven implementation.
+ *****************************************************************************/
+static const crc16_plain_t crc_table_reflected[256] = {
+ 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
+ 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
+ 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
+ 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
+ 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
+ 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
+ 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
+ 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
+ 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
+ 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
+ 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
+ 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
+ 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
+ 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
+ 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
+ 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
+ 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
+ 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
+ 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
+ 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
+ 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
+ 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
+ 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
+ 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
+ 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
+ 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
+ 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
+ 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
+ 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
+ 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
+ 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
+ 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040
+};
+
+/**
+ * Reflect all bits of a \a data word of \a data_len bytes.
+ *
+ * \param data The data word to be reflected.
+ * \param data_len The width of \a data expressed in number of bits.
+ * \return The reflected data.
+ *****************************************************************************/
+long crc16_plain_reflect(long data, size_t data_len)
+{
+ unsigned int i;
+ long ret;
+
+ ret = data & 0x01;
+ for (i = 1; i < data_len; i++)
+ {
+ data >>= 1;
+ ret = (ret << 1) | (data & 0x01);
+ }
+ return ret;
+}
+
+
+/**
+ * Update the crc value with new data.
+ *
+ * \param crc The current crc value.
+ * \param data Pointer to a buffer of \a data_len bytes.
+ * \param data_len Number of bytes in the \a data buffer.
+ * \return The updated crc value.
+ *****************************************************************************/
+crc16_plain_t crc16_plain_update(crc16_plain_t crc, const unsigned char *data, size_t data_len)
+{
+ unsigned int tbl_idx;
+
+ while (data_len--) {
+ tbl_idx = (crc ^ *data) & 0xff;
+ crc = (crc_table_reflected[tbl_idx] ^ (crc >> 8)) & 0xffff;
+
+ data++;
+ }
+ return crc & 0xffff;
+}
+
+/* Generated on Tue Jul 24 09:08:46 2012,
+ * by pycrc v0.7.10, http://www.tty1.net/pycrc/
+ * using the configuration:
+ * Width = 16
+ * Poly = 0x8005
+ * XorIn = 0x0000
+ * ReflectIn = False
+ * XorOut = 0x0000
+ * ReflectOut = False
+ * Algorithm = table-driven
+ *****************************************************************************/
+static const uint16_t crc16_table_8005_noreflect_noxor[256] = {
+ 0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011,
+ 0x8033, 0x0036, 0x003c, 0x8039, 0x0028, 0x802d, 0x8027, 0x0022,
+ 0x8063, 0x0066, 0x006c, 0x8069, 0x0078, 0x807d, 0x8077, 0x0072,
+ 0x0050, 0x8055, 0x805f, 0x005a, 0x804b, 0x004e, 0x0044, 0x8041,
+ 0x80c3, 0x00c6, 0x00cc, 0x80c9, 0x00d8, 0x80dd, 0x80d7, 0x00d2,
+ 0x00f0, 0x80f5, 0x80ff, 0x00fa, 0x80eb, 0x00ee, 0x00e4, 0x80e1,
+ 0x00a0, 0x80a5, 0x80af, 0x00aa, 0x80bb, 0x00be, 0x00b4, 0x80b1,
+ 0x8093, 0x0096, 0x009c, 0x8099, 0x0088, 0x808d, 0x8087, 0x0082,
+ 0x8183, 0x0186, 0x018c, 0x8189, 0x0198, 0x819d, 0x8197, 0x0192,
+ 0x01b0, 0x81b5, 0x81bf, 0x01ba, 0x81ab, 0x01ae, 0x01a4, 0x81a1,
+ 0x01e0, 0x81e5, 0x81ef, 0x01ea, 0x81fb, 0x01fe, 0x01f4, 0x81f1,
+ 0x81d3, 0x01d6, 0x01dc, 0x81d9, 0x01c8, 0x81cd, 0x81c7, 0x01c2,
+ 0x0140, 0x8145, 0x814f, 0x014a, 0x815b, 0x015e, 0x0154, 0x8151,
+ 0x8173, 0x0176, 0x017c, 0x8179, 0x0168, 0x816d, 0x8167, 0x0162,
+ 0x8123, 0x0126, 0x012c, 0x8129, 0x0138, 0x813d, 0x8137, 0x0132,
+ 0x0110, 0x8115, 0x811f, 0x011a, 0x810b, 0x010e, 0x0104, 0x8101,
+ 0x8303, 0x0306, 0x030c, 0x8309, 0x0318, 0x831d, 0x8317, 0x0312,
+ 0x0330, 0x8335, 0x833f, 0x033a, 0x832b, 0x032e, 0x0324, 0x8321,
+ 0x0360, 0x8365, 0x836f, 0x036a, 0x837b, 0x037e, 0x0374, 0x8371,
+ 0x8353, 0x0356, 0x035c, 0x8359, 0x0348, 0x834d, 0x8347, 0x0342,
+ 0x03c0, 0x83c5, 0x83cf, 0x03ca, 0x83db, 0x03de, 0x03d4, 0x83d1,
+ 0x83f3, 0x03f6, 0x03fc, 0x83f9, 0x03e8, 0x83ed, 0x83e7, 0x03e2,
+ 0x83a3, 0x03a6, 0x03ac, 0x83a9, 0x03b8, 0x83bd, 0x83b7, 0x03b2,
+ 0x0390, 0x8395, 0x839f, 0x039a, 0x838b, 0x038e, 0x0384, 0x8381,
+ 0x0280, 0x8285, 0x828f, 0x028a, 0x829b, 0x029e, 0x0294, 0x8291,
+ 0x82b3, 0x02b6, 0x02bc, 0x82b9, 0x02a8, 0x82ad, 0x82a7, 0x02a2,
+ 0x82e3, 0x02e6, 0x02ec, 0x82e9, 0x02f8, 0x82fd, 0x82f7, 0x02f2,
+ 0x02d0, 0x82d5, 0x82df, 0x02da, 0x82cb, 0x02ce, 0x02c4, 0x82c1,
+ 0x8243, 0x0246, 0x024c, 0x8249, 0x0258, 0x825d, 0x8257, 0x0252,
+ 0x0270, 0x8275, 0x827f, 0x027a, 0x826b, 0x026e, 0x0264, 0x8261,
+ 0x0220, 0x8225, 0x822f, 0x022a, 0x823b, 0x023e, 0x0234, 0x8231,
+ 0x8213, 0x0216, 0x021c, 0x8219, 0x0208, 0x820d, 0x8207, 0x0202
+};
+
+/**
+ * Calculate the crc-16 (x^16 + x^15 + x^2 + 1) value for data. Note that this
+ * CRC is not equal to crc16_plain.
+ *
+ * \param data Pointer to a buffer of \a data_len bytes.
+ * \param data_len Number of bytes in the \a data buffer.
+ * \return The crc value.
+ *****************************************************************************/
+uint16_t crc16_8005_noreflect_noxor(const uint8_t *data, uint64_t data_len)
+{
+ unsigned tbl_idx;
+ uint16_t crc = 0;
+
+ while (data_len--) {
+ tbl_idx = ((crc >> 8) ^ *data) & 0xff;
+ crc = (crc16_table_8005_noreflect_noxor[tbl_idx] ^ (crc << 8)) & 0xffff;
+
+ data++;
+ }
+ return crc & 0xffff;
+}
+
+/*
+ * 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/wsutil/crc16-plain.h b/wsutil/crc16-plain.h
new file mode 100644
index 00000000..0e1eab6f
--- /dev/null
+++ b/wsutil/crc16-plain.h
@@ -0,0 +1,121 @@
+/** @file
+ * http://www.tty1.net/pycrc/faq_en.html#code-ownership
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/**
+ * \file crc16-plain.h
+ * Functions and types for CRC checks.
+ *
+ * Generated on Wed Mar 18 14:12:15 2009,
+ * by pycrc v0.7, http://www.tty1.net/pycrc/
+ * using the configuration:
+ * Width = 16
+ * Poly = 0x8005
+ * XorIn = 0x0000
+ * ReflectIn = True
+ * XorOut = 0x0000
+ * ReflectOut = True
+ * Algorithm = table-driven
+ * Direct = True
+ *
+ * Modified 2009-03-16 not to include <stdint.h> as our Win32 environment
+ * appears not to have it; we're using GLib types, instead.
+ *****************************************************************************/
+#ifndef __CRC____PLAIN_H__
+#define __CRC____PLAIN_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "ws_symbol_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * The definition of the used algorithm.
+ *****************************************************************************/
+#define CRC_ALGO_TABLE_DRIVEN 1
+
+/**
+ * The type of the CRC values.
+ *
+ * This type must be big enough to contain at least 16 bits.
+ *****************************************************************************/
+typedef uint16_t crc16_plain_t;
+
+/**
+ * Reflect all bits of a \a data word of \a data_len bytes.
+ *
+ * \param data The data word to be reflected.
+ * \param data_len The width of \a data expressed in number of bits.
+ * \return The reflected data.
+ *****************************************************************************/
+long crc16_plain_reflect(long data, size_t data_len);
+
+/**
+ * Calculate the initial crc value.
+ *
+ * \return The initial crc value.
+ *****************************************************************************/
+static inline crc16_plain_t crc16_plain_init(void)
+{
+ return 0x0000;
+}
+
+/**
+ * Update the crc value with new data.
+ *
+ * \param crc The current crc value.
+ * \param data Pointer to a buffer of \a data_len bytes.
+ * \param data_len Number of bytes in the \a data buffer.
+ * \return The updated crc value.
+ *****************************************************************************/
+WS_DLL_PUBLIC
+crc16_plain_t crc16_plain_update(crc16_plain_t crc, const unsigned char *data, size_t data_len);
+
+/**
+ * Calculate the final crc value.
+ *
+ * \param crc The current crc value.
+ * \return The final crc value.
+ *****************************************************************************/
+static inline crc16_plain_t crc16_plain_finalize(crc16_plain_t crc)
+{
+ return crc ^ 0x0000;
+}
+
+/* Generated on Tue Jul 24 09:08:46 2012,
+ * by pycrc v0.7.10, http://www.tty1.net/pycrc/
+ * using the configuration:
+ * Width = 16
+ * Poly = 0x8005
+ * XorIn = 0x0000
+ * ReflectIn = False
+ * XorOut = 0x0000
+ * ReflectOut = False
+ * Algorithm = table-driven
+ *
+ * Calculate the crc-16 (x^16 + x^15 + x^2 + 1) value for data. Note that this
+ * CRC is not equal to crc16_plain.
+ *
+ * \param data Pointer to a buffer of \a data_len bytes.
+ * \param data_len Number of bytes in the \a data buffer.
+ * \return The crc value.
+ *****************************************************************************/
+WS_DLL_PUBLIC
+uint16_t crc16_8005_noreflect_noxor(const uint8_t *data, uint64_t data_len);
+
+
+#ifdef __cplusplus
+} /* closing brace for extern "C" */
+#endif
+
+#endif /* __CRC____PLAIN_H__ */
diff --git a/wsutil/crc16.c b/wsutil/crc16.c
new file mode 100644
index 00000000..dfb2e2d6
--- /dev/null
+++ b/wsutil/crc16.c
@@ -0,0 +1,483 @@
+/* crc16.c
+ * CRC-16 routine
+ *
+ * 2004 Richard van der Hoff <richardv@mxtelecom.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * References:
+ * "A Painless Guide to CRC Error Detection Algorithms", Ross Williams
+ * http://www.repairfaq.org/filipg/LINK/F_crc_v3.html
+ *
+ * ITU-T Recommendation V.42 (2002), "Error-Correcting Procedures for
+ * DCEs using asynchronous-to-synchronous conversion", Para. 8.1.1.6.1
+ */
+
+#include "config.h"
+
+#include <wsutil/crc16.h>
+
+/*****************************************************************/
+
+/*
+ * Table for the CCITT/ITU/CRC-16 16-bit CRC
+ *
+ * Polynomial is
+ *
+ * x^16 + x^12 + x^5 + 1
+ */
+
+/* */
+/* CRC LOOKUP TABLE */
+/* ================ */
+/* The following CRC lookup table was generated automagically */
+/* by the Rocksoft^tm Model CRC Algorithm Table Generation */
+/* Program V1.0 using the following model parameters: */
+/* */
+/* Width : 2 bytes. */
+/* Poly : 0x1021 */
+/* Reverse : true. */
+/* */
+/* For more information on the Rocksoft^tm Model CRC Algorithm, */
+/* see the document titled "A Painless Guide to CRC Error */
+/* Detection Algorithms" by Ross Williams. See */
+/* */
+/* http://www.ross.net/crc/crcpaper.html */
+/* */
+/* which links to a text version and an HTML-but-not-all-on-one- */
+/* page version, or various HTML-all-on-one-page versions such */
+/* as the one at */
+/* */
+/* http://www.geocities.com/SiliconValley/Pines/8659/crc.htm */
+/* */
+/* (search for the title to find others). */
+/* */
+/*****************************************************************/
+
+static const unsigned crc16_ccitt_table_reverse[256] =
+{
+ 0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF,
+ 0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,
+ 0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E,
+ 0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876,
+ 0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD,
+ 0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5,
+ 0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C,
+ 0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974,
+ 0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB,
+ 0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3,
+ 0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A,
+ 0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72,
+ 0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9,
+ 0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1,
+ 0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738,
+ 0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70,
+ 0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7,
+ 0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF,
+ 0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036,
+ 0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E,
+ 0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5,
+ 0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD,
+ 0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134,
+ 0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C,
+ 0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3,
+ 0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB,
+ 0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232,
+ 0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A,
+ 0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1,
+ 0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9,
+ 0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330,
+ 0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78
+};
+
+/* Same as above, only without reverse (Reverse=false) */
+static const unsigned crc16_ccitt_table[256] =
+{
+ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
+ 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
+ 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
+ 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
+ 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
+ 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
+ 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
+ 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
+ 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
+ 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
+ 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
+ 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
+ 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
+ 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
+ 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
+ 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
+ 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
+ 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
+ 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
+ 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
+ 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
+ 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+ 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
+ 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
+ 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
+ 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
+ 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
+ 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
+ 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
+ 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
+ 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
+ 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
+};
+
+/* This table was compiled using the polynom 0x5935 */
+static const unsigned crc16_precompiled_5935[256] =
+{
+ 0x0000, 0x5935, 0xB26A, 0xEB5F, 0x3DE1, 0x64D4, 0x8F8B, 0xD6BE,
+ 0x7BC2, 0x22F7, 0xC9A8, 0x909D, 0x4623, 0x1F16, 0xF449, 0xAD7C,
+ 0xF784, 0xAEB1, 0x45EE, 0x1CDB, 0xCA65, 0x9350, 0x780F, 0x213A,
+ 0x8C46, 0xD573, 0x3E2C, 0x6719, 0xB1A7, 0xE892, 0x03CD, 0x5AF8,
+ 0xB63D, 0xEF08, 0x0457, 0x5D62, 0x8BDC, 0xD2E9, 0x39B6, 0x6083,
+ 0xCDFF, 0x94CA, 0x7F95, 0x26A0, 0xF01E, 0xA92B, 0x4274, 0x1B41,
+ 0x41B9, 0x188C, 0xF3D3, 0xAAE6, 0x7C58, 0x256D, 0xCE32, 0x9707,
+ 0x3A7B, 0x634E, 0x8811, 0xD124, 0x079A, 0x5EAF, 0xB5F0, 0xECC5,
+ 0x354F, 0x6C7A, 0x8725, 0xDE10, 0x08AE, 0x519B, 0xBAC4, 0xE3F1,
+ 0x4E8D, 0x17B8, 0xFCE7, 0xA5D2, 0x736C, 0x2A59, 0xC106, 0x9833,
+ 0xC2CB, 0x9BFE, 0x70A1, 0x2994, 0xFF2A, 0xA61F, 0x4D40, 0x1475,
+ 0xB909, 0xE03C, 0x0B63, 0x5256, 0x84E8, 0xDDDD, 0x3682, 0x6FB7,
+ 0x8372, 0xDA47, 0x3118, 0x682D, 0xBE93, 0xE7A6, 0x0CF9, 0x55CC,
+ 0xF8B0, 0xA185, 0x4ADA, 0x13EF, 0xC551, 0x9C64, 0x773B, 0x2E0E,
+ 0x74F6, 0x2DC3, 0xC69C, 0x9FA9, 0x4917, 0x1022, 0xFB7D, 0xA248,
+ 0x0F34, 0x5601, 0xBD5E, 0xE46B, 0x32D5, 0x6BE0, 0x80BF, 0xD98A,
+ 0x6A9E, 0x33AB, 0xD8F4, 0x81C1, 0x577F, 0x0E4A, 0xE515, 0xBC20,
+ 0x115C, 0x4869, 0xA336, 0xFA03, 0x2CBD, 0x7588, 0x9ED7, 0xC7E2,
+ 0x9D1A, 0xC42F, 0x2F70, 0x7645, 0xA0FB, 0xF9CE, 0x1291, 0x4BA4,
+ 0xE6D8, 0xBFED, 0x54B2, 0x0D87, 0xDB39, 0x820C, 0x6953, 0x3066,
+ 0xDCA3, 0x8596, 0x6EC9, 0x37FC, 0xE142, 0xB877, 0x5328, 0x0A1D,
+ 0xA761, 0xFE54, 0x150B, 0x4C3E, 0x9A80, 0xC3B5, 0x28EA, 0x71DF,
+ 0x2B27, 0x7212, 0x994D, 0xC078, 0x16C6, 0x4FF3, 0xA4AC, 0xFD99,
+ 0x50E5, 0x09D0, 0xE28F, 0xBBBA, 0x6D04, 0x3431, 0xDF6E, 0x865B,
+ 0x5FD1, 0x06E4, 0xEDBB, 0xB48E, 0x6230, 0x3B05, 0xD05A, 0x896F,
+ 0x2413, 0x7D26, 0x9679, 0xCF4C, 0x19F2, 0x40C7, 0xAB98, 0xF2AD,
+ 0xA855, 0xF160, 0x1A3F, 0x430A, 0x95B4, 0xCC81, 0x27DE, 0x7EEB,
+ 0xD397, 0x8AA2, 0x61FD, 0x38C8, 0xEE76, 0xB743, 0x5C1C, 0x0529,
+ 0xE9EC, 0xB0D9, 0x5B86, 0x02B3, 0xD40D, 0x8D38, 0x6667, 0x3F52,
+ 0x922E, 0xCB1B, 0x2044, 0x7971, 0xAFCF, 0xF6FA, 0x1DA5, 0x4490,
+ 0x1E68, 0x475D, 0xAC02, 0xF537, 0x2389, 0x7ABC, 0x91E3, 0xC8D6,
+ 0x65AA, 0x3C9F, 0xD7C0, 0x8EF5, 0x584B, 0x017E, 0xEA21, 0xB314
+};
+
+/* This table was compiled using the polynom 0x755B */
+static const unsigned crc16_precompiled_755B[] =
+{
+ 0x0000, 0x755b, 0xeab6, 0x9fed, 0xa037, 0xd56c, 0x4a81, 0x3fda, /* 0x00 */
+ 0x3535, 0x406e, 0xdf83, 0xaad8, 0x9502, 0xe059, 0x7fb4, 0x0aef, /* 0x08 */
+ 0x6a6a, 0x1f31, 0x80dc, 0xf587, 0xca5d, 0xbf06, 0x20eb, 0x55b0, /* 0x10 */
+ 0x5f5f, 0x2a04, 0xb5e9, 0xc0b2, 0xff68, 0x8a33, 0x15de, 0x6085, /* 0x18 */
+ 0xd4d4, 0xa18f, 0x3e62, 0x4b39, 0x74e3, 0x01b8, 0x9e55, 0xeb0e, /* 0x20 */
+ 0xe1e1, 0x94ba, 0x0b57, 0x7e0c, 0x41d6, 0x348d, 0xab60, 0xde3b, /* 0x28 */
+ 0xbebe, 0xcbe5, 0x5408, 0x2153, 0x1e89, 0x6bd2, 0xf43f, 0x8164, /* 0x30 */
+ 0x8b8b, 0xfed0, 0x613d, 0x1466, 0x2bbc, 0x5ee7, 0xc10a, 0xb451, /* 0x38 */
+ 0xdcf3, 0xa9a8, 0x3645, 0x431e, 0x7cc4, 0x099f, 0x9672, 0xe329, /* 0x40 */
+ 0xe9c6, 0x9c9d, 0x0370, 0x762b, 0x49f1, 0x3caa, 0xa347, 0xd61c, /* 0x48 */
+ 0xb699, 0xc3c2, 0x5c2f, 0x2974, 0x16ae, 0x63f5, 0xfc18, 0x8943, /* 0x50 */
+ 0x83ac, 0xf6f7, 0x691a, 0x1c41, 0x239b, 0x56c0, 0xc92d, 0xbc76, /* 0x58 */
+ 0x0827, 0x7d7c, 0xe291, 0x97ca, 0xa810, 0xdd4b, 0x42a6, 0x37fd, /* 0x60 */
+ 0x3d12, 0x4849, 0xd7a4, 0xa2ff, 0x9d25, 0xe87e, 0x7793, 0x02c8, /* 0x68 */
+ 0x624d, 0x1716, 0x88fb, 0xfda0, 0xc27a, 0xb721, 0x28cc, 0x5d97, /* 0x70 */
+ 0x5778, 0x2223, 0xbdce, 0xc895, 0xf74f, 0x8214, 0x1df9, 0x68a2, /* 0x78 */
+ 0xccbd, 0xb9e6, 0x260b, 0x5350, 0x6c8a, 0x19d1, 0x863c, 0xf367, /* 0x80 */
+ 0xf988, 0x8cd3, 0x133e, 0x6665, 0x59bf, 0x2ce4, 0xb309, 0xc652, /* 0x88 */
+ 0xa6d7, 0xd38c, 0x4c61, 0x393a, 0x06e0, 0x73bb, 0xec56, 0x990d, /* 0x90 */
+ 0x93e2, 0xe6b9, 0x7954, 0x0c0f, 0x33d5, 0x468e, 0xd963, 0xac38, /* 0x98 */
+ 0x1869, 0x6d32, 0xf2df, 0x8784, 0xb85e, 0xcd05, 0x52e8, 0x27b3, /* 0xA0 */
+ 0x2d5c, 0x5807, 0xc7ea, 0xb2b1, 0x8d6b, 0xf830, 0x67dd, 0x1286, /* 0xA8 */
+ 0x7203, 0x0758, 0x98b5, 0xedee, 0xd234, 0xa76f, 0x3882, 0x4dd9, /* 0xB0 */
+ 0x4736, 0x326d, 0xad80, 0xd8db, 0xe701, 0x925a, 0x0db7, 0x78ec, /* 0xB8 */
+ 0x104e, 0x6515, 0xfaf8, 0x8fa3, 0xb079, 0xc522, 0x5acf, 0x2f94, /* 0xC0 */
+ 0x257b, 0x5020, 0xcfcd, 0xba96, 0x854c, 0xf017, 0x6ffa, 0x1aa1, /* 0xC8 */
+ 0x7a24, 0x0f7f, 0x9092, 0xe5c9, 0xda13, 0xaf48, 0x30a5, 0x45fe, /* 0xD0 */
+ 0x4f11, 0x3a4a, 0xa5a7, 0xd0fc, 0xef26, 0x9a7d, 0x0590, 0x70cb, /* 0xD8 */
+ 0xc49a, 0xb1c1, 0x2e2c, 0x5b77, 0x64ad, 0x11f6, 0x8e1b, 0xfb40, /* 0xE0 */
+ 0xf1af, 0x84f4, 0x1b19, 0x6e42, 0x5198, 0x24c3, 0xbb2e, 0xce75, /* 0xE8 */
+ 0xaef0, 0xdbab, 0x4446, 0x311d, 0x0ec7, 0x7b9c, 0xe471, 0x912a, /* 0xF0 */
+ 0x9bc5, 0xee9e, 0x7173, 0x0428, 0x3bf2, 0x4ea9, 0xd144, 0xa41f /* 0xF8 */
+};
+
+/* This table was compiled using the polynom: 0x9949 */
+static const unsigned crc16_precompiled_9949_reverse[] =
+{
+ 0x0000, 0x0ED2, 0x1DA4, 0x1376, 0x3B48, 0x359A, 0x26EC, 0x283E,
+ 0x7690, 0x7842, 0x6B34, 0x65E6, 0x4DD8, 0x430A, 0x507C, 0x5EAE,
+ 0xED20, 0xE3F2, 0xF084, 0xFE56, 0xD668, 0xD8BA, 0xCBCC, 0xC51E,
+ 0x9BB0, 0x9562, 0x8614, 0x88C6, 0xA0F8, 0xAE2A, 0xBD5C, 0xB38E,
+ 0xFF73, 0xF1A1, 0xE2D7, 0xEC05, 0xC43B, 0xCAE9, 0xD99F, 0xD74D,
+ 0x89E3, 0x8731, 0x9447, 0x9A95, 0xB2AB, 0xBC79, 0xAF0F, 0xA1DD,
+ 0x1253, 0x1C81, 0x0FF7, 0x0125, 0x291B, 0x27C9, 0x34BF, 0x3A6D,
+ 0x64C3, 0x6A11, 0x7967, 0x77B5, 0x5F8B, 0x5159, 0x422F, 0x4CFD,
+ 0xDBD5, 0xD507, 0xC671, 0xC8A3, 0xE09D, 0xEE4F, 0xFD39, 0xF3EB,
+ 0xAD45, 0xA397, 0xB0E1, 0xBE33, 0x960D, 0x98DF, 0x8BA9, 0x857B,
+ 0x36F5, 0x3827, 0x2B51, 0x2583, 0x0DBD, 0x036F, 0x1019, 0x1ECB,
+ 0x4065, 0x4EB7, 0x5DC1, 0x5313, 0x7B2D, 0x75FF, 0x6689, 0x685B,
+ 0x24A6, 0x2A74, 0x3902, 0x37D0, 0x1FEE, 0x113C, 0x024A, 0x0C98,
+ 0x5236, 0x5CE4, 0x4F92, 0x4140, 0x697E, 0x67AC, 0x74DA, 0x7A08,
+ 0xC986, 0xC754, 0xD422, 0xDAF0, 0xF2CE, 0xFC1C, 0xEF6A, 0xE1B8,
+ 0xBF16, 0xB1C4, 0xA2B2, 0xAC60, 0x845E, 0x8A8C, 0x99FA, 0x9728,
+ 0x9299, 0x9C4B, 0x8F3D, 0x81EF, 0xA9D1, 0xA703, 0xB475, 0xBAA7,
+ 0xE409, 0xEADB, 0xF9AD, 0xF77F, 0xDF41, 0xD193, 0xC2E5, 0xCC37,
+ 0x7FB9, 0x716B, 0x621D, 0x6CCF, 0x44F1, 0x4A23, 0x5955, 0x5787,
+ 0x0929, 0x07FB, 0x148D, 0x1A5F, 0x3261, 0x3CB3, 0x2FC5, 0x2117,
+ 0x6DEA, 0x6338, 0x704E, 0x7E9C, 0x56A2, 0x5870, 0x4B06, 0x45D4,
+ 0x1B7A, 0x15A8, 0x06DE, 0x080C, 0x2032, 0x2EE0, 0x3D96, 0x3344,
+ 0x80CA, 0x8E18, 0x9D6E, 0x93BC, 0xBB82, 0xB550, 0xA626, 0xA8F4,
+ 0xF65A, 0xF888, 0xEBFE, 0xE52C, 0xCD12, 0xC3C0, 0xD0B6, 0xDE64,
+ 0x494C, 0x479E, 0x54E8, 0x5A3A, 0x7204, 0x7CD6, 0x6FA0, 0x6172,
+ 0x3FDC, 0x310E, 0x2278, 0x2CAA, 0x0494, 0x0A46, 0x1930, 0x17E2,
+ 0xA46C, 0xAABE, 0xB9C8, 0xB71A, 0x9F24, 0x91F6, 0x8280, 0x8C52,
+ 0xD2FC, 0xDC2E, 0xCF58, 0xC18A, 0xE9B4, 0xE766, 0xF410, 0xFAC2,
+ 0xB63F, 0xB8ED, 0xAB9B, 0xA549, 0x8D77, 0x83A5, 0x90D3, 0x9E01,
+ 0xC0AF, 0xCE7D, 0xDD0B, 0xD3D9, 0xFBE7, 0xF535, 0xE643, 0xE891,
+ 0x5B1F, 0x55CD, 0x46BB, 0x4869, 0x6057, 0x6E85, 0x7DF3, 0x7321,
+ 0x2D8F, 0x235D, 0x302B, 0x3EF9, 0x16C7, 0x1815, 0x0B63, 0x05B1
+};
+
+/* This table was compiled using the polynom: 0x3D65 */
+static const unsigned crc16_precompiled_3D65_reverse[] =
+{
+ 0x0000, 0x365E, 0x6CBC, 0x5AE2, 0xD978, 0xEF26, 0xB5C4, 0x839A,
+ 0xFF89, 0xC9D7, 0x9335, 0xA56B, 0x26F1, 0x10AF, 0x4A4D, 0x7C13,
+ 0xB26B, 0x8435, 0xDED7, 0xE889, 0x6B13, 0x5D4D, 0x07AF, 0x31F1,
+ 0x4DE2, 0x7BBC, 0x215E, 0x1700, 0x949A, 0xA2C4, 0xF826, 0xCE78,
+ 0x29AF, 0x1FF1, 0x4513, 0x734D, 0xF0D7, 0xC689, 0x9C6B, 0xAA35,
+ 0xD626, 0xE078, 0xBA9A, 0x8CC4, 0x0F5E, 0x3900, 0x63E2, 0x55BC,
+ 0x9BC4, 0xAD9A, 0xF778, 0xC126, 0x42BC, 0x74E2, 0x2E00, 0x185E,
+ 0x644D, 0x5213, 0x08F1, 0x3EAF, 0xBD35, 0x8B6B, 0xD189, 0xE7D7,
+ 0x535E, 0x6500, 0x3FE2, 0x09BC, 0x8A26, 0xBC78, 0xE69A, 0xD0C4,
+ 0xACD7, 0x9A89, 0xC06B, 0xF635, 0x75AF, 0x43F1, 0x1913, 0x2F4D,
+ 0xE135, 0xD76B, 0x8D89, 0xBBD7, 0x384D, 0x0E13, 0x54F1, 0x62AF,
+ 0x1EBC, 0x28E2, 0x7200, 0x445E, 0xC7C4, 0xF19A, 0xAB78, 0x9D26,
+ 0x7AF1, 0x4CAF, 0x164D, 0x2013, 0xA389, 0x95D7, 0xCF35, 0xF96B,
+ 0x8578, 0xB326, 0xE9C4, 0xDF9A, 0x5C00, 0x6A5E, 0x30BC, 0x06E2,
+ 0xC89A, 0xFEC4, 0xA426, 0x9278, 0x11E2, 0x27BC, 0x7D5E, 0x4B00,
+ 0x3713, 0x014D, 0x5BAF, 0x6DF1, 0xEE6B, 0xD835, 0x82D7, 0xB489,
+ 0xA6BC, 0x90E2, 0xCA00, 0xFC5E, 0x7FC4, 0x499A, 0x1378, 0x2526,
+ 0x5935, 0x6F6B, 0x3589, 0x03D7, 0x804D, 0xB613, 0xECF1, 0xDAAF,
+ 0x14D7, 0x2289, 0x786B, 0x4E35, 0xCDAF, 0xFBF1, 0xA113, 0x974D,
+ 0xEB5E, 0xDD00, 0x87E2, 0xB1BC, 0x3226, 0x0478, 0x5E9A, 0x68C4,
+ 0x8F13, 0xB94D, 0xE3AF, 0xD5F1, 0x566B, 0x6035, 0x3AD7, 0x0C89,
+ 0x709A, 0x46C4, 0x1C26, 0x2A78, 0xA9E2, 0x9FBC, 0xC55E, 0xF300,
+ 0x3D78, 0x0B26, 0x51C4, 0x679A, 0xE400, 0xD25E, 0x88BC, 0xBEE2,
+ 0xC2F1, 0xF4AF, 0xAE4D, 0x9813, 0x1B89, 0x2DD7, 0x7735, 0x416B,
+ 0xF5E2, 0xC3BC, 0x995E, 0xAF00, 0x2C9A, 0x1AC4, 0x4026, 0x7678,
+ 0x0A6B, 0x3C35, 0x66D7, 0x5089, 0xD313, 0xE54D, 0xBFAF, 0x89F1,
+ 0x4789, 0x71D7, 0x2B35, 0x1D6B, 0x9EF1, 0xA8AF, 0xF24D, 0xC413,
+ 0xB800, 0x8E5E, 0xD4BC, 0xE2E2, 0x6178, 0x5726, 0x0DC4, 0x3B9A,
+ 0xDC4D, 0xEA13, 0xB0F1, 0x86AF, 0x0535, 0x336B, 0x6989, 0x5FD7,
+ 0x23C4, 0x159A, 0x4F78, 0x7926, 0xFABC, 0xCCE2, 0x9600, 0xA05E,
+ 0x6E26, 0x5878, 0x029A, 0x34C4, 0xB75E, 0x8100, 0xDBE2, 0xEDBC,
+ 0x91AF, 0xA7F1, 0xFD13, 0xCB4D, 0x48D7, 0x7E89, 0x246B, 0x1235
+};
+
+/* This table was compiled using the polynom: 0x080F */
+static const unsigned crc16_precompiled_080F[] =
+{
+ 0x0000, 0x080F, 0x101E, 0x1811, 0x203C, 0x2833, 0x3022, 0x382D,
+ 0x4078, 0x4877, 0x5066, 0x5869, 0x6044, 0x684B, 0x705A, 0x7855,
+ 0x80F0, 0x88FF, 0x90EE, 0x98E1, 0xA0CC, 0xA8C3, 0xB0D2, 0xB8DD,
+ 0xC088, 0xC887, 0xD096, 0xD899, 0xE0B4, 0xE8BB, 0xF0AA, 0xF8A5,
+ 0x09EF, 0x01E0, 0x19F1, 0x11FE, 0x29D3, 0x21DC, 0x39CD, 0x31C2,
+ 0x4997, 0x4198, 0x5989, 0x5186, 0x69AB, 0x61A4, 0x79B5, 0x71BA,
+ 0x891F, 0x8110, 0x9901, 0x910E, 0xA923, 0xA12C, 0xB93D, 0xB132,
+ 0xC967, 0xC168, 0xD979, 0xD176, 0xE95B, 0xE154, 0xF945, 0xF14A,
+ 0x13DE, 0x1BD1, 0x03C0, 0x0BCF, 0x33E2, 0x3BED, 0x23FC, 0x2BF3,
+ 0x53A6, 0x5BA9, 0x43B8, 0x4BB7, 0x739A, 0x7B95, 0x6384, 0x6B8B,
+ 0x932E, 0x9B21, 0x8330, 0x8B3F, 0xB312, 0xBB1D, 0xA30C, 0xAB03,
+ 0xD356, 0xDB59, 0xC348, 0xCB47, 0xF36A, 0xFB65, 0xE374, 0xEB7B,
+ 0x1A31, 0x123E, 0x0A2F, 0x0220, 0x3A0D, 0x3202, 0x2A13, 0x221C,
+ 0x5A49, 0x5246, 0x4A57, 0x4258, 0x7A75, 0x727A, 0x6A6B, 0x6264,
+ 0x9AC1, 0x92CE, 0x8ADF, 0x82D0, 0xBAFD, 0xB2F2, 0xAAE3, 0xA2EC,
+ 0xDAB9, 0xD2B6, 0xCAA7, 0xC2A8, 0xFA85, 0xF28A, 0xEA9B, 0xE294,
+ 0x27BC, 0x2FB3, 0x37A2, 0x3FAD, 0x0780, 0x0F8F, 0x179E, 0x1F91,
+ 0x67C4, 0x6FCB, 0x77DA, 0x7FD5, 0x47F8, 0x4FF7, 0x57E6, 0x5FE9,
+ 0xA74C, 0xAF43, 0xB752, 0xBF5D, 0x8770, 0x8F7F, 0x976E, 0x9F61,
+ 0xE734, 0xEF3B, 0xF72A, 0xFF25, 0xC708, 0xCF07, 0xD716, 0xDF19,
+ 0x2E53, 0x265C, 0x3E4D, 0x3642, 0x0E6F, 0x0660, 0x1E71, 0x167E,
+ 0x6E2B, 0x6624, 0x7E35, 0x763A, 0x4E17, 0x4618, 0x5E09, 0x5606,
+ 0xAEA3, 0xA6AC, 0xBEBD, 0xB6B2, 0x8E9F, 0x8690, 0x9E81, 0x968E,
+ 0xEEDB, 0xE6D4, 0xFEC5, 0xF6CA, 0xCEE7, 0xC6E8, 0xDEF9, 0xD6F6,
+ 0x3462, 0x3C6D, 0x247C, 0x2C73, 0x145E, 0x1C51, 0x0440, 0x0C4F,
+ 0x741A, 0x7C15, 0x6404, 0x6C0B, 0x5426, 0x5C29, 0x4438, 0x4C37,
+ 0xB492, 0xBC9D, 0xA48C, 0xAC83, 0x94AE, 0x9CA1, 0x84B0, 0x8CBF,
+ 0xF4EA, 0xFCE5, 0xE4F4, 0xECFB, 0xD4D6, 0xDCD9, 0xC4C8, 0xCCC7,
+ 0x3D8D, 0x3582, 0x2D93, 0x259C, 0x1DB1, 0x15BE, 0x0DAF, 0x05A0,
+ 0x7DF5, 0x75FA, 0x6DEB, 0x65E4, 0x5DC9, 0x55C6, 0x4DD7, 0x45D8,
+ 0xBD7D, 0xB572, 0xAD63, 0xA56C, 0x9D41, 0x954E, 0x8D5F, 0x8550,
+ 0xFD05, 0xF50A, 0xED1B, 0xE514, 0xDD39, 0xD536, 0xCD27, 0xC528
+};
+
+/**
+ * Generated on Fri Jul 19 17:16:42 2019
+ * by pycrc v0.9.2, https://pycrc.org
+ * using the configuration:
+ * - Width = 16
+ * - Poly = 0x8005
+ * - XorIn = 0xffff
+ * - ReflectIn = True
+ * - XorOut = 0xffff
+ * - ReflectOut = True
+ * - Algorithm = table-driven
+ */
+static const unsigned crc16_usb_table[] = {
+ 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
+ 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
+ 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
+ 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
+ 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
+ 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
+ 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
+ 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
+ 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
+ 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
+ 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
+ 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
+ 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
+ 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
+ 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
+ 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
+ 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
+ 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
+ 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
+ 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
+ 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
+ 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
+ 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
+ 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
+ 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
+ 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
+ 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
+ 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
+ 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
+ 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
+ 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
+ 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040
+};
+
+static const uint16_t crc16_ccitt_start = 0xFFFF;
+static const uint16_t crc16_ccitt_xorout = 0xFFFF;
+
+static const uint16_t crc16_usb_start = 0xFFFF;
+static const uint16_t crc16_usb_xorout = 0xFFFF;
+
+/* two types of crcs are possible: unreflected (bits shift left) and
+ * reflected (bits shift right).
+ */
+static uint16_t crc16_unreflected(const uint8_t *buf, unsigned len,
+ uint16_t crc_in, const unsigned table[])
+{
+ /* we use guints, rather than guint16s, as they are likely to be
+ faster. We just ignore the top 16 bits and let them do what they want.
+ */
+ unsigned crc16 = (unsigned)crc_in;
+
+ while( len-- != 0 )
+ crc16 = table[((crc16 >> 8) ^ *buf++) & 0xff] ^ (crc16 << 8);
+
+ return (uint16_t)crc16;
+}
+
+static uint16_t crc16_reflected(const uint8_t *buf, unsigned len,
+ uint16_t crc_in, const unsigned table[])
+{
+ /* we use guints, rather than guint16s, as they are likely to be
+ faster. We just ignore the top 16 bits and let them do what they want.
+ XXX - does any time saved not zero-extending uint16_t's to 32 bits
+ into a register outweigh any increased cache footprint from the
+ larger CRC table? */
+ unsigned crc16 = (unsigned)crc_in;
+
+ while( len-- != 0 )
+ crc16 = table[(crc16 ^ *buf++) & 0xff] ^ (crc16 >> 8);
+
+ return (uint16_t)crc16;
+}
+
+uint16_t crc16_ccitt(const uint8_t *buf, unsigned len)
+{
+ return crc16_reflected(buf,len,crc16_ccitt_start,crc16_ccitt_table_reverse)
+ ^ crc16_ccitt_xorout;
+}
+
+uint16_t crc16_x25_ccitt_seed(const uint8_t *buf, unsigned len, uint16_t seed)
+{
+ return crc16_unreflected(buf,len,seed,crc16_ccitt_table);
+}
+
+uint16_t crc16_ccitt_seed(const uint8_t *buf, unsigned len, uint16_t seed)
+{
+ return crc16_reflected(buf,len,seed,crc16_ccitt_table_reverse)
+ ^ crc16_ccitt_xorout;
+}
+
+/* ISO14443-3, section 6.2.4: For ISO14443-A, the polynomial 0x1021 is
+ used, the initial register value shall be 0x6363, the final register
+ value is not XORed with anything. */
+uint16_t crc16_iso14443a(const uint8_t *buf, unsigned len)
+{
+ return crc16_reflected(buf,len, 0x6363 ,crc16_ccitt_table_reverse);
+}
+
+uint16_t crc16_usb(const uint8_t *buf, unsigned len)
+{
+ return crc16_reflected(buf, len, crc16_usb_start, crc16_usb_table)
+ ^ crc16_usb_xorout;
+}
+
+uint16_t crc16_0x5935(const uint8_t *buf, uint32_t len, uint16_t seed)
+{
+ return crc16_unreflected(buf, len, seed, crc16_precompiled_5935);
+}
+
+uint16_t crc16_0x755B(const uint8_t *buf, uint32_t len, uint16_t seed)
+{
+ return crc16_unreflected(buf, len, seed, crc16_precompiled_755B);
+}
+
+uint16_t crc16_0x9949_seed(const uint8_t *buf, unsigned len, uint16_t seed)
+{
+ return crc16_reflected(buf, len, seed, crc16_precompiled_9949_reverse);
+}
+
+uint16_t crc16_0x3D65_seed(const uint8_t *buf, unsigned len, uint16_t seed)
+{
+ return crc16_reflected(buf, len, seed, crc16_precompiled_3D65_reverse);
+}
+
+uint16_t crc16_0x080F_seed(const uint8_t *buf, unsigned len, uint16_t seed)
+{
+ uint16_t crc = seed;
+
+ if (len > 0)
+ {
+ while (len-- > 0)
+ {
+ uint8_t data = *buf++;
+ crc = crc16_precompiled_080F[((crc >> 8) ^ data)] ^ (crc << 8);
+ }
+ }
+
+ return crc;
+}
+
+/*
+ * 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/wsutil/crc16.h b/wsutil/crc16.h
new file mode 100644
index 00000000..2c1be91d
--- /dev/null
+++ b/wsutil/crc16.h
@@ -0,0 +1,113 @@
+/** @file
+ * Declaration of CRC-16 routines and table
+ *
+ * 2004 Richard van der Hoff <richardv@mxtelecom.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+*/
+
+#ifndef __CRC16_H__
+#define __CRC16_H__
+
+#include <wireshark.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Calculate the CCITT/ITU/CRC-16 16-bit CRC
+
+ (parameters for this CRC are:
+ Polynomial: x^16 + x^12 + x^5 + 1 (0x1021);
+ Start value 0xFFFF;
+ XOR result with 0xFFFF;
+ First bit is LSB)
+*/
+
+/** Compute CRC16 CCITT checksum of a buffer of data.
+ @param buf The buffer containing the data.
+ @param len The number of bytes to include in the computation.
+ @return The CRC16 CCITT checksum. */
+WS_DLL_PUBLIC uint16_t crc16_ccitt(const uint8_t *buf, unsigned len);
+
+/** Compute CRC16 X.25 CCITT checksum of a buffer of data.
+ @param buf The buffer containing the data.
+ @param len The number of bytes to include in the computation.
+ @return The CRC16 X.25 CCITT checksum. */
+WS_DLL_PUBLIC uint16_t crc16_x25_ccitt_seed(const uint8_t *buf, unsigned len, uint16_t seed);
+
+/** Compute CRC16 CCITT checksum of a buffer of data. If computing the
+ * checksum over multiple buffers and you want to feed the partial CRC16
+ * back in, remember to take the 1's complement of the partial CRC16 first.
+ @param buf The buffer containing the data.
+ @param len The number of bytes to include in the computation.
+ @param seed The seed to use.
+ @return The CRC16 CCITT checksum (using the given seed). */
+WS_DLL_PUBLIC uint16_t crc16_ccitt_seed(const uint8_t *buf, unsigned len, uint16_t seed);
+
+/** Compute the 16bit CRC_A value of a buffer as defined in ISO14443-3.
+ @param buf The buffer containing the data.
+ @param len The number of bytes to include in the computation.
+ @return the CRC16 checksum for the buffer */
+WS_DLL_PUBLIC uint16_t crc16_iso14443a(const uint8_t *buf, unsigned len);
+
+/** Compute the 16bit CRC value of a buffer as defined in USB Specification.
+ @param buf The buffer containing the data.
+ @param len The number of bytes to include in the computation.
+ @return the CRC16 checksum for the buffer */
+WS_DLL_PUBLIC uint16_t crc16_usb(const uint8_t *buf, unsigned len);
+
+/** Calculates a CRC16 checksum for the given buffer with the polynom
+ * 0x5935 using a precompiled CRC table
+ * @param buf a pointer to a buffer of the given length
+ * @param len the length of the given buffer
+ * @param seed The seed to use.
+ * @return the CRC16 checksum for the buffer
+ */
+WS_DLL_PUBLIC uint16_t crc16_0x5935(const uint8_t *buf, uint32_t len, uint16_t seed);
+
+/** Calculates a CRC16 checksum for the given buffer with the polynom
+ * 0x755B using a precompiled CRC table
+ * @param buf a pointer to a buffer of the given length
+ * @param len the length of the given buffer
+ * @param seed The seed to use.
+ * @return the CRC16 checksum for the buffer
+ */
+WS_DLL_PUBLIC uint16_t crc16_0x755B(const uint8_t *buf, uint32_t len, uint16_t seed);
+
+/** Computes CRC16 checksum for the given data with the polynom 0x9949 using
+ * precompiled CRC table
+ * @param buf a pointer to a buffer of the given length
+ * @param len the length of the given buffer
+ * @param seed The seed to use.
+ * @return the CRC16 checksum for the buffer
+ */
+WS_DLL_PUBLIC uint16_t crc16_0x9949_seed(const uint8_t *buf, unsigned len, uint16_t seed);
+
+/** Computes CRC16 checksum for the given data with the polynom 0x3D65 using
+ * precompiled CRC table
+ * @param buf a pointer to a buffer of the given length
+ * @param len the length of the given buffer
+ * @param seed The seed to use.
+ * @return the CRC16 checksum for the buffer
+ */
+WS_DLL_PUBLIC uint16_t crc16_0x3D65_seed(const uint8_t *buf, unsigned len, uint16_t seed);
+
+/** Computes CRC16 checksum for the given data with the polynom 0x080F using
+ * precompiled CRC table
+ * @param buf a pointer to a buffer of the given length
+ * @param len the length of the given buffer
+ * @param seed The seed to use.
+ * @return the CRC16 checksum for the buffer
+ */
+WS_DLL_PUBLIC uint16_t crc16_0x080F_seed(const uint8_t *buf, unsigned len, uint16_t seed);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* crc16.h */
diff --git a/wsutil/crc32.c b/wsutil/crc32.c
new file mode 100644
index 00000000..4be3d651
--- /dev/null
+++ b/wsutil/crc32.c
@@ -0,0 +1,461 @@
+/* crc32.c
+ * CRC-32 routine
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Credits:
+ *
+ * Table from Solomon Peachy
+ * Routine from Chris Waters
+ */
+
+#include "config.h"
+
+#include <wsutil/crc32.h>
+
+#define CRC32_ACCUMULATE(c,d,table) (c=(c>>8)^(table)[(c^(d))&0xFF])
+
+/*****************************************************************/
+/* */
+/* CRC32C LOOKUP TABLE */
+/* +++================ */
+/* The following CRC lookup table was generated automagically */
+/* by the Rocksoft^tm Model CRC Algorithm Table Generation */
+/* Program V1.0 using the following model parameters: */
+/* */
+/* Width : 4 bytes. */
+/* Poly : 0x1EDC6F41L */
+/* Reverse : true. */
+/* */
+/* For more information on the Rocksoft^tm Model CRC Algorithm, */
+/* see the document titled "A Painless Guide to CRC Error */
+/* Detection Algorithms" by Ross Williams */
+/* (ross@guest.adelaide.edu.au.). This document is likely to be */
+/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft". */
+/* */
+/*****************************************************************/
+#define CRC32C(c,d) CRC32_ACCUMULATE(c,d,crc32c_table)
+
+static const uint32_t crc32c_table[256] = {
+ 0x00000000U, 0xF26B8303U, 0xE13B70F7U, 0x1350F3F4U, 0xC79A971FU,
+ 0x35F1141CU, 0x26A1E7E8U, 0xD4CA64EBU, 0x8AD958CFU, 0x78B2DBCCU,
+ 0x6BE22838U, 0x9989AB3BU, 0x4D43CFD0U, 0xBF284CD3U, 0xAC78BF27U,
+ 0x5E133C24U, 0x105EC76FU, 0xE235446CU, 0xF165B798U, 0x030E349BU,
+ 0xD7C45070U, 0x25AFD373U, 0x36FF2087U, 0xC494A384U, 0x9A879FA0U,
+ 0x68EC1CA3U, 0x7BBCEF57U, 0x89D76C54U, 0x5D1D08BFU, 0xAF768BBCU,
+ 0xBC267848U, 0x4E4DFB4BU, 0x20BD8EDEU, 0xD2D60DDDU, 0xC186FE29U,
+ 0x33ED7D2AU, 0xE72719C1U, 0x154C9AC2U, 0x061C6936U, 0xF477EA35U,
+ 0xAA64D611U, 0x580F5512U, 0x4B5FA6E6U, 0xB93425E5U, 0x6DFE410EU,
+ 0x9F95C20DU, 0x8CC531F9U, 0x7EAEB2FAU, 0x30E349B1U, 0xC288CAB2U,
+ 0xD1D83946U, 0x23B3BA45U, 0xF779DEAEU, 0x05125DADU, 0x1642AE59U,
+ 0xE4292D5AU, 0xBA3A117EU, 0x4851927DU, 0x5B016189U, 0xA96AE28AU,
+ 0x7DA08661U, 0x8FCB0562U, 0x9C9BF696U, 0x6EF07595U, 0x417B1DBCU,
+ 0xB3109EBFU, 0xA0406D4BU, 0x522BEE48U, 0x86E18AA3U, 0x748A09A0U,
+ 0x67DAFA54U, 0x95B17957U, 0xCBA24573U, 0x39C9C670U, 0x2A993584U,
+ 0xD8F2B687U, 0x0C38D26CU, 0xFE53516FU, 0xED03A29BU, 0x1F682198U,
+ 0x5125DAD3U, 0xA34E59D0U, 0xB01EAA24U, 0x42752927U, 0x96BF4DCCU,
+ 0x64D4CECFU, 0x77843D3BU, 0x85EFBE38U, 0xDBFC821CU, 0x2997011FU,
+ 0x3AC7F2EBU, 0xC8AC71E8U, 0x1C661503U, 0xEE0D9600U, 0xFD5D65F4U,
+ 0x0F36E6F7U, 0x61C69362U, 0x93AD1061U, 0x80FDE395U, 0x72966096U,
+ 0xA65C047DU, 0x5437877EU, 0x4767748AU, 0xB50CF789U, 0xEB1FCBADU,
+ 0x197448AEU, 0x0A24BB5AU, 0xF84F3859U, 0x2C855CB2U, 0xDEEEDFB1U,
+ 0xCDBE2C45U, 0x3FD5AF46U, 0x7198540DU, 0x83F3D70EU, 0x90A324FAU,
+ 0x62C8A7F9U, 0xB602C312U, 0x44694011U, 0x5739B3E5U, 0xA55230E6U,
+ 0xFB410CC2U, 0x092A8FC1U, 0x1A7A7C35U, 0xE811FF36U, 0x3CDB9BDDU,
+ 0xCEB018DEU, 0xDDE0EB2AU, 0x2F8B6829U, 0x82F63B78U, 0x709DB87BU,
+ 0x63CD4B8FU, 0x91A6C88CU, 0x456CAC67U, 0xB7072F64U, 0xA457DC90U,
+ 0x563C5F93U, 0x082F63B7U, 0xFA44E0B4U, 0xE9141340U, 0x1B7F9043U,
+ 0xCFB5F4A8U, 0x3DDE77ABU, 0x2E8E845FU, 0xDCE5075CU, 0x92A8FC17U,
+ 0x60C37F14U, 0x73938CE0U, 0x81F80FE3U, 0x55326B08U, 0xA759E80BU,
+ 0xB4091BFFU, 0x466298FCU, 0x1871A4D8U, 0xEA1A27DBU, 0xF94AD42FU,
+ 0x0B21572CU, 0xDFEB33C7U, 0x2D80B0C4U, 0x3ED04330U, 0xCCBBC033U,
+ 0xA24BB5A6U, 0x502036A5U, 0x4370C551U, 0xB11B4652U, 0x65D122B9U,
+ 0x97BAA1BAU, 0x84EA524EU, 0x7681D14DU, 0x2892ED69U, 0xDAF96E6AU,
+ 0xC9A99D9EU, 0x3BC21E9DU, 0xEF087A76U, 0x1D63F975U, 0x0E330A81U,
+ 0xFC588982U, 0xB21572C9U, 0x407EF1CAU, 0x532E023EU, 0xA145813DU,
+ 0x758FE5D6U, 0x87E466D5U, 0x94B49521U, 0x66DF1622U, 0x38CC2A06U,
+ 0xCAA7A905U, 0xD9F75AF1U, 0x2B9CD9F2U, 0xFF56BD19U, 0x0D3D3E1AU,
+ 0x1E6DCDEEU, 0xEC064EEDU, 0xC38D26C4U, 0x31E6A5C7U, 0x22B65633U,
+ 0xD0DDD530U, 0x0417B1DBU, 0xF67C32D8U, 0xE52CC12CU, 0x1747422FU,
+ 0x49547E0BU, 0xBB3FFD08U, 0xA86F0EFCU, 0x5A048DFFU, 0x8ECEE914U,
+ 0x7CA56A17U, 0x6FF599E3U, 0x9D9E1AE0U, 0xD3D3E1ABU, 0x21B862A8U,
+ 0x32E8915CU, 0xC083125FU, 0x144976B4U, 0xE622F5B7U, 0xF5720643U,
+ 0x07198540U, 0x590AB964U, 0xAB613A67U, 0xB831C993U, 0x4A5A4A90U,
+ 0x9E902E7BU, 0x6CFBAD78U, 0x7FAB5E8CU, 0x8DC0DD8FU, 0xE330A81AU,
+ 0x115B2B19U, 0x020BD8EDU, 0xF0605BEEU, 0x24AA3F05U, 0xD6C1BC06U,
+ 0xC5914FF2U, 0x37FACCF1U, 0x69E9F0D5U, 0x9B8273D6U, 0x88D28022U,
+ 0x7AB90321U, 0xAE7367CAU, 0x5C18E4C9U, 0x4F48173DU, 0xBD23943EU,
+ 0xF36E6F75U, 0x0105EC76U, 0x12551F82U, 0xE03E9C81U, 0x34F4F86AU,
+ 0xC69F7B69U, 0xD5CF889DU, 0x27A40B9EU, 0x79B737BAU, 0x8BDCB4B9U,
+ 0x988C474DU, 0x6AE7C44EU, 0xBE2DA0A5U, 0x4C4623A6U, 0x5F16D052U,
+ 0xAD7D5351U };
+
+/*
+ * Table for the AUTODIN/HDLC/802.x CRC.
+ *
+ * Polynomial is
+ *
+ * x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 +
+ * x^7 + x^5 + x^4 + x^2 + x + 1
+ */
+static const uint32_t crc32_ccitt_table[256] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
+ 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
+ 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
+ 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+ 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
+ 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
+ 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+ 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
+ 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
+ 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
+ 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
+ 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
+ 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
+ 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+ 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
+ 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
+ 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+ 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
+ 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
+ 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
+ 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
+ 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
+ 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
+ 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+ 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
+ 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
+ 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+ 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
+ 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
+ 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
+ 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
+ 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
+ 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
+ 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+ 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
+ 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
+ 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+ 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
+ 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
+ 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
+ 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
+ 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
+ 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
+ 0x2d02ef8d
+};
+
+/*
+ * Table for the MPEG-2 CRC.
+ *
+ * Polynomial is
+ *
+ * x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 +
+ * x^7 + x^5 + x^4 + x^2 + x + 1
+ *
+ * (which is the same polynomial as the one above us).
+ *
+ * NOTE: this is also used for ATM AAL5.
+ */
+static const uint32_t crc32_mpeg2_table[256] = {
+ 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
+ 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
+ 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
+ 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
+ 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
+ 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
+ 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
+ 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
+ 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
+ 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
+ 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
+ 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
+ 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
+ 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
+ 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
+ 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
+ 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
+ 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
+ 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
+ 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
+ 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
+ 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
+ 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
+ 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
+ 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
+ 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
+ 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
+ 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
+ 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
+ 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
+ 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
+ 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
+ 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
+ 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
+ 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
+ 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
+ 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
+ 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
+ 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
+ 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
+ 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
+ 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
+ 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
+};
+
+/* This table was compiled using the polynom: 0x0AA725CF*/
+static const uint32_t crc32_0AA725CF_reverse[] = {
+ 0x00000000U, 0xCEAA95CEU, 0x7A1CE13DU, 0xB4B674F3U,
+ 0xF439C27AU, 0x3A9357B4U, 0x8E252347U, 0x408FB689U,
+ 0x0F3A4E55U, 0xC190DB9BU, 0x7526AF68U, 0xBB8C3AA6U,
+ 0xFB038C2FU, 0x35A919E1U, 0x811F6D12U, 0x4FB5F8DCU,
+ 0x1E749CAAU, 0xD0DE0964U, 0x64687D97U, 0xAAC2E859U,
+ 0xEA4D5ED0U, 0x24E7CB1EU, 0x9051BFEDU, 0x5EFB2A23U,
+ 0x114ED2FFU, 0xDFE44731U, 0x6B5233C2U, 0xA5F8A60CU,
+ 0xE5771085U, 0x2BDD854BU, 0x9F6BF1B8U, 0x51C16476U,
+ 0x3CE93954U, 0xF243AC9AU, 0x46F5D869U, 0x885F4DA7U,
+ 0xC8D0FB2EU, 0x067A6EE0U, 0xB2CC1A13U, 0x7C668FDDU,
+ 0x33D37701U, 0xFD79E2CFU, 0x49CF963CU, 0x876503F2U,
+ 0xC7EAB57BU, 0x094020B5U, 0xBDF65446U, 0x735CC188U,
+ 0x229DA5FEU, 0xEC373030U, 0x588144C3U, 0x962BD10DU,
+ 0xD6A46784U, 0x180EF24AU, 0xACB886B9U, 0x62121377U,
+ 0x2DA7EBABU, 0xE30D7E65U, 0x57BB0A96U, 0x99119F58U,
+ 0xD99E29D1U, 0x1734BC1FU, 0xA382C8ECU, 0x6D285D22U,
+ 0x79D272A8U, 0xB778E766U, 0x03CE9395U, 0xCD64065BU,
+ 0x8DEBB0D2U, 0x4341251CU, 0xF7F751EFU, 0x395DC421U,
+ 0x76E83CFDU, 0xB842A933U, 0x0CF4DDC0U, 0xC25E480EU,
+ 0x82D1FE87U, 0x4C7B6B49U, 0xF8CD1FBAU, 0x36678A74U,
+ 0x67A6EE02U, 0xA90C7BCCU, 0x1DBA0F3FU, 0xD3109AF1U,
+ 0x939F2C78U, 0x5D35B9B6U, 0xE983CD45U, 0x2729588BU,
+ 0x689CA057U, 0xA6363599U, 0x1280416AU, 0xDC2AD4A4U,
+ 0x9CA5622DU, 0x520FF7E3U, 0xE6B98310U, 0x281316DEU,
+ 0x453B4BFCU, 0x8B91DE32U, 0x3F27AAC1U, 0xF18D3F0FU,
+ 0xB1028986U, 0x7FA81C48U, 0xCB1E68BBU, 0x05B4FD75U,
+ 0x4A0105A9U, 0x84AB9067U, 0x301DE494U, 0xFEB7715AU,
+ 0xBE38C7D3U, 0x7092521DU, 0xC42426EEU, 0x0A8EB320U,
+ 0x5B4FD756U, 0x95E54298U, 0x2153366BU, 0xEFF9A3A5U,
+ 0xAF76152CU, 0x61DC80E2U, 0xD56AF411U, 0x1BC061DFU,
+ 0x54759903U, 0x9ADF0CCDU, 0x2E69783EU, 0xE0C3EDF0U,
+ 0xA04C5B79U, 0x6EE6CEB7U, 0xDA50BA44U, 0x14FA2F8AU,
+ 0xF3A4E550U, 0x3D0E709EU, 0x89B8046DU, 0x471291A3U,
+ 0x079D272AU, 0xC937B2E4U, 0x7D81C617U, 0xB32B53D9U,
+ 0xFC9EAB05U, 0x32343ECBU, 0x86824A38U, 0x4828DFF6U,
+ 0x08A7697FU, 0xC60DFCB1U, 0x72BB8842U, 0xBC111D8CU,
+ 0xEDD079FAU, 0x237AEC34U, 0x97CC98C7U, 0x59660D09U,
+ 0x19E9BB80U, 0xD7432E4EU, 0x63F55ABDU, 0xAD5FCF73U,
+ 0xE2EA37AFU, 0x2C40A261U, 0x98F6D692U, 0x565C435CU,
+ 0x16D3F5D5U, 0xD879601BU, 0x6CCF14E8U, 0xA2658126U,
+ 0xCF4DDC04U, 0x01E749CAU, 0xB5513D39U, 0x7BFBA8F7U,
+ 0x3B741E7EU, 0xF5DE8BB0U, 0x4168FF43U, 0x8FC26A8DU,
+ 0xC0779251U, 0x0EDD079FU, 0xBA6B736CU, 0x74C1E6A2U,
+ 0x344E502BU, 0xFAE4C5E5U, 0x4E52B116U, 0x80F824D8U,
+ 0xD13940AEU, 0x1F93D560U, 0xAB25A193U, 0x658F345DU,
+ 0x250082D4U, 0xEBAA171AU, 0x5F1C63E9U, 0x91B6F627U,
+ 0xDE030EFBU, 0x10A99B35U, 0xA41FEFC6U, 0x6AB57A08U,
+ 0x2A3ACC81U, 0xE490594FU, 0x50262DBCU, 0x9E8CB872U,
+ 0x8A7697F8U, 0x44DC0236U, 0xF06A76C5U, 0x3EC0E30BU,
+ 0x7E4F5582U, 0xB0E5C04CU, 0x0453B4BFU, 0xCAF92171U,
+ 0x854CD9ADU, 0x4BE64C63U, 0xFF503890U, 0x31FAAD5EU,
+ 0x71751BD7U, 0xBFDF8E19U, 0x0B69FAEAU, 0xC5C36F24U,
+ 0x94020B52U, 0x5AA89E9CU, 0xEE1EEA6FU, 0x20B47FA1U,
+ 0x603BC928U, 0xAE915CE6U, 0x1A272815U, 0xD48DBDDBU,
+ 0x9B384507U, 0x5592D0C9U, 0xE124A43AU, 0x2F8E31F4U,
+ 0x6F01877DU, 0xA1AB12B3U, 0x151D6640U, 0xDBB7F38EU,
+ 0xB69FAEACU, 0x78353B62U, 0xCC834F91U, 0x0229DA5FU,
+ 0x42A66CD6U, 0x8C0CF918U, 0x38BA8DEBU, 0xF6101825U,
+ 0xB9A5E0F9U, 0x770F7537U, 0xC3B901C4U, 0x0D13940AU,
+ 0x4D9C2283U, 0x8336B74DU, 0x3780C3BEU, 0xF92A5670U,
+ 0xA8EB3206U, 0x6641A7C8U, 0xD2F7D33BU, 0x1C5D46F5U,
+ 0x5CD2F07CU, 0x927865B2U, 0x26CE1141U, 0xE864848FU,
+ 0xA7D17C53U, 0x697BE99DU, 0xDDCD9D6EU, 0x136708A0U,
+ 0x53E8BE29U, 0x9D422BE7U, 0x29F45F14U, 0xE75ECADAU
+};
+
+/* This table was compiled using the polynom: 0x5D6DCB */
+static const uint32_t crc32_5D6DCB[] =
+{
+ 0x00000000, 0x005d6dcb, 0x00badb96, 0x00e7b65d,
+ 0x0028dae7, 0x0075b72c, 0x00920171, 0x00cf6cba,
+ 0x0051b5ce, 0x000cd805, 0x00eb6e58, 0x00b60393,
+ 0x00796f29, 0x002402e2, 0x00c3b4bf, 0x009ed974,
+ 0x00a36b9c, 0x00fe0657, 0x0019b00a, 0x0044ddc1,
+ 0x008bb17b, 0x00d6dcb0, 0x00316aed, 0x006c0726,
+ 0x00f2de52, 0x00afb399, 0x004805c4, 0x0015680f,
+ 0x00da04b5, 0x0087697e, 0x0060df23, 0x003db2e8,
+ 0x001bbaf3, 0x0046d738, 0x00a16165, 0x00fc0cae,
+ 0x00336014, 0x006e0ddf, 0x0089bb82, 0x00d4d649,
+ 0x004a0f3d, 0x001762f6, 0x00f0d4ab, 0x00adb960,
+ 0x0062d5da, 0x003fb811, 0x00d80e4c, 0x00856387,
+ 0x00b8d16f, 0x00e5bca4, 0x00020af9, 0x005f6732,
+ 0x00900b88, 0x00cd6643, 0x002ad01e, 0x0077bdd5,
+ 0x00e964a1, 0x00b4096a, 0x0053bf37, 0x000ed2fc,
+ 0x00c1be46, 0x009cd38d, 0x007b65d0, 0x0026081b,
+ 0x003775e6, 0x006a182d, 0x008dae70, 0x00d0c3bb,
+ 0x001faf01, 0x0042c2ca, 0x00a57497, 0x00f8195c,
+ 0x0066c028, 0x003bade3, 0x00dc1bbe, 0x00817675,
+ 0x004e1acf, 0x00137704, 0x00f4c159, 0x00a9ac92,
+ 0x00941e7a, 0x00c973b1, 0x002ec5ec, 0x0073a827,
+ 0x00bcc49d, 0x00e1a956, 0x00061f0b, 0x005b72c0,
+ 0x00c5abb4, 0x0098c67f, 0x007f7022, 0x00221de9,
+ 0x00ed7153, 0x00b01c98, 0x0057aac5, 0x000ac70e,
+ 0x002ccf15, 0x0071a2de, 0x00961483, 0x00cb7948,
+ 0x000415f2, 0x00597839, 0x00bece64, 0x00e3a3af,
+ 0x007d7adb, 0x00201710, 0x00c7a14d, 0x009acc86,
+ 0x0055a03c, 0x0008cdf7, 0x00ef7baa, 0x00b21661,
+ 0x008fa489, 0x00d2c942, 0x00357f1f, 0x006812d4,
+ 0x00a77e6e, 0x00fa13a5, 0x001da5f8, 0x0040c833,
+ 0x00de1147, 0x00837c8c, 0x0064cad1, 0x0039a71a,
+ 0x00f6cba0, 0x00aba66b, 0x004c1036, 0x00117dfd,
+ 0x006eebcc, 0x00338607, 0x00d4305a, 0x00895d91,
+ 0x0046312b, 0x001b5ce0, 0x00fceabd, 0x00a18776,
+ 0x003f5e02, 0x006233c9, 0x00858594, 0x00d8e85f,
+ 0x001784e5, 0x004ae92e, 0x00ad5f73, 0x00f032b8,
+ 0x00cd8050, 0x0090ed9b, 0x00775bc6, 0x002a360d,
+ 0x00e55ab7, 0x00b8377c, 0x005f8121, 0x0002ecea,
+ 0x009c359e, 0x00c15855, 0x0026ee08, 0x007b83c3,
+ 0x00b4ef79, 0x00e982b2, 0x000e34ef, 0x00535924,
+ 0x0075513f, 0x00283cf4, 0x00cf8aa9, 0x0092e762,
+ 0x005d8bd8, 0x0000e613, 0x00e7504e, 0x00ba3d85,
+ 0x0024e4f1, 0x0079893a, 0x009e3f67, 0x00c352ac,
+ 0x000c3e16, 0x005153dd, 0x00b6e580, 0x00eb884b,
+ 0x00d63aa3, 0x008b5768, 0x006ce135, 0x00318cfe,
+ 0x00fee044, 0x00a38d8f, 0x00443bd2, 0x00195619,
+ 0x00878f6d, 0x00dae2a6, 0x003d54fb, 0x00603930,
+ 0x00af558a, 0x00f23841, 0x00158e1c, 0x0048e3d7,
+ 0x00599e2a, 0x0004f3e1, 0x00e345bc, 0x00be2877,
+ 0x007144cd, 0x002c2906, 0x00cb9f5b, 0x0096f290,
+ 0x00082be4, 0x0055462f, 0x00b2f072, 0x00ef9db9,
+ 0x0020f103, 0x007d9cc8, 0x009a2a95, 0x00c7475e,
+ 0x00faf5b6, 0x00a7987d, 0x00402e20, 0x001d43eb,
+ 0x00d22f51, 0x008f429a, 0x0068f4c7, 0x0035990c,
+ 0x00ab4078, 0x00f62db3, 0x00119bee, 0x004cf625,
+ 0x00839a9f, 0x00def754, 0x00394109, 0x00642cc2,
+ 0x004224d9, 0x001f4912, 0x00f8ff4f, 0x00a59284,
+ 0x006afe3e, 0x003793f5, 0x00d025a8, 0x008d4863,
+ 0x00139117, 0x004efcdc, 0x00a94a81, 0x00f4274a,
+ 0x003b4bf0, 0x0066263b, 0x00819066, 0x00dcfdad,
+ 0x00e14f45, 0x00bc228e, 0x005b94d3, 0x0006f918,
+ 0x00c995a2, 0x0094f869, 0x00734e34, 0x002e23ff,
+ 0x00b0fa8b, 0x00ed9740, 0x000a211d, 0x00574cd6,
+ 0x0098206c, 0x00c54da7, 0x0022fbfa, 0x007f9631
+};
+
+uint32_t
+crc32c_table_lookup (unsigned char pos)
+{
+ return crc32c_table[pos];
+}
+
+uint32_t
+crc32_ccitt_table_lookup (unsigned char pos)
+{
+ return crc32_ccitt_table[pos];
+}
+
+uint32_t
+crc32c_calculate(const void *buf, int len, uint32_t crc)
+{
+ const uint8_t *p = (const uint8_t *)buf;
+ crc = CRC32C_SWAP(crc);
+ while (len-- > 0) {
+ CRC32C(crc, *p++);
+ }
+ return CRC32C_SWAP(crc);
+}
+
+uint32_t
+crc32c_calculate_no_swap(const void *buf, int len, uint32_t crc)
+{
+ const uint8_t *p = (const uint8_t *)buf;
+ while (len-- > 0) {
+ CRC32C(crc, *p++);
+ }
+
+ return crc;
+}
+
+uint32_t
+crc32_ccitt(const uint8_t *buf, unsigned len)
+{
+ return (crc32_ccitt_seed(buf, len, CRC32_CCITT_SEED));
+}
+
+uint32_t
+crc32_ccitt_seed(const uint8_t *buf, unsigned len, uint32_t seed)
+{
+ unsigned i;
+ uint32_t crc32 = seed;
+
+ for (i = 0; i < len; i++)
+ CRC32_ACCUMULATE(crc32, buf[i], crc32_ccitt_table);
+
+ return ( ~crc32 );
+}
+
+uint32_t
+crc32_mpeg2_seed(const uint8_t *buf, unsigned len, uint32_t seed)
+{
+ unsigned i;
+ uint32_t crc32;
+
+ crc32 = seed;
+
+ for (i = 0; i < len; i++)
+ crc32 = (crc32 << 8) ^ crc32_mpeg2_table[((crc32 >> 24) ^ buf[i]) & 0xff];
+
+ return ( crc32 );
+}
+
+uint32_t
+crc32_0x0AA725CF_seed(const uint8_t *buf, unsigned len, uint32_t seed)
+{
+ unsigned crc32;
+
+ crc32 = (unsigned)seed;
+ while( len-- != 0 )
+ CRC32_ACCUMULATE(crc32, *buf++, crc32_0AA725CF_reverse);
+
+ return (uint32_t)crc32;
+}
+
+uint32_t
+crc32_0x5D6DCB_seed(const uint8_t *buf, unsigned len, uint32_t seed)
+{
+ uint32_t crc = seed;
+ if (len > 0)
+ {
+ while (len-- > 0)
+ {
+ uint8_t data = *buf++;
+ /* XOR data with CRC2, look up result, then XOR that with CRC; */
+ crc = crc32_5D6DCB[((crc >> 16) ^ data) & 0xff] ^ (crc << 8);
+ }
+ }
+ return (crc & 0x00ffffff);
+}
+
+
+/*
+ * 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:
+ */
diff --git a/wsutil/crc32.h b/wsutil/crc32.h
new file mode 100644
index 00000000..c6891730
--- /dev/null
+++ b/wsutil/crc32.h
@@ -0,0 +1,106 @@
+/** @file
+ * Declaration of CRC-32 routine and table
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __CRC32_H__
+#define __CRC32_H__
+
+#include <wireshark.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define CRC32_CCITT_SEED 0xFFFFFFFF
+#define CRC32C_PRELOAD 0xffffffff
+#define CRC32_MPEG2_SEED 0xFFFFFFFF
+
+/*
+ * Byte swap fix contributed by Dave Wysochanski <davidw@netapp.com>.
+ */
+#define CRC32C_SWAP(crc32c_value) \
+ (((crc32c_value & 0xff000000) >> 24) | \
+ ((crc32c_value & 0x00ff0000) >> 8) | \
+ ((crc32c_value & 0x0000ff00) << 8) | \
+ ((crc32c_value & 0x000000ff) << 24))
+
+/** Lookup the crc value in the crc32_ccitt_table
+ @param pos Position in the table. */
+WS_DLL_PUBLIC uint32_t crc32_ccitt_table_lookup (unsigned char pos);
+
+/** Lookup the crc value in the crc32c_table
+ @param pos Position in the table. */
+WS_DLL_PUBLIC uint32_t crc32c_table_lookup (unsigned char pos);
+
+/** Compute CRC32C checksum of a buffer of data.
+ @param buf The buffer containing the data.
+ @param len The number of bytes to include in the computation.
+ @param crc The preload value for the CRC32C computation.
+ @return The CRC32C checksum. */
+WS_DLL_PUBLIC uint32_t crc32c_calculate(const void *buf, int len, uint32_t crc);
+
+/** Compute CRC32C checksum of a buffer of data without swapping seed crc
+ or completed checksum
+ @param buf The buffer containing the data.
+ @param len The number of bytes to include in the computation.
+ @param crc The preload value for the CRC32C computation.
+ @return The CRC32C checksum. */
+WS_DLL_PUBLIC uint32_t crc32c_calculate_no_swap(const void *buf, int len, uint32_t crc);
+
+/** Compute CRC32 CCITT checksum of a buffer of data.
+ @param buf The buffer containing the data.
+ @param len The number of bytes to include in the computation.
+ @return The CRC32 CCITT checksum. */
+WS_DLL_PUBLIC uint32_t crc32_ccitt(const uint8_t *buf, unsigned len);
+
+/** Compute CRC32 CCITT checksum of a buffer of data. If computing the
+ * checksum over multiple buffers and you want to feed the partial CRC32
+ * back in, remember to take the 1's complement of the partial CRC32 first.
+ @param buf The buffer containing the data.
+ @param len The number of bytes to include in the computation.
+ @param seed The seed to use.
+ @return The CRC32 CCITT checksum (using the given seed). */
+WS_DLL_PUBLIC uint32_t crc32_ccitt_seed(const uint8_t *buf, unsigned len, uint32_t seed);
+
+/** Compute MPEG-2 CRC32 checksum of a buffer of data.
+ @param buf The buffer containing the data.
+ @param len The number of bytes to include in the computation.
+ @param seed The seed to use.
+ @return The CRC32 MPEG-2 checksum (using the given seed). */
+WS_DLL_PUBLIC uint32_t crc32_mpeg2_seed(const uint8_t *buf, unsigned len, uint32_t seed);
+
+/** Computes CRC32 checksum for the given data with the polynom 0x0AA725CF using
+ * precompiled CRC table
+ * @param buf a pointer to a buffer of the given length
+ * @param len the length of the given buffer
+ * @param seed The seed to use.
+ * @return the CRC32 checksum for the buffer
+ */
+WS_DLL_PUBLIC uint32_t crc32_0x0AA725CF_seed(const uint8_t *buf, unsigned len, uint32_t seed);
+
+/** Computes CRC32 checksum for the given data with the polynom 0x5D6DCB using
+ * precompiled CRC table
+ * @param buf a pointer to a buffer of the given length
+ * @param len the length of the given buffer
+ * @param seed The seed to use.
+ * @return the CRC32 checksum for the buffer
+ */
+WS_DLL_PUBLIC uint32_t crc32_0x5D6DCB_seed(const uint8_t *buf, unsigned len, uint32_t seed);
+
+WS_DLL_PUBLIC int Dot11DecryptWepDecrypt(
+ const unsigned char *seed,
+ const size_t seed_len,
+ unsigned char *cypher_text,
+ const size_t data_len);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* crc32.h */
diff --git a/wsutil/crc5.c b/wsutil/crc5.c
new file mode 100644
index 00000000..b62f26b1
--- /dev/null
+++ b/wsutil/crc5.c
@@ -0,0 +1,67 @@
+/* crc5.c
+ * CRC-5 routine
+ *
+ * 2019 Tomasz Mon <desowin@gmail.com>
+ *
+ * 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 <wsutil/crc5.h>
+
+static uint8_t crc5_usb_bits(uint32_t v, int vl, uint8_t ival)
+{
+ /* This function is based on code posted by John Sullivan to Wireshark-dev
+ * mailing list on Jul 21, 2019.
+ *
+ * "One of the properties of LFSRs is that a 1 bit in the input toggles a
+ * completely predictable set of register bits *at any point in the
+ * future*. This isn't often useful for most CRC caculations on variable
+ * sized input, as the cost of working out which those bits are vastly
+ * outweighs most other methods."
+ *
+ * In USB 2.0, the CRC5 is calculated on either 11 or 19 bits inputs,
+ * and thus this approach is viable.
+ */
+ uint8_t rv = ival;
+ static const uint8_t bvals[19] = {
+ 0x1e, 0x15, 0x03, 0x06, 0x0c, 0x18, 0x19, 0x1b,
+ 0x1f, 0x17, 0x07, 0x0e, 0x1c, 0x11, 0x0b, 0x16,
+ 0x05, 0x0a, 0x14
+ };
+
+ for (int i = 0; i < vl; i++) {
+ if (v & (1 << i)) {
+ rv ^= bvals[19 - vl + i];
+ }
+ }
+ return rv;
+}
+
+uint8_t crc5_usb_11bit_input(uint16_t input)
+{
+ return crc5_usb_bits(input, 11, 0x02);
+}
+
+uint8_t crc5_usb_19bit_input(uint32_t input)
+{
+ return crc5_usb_bits(input, 19, 0x1d);
+}
+
+/*
+ * 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/wsutil/crc5.h b/wsutil/crc5.h
new file mode 100644
index 00000000..4f834d53
--- /dev/null
+++ b/wsutil/crc5.h
@@ -0,0 +1,40 @@
+/** @file
+ * Declaration of CRC-5 routines and table
+ *
+ * 2019 Tomasz Mon <desowin@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+*/
+
+#ifndef __CRC5_H__
+#define __CRC5_H__
+
+#include <wireshark.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/** Compute the 5-bit CRC value of a input value matching the CRC-5
+ defined in USB 2.0 Specification. This function calculates the CRC
+ on low 11 bits of the input value. High bits are ignored.
+ @param input Source data for which the CRC-5 should be calculated.
+ @return the CRC5 checksum for input value */
+WS_DLL_PUBLIC uint8_t crc5_usb_11bit_input(uint16_t input);
+
+/** Compute the 5-bit CRC value of a input value matching the CRC-5
+ defined in USB 2.0 Specification. This function calculates the CRC
+ on low 19 bits of the input value. High bits are ignored.
+ @param input Source data for which the CRC-5 should be calculated.
+ @return the CRC5 checksum for input value */
+WS_DLL_PUBLIC uint8_t crc5_usb_19bit_input(uint32_t input);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __CRC5_H__ */
diff --git a/wsutil/crc6.c b/wsutil/crc6.c
new file mode 100644
index 00000000..f55df00a
--- /dev/null
+++ b/wsutil/crc6.c
@@ -0,0 +1,82 @@
+/*
+ * crc6.c
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/*
+ Patch by Ross Jacobs <rossbjacobs@gmail.com>:
+ Fixed CRC6 0x6F lookup table + function per Wireshark bug 14875
+*/
+
+#include "config.h"
+
+#include "crc6.h"
+
+/**
+ * Functions and types for CRC checks.
+ *
+ * Generated on Wed Jan 2 2019,
+ * by pycrc v0.9.1, http://www.tty1.net/pycrc/
+ * using the configuration:
+ * Width = 6
+ * Poly = 0x6f
+ * XorIn = 0
+ * ReflectIn = False
+ * XorOut = 0
+ * ReflectOut = False
+ */
+static const uint8_t crc6_table[256] = {
+ 0x00, 0x2f, 0x31, 0x1e, 0x0d, 0x22, 0x3c, 0x13, 0x1a, 0x35, 0x2b, 0x04, 0x17, 0x38, 0x26, 0x09,
+ 0x34, 0x1b, 0x05, 0x2a, 0x39, 0x16, 0x08, 0x27, 0x2e, 0x01, 0x1f, 0x30, 0x23, 0x0c, 0x12, 0x3d,
+ 0x07, 0x28, 0x36, 0x19, 0x0a, 0x25, 0x3b, 0x14, 0x1d, 0x32, 0x2c, 0x03, 0x10, 0x3f, 0x21, 0x0e,
+ 0x33, 0x1c, 0x02, 0x2d, 0x3e, 0x11, 0x0f, 0x20, 0x29, 0x06, 0x18, 0x37, 0x24, 0x0b, 0x15, 0x3a,
+ 0x0e, 0x21, 0x3f, 0x10, 0x03, 0x2c, 0x32, 0x1d, 0x14, 0x3b, 0x25, 0x0a, 0x19, 0x36, 0x28, 0x07,
+ 0x3a, 0x15, 0x0b, 0x24, 0x37, 0x18, 0x06, 0x29, 0x20, 0x0f, 0x11, 0x3e, 0x2d, 0x02, 0x1c, 0x33,
+ 0x09, 0x26, 0x38, 0x17, 0x04, 0x2b, 0x35, 0x1a, 0x13, 0x3c, 0x22, 0x0d, 0x1e, 0x31, 0x2f, 0x00,
+ 0x3d, 0x12, 0x0c, 0x23, 0x30, 0x1f, 0x01, 0x2e, 0x27, 0x08, 0x16, 0x39, 0x2a, 0x05, 0x1b, 0x34,
+ 0x1c, 0x33, 0x2d, 0x02, 0x11, 0x3e, 0x20, 0x0f, 0x06, 0x29, 0x37, 0x18, 0x0b, 0x24, 0x3a, 0x15,
+ 0x28, 0x07, 0x19, 0x36, 0x25, 0x0a, 0x14, 0x3b, 0x32, 0x1d, 0x03, 0x2c, 0x3f, 0x10, 0x0e, 0x21,
+ 0x1b, 0x34, 0x2a, 0x05, 0x16, 0x39, 0x27, 0x08, 0x01, 0x2e, 0x30, 0x1f, 0x0c, 0x23, 0x3d, 0x12,
+ 0x2f, 0x00, 0x1e, 0x31, 0x22, 0x0d, 0x13, 0x3c, 0x35, 0x1a, 0x04, 0x2b, 0x38, 0x17, 0x09, 0x26,
+ 0x12, 0x3d, 0x23, 0x0c, 0x1f, 0x30, 0x2e, 0x01, 0x08, 0x27, 0x39, 0x16, 0x05, 0x2a, 0x34, 0x1b,
+ 0x26, 0x09, 0x17, 0x38, 0x2b, 0x04, 0x1a, 0x35, 0x3c, 0x13, 0x0d, 0x22, 0x31, 0x1e, 0x00, 0x2f,
+ 0x15, 0x3a, 0x24, 0x0b, 0x18, 0x37, 0x29, 0x06, 0x0f, 0x20, 0x3e, 0x11, 0x02, 0x2d, 0x33, 0x1c,
+ 0x21, 0x0e, 0x10, 0x3f, 0x2c, 0x03, 0x1d, 0x32, 0x3b, 0x14, 0x0a, 0x25, 0x36, 0x19, 0x07, 0x28
+};
+
+/**
+ * CRC6 is used by 3GPP (TS 25.415, TS 25.446) for header CRCs
+ * Poly: D^6 + D^5 + D^3 + D^2 + D^1 + 1
+ *
+ * TS 25.415 docs: https://www.etsi.org/deliver/etsi_ts/125400_125499/125415/04.06.00_60/ts_125415v040600p.pdf
+ * TS 25.446 docs: https://www.etsi.org/deliver/etsi_ts/125400_125499/125446/10.01.00_60/ts_125446v100100p.pdf
+*/
+uint16_t crc6_0X6F(uint16_t crc, const uint8_t *data, int data_len)
+{
+ uint8_t tbl_idx;
+
+ while (data_len--) {
+ tbl_idx = (crc << 2) ^ *data;
+ crc = crc6_table[tbl_idx] & 0x3f;
+ data++;
+ }
+ return crc & 0x3f;
+}
+
+/*
+ * 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/wsutil/crc6.h b/wsutil/crc6.h
new file mode 100644
index 00000000..3795c87e
--- /dev/null
+++ b/wsutil/crc6.h
@@ -0,0 +1,17 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __CRC6_H__
+#define __CRC6_H__
+
+#include <wireshark.h>
+
+WS_DLL_PUBLIC uint16_t crc6_0X6F(uint16_t crc6, const uint8_t *data_blk_ptr, int data_blk_size);
+
+#endif /* __CRC6_H__ */
diff --git a/wsutil/crc7.c b/wsutil/crc7.c
new file mode 100644
index 00000000..0d4e9af3
--- /dev/null
+++ b/wsutil/crc7.c
@@ -0,0 +1,84 @@
+/**
+ * crc7.c
+ *
+ * Functions and types for CRC checks.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ *
+ * Generated on Wed Jul 11 17:24:30 2012,
+ * by pycrc v0.7.10, http://www.tty1.net/pycrc/
+ * using the configuration:
+ * Width = 7
+ * Poly = 0x45
+ * XorIn = 0x00
+ * ReflectIn = False
+ * XorOut = 0x00
+ * ReflectOut = False
+ * Algorithm = table-driven
+ *****************************************************************************/
+#include "config.h"
+
+#include "crc7.h" /* include the header file generated with pycrc */
+
+/**
+ * Static table used for the table_driven implementation.
+ *****************************************************************************/
+static const uint8_t crc_table[256] = {
+ 0x00, 0x8a, 0x9e, 0x14, 0xb6, 0x3c, 0x28, 0xa2, 0xe6, 0x6c, 0x78, 0xf2, 0x50, 0xda, 0xce, 0x44,
+ 0x46, 0xcc, 0xd8, 0x52, 0xf0, 0x7a, 0x6e, 0xe4, 0xa0, 0x2a, 0x3e, 0xb4, 0x16, 0x9c, 0x88, 0x02,
+ 0x8c, 0x06, 0x12, 0x98, 0x3a, 0xb0, 0xa4, 0x2e, 0x6a, 0xe0, 0xf4, 0x7e, 0xdc, 0x56, 0x42, 0xc8,
+ 0xca, 0x40, 0x54, 0xde, 0x7c, 0xf6, 0xe2, 0x68, 0x2c, 0xa6, 0xb2, 0x38, 0x9a, 0x10, 0x04, 0x8e,
+ 0x92, 0x18, 0x0c, 0x86, 0x24, 0xae, 0xba, 0x30, 0x74, 0xfe, 0xea, 0x60, 0xc2, 0x48, 0x5c, 0xd6,
+ 0xd4, 0x5e, 0x4a, 0xc0, 0x62, 0xe8, 0xfc, 0x76, 0x32, 0xb8, 0xac, 0x26, 0x84, 0x0e, 0x1a, 0x90,
+ 0x1e, 0x94, 0x80, 0x0a, 0xa8, 0x22, 0x36, 0xbc, 0xf8, 0x72, 0x66, 0xec, 0x4e, 0xc4, 0xd0, 0x5a,
+ 0x58, 0xd2, 0xc6, 0x4c, 0xee, 0x64, 0x70, 0xfa, 0xbe, 0x34, 0x20, 0xaa, 0x08, 0x82, 0x96, 0x1c,
+ 0xae, 0x24, 0x30, 0xba, 0x18, 0x92, 0x86, 0x0c, 0x48, 0xc2, 0xd6, 0x5c, 0xfe, 0x74, 0x60, 0xea,
+ 0xe8, 0x62, 0x76, 0xfc, 0x5e, 0xd4, 0xc0, 0x4a, 0x0e, 0x84, 0x90, 0x1a, 0xb8, 0x32, 0x26, 0xac,
+ 0x22, 0xa8, 0xbc, 0x36, 0x94, 0x1e, 0x0a, 0x80, 0xc4, 0x4e, 0x5a, 0xd0, 0x72, 0xf8, 0xec, 0x66,
+ 0x64, 0xee, 0xfa, 0x70, 0xd2, 0x58, 0x4c, 0xc6, 0x82, 0x08, 0x1c, 0x96, 0x34, 0xbe, 0xaa, 0x20,
+ 0x3c, 0xb6, 0xa2, 0x28, 0x8a, 0x00, 0x14, 0x9e, 0xda, 0x50, 0x44, 0xce, 0x6c, 0xe6, 0xf2, 0x78,
+ 0x7a, 0xf0, 0xe4, 0x6e, 0xcc, 0x46, 0x52, 0xd8, 0x9c, 0x16, 0x02, 0x88, 0x2a, 0xa0, 0xb4, 0x3e,
+ 0xb0, 0x3a, 0x2e, 0xa4, 0x06, 0x8c, 0x98, 0x12, 0x56, 0xdc, 0xc8, 0x42, 0xe0, 0x6a, 0x7e, 0xf4,
+ 0xf6, 0x7c, 0x68, 0xe2, 0x40, 0xca, 0xde, 0x54, 0x10, 0x9a, 0x8e, 0x04, 0xa6, 0x2c, 0x38, 0xb2
+};
+
+
+
+/**
+ * Update the crc value with new data.
+ *
+ * \param crc The current crc value.
+ * \param data Pointer to a buffer of \a data_len bytes.
+ * \param data_len Number of bytes in the \a data buffer.
+ * \return The updated crc value.
+ *****************************************************************************/
+uint8_t crc7update(uint8_t crc, const unsigned char *data, int data_len)
+{
+ unsigned int tbl_idx;
+
+ while (data_len--) {
+ tbl_idx = ((crc >> 0) ^ *data) & 0xff;
+ crc = (crc_table[tbl_idx] ^ (crc << (8 - 1))) & (0x7f << 1);
+
+ data++;
+ }
+ return crc & (0x7f << 1);
+}
+
+/*
+ * 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/wsutil/crc7.h b/wsutil/crc7.h
new file mode 100644
index 00000000..7364d043
--- /dev/null
+++ b/wsutil/crc7.h
@@ -0,0 +1,76 @@
+/** @file
+ *
+ * Functions and types for CRC checks.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Generated on Wed Jul 11 17:24:57 2012,
+ * by pycrc v0.7.10, http://www.tty1.net/pycrc/
+ * using the configuration:
+ * Width = 7
+ * Poly = 0x45
+ * XorIn = 0x00
+ * ReflectIn = False
+ * XorOut = 0x00
+ * ReflectOut = False
+ * Algorithm = table-driven
+ ****************************************************************************
+ */
+#ifndef __CRC7__H__
+#define __CRC7__H__
+
+#include <wireshark.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * The definition of the used algorithm.
+ *****************************************************************************/
+#define CRC_ALGO_TABLE_DRIVEN 1
+
+/**
+ * Calculate the initial crc value.
+ *
+ * \return The initial crc value.
+ *****************************************************************************/
+static inline uint8_t crc7init(void)
+{
+ return 0x00 << 1;
+}
+
+
+/**
+ * Update the crc value with new data.
+ *
+ * \param crc The current crc value.
+ * \param data Pointer to a buffer of \a data_len bytes.
+ * \param data_len Number of bytes in the \a data buffer.
+ * \return The updated crc value.
+ *****************************************************************************/
+WS_DLL_PUBLIC uint8_t crc7update(uint8_t crc, const unsigned char *data, int data_len);
+
+
+/**
+ * Calculate the final crc value.
+ *
+ * \param crc The current crc value.
+ * \return The final crc value.
+ *****************************************************************************/
+static inline uint8_t crc7finalize(uint8_t crc)
+{
+ return (crc >> 1) ^ 0x00;
+}
+
+
+#ifdef __cplusplus
+} /* closing brace for extern "C" */
+#endif
+
+#endif /* __CRC7__H__ */
diff --git a/wsutil/crc8.c b/wsutil/crc8.c
new file mode 100644
index 00000000..ae150267
--- /dev/null
+++ b/wsutil/crc8.c
@@ -0,0 +1,204 @@
+/* crc8.c
+ * Implementation CRC-8 declarations and routines
+ *
+ * 2011 Roland Knall <rknall@gmail.com>
+ *
+ * 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 <wsutil/crc8.h>
+
+/* @brief Precompiled table for CRC8 values for the polynom 0x2F */
+static const uint8_t crc8_precompiled_2F[256] =
+{
+ 0x00, 0x2F, 0x5E, 0x71, 0xBC, 0x93, 0xE2, 0xCD,
+ 0x57, 0x78, 0x09, 0x26, 0xEB, 0xC4, 0xB5, 0x9A,
+ 0xAE, 0x81, 0xF0, 0xDF, 0x12, 0x3D, 0x4C, 0x63,
+ 0xF9, 0xD6, 0xA7, 0x88, 0x45, 0x6A, 0x1B, 0x34,
+ 0x73, 0x5C, 0x2D, 0x02, 0xCF, 0xE0, 0x91, 0xBE,
+ 0x24, 0x0B, 0x7A, 0x55, 0x98, 0xB7, 0xC6, 0xE9,
+ 0xDD, 0xF2, 0x83, 0xAC, 0x61, 0x4E, 0x3F, 0x10,
+ 0x8A, 0xA5, 0xD4, 0xFB, 0x36, 0x19, 0x68, 0x47,
+ 0xE6, 0xC9, 0xB8, 0x97, 0x5A, 0x75, 0x04, 0x2B,
+ 0xB1, 0x9E, 0xEF, 0xC0, 0x0D, 0x22, 0x53, 0x7C,
+ 0x48, 0x67, 0x16, 0x39, 0xF4, 0xDB, 0xAA, 0x85,
+ 0x1F, 0x30, 0x41, 0x6E, 0xA3, 0x8C, 0xFD, 0xD2,
+ 0x95, 0xBA, 0xCB, 0xE4, 0x29, 0x06, 0x77, 0x58,
+ 0xC2, 0xED, 0x9C, 0xB3, 0x7E, 0x51, 0x20, 0x0F,
+ 0x3B, 0x14, 0x65, 0x4A, 0x87, 0xA8, 0xD9, 0xF6,
+ 0x6C, 0x43, 0x32, 0x1D, 0xD0, 0xFF, 0x8E, 0xA1,
+ 0xE3, 0xCC, 0xBD, 0x92, 0x5F, 0x70, 0x01, 0x2E,
+ 0xB4, 0x9B, 0xEA, 0xC5, 0x08, 0x27, 0x56, 0x79,
+ 0x4D, 0x62, 0x13, 0x3C, 0xF1, 0xDE, 0xAF, 0x80,
+ 0x1A, 0x35, 0x44, 0x6B, 0xA6, 0x89, 0xF8, 0xD7,
+ 0x90, 0xBF, 0xCE, 0xE1, 0x2C, 0x03, 0x72, 0x5D,
+ 0xC7, 0xE8, 0x99, 0xB6, 0x7B, 0x54, 0x25, 0x0A,
+ 0x3E, 0x11, 0x60, 0x4F, 0x82, 0xAD, 0xDC, 0xF3,
+ 0x69, 0x46, 0x37, 0x18, 0xD5, 0xFA, 0x8B, 0xA4,
+ 0x05, 0x2A, 0x5B, 0x74, 0xB9, 0x96, 0xE7, 0xC8,
+ 0x52, 0x7D, 0x0C, 0x23, 0xEE, 0xC1, 0xB0, 0x9F,
+ 0xAB, 0x84, 0xF5, 0xDA, 0x17, 0x38, 0x49, 0x66,
+ 0xFC, 0xD3, 0xA2, 0x8D, 0x40, 0x6F, 0x1E, 0x31,
+ 0x76, 0x59, 0x28, 0x07, 0xCA, 0xE5, 0x94, 0xBB,
+ 0x21, 0x0E, 0x7F, 0x50, 0x9D, 0xB2, 0xC3, 0xEC,
+ 0xD8, 0xF7, 0x86, 0xA9, 0x64, 0x4B, 0x3A, 0x15,
+ 0x8F, 0xA0, 0xD1, 0xFE, 0x33, 0x1C, 0x6D, 0x42
+};
+
+/* @brief Precompiled table for CRC8 values for the polynom 0x37 */
+static const unsigned char crc8_precompiled_37[256] =
+{
+ 0x00, 0x37, 0x6e, 0x59, 0xdc, 0xeb, 0xb2, 0x85,
+ 0x8f, 0xb8, 0xe1, 0xd6, 0x53, 0x64, 0x3d, 0x0a,
+ 0x29, 0x1e, 0x47, 0x70, 0xf5, 0xc2, 0x9b, 0xac,
+ 0xa6, 0x91, 0xc8, 0xff, 0x7a, 0x4d, 0x14, 0x23,
+ 0x52, 0x65, 0x3c, 0x0b, 0x8e, 0xb9, 0xe0, 0xd7,
+ 0xdd, 0xea, 0xb3, 0x84, 0x01, 0x36, 0x6f, 0x58,
+ 0x7b, 0x4c, 0x15, 0x22, 0xa7, 0x90, 0xc9, 0xfe,
+ 0xf4, 0xc3, 0x9a, 0xad, 0x28, 0x1f, 0x46, 0x71,
+ 0xa4, 0x93, 0xca, 0xfd, 0x78, 0x4f, 0x16, 0x21,
+ 0x2b, 0x1c, 0x45, 0x72, 0xf7, 0xc0, 0x99, 0xae,
+ 0x8d, 0xba, 0xe3, 0xd4, 0x51, 0x66, 0x3f, 0x08,
+ 0x02, 0x35, 0x6c, 0x5b, 0xde, 0xe9, 0xb0, 0x87,
+ 0xf6, 0xc1, 0x98, 0xaf, 0x2a, 0x1d, 0x44, 0x73,
+ 0x79, 0x4e, 0x17, 0x20, 0xa5, 0x92, 0xcb, 0xfc,
+ 0xdf, 0xe8, 0xb1, 0x86, 0x03, 0x34, 0x6d, 0x5a,
+ 0x50, 0x67, 0x3e, 0x09, 0x8c, 0xbb, 0xe2, 0xd5,
+ 0x7f, 0x48, 0x11, 0x26, 0xa3, 0x94, 0xcd, 0xfa,
+ 0xf0, 0xc7, 0x9e, 0xa9, 0x2c, 0x1b, 0x42, 0x75,
+ 0x56, 0x61, 0x38, 0x0f, 0x8a, 0xbd, 0xe4, 0xd3,
+ 0xd9, 0xee, 0xb7, 0x80, 0x05, 0x32, 0x6b, 0x5c,
+ 0x2d, 0x1a, 0x43, 0x74, 0xf1, 0xc6, 0x9f, 0xa8,
+ 0xa2, 0x95, 0xcc, 0xfb, 0x7e, 0x49, 0x10, 0x27,
+ 0x04, 0x33, 0x6a, 0x5d, 0xd8, 0xef, 0xb6, 0x81,
+ 0x8b, 0xbc, 0xe5, 0xd2, 0x57, 0x60, 0x39, 0x0e,
+ 0xdb, 0xec, 0xb5, 0x82, 0x07, 0x30, 0x69, 0x5e,
+ 0x54, 0x63, 0x3a, 0x0d, 0x88, 0xbf, 0xe6, 0xd1,
+ 0xf2, 0xc5, 0x9c, 0xab, 0x2e, 0x19, 0x40, 0x77,
+ 0x7d, 0x4a, 0x13, 0x24, 0xa1, 0x96, 0xcf, 0xf8,
+ 0x89, 0xbe, 0xe7, 0xd0, 0x55, 0x62, 0x3b, 0x0c,
+ 0x06, 0x31, 0x68, 0x5f, 0xda, 0xed, 0xb4, 0x83,
+ 0xa0, 0x97, 0xce, 0xf9, 0x7c, 0x4b, 0x12, 0x25,
+ 0x2f, 0x18, 0x41, 0x76, 0xf3, 0xc4, 0x9d, 0xaa,
+};
+
+/* @brief Precompiled table for CRC8 values for the polynom 0x3B */
+static const unsigned char crc8_precompiled_3b[256] =
+{
+ 0x00, 0x3b, 0x76, 0x4d, 0xec, 0xd7, 0x9a, 0xa1,
+ 0xe3, 0xd8, 0x95, 0xae, 0x0f, 0x34, 0x79, 0x42,
+ 0xfd, 0xc6, 0x8b, 0xb0, 0x11, 0x2a, 0x67, 0x5c,
+ 0x1e, 0x25, 0x68, 0x53, 0xf2, 0xc9, 0x84, 0xbf,
+ 0xc1, 0xfa, 0xb7, 0x8c, 0x2d, 0x16, 0x5b, 0x60,
+ 0x22, 0x19, 0x54, 0x6f, 0xce, 0xf5, 0xb8, 0x83,
+ 0x3c, 0x07, 0x4a, 0x71, 0xd0, 0xeb, 0xa6, 0x9d,
+ 0xdf, 0xe4, 0xa9, 0x92, 0x33, 0x08, 0x45, 0x7e,
+ 0xb9, 0x82, 0xcf, 0xf4, 0x55, 0x6e, 0x23, 0x18,
+ 0x5a, 0x61, 0x2c, 0x17, 0xb6, 0x8d, 0xc0, 0xfb,
+ 0x44, 0x7f, 0x32, 0x09, 0xa8, 0x93, 0xde, 0xe5,
+ 0xa7, 0x9c, 0xd1, 0xea, 0x4b, 0x70, 0x3d, 0x06,
+ 0x78, 0x43, 0x0e, 0x35, 0x94, 0xaf, 0xe2, 0xd9,
+ 0x9b, 0xa0, 0xed, 0xd6, 0x77, 0x4c, 0x01, 0x3a,
+ 0x85, 0xbe, 0xf3, 0xc8, 0x69, 0x52, 0x1f, 0x24,
+ 0x66, 0x5d, 0x10, 0x2b, 0x8a, 0xb1, 0xfc, 0xc7,
+ 0x49, 0x72, 0x3f, 0x04, 0xa5, 0x9e, 0xd3, 0xe8,
+ 0xaa, 0x91, 0xdc, 0xe7, 0x46, 0x7d, 0x30, 0x0b,
+ 0xb4, 0x8f, 0xc2, 0xf9, 0x58, 0x63, 0x2e, 0x15,
+ 0x57, 0x6c, 0x21, 0x1a, 0xbb, 0x80, 0xcd, 0xf6,
+ 0x88, 0xb3, 0xfe, 0xc5, 0x64, 0x5f, 0x12, 0x29,
+ 0x6b, 0x50, 0x1d, 0x26, 0x87, 0xbc, 0xf1, 0xca,
+ 0x75, 0x4e, 0x03, 0x38, 0x99, 0xa2, 0xef, 0xd4,
+ 0x96, 0xad, 0xe0, 0xdb, 0x7a, 0x41, 0x0c, 0x37,
+ 0xf0, 0xcb, 0x86, 0xbd, 0x1c, 0x27, 0x6a, 0x51,
+ 0x13, 0x28, 0x65, 0x5e, 0xff, 0xc4, 0x89, 0xb2,
+ 0x0d, 0x36, 0x7b, 0x40, 0xe1, 0xda, 0x97, 0xac,
+ 0xee, 0xd5, 0x98, 0xa3, 0x02, 0x39, 0x74, 0x4f,
+ 0x31, 0x0a, 0x47, 0x7c, 0xdd, 0xe6, 0xab, 0x90,
+ 0xd2, 0xe9, 0xa4, 0x9f, 0x3e, 0x05, 0x48, 0x73,
+ 0xcc, 0xf7, 0xba, 0x81, 0x20, 0x1b, 0x56, 0x6d,
+ 0x2f, 0x14, 0x59, 0x62, 0xc3, 0xf8, 0xb5, 0x8e,
+};
+/** Calculates a CRC8 checksum for the given buffer with the polynom
+ * stored in the crc_table
+ * @param buf a pointer to a buffer of the given length
+ * @param len the length of the given buffer
+ * @param seed The seed to use.
+ * @param crc_table a table storing 256 entries for crc8 checksums
+ * @return the CRC8 checksum for the buffer
+ */
+static uint8_t crc8_precompiled(const uint8_t *buf, uint32_t len, uint8_t seed, const uint8_t crc_table[])
+{
+ uint8_t crc;
+
+ crc = seed;
+ while(len-- > 0)
+ crc = crc_table[(uint8_t)(*buf++) ^ crc];
+
+ return crc;
+}
+
+/** Calculates a CRC8 checksum for the given buffer with the polynom
+ * 0x2F using the precompiled CRC table
+ * @param buf a pointer to a buffer of the given length
+ * @param len the length of the given buffer
+ * @param seed The seed to use.
+ * @return the CRC8 checksum for the buffer
+ */
+uint8_t crc8_0x2F(const uint8_t *buf, uint32_t len, uint8_t seed)
+{
+ return crc8_precompiled(buf, len, seed, crc8_precompiled_2F);
+}
+
+/** Calculates a CRC8 checksum for the given buffer with the polynom
+ * 0x37 using the precompiled CRC table
+ * @param buf a pointer to a buffer of the given length
+ * @param len the length of the given buffer
+ * @param seed The seed to use.
+ * @return the CRC8 checksum for the buffer
+ */
+uint8_t crc8_0x37(const uint8_t *buf, uint32_t len, uint8_t seed)
+{
+ uint8_t crc = seed;
+ while (len-- > 0)
+ {
+ crc = crc8_precompiled_37[(crc ^ *buf++)];
+ }
+ return (crc);
+}
+
+/** Calculates a CRC8 checksum for the given buffer with the polynom
+ * 0x3B using the precompiled CRC table
+ * @param buf a pointer to a buffer of the given length
+ * @param len the length of the given buffer
+ * @param seed The seed to use.
+ * @return the CRC8 checksum for the buffer
+ */
+uint8_t crc8_0x3B(const uint8_t *buf, uint32_t len, uint8_t seed)
+{
+ uint8_t crc = seed;
+ while (len-- > 0)
+ {
+ crc = crc8_precompiled_3b[(crc ^ *buf++)];
+ }
+ return (crc);
+}
+
+/*
+ * 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/wsutil/crc8.h b/wsutil/crc8.h
new file mode 100644
index 00000000..8695640b
--- /dev/null
+++ b/wsutil/crc8.h
@@ -0,0 +1,53 @@
+/** @file
+ * Declaration of CRC-8 routine and tables
+ *
+ * 2011 Roland Knall <rknall@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __CRC8_H__
+#define __CRC8_H__
+
+#include <wireshark.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/** Calculates a CRC8 checksum for the given buffer with the polynom
+ * 0x2F using the precompiled CRC table
+ * @param buf a pointer to a buffer of the given length
+ * @param len the length of the given buffer
+ * @param seed The seed to use.
+ * @return the CRC8 checksum for the buffer
+ */
+WS_DLL_PUBLIC uint8_t crc8_0x2F(const uint8_t *buf, uint32_t len, uint8_t seed);
+
+/** Calculates a CRC8 checksum for the given buffer with the polynom
+ * 0x37 using the precompiled CRC table
+ * @param buf a pointer to a buffer of the given length
+ * @param len the length of the given buffer
+ * @param seed The seed to use.
+ * @return the CRC8 checksum for the buffer
+ */
+WS_DLL_PUBLIC uint8_t crc8_0x37(const uint8_t *buf, uint32_t len, uint8_t seed);
+
+/** Calculates a CRC8 checksum for the given buffer with the polynom
+ * 0x3B using the precompiled CRC table
+ * @param buf a pointer to a buffer of the given length
+ * @param len the length of the given buffer
+ * @param seed The seed to use.
+ * @return the CRC8 checksum for the buffer
+ */
+WS_DLL_PUBLIC uint8_t crc8_0x3B(const uint8_t *buf, uint32_t len, uint8_t seed);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* crc8.h */
diff --git a/wsutil/curve25519.c b/wsutil/curve25519.c
new file mode 100644
index 00000000..d40c0cfa
--- /dev/null
+++ b/wsutil/curve25519.c
@@ -0,0 +1,102 @@
+/* curve25519.c
+ * NaCl/Sodium-compatible API for Curve25519 cryptography.
+ *
+ * Copyright (c) 2018, Peter Wu <peter@lekensteyn.nl>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "curve25519.h"
+#include <gcrypt.h>
+
+static inline void
+copy_and_reverse(unsigned char *dest, const unsigned char *src, size_t n)
+{
+ for (size_t i = 0; i < n; i++) {
+ dest[n - 1 - i] = src[i];
+ }
+}
+
+static int
+x25519_mpi(unsigned char *q, const unsigned char *n, gcry_mpi_t mpi_p)
+{
+ unsigned char priv_be[32];
+ unsigned char result_be[32];
+ size_t result_len = 0;
+ gcry_mpi_t mpi = NULL;
+ gcry_ctx_t ctx = NULL;
+ gcry_mpi_point_t P = NULL;
+ gcry_mpi_point_t Q = NULL;
+ int r = -1;
+
+ /* Default to infinity (all zeroes). */
+ memset(q, 0, 32);
+
+ /* Keys are in little-endian, but gcry_mpi_scan expects big endian. Convert
+ * keys and ensure that the result is a valid Curve25519 secret scalar. */
+ copy_and_reverse(priv_be, n, 32);
+ priv_be[0] &= 127;
+ priv_be[0] |= 64;
+ priv_be[31] &= 248;
+ gcry_mpi_scan(&mpi, GCRYMPI_FMT_USG, priv_be, 32, NULL);
+
+ if (gcry_mpi_ec_new(&ctx, NULL, "Curve25519")) {
+ /* Should not happen, possibly out-of-memory. */
+ goto leave;
+ }
+
+ /* Compute Q = nP */
+ Q = gcry_mpi_point_new(0);
+ P = gcry_mpi_point_set(NULL, mpi_p, NULL, GCRYMPI_CONST_ONE);
+ gcry_mpi_ec_mul(Q, mpi, P, ctx);
+
+ /* Note: mpi is reused to store the result. */
+ if (gcry_mpi_ec_get_affine(mpi, NULL, Q, ctx)) {
+ /* Infinity. */
+ goto leave;
+ }
+
+ if (gcry_mpi_print(GCRYMPI_FMT_USG, result_be, 32, &result_len, mpi)) {
+ /* Should not happen, possibly out-of-memory. */
+ goto leave;
+ }
+ copy_and_reverse(q, result_be, result_len);
+ r = 0;
+
+leave:
+ gcry_mpi_point_release(P);
+ gcry_mpi_point_release(Q);
+ gcry_ctx_release(ctx);
+ gcry_mpi_release(mpi);
+ /* XXX erase priv_be and result_be */
+ return r;
+}
+
+int
+crypto_scalarmult_curve25519(unsigned char *q, const unsigned char *n,
+ const unsigned char *p)
+{
+ unsigned char p_be[32];
+ gcry_mpi_t mpi_p = NULL;
+
+ copy_and_reverse(p_be, p, 32);
+ /* Clear unused bit. */
+ p_be[0] &= 0x7f;
+ gcry_mpi_scan(&mpi_p, GCRYMPI_FMT_USG, p_be, 32, NULL);
+ int r = x25519_mpi(q, n, mpi_p);
+ gcry_mpi_release(mpi_p);
+ return r;
+}
+
+int
+crypto_scalarmult_curve25519_base(unsigned char *q, const unsigned char *n)
+{
+ gcry_mpi_t mpi_basepoint_x = gcry_mpi_set_ui(NULL, 9);
+ int r = x25519_mpi(q, n, mpi_basepoint_x);
+ gcry_mpi_release(mpi_basepoint_x);
+ return r;
+}
diff --git a/wsutil/curve25519.h b/wsutil/curve25519.h
new file mode 100644
index 00000000..1eda0200
--- /dev/null
+++ b/wsutil/curve25519.h
@@ -0,0 +1,34 @@
+/** @file
+ * NaCl/Sodium-compatible API for Curve25519 cryptography.
+ *
+ * Copyright (c) 2018, Peter Wu <peter@lekensteyn.nl>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __CURVE25519_H__
+#define __CURVE25519_H__
+
+#include <wireshark.h>
+
+/*
+ * Computes Q = X25519(n, P). In other words, given the secret key n, the public
+ * key P, compute the shared secret Q. Each key is 32 bytes long.
+ * Returns 0 on success or -1 on failure.
+ */
+WS_DLL_PUBLIC
+int crypto_scalarmult_curve25519(unsigned char *q, const unsigned char *n,
+ const unsigned char *p);
+
+/*
+ * Computes the Curve25519 32-byte public key Q from the 32-byte secret key n.
+ * Returns 0 on success or -1 on failure.
+ */
+WS_DLL_PUBLIC
+int crypto_scalarmult_curve25519_base(unsigned char *q, const unsigned char *n);
+
+#endif /* __CURVE25519_H__ */
diff --git a/wsutil/dot11decrypt_wep.c b/wsutil/dot11decrypt_wep.c
new file mode 100644
index 00000000..37473750
--- /dev/null
+++ b/wsutil/dot11decrypt_wep.c
@@ -0,0 +1,86 @@
+/* dot11decrypt_wep.c
+ *
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2006 CACE Technologies, Davis (California)
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "config.h"
+
+/************************************************************************/
+/* File includes */
+
+#include "crc32.h"
+
+/************************************************************************/
+/* Note: copied from net80211/ieee80211_airpdcap_tkip.c */
+#define S_SWAP(a,b) { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; }
+
+/* Note: copied from FreeBSD source code, RELENG 6, */
+/* sys/net80211/ieee80211_crypto_wep.c, 391 */
+int Dot11DecryptWepDecrypt(
+ const unsigned char *seed,
+ const size_t seed_len,
+ unsigned char *cypher_text,
+ const size_t data_len)
+{
+ uint32_t i, j, k, crc;
+ uint8_t S[256];
+ uint8_t icv[4];
+ size_t buflen;
+
+ /* Generate key stream (RC4 Pseudo-Random Number Generator) */
+ for (i = 0; i < 256; i++)
+ S[i] = (uint8_t)i;
+ for (j = i = 0; i < 256; i++) {
+ j = (j + S[i] + seed[i % seed_len]) & 0xff;
+ S_SWAP(i, j);
+ }
+
+ /* Apply RC4 to data and compute CRC32 over decrypted data */
+ crc = ~(uint32_t)0;
+ buflen = data_len;
+
+ for (i = j = k = 0; k < buflen; k++) {
+ i = (i + 1) & 0xff;
+ j = (j + S[i]) & 0xff;
+ S_SWAP(i, j);
+ *cypher_text ^= S[(S[i] + S[j]) & 0xff];
+ crc = crc32_ccitt_table_lookup((crc ^ *cypher_text) & 0xff) ^ (crc >> 8);
+ cypher_text++;
+ }
+
+ crc = ~crc;
+
+ /* Encrypt little-endian CRC32 and verify that it matches with the received ICV */
+ icv[0] = (uint8_t)crc;
+ icv[1] = (uint8_t)(crc >> 8);
+ icv[2] = (uint8_t)(crc >> 16);
+ icv[3] = (uint8_t)(crc >> 24);
+ for (k = 0; k < 4; k++) {
+ i = (i + 1) & 0xff;
+ j = (j + S[i]) & 0xff;
+ S_SWAP(i, j);
+ if ((icv[k] ^ S[(S[i] + S[j]) & 0xff]) != *cypher_text++) {
+ /* ICV mismatch - drop frame */
+ return 1/*DOT11DECRYPT_RET_UNSUCCESS*/;
+ }
+ }
+
+ return 0/*DOT11DECRYPT_RET_SUCCESS*/;
+}
+
+/*
+ * 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:
+ */
diff --git a/wsutil/eax.c b/wsutil/eax.c
new file mode 100644
index 00000000..208cf4e4
--- /dev/null
+++ b/wsutil/eax.c
@@ -0,0 +1,249 @@
+/* eax.c
+ * Encryption and decryption routines implementing the EAX' encryption mode
+ * Copyright 2010, Edward J. Beroset, edward.j.beroset@us.elster.com
+ *
+ * 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 "eax.h"
+#include <stdlib.h>
+#include <string.h>
+/* Use libgcrypt for cipher libraries. */
+#include <gcrypt.h>
+
+typedef struct {
+ uint8_t L[EAX_SIZEOF_KEY];
+ uint8_t D[EAX_SIZEOF_KEY];
+ uint8_t Q[EAX_SIZEOF_KEY];
+} eax_s;
+
+static eax_s instance;
+
+/* these are defined as macros so they'll be easy to redo in assembly if desired */
+#define BLK_CPY(dst, src) { memcpy(dst, src, EAX_SIZEOF_KEY); }
+#define BLK_XOR(dst, src) { int z; for (z=0; z < EAX_SIZEOF_KEY; z++) dst[z] ^= src[z]; }
+static void Dbl(uint8_t *out, const uint8_t *in);
+static void CTR(const uint8_t *ws, uint8_t *pK, uint8_t *pN, uint16_t SizeN);
+static void CMAC(uint8_t *pK, uint8_t *ws, const uint8_t *pN, uint16_t SizeN);
+static void dCMAC(uint8_t *pK, uint8_t *ws, const uint8_t *pN, uint16_t SizeN, const uint8_t *pC, uint16_t SizeC);
+void AesEncrypt(unsigned char msg[EAX_SIZEOF_KEY], unsigned char key[EAX_SIZEOF_KEY]);
+
+/*!
+ Decrypts cleartext data using EAX' mode (see ANSI Standard C12.22-2008).
+
+ @param[in] pN pointer to cleartext (canonified form)
+ @param[in] pK pointer to secret key
+ @param[in,out] pC pointer to ciphertext
+ @param[in] SizeN byte length of cleartext (pN) buffer
+ @param[in] SizeK byte length of secret key (pK)
+ @param[in] SizeC byte length of ciphertext (pC) buffer
+ @param[in] pMac four-byte Message Authentication Code
+ @param[in] Mode EAX_MODE_CLEARTEXT_AUTH or EAX_MODE_CIPHERTEXT_AUTH
+ @return true if message has been authenticated; false if not
+ authenticated, invalid Mode or error
+ */
+bool Eax_Decrypt(uint8_t *pN, uint8_t *pK, uint8_t *pC,
+ uint32_t SizeN, uint32_t SizeK, uint32_t SizeC, MAC_T *pMac,
+ uint8_t Mode)
+{
+ uint8_t wsn[EAX_SIZEOF_KEY];
+ uint8_t wsc[EAX_SIZEOF_KEY];
+ int i;
+
+ /* key size must match this implementation */
+ if (SizeK != EAX_SIZEOF_KEY)
+ return false;
+
+ /* the key is new */
+ for (i = 0; i < EAX_SIZEOF_KEY; i++)
+ instance.L[i] = 0;
+ AesEncrypt(instance.L, pK);
+ Dbl(instance.D, instance.L);
+ Dbl(instance.Q, instance.D);
+ /* the key is set up */
+ /* first copy the nonce into our working space */
+ BLK_CPY(wsn, instance.D);
+ if (Mode == EAX_MODE_CLEARTEXT_AUTH) {
+ dCMAC(pK, wsn, pN, SizeN, pC, SizeC);
+ } else {
+ CMAC(pK, wsn, pN, SizeN);
+ }
+ /*
+ * In authentication mode the inputs are: pN, pK (and associated sizes),
+ * the result is the 4 byte MAC.
+ */
+ if (Mode == EAX_MODE_CLEARTEXT_AUTH)
+ {
+ return (memcmp(pMac, &wsn[EAX_SIZEOF_KEY-sizeof(*pMac)], sizeof(*pMac)) ? false : true);
+
+ }
+
+ /*
+ * In cipher mode the inputs are: pN, pK, pP (and associated sizes),
+ * the results are pC (and its size) along with the 4 byte MAC.
+ */
+ else if (Mode == EAX_MODE_CIPHERTEXT_AUTH)
+ {
+ if (SizeC == 0)
+ return (memcmp(pMac, &wsn[EAX_SIZEOF_KEY-sizeof(*pMac)], sizeof(*pMac)) ? false : true);
+ {
+ /* first copy the nonce into our working space */
+ BLK_CPY(wsc, instance.Q);
+ CMAC(pK, wsc, pC, SizeC);
+ BLK_XOR(wsc, wsn);
+ }
+ if (memcmp(pMac, &wsc[EAX_SIZEOF_KEY-sizeof(*pMac)], sizeof(*pMac)) == 0)
+ {
+ CTR(wsn, pK, pC, SizeC);
+ return true;
+ }
+ }
+ return false;
+}
+
+/* set up D or Q from L */
+static void Dbl(uint8_t *out, const uint8_t *in)
+{
+ int i;
+ uint8_t carry = 0;
+
+ /* this might be a lot more efficient in assembly language */
+ for (i=0; i < EAX_SIZEOF_KEY; i++)
+ {
+ out[i] = ( in[i] << 1 ) | carry;
+ carry = (in[i] & 0x80) ? 1 : 0;
+ }
+ if (carry)
+ out[0] ^= 0x87;
+}
+
+static void CMAC(uint8_t *pK, uint8_t *ws, const uint8_t *pN, uint16_t SizeN)
+{
+ dCMAC(pK, ws, pN, SizeN, NULL, 0);
+}
+
+static void dCMAC(uint8_t *pK, uint8_t *ws, const uint8_t *pN, uint16_t SizeN, const uint8_t *pC, uint16_t SizeC)
+{
+ gcry_cipher_hd_t cipher_hd;
+ uint8_t *work;
+ uint8_t *ptr;
+ uint16_t SizeT = SizeN + SizeC;
+ uint16_t worksize = SizeT;
+
+ /* worksize must be an integral multiple of 16 */
+ if (SizeT & 0xf) {
+ worksize += 0x10 - (worksize & 0xf);
+ }
+ work = (uint8_t *)g_malloc(worksize);
+ if (work == NULL) {
+ return;
+ }
+ memcpy(work, pN, SizeN);
+ if (pC != NULL) {
+ memcpy(&work[SizeN], pC, SizeC);
+ }
+ /*
+ * pad the data if necessary, and XOR Q or D, depending on
+ * whether data was padded or not
+ */
+ if (worksize != SizeT) {
+ work[SizeT] = 0x80;
+ for (ptr = &work[SizeT+1]; ptr < &work[worksize]; ptr++)
+ *ptr = 0;
+ ptr= &work[worksize-0x10];
+ BLK_XOR(ptr, instance.Q);
+ } else {
+ ptr = &work[worksize-0x10];
+ BLK_XOR(ptr, instance.D);
+ }
+ /* open the cipher */
+ if (gcry_cipher_open(&cipher_hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC,0)){/* GCRY_CIPHER_CBC_MAC)) { */
+ g_free(work);
+ return;
+ }
+ if (gcry_cipher_setkey(cipher_hd, pK, EAX_SIZEOF_KEY)) {
+ g_free(work);
+ gcry_cipher_close(cipher_hd);
+ return;
+ }
+ if (gcry_cipher_setiv(cipher_hd, ws, EAX_SIZEOF_KEY)) {
+ g_free(work);
+ gcry_cipher_close(cipher_hd);
+ return;
+ }
+ if (gcry_cipher_encrypt(cipher_hd, work, worksize, work, worksize)) {
+ g_free(work);
+ gcry_cipher_close(cipher_hd);
+ return;
+ }
+ memcpy(ws, ptr, EAX_SIZEOF_KEY);
+
+ g_free(work);
+ gcry_cipher_close(cipher_hd);
+ return;
+}
+
+static void CTR(const uint8_t *ws, uint8_t *pK, uint8_t *pN, uint16_t SizeN)
+{
+ gcry_cipher_hd_t cipher_hd;
+ uint8_t ctr[EAX_SIZEOF_KEY];
+
+ BLK_CPY(ctr, ws);
+ ctr[12] &= 0x7f;
+ ctr[14] &= 0x7f;
+ /* open the cipher */
+ if (gcry_cipher_open(&cipher_hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CTR, 0)) {
+ return;
+ }
+ if (gcry_cipher_setkey(cipher_hd, pK, EAX_SIZEOF_KEY)) {
+ gcry_cipher_close(cipher_hd);
+ return;
+ }
+ if (gcry_cipher_setctr(cipher_hd, ctr, EAX_SIZEOF_KEY)) {
+ gcry_cipher_close(cipher_hd);
+ return;
+ }
+ if (gcry_cipher_encrypt(cipher_hd, pN, SizeN, pN, SizeN)) {
+ gcry_cipher_close(cipher_hd);
+ return;
+ }
+ gcry_cipher_close(cipher_hd);
+ return;
+}
+
+void AesEncrypt(unsigned char msg[EAX_SIZEOF_KEY], unsigned char key[EAX_SIZEOF_KEY])
+{
+ gcry_cipher_hd_t cipher_hd;
+
+ /* open the cipher */
+ if (gcry_cipher_open(&cipher_hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_ECB, 0)) {
+ return;
+ }
+ if (gcry_cipher_setkey(cipher_hd, key, EAX_SIZEOF_KEY)) {
+ gcry_cipher_close(cipher_hd);
+ return;
+ }
+ if (gcry_cipher_encrypt(cipher_hd, msg, EAX_SIZEOF_KEY, msg, EAX_SIZEOF_KEY)) {
+ gcry_cipher_close(cipher_hd);
+ return;
+ }
+ gcry_cipher_close(cipher_hd);
+ return;
+}
+
+/*
+ * 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/wsutil/eax.h b/wsutil/eax.h
new file mode 100644
index 00000000..ccf4dc62
--- /dev/null
+++ b/wsutil/eax.h
@@ -0,0 +1,46 @@
+/** @file
+ * Encryption and decryption routines implementing the EAX' encryption mode
+ * Copyright 2010, Edward J. Beroset, edward.j.beroset@us.elster.com
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef _EAX_H
+#define _EAX_H
+
+#include <wireshark.h>
+
+typedef struct tagMAC_T
+{
+ uint8_t Mac[4];
+} MAC_T;
+
+#define EAX_MODE_CLEARTEXT_AUTH 1
+#define EAX_MODE_CIPHERTEXT_AUTH 2
+
+#define EAX_SIZEOF_KEY 16
+
+/*!
+ Decrypts cleartext data using EAX' mode (see ANSI Standard C12.22-2008).
+
+ @param[in] pN pointer to cleartext (canonified form)
+ @param[in] pK pointer to secret key
+ @param[in,out] pC pointer to ciphertext
+ @param[in] SizeN byte length of cleartext (pN) buffer
+ @param[in] SizeK byte length of secret key (pK)
+ @param[in] SizeC byte length of ciphertext (pC) buffer
+ @param[in] pMac four-byte Message Authentication Code
+ @param[in] Mode EAX_MODE_CLEARTEXT_AUTH or EAX_MODE_CIPHERTEXT_AUTH
+ @return true if message has been authenticated; false if not
+ authenticated, invalid Mode or error
+ */
+WS_DLL_PUBLIC
+bool Eax_Decrypt(uint8_t *pN, uint8_t *pK, uint8_t *pC,
+ uint32_t SizeN, uint32_t SizeK, uint32_t SizeC, MAC_T *pMac,
+ uint8_t Mode);
+
+#endif
diff --git a/wsutil/epochs.h b/wsutil/epochs.h
new file mode 100644
index 00000000..17d62728
--- /dev/null
+++ b/wsutil/epochs.h
@@ -0,0 +1,67 @@
+/** @file
+ *
+ * Definitions of epoch values for various absolute time types.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2006 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __EPOCHS_H__
+#define __EPOCHS_H__
+
+#include <glib.h>
+
+/*
+ * Deltas between the epochs for various non-UN*X time stamp formats and
+ * the January 1, 1970, 00:00:00 (proleptic?) UTC epoch for the UN*X time
+ * stamp format.
+ */
+
+/*
+ * 1900-01-01 00:00:00 (proleptic?) UTC.
+ * Used by a number of time formats.
+ */
+#define EPOCH_DELTA_1900_01_01_00_00_00_UTC 2208988800U
+
+/*
+ * 1904-01-01 00:00:00 (proleptic?) UTC.
+ * Used in the classic Mac OS, and by formats, such as MPEG-4 Part 14 (MP4),
+ * which is based on Apple's QuickTime format.
+ */
+#define EPOCH_DELTA_1904_01_01_00_00_00_UTC 2082844800U
+
+/*
+ * 1601-01-01 (proleptic Gregorian) 00:00:00 (proleptic?) UTC.
+ * The Windows NT epoch, used in a number of places, as it is
+ * the start of a 400 year Gregorian cycle.
+ *
+ * This is
+ *
+ * 369*365.25*24*60*60-(3*24*60*60+6*60*60)
+ *
+ * or equivalently,
+ *
+ * (89*4*365.25+(3*4+1)*365)*24*60*60
+ *
+ * 1970-1601 is 369; 365.25 is the average length of a year in days,
+ * including leap years.
+ *
+ * 369 = 4*92 + 1, so there are 92 groups of 4 consecutive years plus
+ * one leftover year, 1969, with 365 days.
+ *
+ * All but three of the groups of 4 consecutive years average 365.25 days
+ * per year, as they have one leap year in the group. However, 1700, 1800,
+ * and 1900 were not leap years, as, while they're all evenly divisible by 4,
+ * they're also evenly divisible by 100, but not evenly divisible by 400.
+ *
+ * So we have 89 groups of 4 consecutive years that average 365.25
+ * days per year, 3 groups of 4 consecutive years that average 365 days
+ * (as they lack a leap year), and one leftover year, 1969, that is
+ * 365 days long.
+ */
+#define EPOCH_DELTA_1601_01_01_00_00_00_UTC G_GUINT64_CONSTANT(11644473600)
+
+#endif /* __EPOCHS_H__ */
diff --git a/wsutil/exported_pdu_tlvs.h b/wsutil/exported_pdu_tlvs.h
new file mode 100644
index 00000000..4850be8e
--- /dev/null
+++ b/wsutil/exported_pdu_tlvs.h
@@ -0,0 +1,198 @@
+/** @file
+ *
+ * Definitions for exported_pdu TLVs
+ * Copyright 2013, Anders Broman <anders-broman@ericsson.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef EXPORTED_PDU_TLVS_H
+#define EXPORTED_PDU_TLVS_H
+
+/**
+ * This is the format of the link-layer header of packets of type
+ * LINKTYPE_WIRESHARK_UPPER_PDU in pcap and pcapng files.
+ *
+ * It is a sequence of TLVs; at least one TLV MUST indicate what protocol is
+ * in the PDU following the TLVs.
+ *
+ * Each TLV contains, in order:
+ *
+ * a 2-byte big-endian type field;
+ * a 2-byte big-endian length field;
+ * a value, the length of which is indicated by the value of
+ * the length field (that value does not include the length
+ * of the type or length fields themselves).
+ *
+ * Buffer layout:
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Option Code | Option Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * / Option Value /
+ * / variable length, aligned to 32 bits /
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * / /
+ * / . . . other options . . . /
+ * / /
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Option Code == opt_endofopt | Option Length == 0 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * The list of TLVs may begin with a TLV of type EXP_PDU_TAG_OPTIONS_LENGTH;
+ * its value is a 4-byte integer value, giving the length of all TLVs
+ * following that TLV (i.e., the length does not include the length of
+ * the EXP_PDU_TAG_OPTIONS_LENGTH TLV). This tag is deprecated; it is
+ * not guaranteed to be present, and code reading packets should not
+ * require it to be present.
+ *
+ * The last TLV is of type EXP_PDU_TAG_END_OF_OPT; it has a length
+ * of 0, and the value is zero-length.
+ *
+ * For string values, a string may have zero, one, or more null bytes
+ * at the end; code that reads the string value must not assume that
+ * there are, or are not, null bytes at the end. Null bytes are included
+ * in the length field, but are not part of the string value.
+ *
+ * For integral values, the values are in big-endian format.
+ */
+
+/* Tag values
+ *
+ * Do NOT add new values to this list without asking
+ * wireshark-dev[AT]wireshark.org for a value. Otherwise, you run the risk of
+ * using a value that's already being used for some other purpose, and of
+ * having tools that read exported_pdu captures not being able to handle
+ * captures with your new tag value, with no hope that they will ever be
+ * changed to do so (as that would destroy their ability to read captures
+ * using that value for that other purpose).
+ */
+#define EXP_PDU_TAG_END_OF_OPT 0 /**< End-of-options Tag. */
+/* 1 - 9 reserved */
+#define EXP_PDU_TAG_OPTIONS_LENGTH 10 /**< Total length of the options excluding this TLV
+ * Deprecated - do not use
+ */
+#define EXP_PDU_TAG_LINKTYPE 11 /**< Deprecated - do not use */
+#define EXP_PDU_TAG_DISSECTOR_NAME 12 /**< The value part should be an ASCII non NULL terminated string
+ * of the registered dissector used by Wireshark e.g "sip"
+ * Will be used to call the next dissector.
+ * NOTE: this is NOT a protocol name;
+ * a given protocol may have multiple
+ * dissectors, if, for example, the
+ * protocol headers depend on the
+ * protocol being used to transport
+ * the protocol in question.
+ */
+#define EXP_PDU_TAG_HEUR_DISSECTOR_NAME 13 /**< The value part should be an ASCII non NULL terminated string
+ * containing the heuristic dissector unique short name given
+ * during registration, e.g "sip_udp"
+ * Will be used to call the next dissector.
+ */
+#define EXP_PDU_TAG_DISSECTOR_TABLE_NAME 14 /**< The value part should be an ASCII non NULL terminated string
+ * containing the dissector table name given
+ * during registration, e.g "gsm_map.v3.arg.opcode"
+ * Will be used to call the next dissector.
+ */
+
+/* For backwards source compatibility */
+#define EXP_PDU_TAG_PROTO_NAME EXP_PDU_TAG_DISSECTOR_NAME
+#define EXP_PDU_TAG_HEUR_PROTO_NAME EXP_PDU_TAG_HEUR_DISSECTOR_NAME
+
+/* Add protocol type related tags here.
+ * NOTE Only one protocol type tag may be present in a packet, the first one
+ * found will be used*/
+/* 13 - 19 reserved */
+#define EXP_PDU_TAG_IPV4_SRC 20 /**< IPv4 source address - 4 bytes */
+#define EXP_PDU_TAG_IPV4_DST 21 /**< IPv4 destination address - 4 bytes */
+#define EXP_PDU_TAG_IPV6_SRC 22 /**< IPv6 source address - 16 bytes */
+#define EXP_PDU_TAG_IPV6_DST 23 /**< IPv6 destination address - 16 bytes */
+
+/* Port type values for EXP_PDU_TAG_PORT_TYPE; these do not necessarily
+ * correspond to port type values inside libwireshark. */
+#define EXP_PDU_PT_NONE 0
+#define EXP_PDU_PT_SCTP 1
+#define EXP_PDU_PT_TCP 2
+#define EXP_PDU_PT_UDP 3
+#define EXP_PDU_PT_DCCP 4
+#define EXP_PDU_PT_IPX 5
+#define EXP_PDU_PT_NCP 6
+#define EXP_PDU_PT_EXCHG 7
+#define EXP_PDU_PT_DDP 8
+#define EXP_PDU_PT_SBCCS 9
+#define EXP_PDU_PT_IDP 10
+#define EXP_PDU_PT_TIPC 11
+#define EXP_PDU_PT_USB 12
+#define EXP_PDU_PT_I2C 13
+#define EXP_PDU_PT_IBQP 14
+#define EXP_PDU_PT_BLUETOOTH 15
+#define EXP_PDU_PT_TDMOP 16
+#define EXP_PDU_PT_IWARP_MPA 17
+#define EXP_PDU_PT_MCTP 18
+
+#define EXP_PDU_TAG_PORT_TYPE 24 /**< part type - 4 bytes, EXP_PDU_PT value */
+#define EXP_PDU_TAG_SRC_PORT 25 /**< source port - 4 bytes (even for protocols with 2-byte ports) */
+#define EXP_PDU_TAG_DST_PORT 26 /**< destination port - 4 bytes (even for protocols with 2-byte ports) */
+
+#define EXP_PDU_TAG_SS7_OPC 28
+#define EXP_PDU_TAG_SS7_DPC 29
+
+#define EXP_PDU_TAG_ORIG_FNO 30
+
+#define EXP_PDU_TAG_DVBCI_EVT 31
+
+#define EXP_PDU_TAG_DISSECTOR_TABLE_NAME_NUM_VAL 32 /**< value part is the numeric value to be used calling the dissector table
+ * given with tag EXP_PDU_TAG_DISSECTOR_TABLE_NAME, must follow immediately after the table tag.
+ */
+
+#define EXP_PDU_TAG_COL_PROT_TEXT 33 /**< UTF-8 text string to put in COL_PROTOCOL, one use case is in conjunction with dissector tables where
+ * COL_PROTOCOL might not be filled in.
+ */
+
+
+/**< value part is structure passed into TCP subdissectors. The field
+ begins with a 2-byte version number; if the version number value is
+ 1, the value part is in the form:
+
+ version 2 bytes - xport PDU version of structure (for backwards/forwards compatibility)
+ seq 4 bytes - Sequence number of first byte in the data
+ nxtseq 4 bytes - Sequence number of first byte after data
+ lastackseq 4 bytes - Sequence number of last ack
+ is_reassembled 1 byte - Non-zero if this is reassembled data
+ flags 2 bytes - TCP flags
+ urgent_pointer 2 bytes - Urgent pointer value for the current packet
+
+ All multi-byte values are in big-endian format. There is no alignment
+ padding between values, so seq. nxtseq, and lastackseq are not aligned
+ on 4-byte boundaries, andflags and urgent_pointer are not aligned on
+ 2-byte boundaries.
+*/
+#define EXP_PDU_TAG_TCP_INFO_DATA 34
+
+#define EXP_PDU_TAG_P2P_DIRECTION 35 /**< The packet direction (P2P_DIR_SENT, P2P_DIR_RECV). */
+
+#define EXP_PDU_TAG_COL_INFO_TEXT 36 /**< UTF-8 text string to put in COL_INFO, useful when puting meta data into the packet list.
+ */
+
+#define EXP_PDU_TAG_USER_DATA_PDU 37 /**< Raw user data PDU which can be dissected as any protocol. */
+
+#define EXP_PDU_TAG_IPV4_LEN 4
+#define EXP_PDU_TAG_IPV6_LEN 16
+
+#define EXP_PDU_TAG_PORT_TYPE_LEN 4
+#define EXP_PDU_TAG_PORT_LEN 4
+
+#define EXP_PDU_TAG_SS7_OPC_LEN 8 /* 4 bytes PC, 2 bytes standard type, 1 byte NI, 1 byte padding */
+#define EXP_PDU_TAG_SS7_DPC_LEN 8 /* 4 bytes PC, 2 bytes standard type, 1 byte NI, 1 byte padding */
+
+#define EXP_PDU_TAG_ORIG_FNO_LEN 4
+
+#define EXP_PDU_TAG_DVBCI_EVT_LEN 1
+
+#define EXP_PDU_TAG_DISSECTOR_TABLE_NUM_VAL_LEN 4
+
+#endif /* EXPORTED_PDU_TLVS_H */
diff --git a/wsutil/feature_list.c b/wsutil/feature_list.c
new file mode 100644
index 00000000..f0c30d66
--- /dev/null
+++ b/wsutil/feature_list.c
@@ -0,0 +1,56 @@
+/* feature_list.c
+ * Routines for gathering and handling lists of present/absent features
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <stdbool.h>
+
+#include "config.h"
+
+#include <wsutil/feature_list.h>
+
+void
+with_feature(feature_list l, const char *fmt, ...)
+{
+ va_list arg;
+ GString *msg = g_string_new("+");
+ va_start(arg, fmt);
+ g_string_append_vprintf(msg, fmt, arg);
+ va_end(arg);
+ *l = g_list_prepend(*l, g_string_free(msg, false));
+}
+
+void
+without_feature(feature_list l, const char *fmt, ...)
+{
+ va_list arg;
+ GString *msg = g_string_new("-");
+ va_start(arg, fmt);
+ g_string_append_vprintf(msg, fmt, arg);
+ va_end(arg);
+ *l = g_list_prepend(*l, g_string_free(msg, false));
+}
+
+static int
+feature_sort_alpha(gconstpointer a, gconstpointer b)
+{
+ return g_ascii_strcasecmp((char *)a + 1, (char *)b + 1);
+}
+
+void
+sort_features(feature_list l)
+{
+ *l = g_list_sort(*l, feature_sort_alpha);
+}
+
+void
+free_features(feature_list l)
+{
+ g_list_free_full(*l, g_free);
+ *l = NULL;
+}
diff --git a/wsutil/feature_list.h b/wsutil/feature_list.h
new file mode 100644
index 00000000..a9c2d8ec
--- /dev/null
+++ b/wsutil/feature_list.h
@@ -0,0 +1,77 @@
+/** @file
+ * Declarations of routines for gathering and handling lists of
+ * present/absent features (usually actually dependencies)
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WSUTIL_FEATURE_LIST_H__
+#define __WSUTIL_FEATURE_LIST_H__
+
+#include <glib.h>
+#include "ws_symbol_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * Handle to a list of features/dependencies.
+ * Semi-opaque. Functions which gather the list of features
+ * will be passed one of these to use with
+ * `with_feature()`/`without_feature()` (below).
+ */
+typedef GList **feature_list;
+
+/*
+ * The format of entries in a feature_list is a char* starting with a
+ * '+' or '-' character indicating if the feature is respectively
+ * present or absent, followed by the unchanged feature description.
+ * This allows the insert order of features to be preserved,
+ * while still preserving the present/absent status in a simple way.
+ */
+
+
+/*
+ * Pointer to a function which gathers a list of features.
+ */
+typedef void(*gather_feature_func)(feature_list l);
+
+/*
+ * Add an indicator to the given feature_list that the named
+ * feature is present.
+ */
+WS_DLL_PUBLIC
+void with_feature(feature_list l, const char *fmt, ...) G_GNUC_PRINTF(2,3);
+
+/*
+ * Add an indicator to the given feature_list that the named
+ * feature is absent.
+ */
+WS_DLL_PUBLIC
+void without_feature(feature_list l, const char *fmt, ...) G_GNUC_PRINTF(2,3);
+
+/*
+ * Sort the given feature list, alphabetically by feature name.
+ * (The leading '+' or '-' is not factored into the sort.)
+ * Currently unused.
+ */
+WS_DLL_PUBLIC
+void sort_features(feature_list l);
+
+/*
+ * Free the memory used by the feature list,
+ * and reset its pointer to NULL.
+ */
+WS_DLL_PUBLIC
+void free_features(feature_list l);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WSUTIL_FEATURE_LIST_H__ */
diff --git a/wsutil/file_util.c b/wsutil/file_util.c
new file mode 100644
index 00000000..4fd55180
--- /dev/null
+++ b/wsutil/file_util.c
@@ -0,0 +1,700 @@
+/* file_util.c
+ *
+ * (Originally part of the Wiretap Library, now part of the Wireshark
+ * utility library)
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/*
+ * File wrapper functions to replace the file functions from GLib like
+ * g_open().
+ *
+ * With MSVC, code using the C support library from one version of MSVC
+ * cannot use file descriptors or FILE *'s returned from code using
+ * the C support library from another version of MSVC.
+ *
+ * We therefore provide our own versions of the routines to open files,
+ * so that they're built to use the same C support library as our code
+ * that reads them.
+ *
+ * (If both were built to use the Universal CRT:
+ *
+ * http://blogs.msdn.com/b/vcblog/archive/2015/03/03/introducing-the-universal-crt.aspx
+ *
+ * this would not be a problem.)
+ *
+ * DO NOT USE THESE FUNCTIONS DIRECTLY, USE ws_open() AND ALIKE FUNCTIONS
+ * FROM file_util.h INSTEAD!!!
+ *
+ * The following code is stripped down code copied from the GLib file
+ * glib/gstdio.h - stripped down because this is used only on Windows
+ * and we use only wide char functions.
+ *
+ * In addition, we have our own ws_stdio_stat64(), which uses
+ * _wstati64(), so that we can get file sizes for files > 4 GB in size.
+ *
+ * XXX - is there any reason why we supply our own versions of routines
+ * that *don't* return file descriptors, other than ws_stdio_stat64()?
+ * Is there an issue with UTF-16 support in _wmkdir() with some versions
+ * of the C runtime, so that if GLib is built to use that version, it
+ * won't handle UTF-16 paths?
+ */
+
+#ifndef _WIN32
+#error "This is only for Windows"
+#endif
+
+#include "config.h"
+
+#include <glib.h>
+
+#include <windows.h>
+#include <winsock2.h>
+#include <errno.h>
+#include <wchar.h>
+#include <tchar.h>
+#include <stdlib.h>
+
+#include "file_util.h"
+#include "ws_attributes.h"
+
+static char *program_path = NULL;
+static char *system_path = NULL;
+static char *npcap_path = NULL;
+
+/**
+ * g_open:
+ * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
+ * @flags: as in open()
+ * @mode: as in open()
+ *
+ * A wrapper for the POSIX open() function. The open() function is
+ * used to convert a pathname into a file descriptor. Note that on
+ * POSIX systems file descriptors are implemented by the operating
+ * system. On Windows, it's the C library that implements open() and
+ * file descriptors. The actual Windows API for opening files is
+ * something different.
+ *
+ * See the C library manual for more details about open().
+ *
+ * Returns: a new file descriptor, or -1 if an error occurred. The
+ * return value can be used exactly like the return value from open().
+ *
+ * Since: 2.6
+ */
+int
+ws_stdio_open (const char *filename, int flags, int mode)
+{
+ wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
+ int retval;
+ int save_errno;
+
+ if (wfilename == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ retval = _wopen (wfilename, flags, mode);
+ save_errno = errno;
+
+ g_free (wfilename);
+
+ errno = save_errno;
+ return retval;
+}
+
+
+/**
+ * g_rename:
+ * @oldfilename: a pathname in the GLib file name encoding (UTF-8 on Windows)
+ * @newfilename: a pathname in the GLib file name encoding
+ *
+ * A wrapper for the POSIX rename() function. The rename() function
+ * renames a file, moving it between directories if required.
+ *
+ * See your C library manual for more details about how rename() works
+ * on your system. Note in particular that on Win9x it is not possible
+ * to rename a file if a file with the new name already exists. Also
+ * it is not possible in general on Windows to rename an open file.
+ *
+ * Returns: 0 if the renaming succeeded, -1 if an error occurred
+ *
+ * Since: 2.6
+ */
+int
+ws_stdio_rename (const char *oldfilename, const char *newfilename)
+{
+ wchar_t *woldfilename = g_utf8_to_utf16 (oldfilename, -1, NULL, NULL, NULL);
+ wchar_t *wnewfilename;
+ int retval;
+ int save_errno = 0;
+
+ if (woldfilename == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ wnewfilename = g_utf8_to_utf16 (newfilename, -1, NULL, NULL, NULL);
+
+ if (wnewfilename == NULL)
+ {
+ g_free (woldfilename);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (MoveFileExW (woldfilename, wnewfilename, MOVEFILE_REPLACE_EXISTING))
+ retval = 0;
+ else
+ {
+ retval = -1;
+ switch (GetLastError ())
+ {
+#define CASE(a,b) case ERROR_##a: save_errno = b; break
+ CASE (FILE_NOT_FOUND, ENOENT);
+ CASE (PATH_NOT_FOUND, ENOENT);
+ CASE (ACCESS_DENIED, EACCES);
+ CASE (NOT_SAME_DEVICE, EXDEV);
+ CASE (LOCK_VIOLATION, EACCES);
+ CASE (SHARING_VIOLATION, EACCES);
+ CASE (FILE_EXISTS, EEXIST);
+ CASE (ALREADY_EXISTS, EEXIST);
+#undef CASE
+ default: save_errno = EIO;
+ }
+ }
+
+ g_free (woldfilename);
+ g_free (wnewfilename);
+
+ errno = save_errno;
+ return retval;
+}
+
+/**
+ * g_mkdir:
+ * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
+ * @mode: permissions to use for the newly created directory
+ *
+ * A wrapper for the POSIX mkdir() function. The mkdir() function
+ * attempts to create a directory with the given name and permissions.
+ *
+ * See the C library manual for more details about mkdir().
+ *
+ * Returns: 0 if the directory was successfully created, -1 if an error
+ * occurred
+ *
+ * Since: 2.6
+ */
+int
+ws_stdio_mkdir (const char *filename, int mode _U_)
+{
+ wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
+ int retval;
+ int save_errno;
+
+ if (wfilename == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ retval = _wmkdir (wfilename);
+ save_errno = errno;
+
+ g_free (wfilename);
+
+ errno = save_errno;
+ return retval;
+}
+
+/**
+ * g_stat:
+ * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
+ * @buf: a pointer to a <structname>stat</structname> struct, which
+ * will be filled with the file information
+ *
+ * A wrapper for the POSIX stat() function. The stat() function
+ * returns information about a file.
+ *
+ * See the C library manual for more details about stat().
+ *
+ * Returns: 0 if the information was successfully retrieved, -1 if an error
+ * occurred
+ *
+ * Since: 2.6
+ */
+int
+ws_stdio_stat64 (const char *filename, ws_statb64 *buf)
+{
+ wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
+ int retval;
+ int save_errno;
+ size_t len;
+
+ if (wfilename == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ len = wcslen (wfilename);
+ while (len > 0 && G_IS_DIR_SEPARATOR (wfilename[len-1]))
+ len--;
+ if (len > 0 &&
+ (!g_path_is_absolute (filename) || len > (size_t) (g_path_skip_root (filename) - filename)))
+ wfilename[len] = '\0';
+
+ retval = _wstati64 (wfilename, buf);
+ save_errno = errno;
+
+ g_free (wfilename);
+
+ errno = save_errno;
+ return retval;
+}
+/**
+ * g_unlink:
+ * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
+ *
+ * A wrapper for the POSIX unlink() function. The unlink() function
+ * deletes a name from the filesystem. If this was the last link to the
+ * file and no processes have it opened, the diskspace occupied by the
+ * file is freed.
+ *
+ * See your C library manual for more details about unlink(). Note
+ * that on Windows, it is in general not possible to delete files that
+ * are open to some process, or mapped into memory.
+ *
+ * Returns: 0 if the name was successfully deleted, -1 if an error
+ * occurred
+ *
+ * Since: 2.6
+ */
+
+int
+ws_stdio_unlink (const char *filename)
+{
+ wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
+ int retval;
+ int save_errno;
+
+ if (wfilename == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ retval = _wunlink (wfilename);
+ save_errno = errno;
+
+ g_free (wfilename);
+
+ errno = save_errno;
+ return retval;
+}
+
+/**
+ * g_remove:
+ * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
+ *
+ * A wrapper for the POSIX remove() function. The remove() function
+ * deletes a name from the filesystem.
+ *
+ * See your C library manual for more details about how remove() works
+ * on your system. On Unix, remove() removes also directories, as it
+ * calls unlink() for files and rmdir() for directories. On Windows,
+ * although remove() in the C library only works for files, this
+ * function tries first remove() and then if that fails rmdir(), and
+ * thus works for both files and directories. Note however, that on
+ * Windows, it is in general not possible to remove a file that is
+ * open to some process, or mapped into memory.
+ *
+ * If this function fails on Windows you can't infer too much from the
+ * errno value. rmdir() is tried regardless of what caused remove() to
+ * fail. Any errno value set by remove() will be overwritten by that
+ * set by rmdir().
+ *
+ * Returns: 0 if the file was successfully removed, -1 if an error
+ * occurred
+ *
+ * Since: 2.6
+ */
+int
+ws_stdio_remove (const char *filename)
+{
+ wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
+ int retval;
+ int save_errno;
+
+ if (wfilename == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ retval = _wremove (wfilename);
+ if (retval == -1)
+ retval = _wrmdir (wfilename);
+ save_errno = errno;
+
+ g_free (wfilename);
+
+ errno = save_errno;
+ return retval;
+}
+
+/**
+ * g_fopen:
+ * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
+ * @mode: a string describing the mode in which the file should be
+ * opened
+ *
+ * A wrapper for the POSIX fopen() function. The fopen() function opens
+ * a file and associates a new stream with it.
+ *
+ * See the C library manual for more details about fopen().
+ *
+ * Returns: A <type>FILE</type> pointer if the file was successfully
+ * opened, or %NULL if an error occurred
+ *
+ * Since: 2.6
+ */
+FILE *
+ws_stdio_fopen (const char *filename, const char *mode)
+{
+ wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
+ wchar_t *wmode;
+ FILE *retval;
+ int save_errno;
+
+ if (wfilename == NULL)
+ {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ wmode = g_utf8_to_utf16 (mode, -1, NULL, NULL, NULL);
+
+ if (wmode == NULL)
+ {
+ g_free (wfilename);
+ errno = EINVAL;
+ return NULL;
+ }
+
+ retval = _wfopen (wfilename, wmode);
+ save_errno = errno;
+
+ g_free (wfilename);
+ g_free (wmode);
+
+ errno = save_errno;
+ return retval;
+}
+
+/**
+ * g_freopen:
+ * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
+ * @mode: a string describing the mode in which the file should be
+ * opened
+ * @stream: an existing stream which will be reused, or %NULL
+ *
+ * A wrapper for the POSIX freopen() function. The freopen() function
+ * opens a file and associates it with an existing stream.
+ *
+ * See the C library manual for more details about freopen().
+ *
+ * Returns: A <type>FILE</type> pointer if the file was successfully
+ * opened, or %NULL if an error occurred.
+ *
+ * Since: 2.6
+ */
+FILE *
+ws_stdio_freopen (const char *filename, const char *mode, FILE *stream)
+{
+ wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
+ wchar_t *wmode;
+ FILE *retval;
+ int save_errno;
+
+ if (wfilename == NULL)
+ {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ wmode = g_utf8_to_utf16 (mode, -1, NULL, NULL, NULL);
+
+ if (wmode == NULL)
+ {
+ g_free (wfilename);
+ errno = EINVAL;
+ return NULL;
+ }
+
+ retval = _wfreopen (wfilename, wmode, stream);
+ save_errno = errno;
+
+ g_free (wfilename);
+ g_free (wmode);
+
+ errno = save_errno;
+ return retval;
+}
+
+
+/* DLL loading */
+static bool
+init_dll_load_paths(void)
+{
+ TCHAR path_w[MAX_PATH];
+
+ if (program_path && system_path && npcap_path)
+ return true;
+
+ /* XXX - Duplicate code in filesystem.c:configuration_init */
+ if (GetModuleFileName(NULL, path_w, MAX_PATH) == 0 || GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+ return false;
+ }
+
+ if (!program_path) {
+ char *app_path;
+ app_path = g_utf16_to_utf8(path_w, -1, NULL, NULL, NULL);
+ /* We could use PathRemoveFileSpec here but we'd have to link to Shlwapi.dll */
+ program_path = g_path_get_dirname(app_path);
+ g_free(app_path);
+ }
+
+ if (GetSystemDirectory(path_w, MAX_PATH) == 0) {
+ return false;
+ }
+
+ if (!system_path) {
+ system_path = g_utf16_to_utf8(path_w, -1, NULL, NULL, NULL);
+ }
+
+ _tcscat_s(path_w, MAX_PATH, _T("\\Npcap"));
+
+ if (!npcap_path) {
+ npcap_path = g_utf16_to_utf8(path_w, -1, NULL, NULL, NULL);
+ }
+
+ if (program_path && system_path && npcap_path)
+ return true;
+
+ return false;
+}
+
+bool
+ws_init_dll_search_path(void)
+{
+ bool dll_dir_set = false;
+ wchar_t *program_path_w;
+
+ /* Remove the current directory from the default DLL search path. */
+ SetDllDirectory(_T(""));
+
+ if (init_dll_load_paths()) {
+ /* Ensure that extcap executables can find wsutil, etc. */
+ program_path_w = g_utf8_to_utf16(program_path, -1, NULL, NULL, NULL);
+ dll_dir_set = SetDllDirectory(program_path_w);
+ g_free(program_path_w);
+ }
+
+ return dll_dir_set;
+}
+
+/*
+ * Internally g_module_open uses LoadLibrary on Windows and returns an
+ * HMODULE cast to a GModule *. However there's no guarantee that this
+ * will always be the case, so we call LoadLibrary and g_module_open
+ * separately.
+ */
+
+void *
+ws_load_library(const char *library_name)
+{
+ char *full_path;
+ wchar_t *full_path_w;
+ HMODULE dll_h;
+
+ if (!init_dll_load_paths() || !library_name)
+ return NULL;
+
+ /* First try the program directory */
+ full_path = g_strconcat(program_path, G_DIR_SEPARATOR_S, library_name, NULL);
+ full_path_w = g_utf8_to_utf16(full_path, -1, NULL, NULL, NULL);
+
+ if (full_path && full_path_w) {
+ dll_h = LoadLibraryW(full_path_w);
+ if (dll_h) {
+ g_free(full_path);
+ g_free(full_path_w);
+ return dll_h;
+ }
+ }
+
+ /* Next try the system directory */
+ full_path = g_strconcat(system_path, G_DIR_SEPARATOR_S, library_name, NULL);
+ full_path_w = g_utf8_to_utf16(full_path, -1, NULL, NULL, NULL);
+
+ if (full_path && full_path_w) {
+ dll_h = LoadLibraryW(full_path_w);
+ if (dll_h) {
+ g_free(full_path);
+ g_free(full_path_w);
+ return dll_h;
+ }
+ }
+
+ return NULL;
+}
+
+static GModule *
+load_npcap_module(const char *full_path, GModuleFlags flags)
+{
+ /*
+ * Npcap's wpcap.dll requires packet.dll from the same directory. Either
+ * SetDllDirectory or SetCurrentDirectory could make this work, but it
+ * interferes with other uses of these settings. LoadLibraryEx is ideal as
+ * it can be configured to put the directory containing the DLL to the
+ * search path. Unfortunately g_module_open uses LoadLibrary internally, so
+ * as a workaround manually load the Npcap libraries first and then use
+ * g_module_open to obtain a GModule for the loaded library.
+ */
+
+ wchar_t *wpath = g_utf8_to_utf16(full_path, -1, NULL, NULL, NULL);
+ HMODULE module = LoadLibraryEx(wpath, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR);
+ g_free(wpath);
+ if (!module) {
+ return NULL;
+ }
+ GModule *mod = g_module_open(full_path, flags);
+ FreeLibrary(module);
+ return mod;
+}
+
+GModule *
+load_wpcap_module(void)
+{
+ char *module_name = "wpcap.dll";
+ char *full_path;
+ GModule *mod;
+ GModuleFlags flags = 0;
+
+ if (!init_dll_load_paths())
+ return NULL;
+
+ /* First try the program directory */
+ full_path = g_strconcat(program_path, G_DIR_SEPARATOR_S, module_name, NULL);
+
+ if (full_path) {
+ mod = g_module_open(full_path, flags);
+ g_free(full_path);
+ if (mod) {
+ return mod;
+ }
+ }
+
+ /* Next try the Npcap directory */
+ full_path = g_strconcat(npcap_path, G_DIR_SEPARATOR_S, module_name, NULL);
+
+ if (full_path) {
+ mod = load_npcap_module(full_path, flags);
+ g_free(full_path);
+ if (mod) {
+ return mod;
+ }
+ }
+
+ /* At last try the system directory */
+ full_path = g_strconcat(system_path, G_DIR_SEPARATOR_S, module_name, NULL);
+
+ if (full_path) {
+ mod = g_module_open(full_path, flags);
+ g_free(full_path);
+ if (mod) {
+ return mod;
+ }
+ }
+
+ return NULL;
+}
+
+/** Create or open a "Wireshark is running" mutex.
+ */
+#define WIRESHARK_IS_RUNNING_UUID "9CA78EEA-EA4D-4490-9240-FC01FCEF464B"
+
+static HANDLE local_running_mutex = NULL;
+static HANDLE global_running_mutex = NULL;
+
+void create_app_running_mutex(void) {
+ SECURITY_DESCRIPTOR sec_descriptor;
+ SECURITY_ATTRIBUTES sec_attributes;
+ SECURITY_ATTRIBUTES *sa;
+ memset(&sec_descriptor, 0, sizeof(SECURITY_DESCRIPTOR));
+ if (!InitializeSecurityDescriptor(&sec_descriptor, SECURITY_DESCRIPTOR_REVISION) ||
+ !SetSecurityDescriptorDacl(&sec_descriptor, true, NULL, false)) {
+ /*
+ * We couldn't set up the security descriptor, so use the default
+ * security attributes when creating the mutexes.
+ */
+ sa = NULL;
+ } else {
+ /*
+ * We could set it up, so set up some attributes that refer
+ * to it.
+ */
+ memset(&sec_attributes, 0, sizeof(SECURITY_ATTRIBUTES));
+ sec_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sec_attributes.lpSecurityDescriptor = &sec_descriptor;
+ sec_attributes.bInheritHandle = true;
+ sa = &sec_attributes;
+ }
+ local_running_mutex = CreateMutex(sa, false, _T("Wireshark-is-running-{") _T(WIRESHARK_IS_RUNNING_UUID) _T("}"));
+ global_running_mutex = CreateMutex(sa, false, _T("Global\\Wireshark-is-running-{") _T(WIRESHARK_IS_RUNNING_UUID) _T("}"));
+}
+
+void close_app_running_mutex(void) {
+ if (local_running_mutex) {
+ CloseHandle(local_running_mutex);
+ local_running_mutex = NULL;
+ }
+ if (global_running_mutex) {
+ CloseHandle(global_running_mutex);
+ global_running_mutex = NULL;
+ }
+}
+
+int ws_close_if_possible(int fd) {
+ fd_set rfds;
+ struct timeval tv = { 0, 1 };
+ int retval;
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+
+ retval = select(1, &rfds, NULL, NULL, &tv);
+ if (retval > -1)
+ return _close(fd);
+
+ return -1;
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wsutil/file_util.h b/wsutil/file_util.h
new file mode 100644
index 00000000..768182e8
--- /dev/null
+++ b/wsutil/file_util.h
@@ -0,0 +1,241 @@
+/** @file
+ * File utility definitions
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __FILE_UTIL_H__
+#define __FILE_UTIL_H__
+
+#include <stdbool.h>
+
+#include "ws_symbol_export.h"
+
+#ifdef _WIN32
+#include <io.h> /* for _read(), _write(), etc. */
+#include <gmodule.h>
+#endif
+
+#include <fcntl.h> /* for open() */
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* for read(), write(), close(), etc. */
+#endif
+
+#include <sys/stat.h> /* for stat() and struct stat */
+
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* We set a larger IO Buffer size for the capture files */
+#define IO_BUF_SIZE (64 * 1024)
+
+/*
+ * 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
+
+#ifdef _WIN32
+
+/*
+ * The structure to pass to ws_stat64() and ws_fstat64().
+ */
+#define ws_statb64 struct _stat64
+
+/* Win32 (and Win64): we use UTF-8 for filenames and pathnames throughout
+ * the code, so file functions must convert filenames and pathnames from
+ * UTF-8 to UTF-16 as we use NT Unicode (Win9x - now unsupported - used
+ * locale-based encoding here). Microsoft's UN*X-style wrappers don't
+ * do that - they expect locale-based encodings - so we need our own
+ * wrappers. (We don't use the wrappers from GLib as that would, at
+ * least for the wrappers that return file descriptors or take them
+ * as arguments, require that we use the version of the C runtime with
+ * which the GLib binaries were built, and we can't guarantee to do that.)
+ *
+ * Note also that ws_stdio_rename() uses MoveFileEx() with
+ * MOVEFILE_REPLACE_EXISTING, so that it acts like UN*X rename(),
+ * removing the target if necessary.
+ */
+
+WS_DLL_PUBLIC int ws_stdio_open (const char *filename, int flags, int mode);
+WS_DLL_PUBLIC int ws_stdio_rename (const char *oldfilename, const char *newfilename);
+WS_DLL_PUBLIC int ws_stdio_mkdir (const char *filename, int mode);
+WS_DLL_PUBLIC int ws_stdio_stat64 (const char *filename, ws_statb64 *buf);
+WS_DLL_PUBLIC int ws_stdio_unlink (const char *filename);
+WS_DLL_PUBLIC int ws_stdio_remove (const char *filename);
+
+WS_DLL_PUBLIC FILE * ws_stdio_fopen (const char *filename, const char *mode);
+WS_DLL_PUBLIC FILE * ws_stdio_freopen (const char *filename, const char *mode, FILE *stream);
+
+#define ws_open ws_stdio_open
+#define ws_rename ws_stdio_rename
+#define ws_mkdir ws_stdio_mkdir
+#define ws_stat64 ws_stdio_stat64
+#define ws_unlink ws_stdio_unlink
+#define ws_remove ws_stdio_remove
+#define ws_fopen ws_stdio_fopen
+#define ws_freopen ws_stdio_freopen
+
+/*
+ * These routines don't take pathnames, so they don't require
+ * pathname-converting wrappers on Windows.
+ */
+
+typedef unsigned int ws_file_size_t;
+typedef signed int ws_file_ssize_t;
+
+#define ws_read _read
+#define ws_write _write
+#define ws_close _close
+#define ws_dup _dup
+#define ws_fseek64 _fseeki64 /* use _fseeki64 for 64-bit offset support */
+#define ws_fstat64 _fstati64 /* use _fstati64 for 64-bit size support */
+#define ws_ftell64 _ftelli64 /* use _ftelli64 for 64-bit offset support */
+#define ws_lseek64 _lseeki64 /* use _lseeki64 for 64-bit offset support */
+#define ws_fdopen _fdopen
+#define ws_fileno _fileno
+#define ws_isatty _isatty
+#define ws_getc_unlocked _fgetc_nolock
+
+/*
+ * Other CRT functions. getpid probably belongs in sys_util.h or proc_util.h
+ * but neither yet exist.
+ */
+#define ws_getpid _getpid
+#define ws_umask _umask
+
+/* DLL loading */
+
+/** Try to remove the current directory from the DLL search path.
+ * SetDllDirectory is tried, then SetCurrentDirectory(program_dir)
+ *
+ * @return true if we were able to call SetDllDirectory, false otherwise.
+ */
+WS_DLL_PUBLIC
+bool ws_init_dll_search_path(void);
+
+/** Load a DLL using LoadLibrary.
+ * Only the system and program directories are searched.
+ *
+ * @param library_name The name of the DLL.
+ * @return A handle to the DLL if found, NULL on failure.
+ */
+
+WS_DLL_PUBLIC
+void *ws_load_library(const char *library_name);
+
+/** Load wpcap.dll using g_module_open.
+ * Only the system and program directories are searched.
+ *
+ * @return A handle to the DLL if found, NULL on failure.
+ */
+WS_DLL_PUBLIC
+GModule *load_wpcap_module(void);
+
+/** Create or open a "Wireshark is running" mutex.
+ * Create or open a mutex which signals that Wireshark or its associated
+ * executables is running. Used by the installer to test for a running application.
+ */
+WS_DLL_PUBLIC void create_app_running_mutex(void);
+
+/** Close our "Wireshark is running" mutex.
+ */
+WS_DLL_PUBLIC void close_app_running_mutex(void);
+
+/** Close a file descriptor if it is not open
+ */
+WS_DLL_PUBLIC int ws_close_if_possible(int fd);
+
+#else /* _WIN32 */
+
+/*
+ * The structure to pass to ws_fstat64().
+ */
+#define ws_statb64 struct stat
+
+/* Not Windows, presumed to be UN*X-compatible */
+#define ws_open open
+#define ws_rename rename
+#define ws_mkdir(dir,mode) mkdir(dir,mode)
+#define ws_stat64 stat
+#define ws_unlink unlink
+#define ws_remove remove
+#define ws_fopen fopen
+#define ws_freopen freopen
+
+typedef size_t ws_file_size_t;
+typedef ssize_t ws_file_ssize_t;
+
+#define ws_read read
+#define ws_write write
+#ifdef __cplusplus
+/*
+ * Just in case this is used in a class with a close method or member.
+ */
+#define ws_close ::close
+#else
+#define ws_close close
+#endif
+
+#define ws_close_if_possible ws_close
+
+#define ws_dup dup
+#ifdef HAVE_FSEEKO
+#define ws_fseek64 fseeko /* AC_SYS_LARGEFILE should make off_t 64-bit */
+#define ws_ftell64 ftello /* AC_SYS_LARGEFILE should make off_t 64-bit */
+#else
+#define ws_fseek64(fh,offset,whence) fseek(fh,(long)(offset),whence)
+#define ws_ftell64 ftell
+#endif
+#define ws_fstat64 fstat /* AC_SYS_LARGEFILE should make off_t 64-bit */
+#define ws_lseek64 lseek /* AC_SYS_LARGEFILE should make off_t 64-bit */
+#define ws_fdopen fdopen
+#define ws_fileno fileno
+#define ws_isatty isatty
+#define ws_getc_unlocked getc_unlocked
+#define O_BINARY 0 /* Win32 needs the O_BINARY flag for open() */
+
+/* Other CRT functions */
+#define ws_getpid getpid
+#define ws_umask umask
+
+#endif /* _WIN32 */
+
+/* directory handling */
+#define WS_DIR GDir
+#define WS_DIRENT const char
+#define ws_dir_open g_dir_open
+#define ws_dir_read_name g_dir_read_name
+#define ws_dir_get_name(dirent) dirent
+#define ws_dir_rewind g_dir_rewind
+#define ws_dir_close g_dir_close
+
+/* XXX - remove include "sys/stat.h" from files that include this header */
+/* XXX - update docs (e.g. README.developer) */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __FILE_UTIL_H__ */
diff --git a/wsutil/filesystem.c b/wsutil/filesystem.c
new file mode 100644
index 00000000..065cdd3c
--- /dev/null
+++ b/wsutil/filesystem.c
@@ -0,0 +1,2722 @@
+/* filesystem.c
+ * Filesystem utility routines
+ *
+ * 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 "filesystem.h"
+
+#define WS_LOG_DOMAIN LOG_DOMAIN_WSUTIL
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#include <tchar.h>
+#include <shlobj.h>
+#include <wsutil/unicode-utils.h>
+#else /* _WIN32 */
+#ifdef ENABLE_APPLICATION_BUNDLE
+#include <mach-o/dyld.h>
+#endif
+#ifdef __linux__
+#include <sys/utsname.h>
+#endif
+#ifdef __FreeBSD__
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#endif
+#ifdef HAVE_DLGET
+#include <dlfcn.h>
+#endif
+#include <pwd.h>
+#endif /* _WIN32 */
+
+#include <wsutil/report_message.h>
+#include <wsutil/privileges.h>
+#include <wsutil/file_util.h>
+#include <wsutil/utf8_entities.h>
+
+#include <wiretap/wtap.h> /* for WTAP_ERR_SHORT_WRITE */
+
+#include "path_config.h"
+
+#define PROFILES_DIR "profiles"
+#define PLUGINS_DIR_NAME "plugins"
+#define EXTCAP_DIR_NAME "extcap"
+#define PROFILES_INFO_NAME "profile_files.txt"
+
+#define _S G_DIR_SEPARATOR_S
+
+/*
+ * Application configuration namespace. Used to construct configuration
+ * paths and environment variables.
+ * XXX We might want to use the term "application flavor" instead, with
+ * "packet" and "log" flavors.
+ */
+enum configuration_namespace_e {
+ CONFIGURATION_NAMESPACE_UNINITIALIZED,
+ CONFIGURATION_NAMESPACE_WIRESHARK,
+ CONFIGURATION_NAMESPACE_LOGRAY
+};
+enum configuration_namespace_e configuration_namespace = CONFIGURATION_NAMESPACE_UNINITIALIZED;
+
+#define CONFIGURATION_NAMESPACE_PROPER (configuration_namespace == CONFIGURATION_NAMESPACE_WIRESHARK ? "Wireshark" : "Logray")
+#define CONFIGURATION_NAMESPACE_LOWER (configuration_namespace == CONFIGURATION_NAMESPACE_WIRESHARK ? "wireshark" : "logray")
+#define CONFIGURATION_ENVIRONMENT_VARIABLE(suffix) (configuration_namespace == CONFIGURATION_NAMESPACE_WIRESHARK ? "WIRESHARK_" suffix : "LOGRAY_" suffix)
+
+char *persconffile_dir = NULL;
+char *datafile_dir = NULL;
+char *persdatafile_dir = NULL;
+char *persconfprofile = NULL;
+char *doc_dir = NULL;
+
+/* Directory from which the executable came. */
+static char *progfile_dir = NULL;
+static char *install_prefix = NULL;
+
+static bool do_store_persconffiles = false;
+static GHashTable *profile_files = NULL;
+
+/*
+ * Given a pathname, return a pointer to the last pathname separator
+ * character in the pathname, or NULL if the pathname contains no
+ * separators.
+ */
+char *
+find_last_pathname_separator(const char *path)
+{
+ char *separator;
+
+#ifdef _WIN32
+ char c;
+
+ /*
+ * We have to scan for '\' or '/'.
+ * Get to the end of the string.
+ */
+ separator = strchr(path, '\0'); /* points to ending '\0' */
+ while (separator > path) {
+ c = *--separator;
+ if (c == '\\' || c == '/')
+ return separator; /* found it */
+ }
+
+ /*
+ * OK, we didn't find any, so no directories - but there might
+ * be a drive letter....
+ */
+ return strchr(path, ':');
+#else
+ separator = strrchr(path, '/');
+ return separator;
+#endif
+}
+
+/*
+ * Given a pathname, return the last component.
+ */
+const char *
+get_basename(const char *path)
+{
+ const char *filename;
+
+ ws_assert(path != NULL);
+ filename = find_last_pathname_separator(path);
+ if (filename == NULL) {
+ /*
+ * There're no directories, drive letters, etc. in the
+ * name; the pathname *is* the file name.
+ */
+ filename = path;
+ } else {
+ /*
+ * Skip past the pathname or drive letter separator.
+ */
+ filename++;
+ }
+ return filename;
+}
+
+/*
+ * Given a pathname, return a string containing everything but the
+ * last component. NOTE: this overwrites the pathname handed into
+ * it....
+ */
+char *
+get_dirname(char *path)
+{
+ char *separator;
+
+ ws_assert(path != NULL);
+ separator = find_last_pathname_separator(path);
+ if (separator == NULL) {
+ /*
+ * There're no directories, drive letters, etc. in the
+ * name; there is no directory path to return.
+ */
+ return NULL;
+ }
+
+ /*
+ * Get rid of the last pathname separator and the final file
+ * name following it.
+ */
+ *separator = '\0';
+
+ /*
+ * "path" now contains the pathname of the directory containing
+ * the file/directory to which it referred.
+ */
+ return path;
+}
+
+/*
+ * Given a pathname, return:
+ *
+ * the errno, if an attempt to "stat()" the file fails;
+ *
+ * EISDIR, if the attempt succeeded and the file turned out
+ * to be a directory;
+ *
+ * 0, if the attempt succeeded and the file turned out not
+ * to be a directory.
+ */
+
+int
+test_for_directory(const char *path)
+{
+ ws_statb64 statb;
+
+ if (ws_stat64(path, &statb) < 0)
+ return errno;
+
+ if (S_ISDIR(statb.st_mode))
+ return EISDIR;
+ else
+ return 0;
+}
+
+int
+test_for_fifo(const char *path)
+{
+ ws_statb64 statb;
+
+ if (ws_stat64(path, &statb) < 0)
+ return errno;
+
+ if (S_ISFIFO(statb.st_mode))
+ return ESPIPE;
+ else
+ return 0;
+}
+
+#ifdef ENABLE_APPLICATION_BUNDLE
+/*
+ * Directory of the application bundle in which we're contained,
+ * if we're contained in an application bundle. Otherwise, NULL.
+ *
+ * Note: Table 2-5 "Subdirectories of the Contents directory" of
+ *
+ * https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html#//apple_ref/doc/uid/10000123i-CH101-SW1
+ *
+ * says that the "Frameworks" directory
+ *
+ * Contains any private shared libraries and frameworks used by the
+ * executable. The frameworks in this directory are revision-locked
+ * to the application and cannot be superseded by any other, even
+ * newer, versions that may be available to the operating system. In
+ * other words, the frameworks included in this directory take precedence
+ * over any other similarly named frameworks found in other parts of
+ * the operating system. For information on how to add private
+ * frameworks to your application bundle, see Framework Programming Guide.
+ *
+ * so if we were to ship with any frameworks (e.g. Qt) we should
+ * perhaps put them in a Frameworks directory rather than under
+ * Resources.
+ *
+ * It also says that the "PlugIns" directory
+ *
+ * Contains loadable bundles that extend the basic features of your
+ * application. You use this directory to include code modules that
+ * must be loaded into your applicationbs process space in order to
+ * be used. You would not use this directory to store standalone
+ * executables.
+ *
+ * Our plugins are just raw .so/.dylib files; I don't know whether by
+ * "bundles" they mean application bundles (i.e., directory hierarchies)
+ * or just "bundles" in the Mach-O sense (which are an image type that
+ * can be loaded with dlopen() but not linked as libraries; our plugins
+ * are, I think, built as dylibs and can be loaded either way).
+ *
+ * And it says that the "SharedSupport" directory
+ *
+ * Contains additional non-critical resources that do not impact the
+ * ability of the application to run. You might use this directory to
+ * include things like document templates, clip art, and tutorials
+ * that your application expects to be present but that do not affect
+ * the ability of your application to run.
+ *
+ * I don't think I'd put the files that currently go under Resources/share
+ * into that category; they're not, for example, sample Lua scripts that
+ * don't actually get run by Wireshark, they're configuration/data files
+ * for Wireshark whose absence might not prevent Wireshark from running
+ * but that would affect how it behaves when run.
+ */
+static char *appbundle_dir;
+#endif
+
+/*
+ * true if we're running from the build directory and we aren't running
+ * with special privileges.
+ */
+static bool running_in_build_directory_flag = false;
+
+/*
+ * Set our configuration namespace. This will be used for top-level
+ * configuration directory names and environment variable prefixes.
+ */
+static void
+set_configuration_namespace(const char *namespace_name)
+{
+
+ if (configuration_namespace != CONFIGURATION_NAMESPACE_UNINITIALIZED) {
+ return;
+ }
+
+ if (!namespace_name || g_ascii_strcasecmp(namespace_name, "wireshark") == 0)
+ {
+ configuration_namespace = CONFIGURATION_NAMESPACE_WIRESHARK;
+ }
+ else if (g_ascii_strcasecmp(namespace_name, "logray") == 0)
+ {
+ configuration_namespace = CONFIGURATION_NAMESPACE_LOGRAY;
+ }
+ else
+ {
+ ws_error("Unknown configuration namespace %s", namespace_name);
+ }
+
+ ws_debug("Using configuration namespace %s.", CONFIGURATION_NAMESPACE_PROPER);
+}
+
+const char *
+get_configuration_namespace(void)
+{
+ return CONFIGURATION_NAMESPACE_PROPER;
+}
+
+bool is_packet_configuration_namespace(void)
+{
+ return configuration_namespace != CONFIGURATION_NAMESPACE_LOGRAY;
+}
+
+#ifndef _WIN32
+/*
+ * Get the pathname of the executable using various platform-
+ * dependent mechanisms for various UN*Xes.
+ *
+ * These calls all should return something independent of the argv[0]
+ * passed to the program, so it shouldn't be fooled by an argv[0]
+ * that doesn't match the executable path.
+ *
+ * We don't use dladdr() because:
+ *
+ * not all UN*Xes necessarily have dladdr();
+ *
+ * those that do have it don't necessarily have dladdr(main)
+ * return information about the executable image;
+ *
+ * those that do have a dladdr() where dladdr(main) returns
+ * information about the executable image don't necessarily
+ * have a mechanism by which the executable image can get
+ * its own path from the kernel (either by a call or by it
+ * being handed to it along with argv[] and the environment),
+ * so they just fall back on getting it from argv[0], which we
+ * already have code to do;
+ *
+ * those that do have such a mechanism don't necessarily use
+ * it in dladdr(), and, instead, just fall back on getting it
+ * from argv[0];
+ *
+ * so the only places where it's worth bothering to use dladdr()
+ * are platforms where dladdr(main) return information about the
+ * executable image by getting it from the kernel rather than
+ * by looking at argv[0], and where we can't get at that information
+ * ourselves, and we haven't seen any indication that there are any
+ * such platforms.
+ *
+ * In particular, some dynamic linkers supply a dladdr() such that
+ * dladdr(main) just returns something derived from argv[0], so
+ * just using dladdr(main) is the wrong thing to do if there's
+ * another mechanism that can get you a more reliable version of
+ * the executable path.
+ *
+ * So, on platforms where we know of a mechanism to get that path
+ * (where getting that path doesn't involve argv[0], which is not
+ * guaranteed to reflect the path to the binary), this routine
+ * attempsts to use that platform's mechanism. On other platforms,
+ * it just returns NULL.
+ *
+ * This is not guaranteed to return an absolute path; if it doesn't,
+ * our caller must prepend the current directory if it's a path.
+ *
+ * This is not guaranteed to return the "real path"; it might return
+ * something with symbolic links in the path. Our caller must
+ * use realpath() if they want the real thing, but that's also true of
+ * something obtained by looking at argv[0].
+ */
+#define xx_free free /* hack so checkAPIs doesn't complain */
+static const char *
+get_current_executable_path(void)
+{
+#if defined(ENABLE_APPLICATION_BUNDLE)
+ static char *executable_path;
+ uint32_t path_buf_size;
+
+ if (executable_path) {
+ return executable_path;
+ }
+
+ path_buf_size = PATH_MAX;
+ executable_path = (char *)g_malloc(path_buf_size);
+ if (_NSGetExecutablePath(executable_path, &path_buf_size) == -1) {
+ executable_path = (char *)g_realloc(executable_path, path_buf_size);
+ if (_NSGetExecutablePath(executable_path, &path_buf_size) == -1)
+ return NULL;
+ }
+ /*
+ * Resolve our path so that it's possible to symlink the executables
+ * in our application bundle.
+ */
+ char *rp_execpath = realpath(executable_path, NULL);
+ if (rp_execpath) {
+ g_free(executable_path);
+ executable_path = g_strdup(rp_execpath);
+ xx_free(rp_execpath);
+ }
+ return executable_path;
+#elif defined(__linux__)
+ /*
+ * In older versions of GNU libc's dynamic linker, as used on Linux,
+ * dladdr(main) supplies a path based on argv[0], so we use
+ * /proc/self/exe instead; there are Linux distributions with
+ * kernels that support /proc/self/exe and those older versions
+ * of the dynamic linker, and this will get a better answer on
+ * those versions.
+ *
+ * It only works on Linux 2.2 or later, so we just give up on
+ * earlier versions.
+ *
+ * XXX - are there OS versions that support "exe" but not "self"?
+ */
+ struct utsname name;
+ static char executable_path[PATH_MAX + 1];
+ ssize_t r;
+
+ if (uname(&name) == -1)
+ return NULL;
+ if (strncmp(name.release, "1.", 2) == 0)
+ return NULL; /* Linux 1.x */
+ if (strcmp(name.release, "2.0") == 0 ||
+ strncmp(name.release, "2.0.", 4) == 0 ||
+ strcmp(name.release, "2.1") == 0 ||
+ strncmp(name.release, "2.1.", 4) == 0)
+ return NULL; /* Linux 2.0.x or 2.1.x */
+ if ((r = readlink("/proc/self/exe", executable_path, PATH_MAX)) == -1)
+ return NULL;
+ executable_path[r] = '\0';
+ return executable_path;
+#elif defined(__FreeBSD__) && defined(KERN_PROC_PATHNAME)
+ /*
+ * In older versions of FreeBSD's dynamic linker, dladdr(main)
+ * supplies a path based on argv[0], so we use the KERN_PROC_PATHNAME
+ * sysctl instead; there are, I think, versions of FreeBSD
+ * that support the sysctl that have and those older versions
+ * of the dynamic linker, and this will get a better answer on
+ * those versions.
+ */
+ int mib[4];
+ char *executable_path;
+ size_t path_buf_size;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PATHNAME;
+ mib[3] = -1;
+ path_buf_size = PATH_MAX;
+ executable_path = (char *)g_malloc(path_buf_size);
+ if (sysctl(mib, 4, executable_path, &path_buf_size, NULL, 0) == -1) {
+ if (errno != ENOMEM)
+ return NULL;
+ executable_path = (char *)g_realloc(executable_path, path_buf_size);
+ if (sysctl(mib, 4, executable_path, &path_buf_size, NULL, 0) == -1)
+ return NULL;
+ }
+ return executable_path;
+#elif defined(__NetBSD__)
+ /*
+ * In all versions of NetBSD's dynamic linker as of 2013-08-12,
+ * dladdr(main) supplies a path based on argv[0], so we use
+ * /proc/curproc/exe instead.
+ *
+ * XXX - are there OS versions that support "exe" but not "curproc"
+ * or "self"? Are there any that support "self" but not "curproc"?
+ */
+ static char executable_path[PATH_MAX + 1];
+ ssize_t r;
+
+ if ((r = readlink("/proc/curproc/exe", executable_path, PATH_MAX)) == -1)
+ return NULL;
+ executable_path[r] = '\0';
+ return executable_path;
+#elif defined(__DragonFly__)
+ /*
+ * In older versions of DragonFly BSD's dynamic linker, dladdr(main)
+ * supplies a path based on argv[0], so we use /proc/curproc/file
+ * instead; it appears to be supported by all versions of DragonFly
+ * BSD.
+ */
+ static char executable_path[PATH_MAX + 1];
+ ssize_t r;
+
+ if ((r = readlink("/proc/curproc/file", executable_path, PATH_MAX)) == -1)
+ return NULL;
+ executable_path[r] = '\0';
+ return executable_path;
+#elif defined(HAVE_GETEXECNAME)
+ /*
+ * Solaris, with getexecname().
+ * It appears that getexecname() dates back to at least Solaris 8,
+ * but /proc/{pid}/path is first documented in the Solaris 10 documentation,
+ * so we use getexecname() if available, rather than /proc/self/path/a.out
+ * (which isn't documented, but appears to be a symlink to the
+ * executable image file).
+ */
+ return getexecname();
+#elif defined(HAVE_DLGET)
+ /*
+ * HP-UX 11, with dlget(); use dlget() and dlgetname().
+ * See
+ *
+ * https://web.archive.org/web/20081025174755/http://h21007.www2.hp.com/portal/site/dspp/menuitem.863c3e4cbcdc3f3515b49c108973a801?ciid=88086d6e1de021106d6e1de02110275d6e10RCRD#two
+ */
+ struct load_module_desc desc;
+
+ if (dlget(-2, &desc, sizeof(desc)) != NULL)
+ return dlgetname(&desc, sizeof(desc), NULL, NULL, NULL);
+ else
+ return NULL;
+#else
+ /* Fill in your favorite UN*X's code here, if there is something */
+ return NULL;
+#endif
+}
+#endif /* _WIN32 */
+
+static void trim_progfile_dir(void)
+{
+ char *progfile_last_dir = find_last_pathname_separator(progfile_dir);
+
+ if (! (progfile_last_dir && strncmp(progfile_last_dir + 1, "extcap", sizeof("extcap")) == 0)) {
+ return;
+ }
+
+ *progfile_last_dir = '\0';
+ char *extcap_progfile_dir = progfile_dir;
+ progfile_dir = g_strdup(extcap_progfile_dir);
+ g_free(extcap_progfile_dir);
+}
+
+#if !defined(_WIN32) || defined(HAVE_MSYSTEM)
+static char *
+trim_last_dir_from_path(const char *_path)
+{
+ char *path = ws_strdup(_path);
+ char *last_dir = find_last_pathname_separator(path);
+ if (last_dir) {
+ *last_dir = '\0';
+ }
+ return path;
+}
+#endif
+
+/*
+ * Construct the path name of a non-extcap Wireshark executable file,
+ * given the program name. The executable name doesn't include ".exe";
+ * append it on Windows, so that callers don't have to worry about that.
+ *
+ * This presumes that all non-extcap executables are in the same directory.
+ *
+ * The returned file name was g_malloc()'d so it must be g_free()d when the
+ * caller is done with it.
+ */
+char *
+get_executable_path(const char *program_name)
+{
+ /*
+ * Fail if we don't know what directory contains the executables.
+ */
+ if (progfile_dir == NULL)
+ return NULL;
+
+#ifdef _WIN32
+ return ws_strdup_printf("%s\\%s.exe", progfile_dir, program_name);
+#else
+ return ws_strdup_printf("%s/%s", progfile_dir, program_name);
+#endif
+}
+
+/*
+ * Get the pathname of the directory from which the executable came,
+ * and save it for future use. Returns NULL on success, and a
+ * g_mallocated string containing an error on failure.
+ */
+#ifdef _WIN32
+char *
+configuration_init_w32(const char* arg0 _U_)
+{
+ TCHAR prog_pathname_w[_MAX_PATH+2];
+ char *prog_pathname;
+ DWORD error;
+ TCHAR *msg_w;
+ unsigned char *msg;
+ size_t msglen;
+
+ /*
+ * Attempt to get the full pathname of the currently running
+ * program.
+ */
+ if (GetModuleFileName(NULL, prog_pathname_w, G_N_ELEMENTS(prog_pathname_w)) != 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ /*
+ * XXX - Should we use g_utf16_to_utf8()?
+ */
+ prog_pathname = utf_16to8(prog_pathname_w);
+ /*
+ * We got it; strip off the last component, which would be
+ * the file name of the executable, giving us the pathname
+ * of the directory where the executable resides.
+ */
+ progfile_dir = g_path_get_dirname(prog_pathname);
+ if (progfile_dir != NULL) {
+ trim_progfile_dir();
+ /* we succeeded */
+ } else {
+ /*
+ * OK, no. What do we do now?
+ */
+ return ws_strdup_printf("No \\ in executable pathname \"%s\"",
+ prog_pathname);
+ }
+ } else {
+ /*
+ * Oh, well. Return an indication of the error.
+ */
+ error = GetLastError();
+ if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, error, 0, (LPTSTR) &msg_w, 0, NULL) == 0) {
+ /*
+ * Gak. We can't format the message.
+ */
+ return ws_strdup_printf("GetModuleFileName failed: %lu (FormatMessage failed: %lu)",
+ error, GetLastError());
+ }
+ msg = utf_16to8(msg_w);
+ LocalFree(msg_w);
+ /*
+ * "FormatMessage()" "helpfully" sticks CR/LF at the
+ * end of the message. Get rid of it.
+ */
+ msglen = strlen(msg);
+ if (msglen >= 2) {
+ msg[msglen - 1] = '\0';
+ msg[msglen - 2] = '\0';
+ }
+ return ws_strdup_printf("GetModuleFileName failed: %s (%lu)",
+ msg, error);
+ }
+
+#ifdef HAVE_MSYSTEM
+ /*
+ * We already have the program_dir. Find the installation prefix.
+ * This is one level up from the bin_dir. If the program_dir does
+ * not end with "bin" then assume we are running in the build directory
+ * and the "installation prefix" (staging directory) is the same as
+ * the program_dir.
+ */
+ if (g_str_has_suffix(progfile_dir, _S"bin")) {
+ install_prefix = trim_last_dir_from_path(progfile_dir);
+ }
+ else {
+ install_prefix = g_strdup(progfile_dir);
+ running_in_build_directory_flag = true;
+ }
+#endif /* HAVE_MSYSTEM */
+
+ return NULL;
+}
+
+#else /* !_WIN32 */
+
+char *
+configuration_init_posix(const char* arg0)
+{
+ const char *execname;
+ char *prog_pathname;
+ char *curdir;
+ long path_max;
+ const char *pathstr;
+ const char *path_start, *path_end;
+ size_t path_component_len, path_len;
+ char *retstr;
+ char *path;
+ char *dir_end;
+
+ /* Hard-coded value used if we cannot obtain the path of the running executable. */
+ install_prefix = g_strdup(INSTALL_PREFIX);
+
+ /*
+ * Check whether XXX_RUN_FROM_BUILD_DIRECTORY is set in the
+ * environment; if so, set running_in_build_directory_flag if we
+ * weren't started with special privileges. (If we were started
+ * with special privileges, it's not safe to allow the user to point
+ * us to some other directory; running_in_build_directory_flag, when
+ * set, causes us to look for plugins and the like in the build
+ * directory.)
+ */
+ const char *run_from_envar = CONFIGURATION_ENVIRONMENT_VARIABLE("RUN_FROM_BUILD_DIRECTORY");
+ if (g_getenv(run_from_envar) != NULL
+ && !started_with_special_privs()) {
+ running_in_build_directory_flag = true;
+ }
+
+ execname = get_current_executable_path();
+ if (execname == NULL) {
+ /*
+ * OK, guess based on argv[0].
+ */
+ execname = arg0;
+ }
+
+ /*
+ * Try to figure out the directory in which the currently running
+ * program resides, given something purporting to be the executable
+ * name (from an OS mechanism or from the argv[0] it was started with).
+ * That might be the absolute path of the program, or a path relative
+ * to the current directory of the process that started it, or
+ * just a name for the program if it was started from the command
+ * line and was searched for in $PATH. It's not guaranteed to be
+ * any of those, however, so there are no guarantees....
+ */
+ if (execname[0] == '/') {
+ /*
+ * It's an absolute path.
+ */
+ prog_pathname = g_strdup(execname);
+ } else if (strchr(execname, '/') != NULL) {
+ /*
+ * It's a relative path, with a directory in it.
+ * Get the current directory, and combine it
+ * with that directory.
+ */
+ path_max = pathconf(".", _PC_PATH_MAX);
+ if (path_max == -1) {
+ /*
+ * We have no idea how big a buffer to
+ * allocate for the current directory.
+ */
+ return ws_strdup_printf("pathconf failed: %s\n",
+ g_strerror(errno));
+ }
+ curdir = (char *)g_malloc(path_max);
+ if (getcwd(curdir, path_max) == NULL) {
+ /*
+ * It failed - give up, and just stick
+ * with DATA_DIR.
+ */
+ g_free(curdir);
+ return ws_strdup_printf("getcwd failed: %s\n",
+ g_strerror(errno));
+ }
+ path = ws_strdup_printf("%s/%s", curdir, execname);
+ g_free(curdir);
+ prog_pathname = path;
+ } else {
+ /*
+ * It's just a file name.
+ * Search the path for a file with that name
+ * that's executable.
+ */
+ prog_pathname = NULL; /* haven't found it yet */
+ pathstr = g_getenv("PATH");
+ path_start = pathstr;
+ if (path_start != NULL) {
+ while (*path_start != '\0') {
+ path_end = strchr(path_start, ':');
+ if (path_end == NULL)
+ path_end = path_start + strlen(path_start);
+ path_component_len = path_end - path_start;
+ path_len = path_component_len + 1
+ + strlen(execname) + 1;
+ path = (char *)g_malloc(path_len);
+ memcpy(path, path_start, path_component_len);
+ path[path_component_len] = '\0';
+ (void) g_strlcat(path, "/", path_len);
+ (void) g_strlcat(path, execname, path_len);
+ if (access(path, X_OK) == 0) {
+ /*
+ * Found it!
+ */
+ prog_pathname = path;
+ break;
+ }
+
+ /*
+ * That's not it. If there are more
+ * path components to test, try them.
+ */
+ if (*path_end == ':')
+ path_end++;
+ path_start = path_end;
+ g_free(path);
+ }
+ if (prog_pathname == NULL) {
+ /*
+ * Program not found in path.
+ */
+ return ws_strdup_printf("\"%s\" not found in \"%s\"",
+ execname, pathstr);
+ }
+ } else {
+ /*
+ * PATH isn't set.
+ * XXX - should we pick a default?
+ */
+ return g_strdup("PATH isn't set");
+ }
+ }
+
+ /*
+ * OK, we have what we think is the pathname
+ * of the program.
+ *
+ * First, find the last "/" in the directory,
+ * as that marks the end of the directory pathname.
+ */
+ dir_end = strrchr(prog_pathname, '/');
+ if (dir_end != NULL) {
+ /*
+ * Found it. Strip off the last component,
+ * as that's the path of the program.
+ */
+ *dir_end = '\0';
+
+ /*
+ * Is there a "/run" at the end?
+ */
+ dir_end = strrchr(prog_pathname, '/');
+ if (dir_end != NULL) {
+ if (!started_with_special_privs()) {
+ /*
+ * Check for the CMake output directory. As people may name
+ * their directories "run" (really?), also check for the
+ * CMakeCache.txt file before assuming a CMake output dir.
+ */
+ if (strcmp(dir_end, "/run") == 0) {
+ char *cmake_file;
+ cmake_file = ws_strdup_printf("%.*s/CMakeCache.txt",
+ (int)(dir_end - prog_pathname),
+ prog_pathname);
+ if (file_exists(cmake_file))
+ running_in_build_directory_flag = true;
+ g_free(cmake_file);
+ }
+#ifdef ENABLE_APPLICATION_BUNDLE
+ {
+ /*
+ * Scan up the path looking for a component
+ * named "Contents". If we find it, we assume
+ * we're in a bundle, and that the top-level
+ * directory of the bundle is the one containing
+ * "Contents".
+ *
+ * Not all executables are in the Contents/MacOS
+ * directory, so we can't just check for those
+ * in the path and strip them off.
+ *
+ * XXX - should we assume that it's either
+ * Contents/MacOS or Resources/bin?
+ */
+ char *component_end, *p;
+
+ component_end = strchr(prog_pathname, '\0');
+ p = component_end;
+ for (;;) {
+ while (p >= prog_pathname && *p != '/')
+ p--;
+ if (p == prog_pathname) {
+ /*
+ * We're looking at the first component of
+ * the pathname now, so we're definitely
+ * not in a bundle, even if we're in
+ * "/Contents".
+ */
+ break;
+ }
+ if (strncmp(p, "/Contents", component_end - p) == 0) {
+ /* Found it. */
+ appbundle_dir = (char *)g_malloc(p - prog_pathname + 1);
+ memcpy(appbundle_dir, prog_pathname, p - prog_pathname);
+ appbundle_dir[p - prog_pathname] = '\0';
+ break;
+ }
+ component_end = p;
+ p--;
+ }
+ }
+#endif
+ }
+ }
+
+ /*
+ * OK, we have the path we want.
+ */
+ progfile_dir = prog_pathname;
+ trim_progfile_dir();
+ } else {
+ /*
+ * This "shouldn't happen"; we apparently
+ * have no "/" in the pathname.
+ * Just free up prog_pathname.
+ */
+ retstr = ws_strdup_printf("No / found in \"%s\"", prog_pathname);
+ g_free(prog_pathname);
+ return retstr;
+ }
+
+ /*
+ * We already have the program_dir. Find the installation prefix.
+ * This is one level up from the bin_dir. If the program_dir does
+ * not end with "bin" then assume we are running in the build directory
+ * and the "installation prefix" (staging directory) is the same as
+ * the program_dir.
+ */
+ g_free(install_prefix);
+ if (g_str_has_suffix(progfile_dir, _S"bin")) {
+ install_prefix = trim_last_dir_from_path(progfile_dir);
+ }
+ else {
+ install_prefix = g_strdup(progfile_dir);
+ running_in_build_directory_flag = true;
+ }
+
+ return NULL;
+}
+#endif /* ?_WIN32 */
+
+char *
+configuration_init(const char* arg0, const char *namespace_name)
+{
+ set_configuration_namespace(namespace_name);
+
+#ifdef _WIN32
+ return configuration_init_w32(arg0);
+#else
+ return configuration_init_posix(arg0);
+#endif
+}
+
+/*
+ * Get the directory in which the program resides.
+ */
+const char *
+get_progfile_dir(void)
+{
+ return progfile_dir;
+}
+
+/*
+ * Get the directory in which the global configuration and data files are
+ * stored.
+ *
+ * On Windows, we use the directory in which the executable for this
+ * process resides.
+ *
+ * On macOS (when executed from an app bundle), use a directory within
+ * that app bundle.
+ *
+ * Otherwise, if the program was executed from the build directory, use the
+ * directory in which the executable for this process resides. In all other
+ * cases, use the DATA_DIR value that was set at compile time.
+ *
+ * XXX - if we ever make libwireshark a real library, used by multiple
+ * applications (more than just TShark and versions of Wireshark with
+ * various UIs), should the configuration files belong to the library
+ * (and be shared by all those applications) or to the applications?
+ *
+ * If they belong to the library, that could be done on UNIX by the
+ * configure script, but it's trickier on Windows, as you can't just
+ * use the pathname of the executable.
+ *
+ * If they belong to the application, that could be done on Windows
+ * by using the pathname of the executable, but we'd have to have it
+ * passed in as an argument, in some call, on UNIX.
+ *
+ * Note that some of those configuration files might be used by code in
+ * libwireshark, some of them might be used by dissectors (would they
+ * belong to libwireshark, the application, or a separate library?),
+ * and some of them might be used by other code (the Wireshark preferences
+ * file includes resolver preferences that control the behavior of code
+ * in libwireshark, dissector preferences, and UI preferences, for
+ * example).
+ */
+const char *
+get_datafile_dir(void)
+{
+ if (datafile_dir != NULL)
+ return datafile_dir;
+
+ const char *data_dir_envar = CONFIGURATION_ENVIRONMENT_VARIABLE("DATA_DIR");
+ if (g_getenv(data_dir_envar) && !started_with_special_privs()) {
+ /*
+ * The user specified a different directory for data files
+ * and we aren't running with special privileges.
+ * Let {WIRESHARK,LOGRAY}_DATA_DIR take precedence.
+ * XXX - We might be able to dispense with the priv check
+ */
+ datafile_dir = g_strdup(g_getenv(data_dir_envar));
+ return datafile_dir;
+ }
+
+#if defined(HAVE_MSYSTEM)
+ if (running_in_build_directory_flag) {
+ datafile_dir = g_strdup(install_prefix);
+ } else {
+ datafile_dir = g_build_filename(install_prefix, DATA_DIR, (char *)NULL);
+ }
+#elif defined(_WIN32)
+ /*
+ * Do we have the pathname of the program? If so, assume we're
+ * running an installed version of the program. If we fail,
+ * we don't change "datafile_dir", and thus end up using the
+ * default.
+ *
+ * XXX - does NSIS put the installation directory into
+ * "\HKEY_LOCAL_MACHINE\SOFTWARE\Wireshark\InstallDir"?
+ * If so, perhaps we should read that from the registry,
+ * instead.
+ */
+ if (progfile_dir != NULL) {
+ /*
+ * Yes, we do; use that.
+ */
+ datafile_dir = g_strdup(progfile_dir);
+ } else {
+ /*
+ * No, we don't.
+ * Fall back on the default installation directory.
+ */
+ datafile_dir = g_strdup("C:\\Program Files\\Wireshark\\");
+ }
+#else
+#ifdef ENABLE_APPLICATION_BUNDLE
+ /*
+ * If we're running from an app bundle and weren't started
+ * with special privileges, use the Contents/Resources/share/wireshark
+ * subdirectory of the app bundle.
+ *
+ * (appbundle_dir is not set to a non-null value if we're
+ * started with special privileges, so we need only check
+ * it; we don't need to call started_with_special_privs().)
+ */
+ else if (appbundle_dir != NULL) {
+ datafile_dir = ws_strdup_printf("%s/Contents/Resources/share/%s",
+ appbundle_dir, CONFIGURATION_NAMESPACE_LOWER);
+ }
+#endif
+ else if (running_in_build_directory_flag && progfile_dir != NULL) {
+ /*
+ * We're (probably) being run from the build directory and
+ * weren't started with special privileges.
+ *
+ * (running_in_build_directory_flag is never set to true
+ * if we're started with special privileges, so we need
+ * only check it; we don't need to call started_with_special_privs().)
+ *
+ * Data files (dtds/, radius/, etc.) are copied to the build
+ * directory during the build which also contains executables. A special
+ * exception is macOS (when built with an app bundle).
+ */
+ datafile_dir = g_strdup(progfile_dir);
+ } else {
+ datafile_dir = g_build_filename(install_prefix, DATA_DIR, (char *)NULL);
+ }
+#endif
+ return datafile_dir;
+}
+
+const char *
+get_doc_dir(void)
+{
+ if (doc_dir != NULL)
+ return doc_dir;
+
+ /* No environment variable for this. */
+ if (false) {
+ ;
+ }
+
+#if defined(HAVE_MSYSTEM)
+ if (running_in_build_directory_flag) {
+ doc_dir = g_strdup(install_prefix);
+ } else {
+ doc_dir = g_build_filename(install_prefix, DOC_DIR, (char *)NULL);
+ }
+#elif defined(_WIN32)
+ if (progfile_dir != NULL) {
+ doc_dir = g_strdup(progfile_dir);
+ } else {
+ /* Fall back on the default installation directory. */
+ doc_dir = g_strdup("C:\\Program Files\\Wireshark\\");
+ }
+#else
+#ifdef ENABLE_APPLICATION_BUNDLE
+ /*
+ * If we're running from an app bundle and weren't started
+ * with special privileges, use the Contents/Resources/share/wireshark
+ * subdirectory of the app bundle.
+ *
+ * (appbundle_dir is not set to a non-null value if we're
+ * started with special privileges, so we need only check
+ * it; we don't need to call started_with_special_privs().)
+ */
+ else if (appbundle_dir != NULL) {
+ doc_dir = ws_strdup_printf("%s/Contents/Resources/%s",
+ appbundle_dir, DOC_DIR);
+ }
+#endif
+ else if (running_in_build_directory_flag && progfile_dir != NULL) {
+ /*
+ * We're (probably) being run from the build directory and
+ * weren't started with special privileges.
+ */
+ doc_dir = g_strdup(progfile_dir);
+ } else {
+ doc_dir = g_build_filename(install_prefix, DOC_DIR, (char *)NULL);
+ }
+#endif
+ return doc_dir;
+}
+
+/*
+ * Find the directory where the plugins are stored.
+ *
+ * On Windows, we use the plugin\{VERSION} subdirectory of the datafile
+ * directory, where {VERSION} is the version number of this version of
+ * Wireshark.
+ *
+ * On UN*X:
+ *
+ * if we appear to be run from the build directory, we use the
+ * "plugin" subdirectory of the datafile directory;
+ *
+ * otherwise, if the WIRESHARK_PLUGIN_DIR environment variable is
+ * set and we aren't running with special privileges, we use the
+ * value of that environment variable;
+ *
+ * otherwise, if we're running from an app bundle in macOS, we
+ * use the Contents/PlugIns/wireshark subdirectory of the app bundle;
+ *
+ * otherwise, we use the PLUGIN_DIR value supplied by the
+ * configure script.
+ */
+static char *plugin_dir = NULL;
+static char *plugin_dir_with_version = NULL;
+static char *plugin_pers_dir = NULL;
+static char *plugin_pers_dir_with_version = NULL;
+static char *extcap_pers_dir = NULL;
+
+static void
+init_plugin_dir(void)
+{
+ const char *plugin_dir_envar = CONFIGURATION_ENVIRONMENT_VARIABLE("PLUGIN_DIR");
+ if (g_getenv(plugin_dir_envar) && !started_with_special_privs()) {
+ /*
+ * The user specified a different directory for plugins
+ * and we aren't running with special privileges.
+ * Let {WIRESHARK,LOGRAY}_PLUGIN_DIR take precedence.
+ */
+ plugin_dir = g_strdup(g_getenv(plugin_dir_envar));
+ return;
+ }
+
+#if defined(HAVE_PLUGINS) || defined(HAVE_LUA)
+#if defined(HAVE_MSYSTEM)
+ if (running_in_build_directory_flag) {
+ plugin_dir = g_build_filename(install_prefix, "plugins", (char *)NULL);
+ } else {
+ plugin_dir = g_build_filename(install_prefix, PLUGIN_DIR, (char *)NULL);
+ }
+#elif defined(_WIN32)
+ /*
+ * On Windows, the data file directory is the installation
+ * directory; the plugins are stored under it.
+ *
+ * Assume we're running the installed version of Wireshark;
+ * on Windows, the data file directory is the directory
+ * in which the Wireshark binary resides.
+ */
+ plugin_dir = g_build_filename(get_datafile_dir(), "plugins", (char *)NULL);
+
+ /*
+ * Make sure that pathname refers to a directory.
+ */
+ if (test_for_directory(plugin_dir) != EISDIR) {
+ /*
+ * Either it doesn't refer to a directory or it
+ * refers to something that doesn't exist.
+ *
+ * Assume that means we're running a version of
+ * Wireshark we've built in a build directory,
+ * in which case {datafile dir}\plugins is the
+ * top-level plugins source directory, and use
+ * that directory and set the "we're running in
+ * a build directory" flag, so the plugin
+ * scanner will check all subdirectories of that
+ * directory for plugins.
+ */
+ g_free(plugin_dir);
+ plugin_dir = g_build_filename(get_datafile_dir(), "plugins", (char *)NULL);
+ running_in_build_directory_flag = true;
+ }
+#else
+#ifdef ENABLE_APPLICATION_BUNDLE
+ /*
+ * If we're running from an app bundle and weren't started
+ * with special privileges, use the Contents/PlugIns/wireshark
+ * subdirectory of the app bundle.
+ *
+ * (appbundle_dir is not set to a non-null value if we're
+ * started with special privileges, so we need only check
+ * it; we don't need to call started_with_special_privs().)
+ */
+ else if (appbundle_dir != NULL) {
+ plugin_dir = g_build_filename(appbundle_dir, "Contents/PlugIns",
+ CONFIGURATION_NAMESPACE_LOWER, (char *)NULL);
+ }
+#endif
+ else if (running_in_build_directory_flag) {
+ /*
+ * We're (probably) being run from the build directory and
+ * weren't started with special privileges, so we'll use
+ * the "plugins" subdirectory of the directory where the program
+ * we're running is (that's the build directory).
+ */
+ plugin_dir = g_build_filename(get_progfile_dir(), "plugins", (char *)NULL);
+ } else {
+ plugin_dir = g_build_filename(install_prefix, PLUGIN_DIR, (char *)NULL);
+ }
+#endif
+#endif /* defined(HAVE_PLUGINS) || defined(HAVE_LUA) */
+}
+
+static void
+init_plugin_pers_dir(void)
+{
+#if defined(HAVE_PLUGINS) || defined(HAVE_LUA)
+#ifdef _WIN32
+ plugin_pers_dir = get_persconffile_path(PLUGINS_DIR_NAME, false);
+#else
+ plugin_pers_dir = g_build_filename(g_get_home_dir(), ".local/lib",
+ CONFIGURATION_NAMESPACE_LOWER, PLUGINS_DIR_NAME, (char *)NULL);
+#endif
+#endif /* defined(HAVE_PLUGINS) || defined(HAVE_LUA) */
+}
+
+/*
+ * Get the directory in which the plugins are stored.
+ */
+const char *
+get_plugins_dir(void)
+{
+ if (!plugin_dir)
+ init_plugin_dir();
+ return plugin_dir;
+}
+
+const char *
+get_plugins_dir_with_version(void)
+{
+ if (!plugin_dir)
+ init_plugin_dir();
+ if (plugin_dir && !plugin_dir_with_version)
+ plugin_dir_with_version = g_build_filename(plugin_dir, PLUGIN_PATH_ID, (char *)NULL);
+ return plugin_dir_with_version;
+}
+
+/* Get the personal plugin dir */
+const char *
+get_plugins_pers_dir(void)
+{
+ if (!plugin_pers_dir)
+ init_plugin_pers_dir();
+ return plugin_pers_dir;
+}
+
+const char *
+get_plugins_pers_dir_with_version(void)
+{
+ if (!plugin_pers_dir)
+ init_plugin_pers_dir();
+ if (plugin_pers_dir && !plugin_pers_dir_with_version)
+ plugin_pers_dir_with_version = g_build_filename(plugin_pers_dir, PLUGIN_PATH_ID, (char *)NULL);
+ return plugin_pers_dir_with_version;
+}
+
+/*
+ * Find the directory where the extcap hooks are stored.
+ *
+ * If the WIRESHARK_EXTCAP_DIR environment variable is set and we are not
+ * running with special privileges, use that. Otherwise:
+ *
+ * On Windows, we use the "extcap" subdirectory of the datafile directory.
+ *
+ * On UN*X:
+ *
+ * if we appear to be run from the build directory, we use the
+ * "extcap" subdirectory of the build directory.
+ *
+ * otherwise, if we're running from an app bundle in macOS, we
+ * use the Contents/MacOS/extcap subdirectory of the app bundle;
+ *
+ * otherwise, we use the EXTCAP_DIR value supplied by CMake.
+ */
+static char *extcap_dir = NULL;
+
+static void
+init_extcap_dir(void)
+{
+ const char *extcap_dir_envar = CONFIGURATION_ENVIRONMENT_VARIABLE("EXTCAP_DIR");
+ if (g_getenv(extcap_dir_envar) && !started_with_special_privs()) {
+ /*
+ * The user specified a different directory for extcap hooks
+ * and we aren't running with special privileges.
+ */
+ extcap_dir = g_strdup(g_getenv(extcap_dir_envar));
+ }
+
+#if defined(HAVE_MSYSTEM)
+ else if (running_in_build_directory_flag) {
+ extcap_dir = g_build_filename(install_prefix, "extcap", (char *)NULL);
+ } else {
+ extcap_dir = g_build_filename(install_prefix, EXTCAP_DIR, (char *)NULL);
+ }
+#elif defined(_WIN32)
+ else {
+ /*
+ * On Windows, the data file directory is the installation
+ * directory; the extcap hooks are stored under it.
+ *
+ * Assume we're running the installed version of Wireshark;
+ * on Windows, the data file directory is the directory
+ * in which the Wireshark binary resides.
+ */
+ extcap_dir = g_build_filename(get_datafile_dir(), "extcap", (char *)NULL);
+ }
+#else
+ else if (running_in_build_directory_flag) {
+ /*
+ * We're (probably) being run from the build directory and
+ * weren't started with special privileges, so we'll use
+ * the "extcap hooks" subdirectory of the directory where the program
+ * we're running is (that's the build directory).
+ */
+ extcap_dir = g_build_filename(get_progfile_dir(), "extcap", (char *)NULL);
+ }
+#ifdef ENABLE_APPLICATION_BUNDLE
+ else if (appbundle_dir != NULL) {
+ /*
+ * If we're running from an app bundle and weren't started
+ * with special privileges, use the Contents/MacOS/extcap
+ * subdirectory of the app bundle.
+ *
+ * (appbundle_dir is not set to a non-null value if we're
+ * started with special privileges, so we need only check
+ * it; we don't need to call started_with_special_privs().)
+ */
+ extcap_dir = g_build_filename(appbundle_dir, "Contents/MacOS/extcap", (char *)NULL);
+ }
+#endif
+ else {
+ extcap_dir = g_build_filename(install_prefix, EXTCAP_DIR, (char *)NULL);
+ }
+#endif
+}
+
+static void
+init_extcap_pers_dir(void)
+{
+#ifdef _WIN32
+ extcap_pers_dir = get_persconffile_path(EXTCAP_DIR_NAME, false);
+#else
+ extcap_pers_dir = g_build_filename(g_get_home_dir(), ".local/lib",
+ CONFIGURATION_NAMESPACE_LOWER, EXTCAP_DIR_NAME, (char *)NULL);
+#endif
+}
+
+/*
+ * Get the directory in which the extcap hooks are stored.
+ *
+ */
+const char *
+get_extcap_dir(void)
+{
+ if (!extcap_dir)
+ init_extcap_dir();
+ return extcap_dir;
+}
+
+/* Get the personal plugin dir */
+const char *
+get_extcap_pers_dir(void)
+{
+ if (!extcap_pers_dir)
+ init_extcap_pers_dir();
+ return extcap_pers_dir;
+}
+
+/*
+ * Get the flag indicating whether we're running from a build
+ * directory.
+ */
+bool
+running_in_build_directory(void)
+{
+ return running_in_build_directory_flag;
+}
+
+/*
+ * Get the directory in which files that, at least on UNIX, are
+ * system files (such as "/etc/ethers") are stored; on Windows,
+ * there's no "/etc" directory, so we get them from the global
+ * configuration and data file directory.
+ */
+const char *
+get_systemfile_dir(void)
+{
+#ifdef _WIN32
+ return get_datafile_dir();
+#else
+ return "/etc";
+#endif
+}
+
+void
+set_profile_name(const char *profilename)
+{
+ g_free (persconfprofile);
+
+ if (profilename && strlen(profilename) > 0 &&
+ strcmp(profilename, DEFAULT_PROFILE) != 0) {
+ persconfprofile = g_strdup (profilename);
+ } else {
+ /* Default Profile */
+ persconfprofile = NULL;
+ }
+}
+
+const char *
+get_profile_name(void)
+{
+ if (persconfprofile) {
+ return persconfprofile;
+ } else {
+ return DEFAULT_PROFILE;
+ }
+}
+
+bool
+is_default_profile(void)
+{
+ return (!persconfprofile || strcmp(persconfprofile, DEFAULT_PROFILE) == 0) ? true : false;
+}
+
+bool
+has_global_profiles(void)
+{
+ WS_DIR *dir;
+ WS_DIRENT *file;
+ char *global_dir = get_global_profiles_dir();
+ char *filename;
+ bool has_global = false;
+
+ if ((test_for_directory(global_dir) == EISDIR) &&
+ ((dir = ws_dir_open(global_dir, 0, NULL)) != NULL))
+ {
+ while ((file = ws_dir_read_name(dir)) != NULL) {
+ filename = ws_strdup_printf ("%s%s%s", global_dir, G_DIR_SEPARATOR_S,
+ ws_dir_get_name(file));
+ if (test_for_directory(filename) == EISDIR) {
+ has_global = true;
+ g_free (filename);
+ break;
+ }
+ g_free (filename);
+ }
+ ws_dir_close(dir);
+ }
+ g_free(global_dir);
+ return has_global;
+}
+
+void
+profile_store_persconffiles(bool store)
+{
+ if (store) {
+ profile_files = g_hash_table_new (g_str_hash, g_str_equal);
+ }
+ do_store_persconffiles = store;
+}
+
+void
+profile_register_persconffile(const char *filename)
+{
+ if (do_store_persconffiles && !g_hash_table_lookup (profile_files, filename)) {
+ /* Store filenames so we know which filenames belongs to a configuration profile */
+ g_hash_table_insert (profile_files, g_strdup(filename), g_strdup(filename));
+ }
+}
+
+/*
+ * Get the directory in which personal configuration files reside.
+ *
+ * On Windows, it's "Wireshark", under %APPDATA% or, if %APPDATA% isn't set,
+ * it's "%USERPROFILE%\Application Data" (which is what %APPDATA% normally
+ * is on Windows 2000).
+ *
+ * On UNIX-compatible systems, we first look in XDG_CONFIG_HOME/wireshark
+ * and, if that doesn't exist, ~/.wireshark, for backwards compatibility.
+ * If neither exists, we use XDG_CONFIG_HOME/wireshark, so that the directory
+ * is initially created as XDG_CONFIG_HOME/wireshark. We use that regardless
+ * of whether the user is running under an XDG desktop or not, so that
+ * if the user's home directory is on a server and shared between
+ * different desktop environments on different machines, they can all
+ * share the same configuration file directory.
+ *
+ * XXX - what about stuff that shouldn't be shared between machines,
+ * such as plugins in the form of shared loadable images?
+ */
+static const char *
+get_persconffile_dir_no_profile(void)
+{
+ const char *env;
+
+ /* Return the cached value, if available */
+ if (persconffile_dir != NULL)
+ return persconffile_dir;
+
+ /*
+ * See if the user has selected an alternate environment.
+ */
+ const char *config_dir_envar = CONFIGURATION_ENVIRONMENT_VARIABLE("CONFIG_DIR");
+ env = g_getenv(config_dir_envar);
+#ifdef _WIN32
+ if (env == NULL) {
+ /* for backward compatibility */
+ env = g_getenv("WIRESHARK_APPDATA");
+ }
+#endif
+ if (env != NULL) {
+ persconffile_dir = g_strdup(env);
+ return persconffile_dir;
+ }
+
+#ifdef _WIN32
+ /*
+ * Use %APPDATA% or %USERPROFILE%, so that configuration
+ * files are stored in the user profile, rather than in
+ * the home directory. The Windows convention is to store
+ * configuration information in the user profile, and doing
+ * so means you can use Wireshark even if the home directory
+ * is an inaccessible network drive.
+ */
+ env = g_getenv("APPDATA");
+ const char *persconf_namespace = CONFIGURATION_NAMESPACE_PROPER;
+ if (env != NULL) {
+ /*
+ * Concatenate %APPDATA% with "\Wireshark" or "\Logray".
+ */
+ persconffile_dir = g_build_filename(env, persconf_namespace, NULL);
+ return persconffile_dir;
+ }
+
+ /*
+ * OK, %APPDATA% wasn't set, so use %USERPROFILE%\Application Data.
+ */
+ env = g_getenv("USERPROFILE");
+ if (env != NULL) {
+ persconffile_dir = g_build_filename(env, "Application Data", persconf_namespace, NULL);
+ return persconffile_dir;
+ }
+
+ /*
+ * Give up and use "C:".
+ */
+ persconffile_dir = g_build_filename("C:", persconf_namespace, NULL);
+ return persconffile_dir;
+#else
+ char *xdg_path, *path;
+ struct passwd *pwd;
+ const char *homedir;
+
+ /*
+ * Check if XDG_CONFIG_HOME/wireshark exists and is a directory.
+ */
+ xdg_path = g_build_filename(g_get_user_config_dir(),
+ CONFIGURATION_NAMESPACE_LOWER, NULL);
+ if (g_file_test(xdg_path, G_FILE_TEST_IS_DIR)) {
+ persconffile_dir = xdg_path;
+ return persconffile_dir;
+ }
+
+ /*
+ * It doesn't exist, or it does but isn't a directory, so try
+ * ~/.wireshark.
+ *
+ * If $HOME is set, use that for ~.
+ *
+ * (Note: before GLib 2.36, g_get_home_dir() didn't look at $HOME,
+ * but we always want to do so, so we don't use g_get_home_dir().)
+ */
+ homedir = g_getenv("HOME");
+ if (homedir == NULL) {
+ /*
+ * It's not set.
+ *
+ * Get their home directory from the password file.
+ * If we can't even find a password file entry for them,
+ * use "/tmp".
+ */
+ pwd = getpwuid(getuid());
+ if (pwd != NULL) {
+ homedir = pwd->pw_dir;
+ } else {
+ homedir = "/tmp";
+ }
+ }
+ path = g_build_filename(homedir,
+ configuration_namespace == CONFIGURATION_NAMESPACE_WIRESHARK ? ".wireshark" : ".logray",
+ NULL);
+ if (g_file_test(path, G_FILE_TEST_IS_DIR)) {
+ g_free(xdg_path);
+ persconffile_dir = path;
+ return persconffile_dir;
+ }
+
+ /*
+ * Neither are directories that exist; use the XDG path, so we'll
+ * create that as necessary.
+ */
+ g_free(path);
+ persconffile_dir = xdg_path;
+ return persconffile_dir;
+#endif
+}
+
+void
+set_persconffile_dir(const char *p)
+{
+ g_free(persconffile_dir);
+ persconffile_dir = g_strdup(p);
+}
+
+char *
+get_profiles_dir(void)
+{
+ return ws_strdup_printf ("%s%s%s", get_persconffile_dir_no_profile (),
+ G_DIR_SEPARATOR_S, PROFILES_DIR);
+}
+
+int
+create_profiles_dir(char **pf_dir_path_return)
+{
+ char *pf_dir_path;
+ ws_statb64 s_buf;
+
+ /*
+ * Create the "Default" personal configuration files directory, if necessary.
+ */
+ if (create_persconffile_profile (NULL, pf_dir_path_return) == -1) {
+ return -1;
+ }
+
+ /*
+ * Check if profiles directory exists.
+ * If not then create it.
+ */
+ pf_dir_path = get_profiles_dir ();
+ if (ws_stat64(pf_dir_path, &s_buf) != 0) {
+ if (errno != ENOENT) {
+ /* Some other problem; give up now. */
+ *pf_dir_path_return = pf_dir_path;
+ return -1;
+ }
+
+ /*
+ * It doesn't exist; try to create it.
+ */
+ int ret = ws_mkdir(pf_dir_path, 0755);
+ if (ret == -1) {
+ *pf_dir_path_return = pf_dir_path;
+ return ret;
+ }
+ }
+ g_free(pf_dir_path);
+
+ return 0;
+}
+
+char *
+get_global_profiles_dir(void)
+{
+ return ws_strdup_printf ("%s%s%s", get_datafile_dir(),
+ G_DIR_SEPARATOR_S, PROFILES_DIR);
+}
+
+static char *
+get_persconffile_dir(const char *profilename)
+{
+ char *persconffile_profile_dir = NULL, *profile_dir;
+
+ if (profilename && strlen(profilename) > 0 &&
+ strcmp(profilename, DEFAULT_PROFILE) != 0) {
+ profile_dir = get_profiles_dir();
+ persconffile_profile_dir = ws_strdup_printf ("%s%s%s", profile_dir,
+ G_DIR_SEPARATOR_S, profilename);
+ g_free(profile_dir);
+ } else {
+ persconffile_profile_dir = g_strdup (get_persconffile_dir_no_profile ());
+ }
+
+ return persconffile_profile_dir;
+}
+
+char *
+get_profile_dir(const char *profilename, bool is_global)
+{
+ char *profile_dir;
+
+ if (is_global) {
+ if (profilename && strlen(profilename) > 0 &&
+ strcmp(profilename, DEFAULT_PROFILE) != 0)
+ {
+ char *global_path = get_global_profiles_dir();
+ profile_dir = g_build_filename(global_path, profilename, NULL);
+ g_free(global_path);
+ } else {
+ profile_dir = g_strdup(get_datafile_dir());
+ }
+ } else {
+ /*
+ * If we didn't supply a profile name, i.e. if profilename is
+ * null, get_persconffile_dir() returns the default profile.
+ */
+ profile_dir = get_persconffile_dir(profilename);
+ }
+
+ return profile_dir;
+}
+
+bool
+profile_exists(const char *profilename, bool global)
+{
+ char *path = NULL;
+ bool exists;
+
+ /*
+ * If we're looking up a global profile, we must have a
+ * profile name.
+ */
+ if (global && !profilename)
+ return false;
+
+ path = get_profile_dir(profilename, global);
+ exists = (test_for_directory(path) == EISDIR) ? true : false;
+
+ g_free(path);
+ return exists;
+}
+
+static int
+delete_directory (const char *directory, char **pf_dir_path_return)
+{
+ WS_DIR *dir;
+ WS_DIRENT *file;
+ char *filename;
+ int ret = 0;
+
+ if ((dir = ws_dir_open(directory, 0, NULL)) != NULL) {
+ while ((file = ws_dir_read_name(dir)) != NULL) {
+ filename = ws_strdup_printf ("%s%s%s", directory, G_DIR_SEPARATOR_S,
+ ws_dir_get_name(file));
+ if (test_for_directory(filename) != EISDIR) {
+ ret = ws_remove(filename);
+#if 0
+ } else {
+ /* The user has manually created a directory in the profile directory */
+ /* I do not want to delete the directory recursively yet */
+ ret = delete_directory (filename, pf_dir_path_return);
+#endif
+ }
+ if (ret != 0) {
+ *pf_dir_path_return = filename;
+ break;
+ }
+ g_free (filename);
+ }
+ ws_dir_close(dir);
+ }
+
+ if (ret == 0 && (ret = ws_remove(directory)) != 0) {
+ *pf_dir_path_return = g_strdup (directory);
+ }
+
+ return ret;
+}
+
+/* Copy files from one directory to another. Does not recursively copy directories */
+static int
+copy_directory(const char *from_dir, const char *to_dir, char **pf_filename_return)
+{
+ int ret = 0;
+ char *from_file, *to_file;
+ const char *filename;
+ WS_DIR *dir;
+ WS_DIRENT *file;
+
+ if ((dir = ws_dir_open(from_dir, 0, NULL)) != NULL) {
+ while ((file = ws_dir_read_name(dir)) != NULL) {
+ filename = ws_dir_get_name(file);
+ from_file = ws_strdup_printf ("%s%s%s", from_dir, G_DIR_SEPARATOR_S, filename);
+ if (test_for_directory(from_file) != EISDIR) {
+ to_file = ws_strdup_printf ("%s%s%s", to_dir, G_DIR_SEPARATOR_S, filename);
+ if (!copy_file_binary_mode(from_file, to_file)) {
+ *pf_filename_return = g_strdup(filename);
+ g_free (from_file);
+ g_free (to_file);
+ ret = -1;
+ break;
+ }
+ g_free (to_file);
+#if 0
+ } else {
+ /* The user has manually created a directory in the profile
+ * directory. Do not copy the directory recursively (yet?)
+ */
+#endif
+ }
+ g_free (from_file);
+ }
+ ws_dir_close(dir);
+ }
+
+ return ret;
+}
+
+static int
+reset_default_profile(char **pf_dir_path_return)
+{
+ char *profile_dir = get_persconffile_dir(NULL);
+ char *filename, *del_file;
+ GList *files, *file;
+ int ret = 0;
+
+ files = g_hash_table_get_keys(profile_files);
+ file = g_list_first(files);
+ while (file) {
+ filename = (char *)file->data;
+ del_file = ws_strdup_printf("%s%s%s", profile_dir, G_DIR_SEPARATOR_S, filename);
+
+ if (file_exists(del_file)) {
+ ret = ws_remove(del_file);
+ if (ret != 0) {
+ *pf_dir_path_return = profile_dir;
+ g_free(del_file);
+ break;
+ }
+ }
+
+ g_free(del_file);
+ file = g_list_next(file);
+ }
+ g_list_free(files);
+
+ g_free(profile_dir);
+ return ret;
+}
+
+int
+delete_persconffile_profile(const char *profilename, char **pf_dir_path_return)
+{
+ if (strcmp(profilename, DEFAULT_PROFILE) == 0) {
+ return reset_default_profile(pf_dir_path_return);
+ }
+
+ char *profile_dir = get_persconffile_dir(profilename);
+ int ret = 0;
+
+ if (test_for_directory (profile_dir) == EISDIR) {
+ ret = delete_directory (profile_dir, pf_dir_path_return);
+ }
+
+ g_free(profile_dir);
+ return ret;
+}
+
+int
+rename_persconffile_profile(const char *fromname, const char *toname,
+ char **pf_from_dir_path_return, char **pf_to_dir_path_return)
+{
+ char *from_dir = get_persconffile_dir(fromname);
+ char *to_dir = get_persconffile_dir(toname);
+ int ret = 0;
+
+ ret = ws_rename (from_dir, to_dir);
+ if (ret != 0) {
+ *pf_from_dir_path_return = from_dir;
+ *pf_to_dir_path_return = to_dir;
+ return ret;
+ }
+
+ g_free (from_dir);
+ g_free (to_dir);
+
+ return 0;
+}
+
+/*
+ * Create the directory that holds personal configuration files, if
+ * necessary. If we attempted to create it, and failed, return -1 and
+ * set "*pf_dir_path_return" to the pathname of the directory we failed
+ * to create (it's g_mallocated, so our caller should free it); otherwise,
+ * return 0.
+ */
+int
+create_persconffile_profile(const char *profilename, char **pf_dir_path_return)
+{
+ char *pf_dir_path;
+#ifdef _WIN32
+ char *pf_dir_path_copy, *pf_dir_parent_path;
+ size_t pf_dir_parent_path_len;
+ int save_errno;
+#endif
+ ws_statb64 s_buf;
+ int ret;
+
+ if (profilename) {
+ /*
+ * Create the personal profiles directory, if necessary.
+ */
+ if (create_profiles_dir(pf_dir_path_return) == -1) {
+ return -1;
+ }
+ }
+
+ pf_dir_path = get_persconffile_dir(profilename);
+ if (ws_stat64(pf_dir_path, &s_buf) != 0) {
+ if (errno != ENOENT) {
+ /* Some other problem; give up now. */
+ *pf_dir_path_return = pf_dir_path;
+ return -1;
+ }
+#ifdef _WIN32
+ /*
+ * Does the parent directory of that directory
+ * exist? %APPDATA% may not exist even though
+ * %USERPROFILE% does.
+ *
+ * We check for the existence of the directory
+ * by first checking whether the parent directory
+ * is just a drive letter and, if it's not, by
+ * doing a "stat()" on it. If it's a drive letter,
+ * or if the "stat()" succeeds, we assume it exists.
+ */
+ pf_dir_path_copy = g_strdup(pf_dir_path);
+ pf_dir_parent_path = get_dirname(pf_dir_path_copy);
+ pf_dir_parent_path_len = strlen(pf_dir_parent_path);
+ if (pf_dir_parent_path_len > 0
+ && pf_dir_parent_path[pf_dir_parent_path_len - 1] != ':'
+ && ws_stat64(pf_dir_parent_path, &s_buf) != 0) {
+ /*
+ * Not a drive letter and the stat() failed.
+ */
+ if (errno != ENOENT) {
+ /* Some other problem; give up now. */
+ *pf_dir_path_return = pf_dir_path;
+ save_errno = errno;
+ g_free(pf_dir_path_copy);
+ errno = save_errno;
+ return -1;
+ }
+ /*
+ * No, it doesn't exist - make it first.
+ */
+ ret = ws_mkdir(pf_dir_parent_path, 0755);
+ if (ret == -1) {
+ *pf_dir_path_return = pf_dir_parent_path;
+ save_errno = errno;
+ g_free(pf_dir_path);
+ errno = save_errno;
+ return -1;
+ }
+ }
+ g_free(pf_dir_path_copy);
+ ret = ws_mkdir(pf_dir_path, 0755);
+#else
+ ret = g_mkdir_with_parents(pf_dir_path, 0755);
+#endif
+ } else {
+ /*
+ * Something with that pathname exists; if it's not
+ * a directory, we'll get an error if we try to put
+ * something in it, so we don't fail here, we wait
+ * for that attempt to fail.
+ */
+ ret = 0;
+ }
+ if (ret == -1)
+ *pf_dir_path_return = pf_dir_path;
+ else
+ g_free(pf_dir_path);
+
+ return ret;
+}
+
+const GHashTable *
+allowed_profile_filenames(void)
+{
+ return profile_files;
+}
+
+int
+create_persconffile_dir(char **pf_dir_path_return)
+{
+ return create_persconffile_profile(persconfprofile, pf_dir_path_return);
+}
+
+int
+copy_persconffile_profile(const char *toname, const char *fromname, bool from_global,
+ char **pf_filename_return, char **pf_to_dir_path_return, char **pf_from_dir_path_return)
+{
+ int ret = 0;
+ char *from_dir;
+ char *to_dir = get_persconffile_dir(toname);
+ char *from_file, *to_file;
+ const char *filename;
+ GHashTableIter files;
+ void * file;
+
+ from_dir = get_profile_dir(fromname, from_global);
+
+ if (!profile_files || do_store_persconffiles) {
+ /* Either the profile_files hashtable does not exist yet
+ * (this is very early in startup) or we are still adding
+ * files to it. Just copy all the non-directories.
+ */
+ ret = copy_directory(from_dir, to_dir, pf_filename_return);
+ } else {
+
+ g_hash_table_iter_init(&files, profile_files);
+ while (g_hash_table_iter_next(&files, &file, NULL)) {
+ filename = (const char *)file;
+ from_file = ws_strdup_printf ("%s%s%s", from_dir, G_DIR_SEPARATOR_S, filename);
+ to_file = ws_strdup_printf ("%s%s%s", to_dir, G_DIR_SEPARATOR_S, filename);
+
+ if (file_exists(from_file) && !copy_file_binary_mode(from_file, to_file)) {
+ *pf_filename_return = g_strdup(filename);
+ g_free (from_file);
+ g_free (to_file);
+ ret = -1;
+ break;
+ }
+
+ g_free (to_file);
+ g_free (from_file);
+ }
+ }
+
+ if (ret != 0) {
+ *pf_to_dir_path_return = to_dir;
+ *pf_from_dir_path_return = from_dir;
+ } else {
+ g_free (to_dir);
+ g_free (from_dir);
+ }
+
+ return ret;
+}
+
+/*
+ * Get the (default) directory in which personal data is stored.
+ *
+ * On Win32, this is the "My Documents" folder in the personal profile.
+ * On UNIX this is simply the current directory, unless that's "/",
+ * which it will be, for example, when Wireshark is run from the
+ * Finder in macOS, in which case we use the user's home directory.
+ */
+/* XXX - should this and the get_home_dir() be merged? */
+extern const char *
+get_persdatafile_dir(void)
+{
+ /* Return the cached value, if available */
+ if (persdatafile_dir != NULL)
+ return persdatafile_dir;
+
+#ifdef _WIN32
+ TCHAR tszPath[MAX_PATH];
+
+ /*
+ * Hint: SHGetFolderPath is not available on MSVC 6 - without
+ * Platform SDK
+ */
+ if (SHGetSpecialFolderPath(NULL, tszPath, CSIDL_PERSONAL, false)) {
+ persdatafile_dir = g_utf16_to_utf8(tszPath, -1, NULL, NULL, NULL);
+ return persdatafile_dir;
+ } else {
+ return "";
+ }
+#else
+ /*
+ * Get the current directory.
+ */
+ persdatafile_dir = g_get_current_dir();
+ if (persdatafile_dir == NULL) {
+ /* XXX - can this fail? */
+ /*
+ * g_get_home_dir() returns a const gchar *; g_strdup() it
+ * so that it's something that can be freed.
+ */
+ persdatafile_dir = g_strdup(g_get_home_dir());
+ } else if (strcmp(persdatafile_dir, "/") == 0) {
+ g_free(persdatafile_dir);
+ /*
+ * See above.
+ */
+ persdatafile_dir = g_strdup(g_get_home_dir());
+ }
+ return persdatafile_dir;
+#endif
+}
+
+void
+set_persdatafile_dir(const char *p)
+{
+ g_free(persdatafile_dir);
+ persdatafile_dir = g_strdup(p);
+}
+
+/*
+ * Construct the path name of a personal configuration file, given the
+ * file name.
+ *
+ * On Win32, if "for_writing" is false, we check whether the file exists
+ * and, if not, construct a path name relative to the ".wireshark"
+ * subdirectory of the user's home directory, and check whether that
+ * exists; if it does, we return that, so that configuration files
+ * from earlier versions can be read.
+ *
+ * The returned file name was g_malloc()'d so it must be g_free()d when the
+ * caller is done with it.
+ */
+char *
+get_persconffile_path(const char *filename, bool from_profile)
+{
+ char *path, *dir = NULL;
+
+ if (from_profile) {
+ /* Store filenames so we know which filenames belongs to a configuration profile */
+ profile_register_persconffile(filename);
+
+ dir = get_persconffile_dir(persconfprofile);
+ } else {
+ dir = get_persconffile_dir(NULL);
+ }
+ path = g_build_filename(dir, filename, NULL);
+
+ g_free(dir);
+ return path;
+}
+
+/*
+ * Construct the path name of a global configuration file, given the
+ * file name.
+ *
+ * The returned file name was g_malloc()'d so it must be g_free()d when the
+ * caller is done with it.
+ */
+char *
+get_datafile_path(const char *filename)
+{
+ if (running_in_build_directory_flag && !strcmp(filename, "hosts")) {
+ /* We're running in the build directory and the requested file is a
+ * generated (or a test) file. Return the file name in the build
+ * directory (not in the source/data directory).
+ * (Oh the things we do to keep the source directory pristine...)
+ */
+ return g_build_filename(get_progfile_dir(), filename, (char *)NULL);
+ } else {
+ return g_build_filename(get_datafile_dir(), filename, (char *)NULL);
+ }
+}
+
+/*
+ * Construct the path name of a global documentation file, given the
+ * file name.
+ *
+ * The returned file name was g_malloc()'d so it must be g_free()d when the
+ * caller is done with it.
+ */
+char *
+get_docfile_path(const char *filename)
+{
+ if (running_in_build_directory_flag) {
+ /* We're running in the build directory and the requested file is a
+ * generated (or a test) file. Return the file name in the build
+ * directory (not in the source/data directory).
+ * (Oh the things we do to keep the source directory pristine...)
+ */
+ return g_build_filename(get_progfile_dir(), filename, (char *)NULL);
+ } else {
+ return g_build_filename(get_doc_dir(), filename, (char *)NULL);
+ }
+}
+
+/*
+ * Return an error message for UNIX-style errno indications on open or
+ * create operations.
+ */
+const char *
+file_open_error_message(int err, bool for_writing)
+{
+ const char *errmsg;
+ static char errmsg_errno[1024+1];
+
+ switch (err) {
+
+ case ENOENT:
+ if (for_writing)
+ errmsg = "The path to the file \"%s\" doesn't exist.";
+ else
+ errmsg = "The file \"%s\" doesn't exist.";
+ break;
+
+ case EACCES:
+ if (for_writing)
+ errmsg = "You don't have permission to create or write to the file \"%s\".";
+ else
+ errmsg = "You don't have permission to read the file \"%s\".";
+ break;
+
+ case EISDIR:
+ errmsg = "\"%s\" is a directory (folder), not a file.";
+ break;
+
+ case ENOSPC:
+ errmsg = "The file \"%s\" could not be created because there is no space left on the file system.";
+ break;
+
+#ifdef EDQUOT
+ case EDQUOT:
+ errmsg = "The file \"%s\" could not be created because you are too close to, or over, your disk quota.";
+ break;
+#endif
+
+ case EINVAL:
+ errmsg = "The file \"%s\" could not be created because an invalid filename was specified.";
+ break;
+
+#ifdef ENAMETOOLONG
+ case ENAMETOOLONG:
+ /* XXX Make sure we truncate on a character boundary. */
+ errmsg = "The file name \"%.80s" UTF8_HORIZONTAL_ELLIPSIS "\" is too long.";
+ break;
+#endif
+
+ case ENOMEM:
+ /*
+ * The problem probably has nothing to do with how much RAM the
+ * user has on their machine, so don't confuse them by saying
+ * "memory". The problem is probably either virtual address
+ * space or swap space.
+ */
+#if GLIB_SIZEOF_VOID_P == 4
+ /*
+ * ILP32; we probably ran out of virtual address space.
+ */
+#define ENOMEM_REASON "it can't be handled by a 32-bit application"
+#else
+ /*
+ * LP64 or LLP64; we probably ran out of swap space.
+ */
+#if defined(_WIN32)
+ /*
+ * You need to make the pagefile bigger.
+ */
+#define ENOMEM_REASON "the pagefile is too small"
+#elif defined(ENABLE_APPLICATION_BUNDLE)
+ /*
+ * dynamic_pager couldn't, or wouldn't, create more swap files.
+ */
+#define ENOMEM_REASON "your system ran out of swap file space"
+#else
+ /*
+ * Either you have a fixed swap partition or a fixed swap file,
+ * and it needs to be made bigger.
+ *
+ * This is UN*X, but it's not macOS, so we assume the user is
+ * *somewhat* nerdy.
+ */
+#define ENOMEM_REASON "your system is out of swap space"
+#endif
+#endif /* GLIB_SIZEOF_VOID_P == 4 */
+ if (for_writing)
+ errmsg = "The file \"%s\" could not be created because " ENOMEM_REASON ".";
+ else
+ errmsg = "The file \"%s\" could not be opened because " ENOMEM_REASON ".";
+ break;
+
+ default:
+ snprintf(errmsg_errno, sizeof(errmsg_errno),
+ "The file \"%%s\" could not be %s: %s.",
+ for_writing ? "created" : "opened",
+ g_strerror(err));
+ errmsg = errmsg_errno;
+ break;
+ }
+ return errmsg;
+}
+
+/*
+ * Return an error message for UNIX-style errno indications on write
+ * operations.
+ */
+const char *
+file_write_error_message(int err)
+{
+ const char *errmsg;
+ static char errmsg_errno[1024+1];
+
+ switch (err) {
+
+ case ENOSPC:
+ errmsg = "The file \"%s\" could not be saved because there is no space left on the file system.";
+ break;
+
+#ifdef EDQUOT
+ case EDQUOT:
+ errmsg = "The file \"%s\" could not be saved because you are too close to, or over, your disk quota.";
+ break;
+#endif
+
+ default:
+ snprintf(errmsg_errno, sizeof(errmsg_errno),
+ "An error occurred while writing to the file \"%%s\": %s.",
+ g_strerror(err));
+ errmsg = errmsg_errno;
+ break;
+ }
+ return errmsg;
+}
+
+
+bool
+file_exists(const char *fname)
+{
+ ws_statb64 file_stat;
+
+ if (!fname) {
+ return false;
+ }
+
+ if (ws_stat64(fname, &file_stat) != 0 && errno == ENOENT) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+bool config_file_exists_with_entries(const char *fname, char comment_char)
+{
+ bool start_of_line = true;
+ bool has_entries = false;
+ FILE *file;
+ int c;
+
+ if (!fname) {
+ return false;
+ }
+
+ if ((file = ws_fopen(fname, "r")) == NULL) {
+ return false;
+ }
+
+ do {
+ c = ws_getc_unlocked(file);
+ if (start_of_line && c != comment_char && !g_ascii_isspace(c) && g_ascii_isprint(c)) {
+ has_entries = true;
+ break;
+ }
+ if (c == '\n' || !g_ascii_isspace(c)) {
+ start_of_line = (c == '\n');
+ }
+ } while (c != EOF);
+
+ fclose(file);
+ return has_entries;
+}
+
+/*
+ * Check that the from file is not the same as to file
+ * We do it here so we catch all cases ...
+ * Unfortunately, the file requester gives us an absolute file
+ * name and the read file name may be relative (if supplied on
+ * the command line), so we can't just compare paths. From Joerg Mayer.
+ */
+bool
+files_identical(const char *fname1, const char *fname2)
+{
+ /* Two different implementations, because:
+ *
+ * - _fullpath is not available on UN*X, so we can't get full
+ * paths and compare them (which wouldn't work with hard links
+ * in any case);
+ *
+ * - st_ino isn't filled in with a meaningful value on Windows.
+ */
+#ifdef _WIN32
+ char full1[MAX_PATH], full2[MAX_PATH];
+
+ /*
+ * Get the absolute full paths of the file and compare them.
+ * That won't work if you have hard links, but those aren't
+ * much used on Windows, even though NTFS supports them.
+ *
+ * XXX - will _fullpath work with UNC?
+ */
+ if( _fullpath( full1, fname1, MAX_PATH ) == NULL ) {
+ return false;
+ }
+
+ if( _fullpath( full2, fname2, MAX_PATH ) == NULL ) {
+ return false;
+ }
+
+ if(strcmp(full1, full2) == 0) {
+ return true;
+ } else {
+ return false;
+ }
+#else
+ ws_statb64 filestat1, filestat2;
+
+ /*
+ * Compare st_dev and st_ino.
+ */
+ if (ws_stat64(fname1, &filestat1) == -1)
+ return false; /* can't get info about the first file */
+ if (ws_stat64(fname2, &filestat2) == -1)
+ return false; /* can't get info about the second file */
+ return (filestat1.st_dev == filestat2.st_dev &&
+ filestat1.st_ino == filestat2.st_ino);
+#endif
+}
+
+bool
+file_needs_reopen(int fd, const char* filename)
+{
+#ifdef _WIN32
+ /* Windows handles st_dev in a way unsuitable here:
+ * * _fstat() simply casts the file descriptor (ws_fileno(fp)) to unsigned
+ * and assigns this value to st_dev and st_rdev
+ * * _wstat() converts drive letter (eg. C) to number (A=0, B=1, C=2, ...)
+ * and assigns such number to st_dev and st_rdev
+ *
+ * The st_ino parameter is simply zero as there is no specific assignment
+ * to it in the Universal CRT source code.
+ *
+ * Thus instead of using fstat(), use Windows specific API.
+ */
+
+ HANDLE open_handle = (HANDLE)_get_osfhandle(fd);
+ HANDLE current_handle = CreateFile(utf_8to16(filename), FILE_READ_ATTRIBUTES,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, 0, NULL);
+ BY_HANDLE_FILE_INFORMATION open_info, current_info;
+
+ if (current_handle == INVALID_HANDLE_VALUE) {
+ return true;
+ }
+
+#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
+ FILE_ID_INFO open_id, current_id;
+ if (GetFileInformationByHandleEx(open_handle, FileIdInfo, &open_id, sizeof(open_id)) &&
+ GetFileInformationByHandleEx(current_handle, FileIdInfo, &current_id, sizeof(current_id))) {
+ /* 128-bit identifier is available, use it */
+ CloseHandle(current_handle);
+ return open_id.VolumeSerialNumber != current_id.VolumeSerialNumber ||
+ memcmp(&open_id.FileId, &current_id.FileId, sizeof(open_id.FileId)) != 0;
+ }
+#endif /* _WIN32_WINNT >= _WIN32_WINNT_WIN8 */
+ if (GetFileInformationByHandle(open_handle, &open_info) &&
+ GetFileInformationByHandle(current_handle, &current_info)) {
+ /* Fallback to 64-bit identifier */
+ CloseHandle(current_handle);
+ uint64_t open_size = (((uint64_t)open_info.nFileSizeHigh) << 32) | open_info.nFileSizeLow;
+ uint64_t current_size = (((uint64_t)current_info.nFileSizeHigh) << 32) | current_info.nFileSizeLow;
+ return open_info.dwVolumeSerialNumber != current_info.dwVolumeSerialNumber ||
+ open_info.nFileIndexHigh != current_info.nFileIndexHigh ||
+ open_info.nFileIndexLow != current_info.nFileIndexLow ||
+ open_size > current_size;
+ }
+ CloseHandle(current_handle);
+ return true;
+#else
+ ws_statb64 open_stat, current_stat;
+
+ /* consider a file deleted when stat fails for either file,
+ * or when the residing device / inode has changed. */
+ if (0 != ws_fstat64(fd, &open_stat))
+ return true;
+ if (0 != ws_stat64(filename, &current_stat))
+ return true;
+
+ return open_stat.st_dev != current_stat.st_dev ||
+ open_stat.st_ino != current_stat.st_ino ||
+ open_stat.st_size > current_stat.st_size;
+#endif
+}
+
+bool
+write_file_binary_mode(const char *filename, const void *content, size_t content_len)
+{
+ int fd;
+ size_t bytes_left;
+ unsigned int bytes_to_write;
+ ssize_t bytes_written;
+ const uint8_t *ptr;
+ int err;
+
+ fd = ws_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
+ if (fd == -1) {
+ report_open_failure(filename, errno, true);
+ return false;
+ }
+
+ /*
+ * The third argument to _write() on Windows is an unsigned int,
+ * so, on Windows, that's the size of the third argument to
+ * ws_write().
+ *
+ * The third argument to write() on UN*X is a size_t, although
+ * the return value is an ssize_t, so one probably shouldn't
+ * write more than the max value of an ssize_t.
+ *
+ * In either case, there's no guarantee that a size_t such as
+ * content_len can be passed to ws_write(), so we write in
+ * chunks of at most 2^31 bytes.
+ */
+
+ ptr = (const uint8_t *)content;
+ bytes_left = content_len;
+ while (bytes_left != 0) {
+ if (bytes_left > 0x40000000) {
+ bytes_to_write = 0x40000000;
+ } else {
+ bytes_to_write = (unsigned int)bytes_left;
+ }
+ bytes_written = ws_write(fd, ptr, bytes_to_write);
+ if (bytes_written <= 0) {
+ if (bytes_written < 0) {
+ err = errno;
+ } else {
+ err = WTAP_ERR_SHORT_WRITE;
+ }
+ report_write_failure(filename, err);
+ ws_close(fd);
+ return false;
+ }
+ bytes_left -= bytes_written;
+ ptr += bytes_written;
+ }
+
+ ws_close(fd);
+ return true;
+}
+
+/*
+ * Copy a file in binary mode, for those operating systems that care about
+ * such things. This should be OK for all files, even text files, as
+ * we'll copy the raw bytes, and we don't look at the bytes as we copy
+ * them.
+ *
+ * Returns true on success, false on failure. If a failure, it also
+ * displays a simple dialog window with the error message.
+ */
+bool
+copy_file_binary_mode(const char *from_filename, const char *to_filename)
+{
+ int from_fd, to_fd, err;
+ ws_file_ssize_t nread, nwritten;
+ uint8_t *pd = NULL;
+
+ /* Copy the raw bytes of the file. */
+ from_fd = ws_open(from_filename, O_RDONLY | O_BINARY, 0000 /* no creation so don't matter */);
+ if (from_fd < 0) {
+ report_open_failure(from_filename, errno, false);
+ goto done;
+ }
+
+ /* Use open() instead of creat() so that we can pass the O_BINARY
+ flag, which is relevant on Win32; it appears that "creat()"
+ may open the file in text mode, not binary mode, but we want
+ to copy the raw bytes of the file, so we need the output file
+ to be open in binary mode. */
+ to_fd = ws_open(to_filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
+ if (to_fd < 0) {
+ report_open_failure(to_filename, errno, true);
+ ws_close(from_fd);
+ goto done;
+ }
+
+#define FS_READ_SIZE 65536
+ pd = (uint8_t *)g_malloc(FS_READ_SIZE);
+ while ((nread = ws_read(from_fd, pd, FS_READ_SIZE)) > 0) {
+ nwritten = ws_write(to_fd, pd, nread);
+ if (nwritten < nread) {
+ if (nwritten < 0)
+ err = errno;
+ else
+ err = WTAP_ERR_SHORT_WRITE;
+ report_write_failure(to_filename, err);
+ ws_close(from_fd);
+ ws_close(to_fd);
+ goto done;
+ }
+ }
+ if (nread < 0) {
+ err = errno;
+ report_read_failure(from_filename, err);
+ ws_close(from_fd);
+ ws_close(to_fd);
+ goto done;
+ }
+ ws_close(from_fd);
+ if (ws_close(to_fd) < 0) {
+ report_write_failure(to_filename, errno);
+ goto done;
+ }
+
+ g_free(pd);
+ pd = NULL;
+ return true;
+
+done:
+ g_free(pd);
+ return false;
+}
+
+char *
+data_file_url(const char *filename)
+{
+ char *file_path;
+ char *uri;
+
+ /* Absolute path? */
+ if(g_path_is_absolute(filename)) {
+ file_path = g_strdup(filename);
+ } else {
+ file_path = ws_strdup_printf("%s/%s", get_datafile_dir(), filename);
+ }
+
+ /* XXX - check, if the file is really existing, otherwise display a simple_dialog about the problem */
+
+ /* convert filename to uri */
+ uri = g_filename_to_uri(file_path, NULL, NULL);
+ g_free(file_path);
+ return uri;
+}
+
+char *
+doc_file_url(const char *filename)
+{
+ char *file_path;
+ char *uri;
+
+ /* Absolute path? */
+ if(g_path_is_absolute(filename)) {
+ file_path = g_strdup(filename);
+ } else {
+ file_path = ws_strdup_printf("%s/%s", get_doc_dir(), filename);
+ }
+
+ /* XXX - check, if the file is really existing, otherwise display a simple_dialog about the problem */
+
+ /* convert filename to uri */
+ uri = g_filename_to_uri(file_path, NULL, NULL);
+ g_free(file_path);
+ return uri;
+}
+
+void
+free_progdirs(void)
+{
+ g_free(persconffile_dir);
+ persconffile_dir = NULL;
+ g_free(datafile_dir);
+ datafile_dir = NULL;
+ g_free(persdatafile_dir);
+ persdatafile_dir = NULL;
+ g_free(persconfprofile);
+ persconfprofile = NULL;
+ g_free(progfile_dir);
+ progfile_dir = NULL;
+ g_free(doc_dir);
+ doc_dir = NULL;
+ g_free(install_prefix);
+ install_prefix = NULL;
+#if defined(HAVE_PLUGINS) || defined(HAVE_LUA)
+ g_free(plugin_dir);
+ plugin_dir = NULL;
+ g_free(plugin_dir_with_version);
+ plugin_dir_with_version = NULL;
+ g_free(plugin_pers_dir);
+ plugin_pers_dir = NULL;
+ g_free(plugin_pers_dir_with_version);
+ plugin_pers_dir_with_version = NULL;
+#endif
+ g_free(extcap_dir);
+ extcap_dir = NULL;
+ g_free(extcap_pers_dir);
+ extcap_pers_dir = NULL;
+}
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wsutil/filesystem.h b/wsutil/filesystem.h
new file mode 100644
index 00000000..fd3e3ebc
--- /dev/null
+++ b/wsutil/filesystem.h
@@ -0,0 +1,426 @@
+/** @file
+ * Filesystem utility definitions
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef FILESYSTEM_H
+#define FILESYSTEM_H
+
+#include <wireshark.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * Default profile name.
+ */
+#define DEFAULT_PROFILE "Default"
+
+/**
+ * Initialize our configuration environment.
+ *
+ * Get the pathname of the directory from which the executable came,
+ * and save it for future use.
+ *
+ * Set our configuration namespace, which determines the top-level
+ * configuration directory name and environment variable prefixes.
+ * Default is "Wireshark".
+ *
+ * @param arg0 Executable name hint. Should be argv[0].
+ * @param namespace_name The namespace to use. "Wireshark" or NULL uses
+ * the Wireshark namespace. "Logray" uses the Logray namespace.
+ * @return NULL on success, and a g_mallocated string containing an error on failure.
+ */
+WS_DLL_PUBLIC char *configuration_init(const char *arg0, const char *namespace_name);
+
+/**
+ * Get the configuration namespace name.
+ * @return The namespace name. One of "Wireshark" or "Logray".
+ */
+WS_DLL_PUBLIC const char *get_configuration_namespace(void);
+
+/**
+ * Check to see if the configuration namespace is for packet analysis
+ * (Wireshark) or log analysis (Logray).
+ * @return true if the configuration namespace is for packets.
+ */
+WS_DLL_PUBLIC bool is_packet_configuration_namespace(void);
+
+/*
+ * Get the directory in which the main (Wireshark, TShark, Logray, etc)
+ * program resides.
+ * Extcaps should use get_extcap_dir() to get their path.
+ *
+ * @return The main program file directory.
+ */
+WS_DLL_PUBLIC const char *get_progfile_dir(void);
+
+/*
+ * Construct the path name of a non-extcap Wireshark executable file,
+ * given the program name. The executable name doesn't include ".exe";
+ * append it on Windows, so that callers don't have to worry about that.
+ *
+ * This presumes that all non-extcap executables are in the same directory.
+ *
+ * The returned file name was g_malloc()'d so it must be g_free()d when the
+ * caller is done with it.
+ */
+WS_DLL_PUBLIC char *get_executable_path(const char *filename);
+
+/*
+ * Get the directory in which plugins are stored; this must not be called
+ * before configuration_init() is called, as they might be stored in a
+ * subdirectory of the program file directory.
+ */
+WS_DLL_PUBLIC const char *get_plugins_dir(void);
+
+/*
+ * Append VERSION_MAJOR.VERSION_MINOR to the plugin dir.
+ */
+WS_DLL_PUBLIC const char *get_plugins_dir_with_version(void);
+
+/*
+ * Get the personal plugin dir.
+ */
+WS_DLL_PUBLIC const char *get_plugins_pers_dir(void);
+
+/*
+ * Append VERSION_MAJOR.VERSION_MINOR to the plugin personal dir.
+ */
+WS_DLL_PUBLIC const char *get_plugins_pers_dir_with_version(void);
+
+/*
+ * Get the directory in which extcap hooks are stored; this must not be called
+ * before configuration_init() is called, as they might be stored in a
+ * subdirectory of the program file directory.
+ */
+WS_DLL_PUBLIC const char *get_extcap_dir(void);
+
+/*
+ * Get the personal extcap dir.
+ */
+WS_DLL_PUBLIC const char *get_extcap_pers_dir(void);
+
+/*
+ * Get the flag indicating whether we're running from a build
+ * directory.
+ */
+WS_DLL_PUBLIC bool running_in_build_directory(void);
+
+/*
+ * Get the directory in which global configuration files are
+ * stored.
+ */
+WS_DLL_PUBLIC const char *get_datafile_dir(void);
+
+/*
+ * Construct the path name of a global configuration file, given the
+ * file name.
+ *
+ * The returned file name was g_malloc()'d so it must be g_free()d when the
+ * caller is done with it.
+ */
+WS_DLL_PUBLIC char *get_datafile_path(const char *filename);
+
+/*
+ * Get the directory in which global documentation files are
+ * stored.
+ */
+WS_DLL_PUBLIC const char *get_doc_dir(void);
+
+/*
+ * Construct the path name of a global documentation file, given the
+ * file name.
+ *
+ * The returned file name was g_malloc()'d so it must be g_free()d when the
+ * caller is done with it.
+ */
+WS_DLL_PUBLIC char *get_docfile_path(const char *filename);
+
+/*
+ * Construct the path URL of a global documentation file, given the
+ * file name.
+ *
+ * The returned file name was g_malloc()'d so it must be g_free()d when the
+ * caller is done with it.
+ */
+WS_DLL_PUBLIC char *doc_file_url(const char *filename);
+
+/*
+ * Get the directory in which files that, at least on UNIX, are
+ * system files (such as "/etc/ethers") are stored; on Windows,
+ * there's no "/etc" directory, so we get them from the Wireshark
+ * global configuration and data file directory.
+ */
+WS_DLL_PUBLIC const char *get_systemfile_dir(void);
+
+/*
+ * Set the configuration profile name to be used for storing
+ * personal configuration files.
+ */
+WS_DLL_PUBLIC void set_profile_name(const char *profilename);
+
+/*
+ * Get the current configuration profile name used for storing
+ * personal configuration files.
+ */
+WS_DLL_PUBLIC const char *get_profile_name(void);
+
+/*
+ * Check if current profile is default profile.
+ */
+WS_DLL_PUBLIC bool is_default_profile(void);
+
+/*
+ * Check if we have global profiles.
+ */
+WS_DLL_PUBLIC bool has_global_profiles(void);
+
+/*
+ * Get the directory used to store configuration profile directories.
+ * Caller must free the returned string
+ */
+WS_DLL_PUBLIC char *get_profiles_dir(void);
+
+/*
+ * Get the directory used to store configuration files for a given profile.
+ * Caller must free the returned string.
+ */
+WS_DLL_PUBLIC char *get_profile_dir(const char *profilename, bool is_global);
+
+/*
+ * Create the directory used to store configuration profile directories.
+ */
+WS_DLL_PUBLIC int create_profiles_dir(char **pf_dir_path_return);
+
+/*
+ * Get the directory used to store global configuration profile directories.
+ * Caller must free the returned string
+ */
+WS_DLL_PUBLIC char *get_global_profiles_dir(void);
+
+
+/*
+ * Store filenames used for personal config files so we know which
+ * files to copy when duplicate a configuration profile.
+ */
+WS_DLL_PUBLIC void profile_store_persconffiles(bool store);
+
+/*
+ * Register a filename to the personal config files storage.
+ * This is for files which are not read using get_persconffile_path() during startup.
+ */
+WS_DLL_PUBLIC void profile_register_persconffile(const char *filename);
+
+/*
+ * Check if given configuration profile exists.
+ */
+WS_DLL_PUBLIC bool profile_exists(const char *profilename, bool global);
+
+/*
+ * Create a directory for the given configuration profile.
+ * If we attempted to create it, and failed, return -1 and
+ * set "*pf_dir_path_return" to the pathname of the directory we failed
+ * to create (it's g_mallocated, so our caller should free it); otherwise,
+ * return 0.
+ */
+WS_DLL_PUBLIC int create_persconffile_profile(const char *profilename,
+ char **pf_dir_path_return);
+
+/*
+ * Returns the list of known profile config filenames
+ */
+WS_DLL_PUBLIC const GHashTable * allowed_profile_filenames(void);
+
+/*
+ * Delete the directory for the given configuration profile.
+ * If we attempted to delete it, and failed, return -1 and
+ * set "*pf_dir_path_return" to the pathname of the directory we failed
+ * to delete (it's g_mallocated, so our caller should free it); otherwise,
+ * return 0.
+ */
+WS_DLL_PUBLIC int delete_persconffile_profile(const char *profilename,
+ char **pf_dir_path_return);
+
+/*
+ * Rename the directory for the given confinguration profile.
+ */
+WS_DLL_PUBLIC int rename_persconffile_profile(const char *fromname, const char *toname,
+ char **pf_from_dir_path_return,
+ char **pf_to_dir_path_return);
+
+/*
+ * Copy files in one profile to the other.
+ */
+WS_DLL_PUBLIC int copy_persconffile_profile(const char *toname, const char *fromname,
+ bool from_global,
+ char **pf_filename_return,
+ char **pf_to_dir_path_return,
+ char **pf_from_dir_path_return);
+
+/*
+ * Create the directory that holds personal configuration files, if
+ * necessary. If we attempted to create it, and failed, return -1 and
+ * set "*pf_dir_path_return" to the pathname of the directory we failed
+ * to create (it's g_mallocated, so our caller should free it); otherwise,
+ * return 0.
+ */
+WS_DLL_PUBLIC int create_persconffile_dir(char **pf_dir_path_return);
+
+/*
+ * Construct the path name of a personal configuration file, given the
+ * file name. If using configuration profiles this directory will be
+ * used if "from_profile" is true.
+ *
+ * The returned file name was g_malloc()'d so it must be g_free()d when the
+ * caller is done with it.
+ */
+WS_DLL_PUBLIC char *get_persconffile_path(const char *filename, bool from_profile);
+
+/*
+ * Set the path of the personal configuration file directory.
+ */
+WS_DLL_PUBLIC void set_persconffile_dir(const char *p);
+
+/*
+ * Get the (default) directory in which personal data is stored.
+ *
+ * On Win32, this is the "My Documents" folder in the personal profile.
+ * On UNIX this is simply the current directory.
+ */
+WS_DLL_PUBLIC const char *get_persdatafile_dir(void);
+
+/*
+ * Set the path of the directory in which personal data is stored.
+ */
+WS_DLL_PUBLIC void set_persdatafile_dir(const char *p);
+
+/*
+ * Return an error message for UNIX-style errno indications on open or
+ * create operations.
+ */
+WS_DLL_PUBLIC const char *file_open_error_message(int err, bool for_writing);
+
+/*
+ * Return an error message for UNIX-style errno indications on write
+ * operations.
+ */
+WS_DLL_PUBLIC const char *file_write_error_message(int err);
+
+/*
+ * Given a pathname, return the last component.
+ */
+WS_DLL_PUBLIC const char *get_basename(const char *);
+
+ /*
+ * Given a pathname, return a pointer to the last pathname separator
+ * character in the pathname, or NULL if the pathname contains no
+ * separators.
+ */
+WS_DLL_PUBLIC char *find_last_pathname_separator(const char *path);
+
+/*
+ * Given a pathname, return a string containing everything but the
+ * last component. NOTE: this overwrites the pathname handed into
+ * it....
+ */
+WS_DLL_PUBLIC char *get_dirname(char *);
+
+/*
+ * Given a pathname, return:
+ *
+ * the errno, if an attempt to "stat()" the file fails;
+ *
+ * EISDIR, if the attempt succeeded and the file turned out
+ * to be a directory;
+ *
+ * 0, if the attempt succeeded and the file turned out not
+ * to be a directory.
+ */
+WS_DLL_PUBLIC int test_for_directory(const char *);
+
+/*
+ * Given a pathname, return:
+ *
+ * the errno, if an attempt to "stat()" the file fails;
+ *
+ * ESPIPE, if the attempt succeeded and the file turned out
+ * to be a FIFO;
+ *
+ * 0, if the attempt succeeded and the file turned out not
+ * to be a FIFO.
+ */
+WS_DLL_PUBLIC int test_for_fifo(const char *);
+
+/*
+ * Check, if file is existing.
+ */
+WS_DLL_PUBLIC bool file_exists(const char *fname);
+
+/*
+ * Check if file is existing and has text entries which does not start
+ * with the comment character.
+ */
+WS_DLL_PUBLIC bool config_file_exists_with_entries(const char *fname, char comment_char);
+
+/*
+ * Check if two filenames are identical (with absolute and relative paths).
+ */
+WS_DLL_PUBLIC bool files_identical(const char *fname1, const char *fname2);
+
+/*
+ * Check if file has been recreated since it was opened.
+ */
+WS_DLL_PUBLIC bool file_needs_reopen(int fd, const char* filename);
+
+/*
+ * Write content to a file in binary mode, for those operating systems that
+ * care about such things. This should be OK for all files, even text files, as
+ * we'll write the raw bytes, and we don't look at the bytes as we copy them.
+ *
+ * Returns true on success, false on failure. If a failure, it also
+ * displays a simple dialog window with the error message.
+ */
+WS_DLL_PUBLIC bool write_file_binary_mode(const char *filename,
+ const void *content, size_t content_len);
+
+/*
+ * Copy a file in binary mode, for those operating systems that care about
+ * such things. This should be OK for all files, even text files, as
+ * we'll copy the raw bytes, and we don't look at the bytes as we copy
+ * them.
+ *
+ * Returns true on success, false on failure. If a failure, it also
+ * displays a simple dialog window with the error message.
+ */
+WS_DLL_PUBLIC bool copy_file_binary_mode(const char *from_filename,
+ const char *to_filename);
+
+
+/*
+ * Given a filename return a filesystem URL. Relative paths are prefixed with
+ * the datafile directory path.
+ *
+ * @param filename A file name or path. Relative paths will be prefixed with
+ * the data file directory path.
+ * @return A filesystem URL for the file or NULL on failure. A non-NULL return
+ * value must be freed with g_free().
+ */
+WS_DLL_PUBLIC char* data_file_url(const char *filename);
+
+/*
+ * Free the internal structtures
+ */
+WS_DLL_PUBLIC void free_progdirs(void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* FILESYSTEM_H */
diff --git a/wsutil/filter_files.c b/wsutil/filter_files.c
new file mode 100644
index 00000000..48464892
--- /dev/null
+++ b/wsutil/filter_files.c
@@ -0,0 +1,564 @@
+/* filter_files.c
+ * Code for reading and writing the filters file.
+ *
+ * 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>
+#define WS_LOG_DOMAIN LOG_DOMAIN_WSUTIL
+#include "filter_files.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <wsutil/file_util.h>
+#include <wsutil/filesystem.h>
+#include <wsutil/report_message.h>
+#include <wsutil/wslog.h>
+#include <wsutil/ws_assert.h>
+
+/*
+ * List of capture filters - saved.
+ */
+static GList *capture_filters = NULL;
+
+/*
+ * List of display filters - saved.
+ */
+static GList *display_filters = NULL;
+
+/*
+ * Read in a list of filters.
+ *
+ * On error, report the error via the UI.
+ */
+
+#define INIT_BUF_SIZE 128
+
+static GList *
+add_filter_entry(GList *fl, const char *filt_name, const char *filt_expr)
+{
+ filter_def *filt;
+
+ filt = g_new(filter_def, 1);
+ filt->name = g_strdup(filt_name);
+ filt->strval = g_strdup(filt_expr);
+ return g_list_prepend(fl, filt);
+}
+
+static void
+free_filter_entry(void * data)
+{
+ filter_def *filt = (filter_def*)data;
+ g_free(filt->name);
+ g_free(filt->strval);
+ g_free(filt);
+}
+
+void free_filter_lists(void)
+{
+ if (capture_filters) {
+ g_list_free_full(capture_filters, free_filter_entry);
+ capture_filters = NULL;
+ }
+ if (display_filters) {
+ g_list_free_full(display_filters, free_filter_entry);
+ display_filters = NULL;
+ }
+}
+
+static GList *
+remove_filter_entry(GList *fl, GList *fl_entry)
+{
+ filter_def *filt;
+
+ filt = (filter_def *) fl_entry->data;
+ g_free(filt->name);
+ g_free(filt->strval);
+ g_free(filt);
+ return g_list_remove_link(fl, fl_entry);
+}
+
+static int
+skip_whitespace(FILE *ff)
+{
+ int c;
+
+ while ((c = getc(ff)) != EOF && c != '\n' && g_ascii_isspace(c))
+ ;
+ return c;
+}
+
+static int
+getc_crlf(FILE *ff)
+{
+ int c;
+
+ c = getc(ff);
+ if (c == '\r') {
+ /* Treat CR-LF at the end of a line like LF, so that if we're reading
+ * a Windows-format file on UN*X, we handle it the same way we'd handle
+ * a UN*X-format file. */
+ c = getc(ff);
+ if (c != EOF && c != '\n') {
+ /* Put back the character after the CR, and process the CR normally. */
+ ungetc(c, ff);
+ c = '\r';
+ }
+ }
+ return c;
+}
+
+void
+read_filter_list(filter_list_type_t list_type)
+{
+ const char *ff_name, *ff_description;
+ char *ff_path;
+ FILE *ff;
+ GList **flpp;
+ int c;
+ char *filt_name, *filt_expr;
+ int filt_name_len, filt_expr_len;
+ int filt_name_index, filt_expr_index;
+ int line = 1;
+
+ switch (list_type) {
+
+ case CFILTER_LIST:
+ ff_name = CFILTER_FILE_NAME;
+ ff_description = "capture";
+ flpp = &capture_filters;
+ break;
+
+ case DFILTER_LIST:
+ ff_name = DFILTER_FILE_NAME;
+ ff_description = "display";
+ flpp = &display_filters;
+ break;
+
+ default:
+ ws_assert_not_reached();
+ return;
+ }
+
+ /* try to open personal "cfilters"/"dfilters" file */
+ ff_path = get_persconffile_path(ff_name, true);
+ if ((ff = ws_fopen(ff_path, "r")) == NULL) {
+ /*
+ * Did that fail because the file didn't exist?
+ */
+ if (errno != ENOENT) {
+ /*
+ * No. Just give up.
+ */
+ report_warning("Could not open your %s filter file\n\"%s\": %s.",
+ ff_description, ff_path, g_strerror(errno));
+ g_free(ff_path);
+ return;
+ }
+
+ /*
+ * Yes. See if there's an "old style" personal "filters" file; if so, read it.
+ * This means that a user will start out with their capture and
+ * display filter lists being identical; each list may contain
+ * filters that don't belong in that list. The user can edit
+ * the filter lists, and delete the ones that don't belong in
+ * a particular list.
+ */
+ g_free(ff_path);
+ ff_path = get_persconffile_path(FILTER_FILE_NAME, false);
+ if ((ff = ws_fopen(ff_path, "r")) == NULL) {
+ /*
+ * Did that fail because the file didn't exist?
+ */
+ if (errno != ENOENT) {
+ /*
+ * No. Just give up.
+ */
+ report_warning("Could not open your %s filter file\n\"%s\": %s.",
+ ff_description, ff_path, g_strerror(errno));
+ g_free(ff_path);
+ return;
+ }
+
+ /*
+ * Try to open the global "cfilters/dfilters" file.
+ */
+ g_free(ff_path);
+ ff_path = get_datafile_path(ff_name);
+ if ((ff = ws_fopen(ff_path, "r")) == NULL) {
+ /*
+ * Well, that didn't work, either. Just give up.
+ * Report an error if the file existed but we couldn't open it.
+ */
+ if (errno != ENOENT) {
+ report_warning("Could not open your %s filter file\n\"%s\": %s.",
+ ff_description, ff_path, g_strerror(errno));
+ }
+ g_free(ff_path);
+ return;
+ }
+ }
+ }
+
+ /* If we already have a list of filters, discard it. */
+ /* this should never happen - this function is called only once for each list! */
+ while(*flpp) {
+ *flpp = remove_filter_entry(*flpp, g_list_first(*flpp));
+ }
+
+ /* Allocate the filter name buffer. */
+ filt_name_len = INIT_BUF_SIZE;
+ filt_name = (char *)g_malloc(filt_name_len + 1);
+ filt_expr_len = INIT_BUF_SIZE;
+ filt_expr = (char *)g_malloc(filt_expr_len + 1);
+
+ for (line = 1; ; line++) {
+ /* Lines in a filter file are of the form
+
+ "name" expression
+
+ where "name" is a name, in quotes - backslashes in the name
+ escape the next character, so quotes and backslashes can appear
+ in the name - and "expression" is a filter expression, not in
+ quotes, running to the end of the line. */
+
+ /* Skip over leading white space, if any. */
+ c = skip_whitespace(ff);
+
+ if (c == EOF)
+ break; /* Nothing more to read */
+ if (c == '\n')
+ continue; /* Blank line. */
+
+ /* "c" is the first non-white-space character.
+ If it's not a quote, it's an error. */
+ if (c != '"') {
+ ws_warning("'%s' line %d doesn't have a quoted filter name.", ff_path,
+ line);
+ while (c != '\n')
+ c = getc(ff); /* skip to the end of the line */
+ continue;
+ }
+
+ /* Get the name of the filter. */
+ filt_name_index = 0;
+ for (;;) {
+ c = getc_crlf(ff);
+ if (c == EOF || c == '\n')
+ break; /* End of line - or end of file */
+ if (c == '"') {
+ /* Closing quote. */
+ if (filt_name_index >= filt_name_len) {
+ /* Filter name buffer isn't long enough; double its length. */
+ filt_name_len *= 2;
+ filt_name = (char *)g_realloc(filt_name, filt_name_len + 1);
+ }
+ filt_name[filt_name_index] = '\0';
+ break;
+ }
+ if (c == '\\') {
+ /* Next character is escaped */
+ c = getc_crlf(ff);
+ if (c == EOF || c == '\n')
+ break; /* End of line - or end of file */
+ }
+ /* Add this character to the filter name string. */
+ if (filt_name_index >= filt_name_len) {
+ /* Filter name buffer isn't long enough; double its length. */
+ filt_name_len *= 2;
+ filt_name = (char *)g_realloc(filt_name, filt_name_len + 1);
+ }
+ filt_name[filt_name_index] = c;
+ filt_name_index++;
+ }
+
+ if (c == EOF) {
+ if (!ferror(ff)) {
+ /* EOF, not error; no newline seen before EOF */
+ ws_warning("'%s' line %d doesn't have a newline.", ff_path,
+ line);
+ }
+ break; /* nothing more to read */
+ }
+
+ if (c != '"') {
+ /* No newline seen before end-of-line */
+ ws_warning("'%s' line %d doesn't have a closing quote.", ff_path,
+ line);
+ continue;
+ }
+
+ /* Skip over separating white space, if any. */
+ c = skip_whitespace(ff);
+
+ if (c == EOF) {
+ if (!ferror(ff)) {
+ /* EOF, not error; no newline seen before EOF */
+ ws_warning("'%s' line %d doesn't have a newline.", ff_path,
+ line);
+ }
+ break; /* nothing more to read */
+ }
+
+ if (c == '\n') {
+ /* No filter expression */
+ ws_warning("'%s' line %d doesn't have a filter expression.", ff_path,
+ line);
+ continue;
+ }
+
+ /* "c" is the first non-white-space character; it's the first
+ character of the filter expression. */
+ filt_expr_index = 0;
+ for (;;) {
+ /* Add this character to the filter expression string. */
+ if (filt_expr_index >= filt_expr_len) {
+ /* Filter expressioin buffer isn't long enough; double its length. */
+ filt_expr_len *= 2;
+ filt_expr = (char *)g_realloc(filt_expr, filt_expr_len + 1);
+ }
+ filt_expr[filt_expr_index] = c;
+ filt_expr_index++;
+
+ /* Get the next character. */
+ c = getc_crlf(ff);
+ if (c == EOF || c == '\n')
+ break;
+ }
+
+ if (c == EOF) {
+ if (!ferror(ff)) {
+ /* EOF, not error; no newline seen before EOF */
+ ws_warning("'%s' line %d doesn't have a newline.", ff_path,
+ line);
+ }
+ break; /* nothing more to read */
+ }
+
+ /* We saw the ending newline; terminate the filter expression string */
+ if (filt_expr_index >= filt_expr_len) {
+ /* Filter expressioin buffer isn't long enough; double its length. */
+ filt_expr_len *= 2;
+ filt_expr = (char *)g_realloc(filt_expr, filt_expr_len + 1);
+ }
+ filt_expr[filt_expr_index] = '\0';
+
+ /* Add the new filter to the list of filters */
+ *flpp = add_filter_entry(*flpp, filt_name, filt_expr);
+ }
+ if (ferror(ff)) {
+ report_warning("Error reading your %s filter file\n\"%s\": %s.",
+ ff_description, ff_path, g_strerror(errno));
+ }
+ g_free(ff_path);
+ fclose(ff);
+ g_free(filt_name);
+ g_free(filt_expr);
+}
+
+/*
+ * Get a pointer to a list of filters.
+ */
+static GList **
+get_filter_list(filter_list_type_t list_type)
+{
+ GList **flpp;
+
+ switch (list_type) {
+
+ case CFILTER_LIST:
+ flpp = &capture_filters;
+ break;
+
+ case DFILTER_LIST:
+ flpp = &display_filters;
+ break;
+
+ default:
+ ws_assert_not_reached();
+ flpp = NULL;
+ }
+ return flpp;
+}
+
+/*
+ * Get a pointer to the first entry in a filter list.
+ */
+GList *
+get_filter_list_first(filter_list_type_t list_type)
+{
+ GList **flpp;
+
+ flpp = get_filter_list(list_type);
+ return g_list_first(*flpp);
+}
+
+/*
+ * Add a new filter to the end of a list.
+ * Returns a pointer to the newly-added entry.
+ */
+GList *
+add_to_filter_list(filter_list_type_t list_type, const char *name,
+ const char *expression)
+{
+ GList **flpp;
+
+ flpp = get_filter_list(list_type);
+ *flpp = add_filter_entry(*flpp, name, expression);
+
+ return g_list_last(*flpp);
+}
+
+/*
+ * Remove a filter from a list.
+ */
+void
+remove_from_filter_list(filter_list_type_t list_type, GList *fl_entry)
+{
+ GList **flpp;
+
+ flpp = get_filter_list(list_type);
+ *flpp = remove_filter_entry(*flpp, fl_entry);
+}
+
+/*
+ * Write out a list of filters.
+ *
+ * On error, report the error via the UI.
+ */
+void
+save_filter_list(filter_list_type_t list_type)
+{
+ char *pf_dir_path;
+ const char *ff_name, *ff_description;
+ char *ff_path, *ff_path_new;
+ GList *fl;
+ GList *flpp;
+ filter_def *filt;
+ FILE *ff;
+ unsigned char *p, c;
+
+ switch (list_type) {
+
+ case CFILTER_LIST:
+ ff_name = CFILTER_FILE_NAME;
+ ff_description = "capture";
+ fl = capture_filters;
+ break;
+
+ case DFILTER_LIST:
+ ff_name = DFILTER_FILE_NAME;
+ ff_description = "display";
+ fl = display_filters;
+ break;
+
+ default:
+ ws_assert_not_reached();
+ return;
+ }
+
+ /* Create the directory that holds personal configuration files,
+ if necessary. */
+ if (create_persconffile_dir(&pf_dir_path) == -1) {
+ report_failure("Can't create directory\n\"%s\"\nfor filter files: %s.",
+ pf_dir_path, g_strerror(errno));
+ g_free(pf_dir_path);
+ return;
+ }
+
+ ff_path = get_persconffile_path(ff_name, true);
+
+ /* Write to "XXX.new", and rename if that succeeds.
+ That means we don't trash the file if we fail to write it out
+ completely. */
+ ff_path_new = ws_strdup_printf("%s.new", ff_path);
+
+ if ((ff = ws_fopen(ff_path_new, "w")) == NULL) {
+ /* We had an error saving the filter. */
+ report_failure("Error saving your %s filter file\nCouldn't open \"%s\": %s.",
+ ff_description, ff_path_new, g_strerror(errno));
+ g_free(ff_path_new);
+ g_free(ff_path);
+ return;
+ }
+ flpp = g_list_first(fl);
+ while (flpp) {
+ filt = (filter_def *) flpp->data;
+
+ /* Write out the filter name as a quoted string; escape any quotes
+ or backslashes. */
+ putc('"', ff);
+ for (p = (unsigned char *)filt->name; (c = *p) != '\0'; p++) {
+ if (c == '"' || c == '\\')
+ putc('\\', ff);
+ putc(c, ff);
+ }
+ putc('"', ff);
+
+ /* Separate the filter name and value with a space. */
+ putc(' ', ff);
+
+ /* Write out the filter expression and a newline. */
+ fprintf(ff, "%s\n", filt->strval);
+ if (ferror(ff)) {
+ report_failure("Error saving your %s filter file\nWrite to \"%s\" failed: %s.",
+ ff_description, ff_path_new, g_strerror(errno));
+ fclose(ff);
+ ws_unlink(ff_path_new);
+ g_free(ff_path_new);
+ g_free(ff_path);
+ return;
+ }
+ flpp = flpp->next;
+ }
+ if (fclose(ff) == EOF) {
+ report_failure("Error saving your %s filter file\nWrite to \"%s\" failed: %s.",
+ ff_description, ff_path_new, g_strerror(errno));
+ ws_unlink(ff_path_new);
+ g_free(ff_path_new);
+ g_free(ff_path);
+ return;
+ }
+
+#ifdef _WIN32
+ /* ANSI C doesn't say whether "rename()" removes the target if it
+ exists; the Win32 call to rename files doesn't do so, which I
+ infer is the reason why the MSVC++ "rename()" doesn't do so.
+ We must therefore remove the target file first, on Windows.
+
+ XXX - ws_rename() should be ws_stdio_rename() on Windows,
+ and ws_stdio_rename() uses MoveFileEx() with MOVEFILE_REPLACE_EXISTING,
+ so it should remove the target if it exists, so this stuff
+ shouldn't be necessary. Perhaps it dates back to when we were
+ calling rename(), with that being a wrapper around Microsoft's
+ _rename(), which didn't remove the target. */
+ if (ws_remove(ff_path) < 0 && errno != ENOENT) {
+ /* It failed for some reason other than "it's not there"; if
+ it's not there, we don't need to remove it, so we just
+ drive on. */
+ report_failure("Error saving your %s filter file\nCouldn't remove \"%s\": %s.",
+ ff_description, ff_path, g_strerror(errno));
+ ws_unlink(ff_path_new);
+ g_free(ff_path_new);
+ g_free(ff_path);
+ return;
+ }
+#endif
+
+ if (ws_rename(ff_path_new, ff_path) < 0) {
+ report_failure("Error saving your %s filter file\nCouldn't rename \"%s\" to \"%s\": %s.",
+ ff_description, ff_path_new, ff_path, g_strerror(errno));
+ ws_unlink(ff_path_new);
+ g_free(ff_path_new);
+ g_free(ff_path);
+ return;
+ }
+ g_free(ff_path_new);
+ g_free(ff_path);
+}
diff --git a/wsutil/filter_files.h b/wsutil/filter_files.h
new file mode 100644
index 00000000..f3e67872
--- /dev/null
+++ b/wsutil/filter_files.h
@@ -0,0 +1,98 @@
+/** @file
+ *
+ * Declarations of routines for reading and writing the filters file.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __FILTER_FILES_H__
+#define __FILTER_FILES_H__
+
+#include <wireshark.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * Old filter file name.
+ */
+#define FILTER_FILE_NAME "filters"
+
+/*
+ * Capture filter file name.
+ */
+#define CFILTER_FILE_NAME "cfilters"
+
+/*
+ * Display filter file name.
+ */
+#define DFILTER_FILE_NAME "dfilters"
+
+/*
+ * Filter lists.
+ */
+typedef enum {
+ CFILTER_LIST, /* capture filter list - saved */
+ DFILTER_LIST /* display filter list - saved */
+} filter_list_type_t;
+
+/*
+ * Item in a list of filters.
+ */
+typedef struct {
+ char *name; /* filter name */
+ char *strval; /* filter expression */
+} filter_def;
+
+/*
+ * Read in a list of filters.
+ *
+ * On error, report the error via the UI.
+ */
+WS_DLL_PUBLIC
+void read_filter_list(filter_list_type_t list_type);
+
+/*
+ * Get a pointer to the first entry in a filter list.
+ */
+WS_DLL_PUBLIC
+GList *get_filter_list_first(filter_list_type_t list);
+
+/*
+ * Add a new filter to the end of a list.
+ * Returns a pointer to the newly-added entry.
+ */
+WS_DLL_PUBLIC
+GList *add_to_filter_list(filter_list_type_t list, const char *name,
+ const char *expression);
+
+/*
+ * Remove a filter from a list.
+ */
+WS_DLL_PUBLIC
+void remove_from_filter_list(filter_list_type_t list, GList *fl_entry);
+
+/*
+ * Write out a list of filters.
+ *
+ * On error, report the error via the UI.
+ */
+WS_DLL_PUBLIC
+void save_filter_list(filter_list_type_t list_type);
+
+/*
+ * Free all filter lists
+ */
+WS_DLL_PUBLIC
+void free_filter_lists(void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __FILTER_FILES_H__ */
diff --git a/wsutil/g711.c b/wsutil/g711.c
new file mode 100644
index 00000000..e6c3dcae
--- /dev/null
+++ b/wsutil/g711.c
@@ -0,0 +1,300 @@
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use. Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#include "g711.h"
+
+/*
+ * g711.c
+ *
+ * u-law, A-law and linear PCM conversions.
+ */
+#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */
+#define QUANT_MASK (0xf) /* Quantization field mask. */
+#define NSEGS (8) /* Number of A-law segments. */
+#define SEG_SHIFT (4) /* Left shift for segment number. */
+#define SEG_MASK (0x70) /* Segment field mask. */
+
+static short seg_end[8] = {0xFF, 0x1FF, 0x3FF, 0x7FF,
+ 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF};
+
+/* copy from CCITT G.711 specifications */
+unsigned char _u2a[128] = { /* u- to A-law conversions */
+ 1, 1, 2, 2, 3, 3, 4, 4,
+ 5, 5, 6, 6, 7, 7, 8, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 27, 29, 31, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 42, 43, 44,
+ 46, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62,
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
+ 81, 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94, 95, 96,
+ 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120,
+ 121, 122, 123, 124, 125, 126, 127, 128};
+
+unsigned char _a2u[128] = { /* A- to u-law conversions */
+ 1, 3, 5, 7, 9, 11, 13, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 32, 33, 33, 34, 34, 35, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 48, 49, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72,
+ 73, 74, 75, 76, 77, 78, 79, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127};
+
+static int
+search(
+ int val,
+ short *table,
+ int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ if (val <= *table++)
+ return (i);
+ }
+ return (size);
+}
+
+/*
+ * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
+ *
+ * linear2alaw() accepts an 16-bit integer and encodes it as A-law data.
+ *
+ * Linear Input Code Compressed Code
+ * ------------------------ ---------------
+ * 0000000wxyza 000wxyz
+ * 0000001wxyza 001wxyz
+ * 000001wxyzab 010wxyz
+ * 00001wxyzabc 011wxyz
+ * 0001wxyzabcd 100wxyz
+ * 001wxyzabcde 101wxyz
+ * 01wxyzabcdef 110wxyz
+ * 1wxyzabcdefg 111wxyz
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+unsigned char
+linear2alaw(
+ int pcm_val) /* 2's complement (16-bit range) */
+{
+ int mask;
+ int seg;
+ unsigned char aval;
+ if (pcm_val >= 0) {
+ mask = 0xD5; /* sign (7th) bit = 1 */
+ } else {
+ mask = 0x55; /* sign bit = 0 */
+ pcm_val = -pcm_val - 8;
+ }
+
+ /* Convert the scaled magnitude to segment number. */
+ seg = search(pcm_val, seg_end, 8);
+
+ /* Combine the sign, segment, and quantization bits. */
+
+ if (seg >= 8) /* out of range, return maximum value. */
+ return (0x7F ^ mask);
+ else {
+ aval = seg << SEG_SHIFT;
+ if (seg < 2)
+ aval |= (pcm_val >> 4) & QUANT_MASK;
+ else
+ aval |= (pcm_val >> (seg + 3)) & QUANT_MASK;
+ return (aval ^ mask);
+ }
+}
+
+/*
+ * alaw2linear() - Convert an A-law value to 16-bit linear PCM
+ *
+ */
+int
+alaw2linear(
+ unsigned char a_val)
+{
+ int t;
+ int seg;
+ /*printf(" vrednost a_val %X ", a_val);*/
+ a_val ^= 0x55;
+
+ t = (a_val & QUANT_MASK) << 4;
+ seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
+ switch (seg) {
+ case 0:
+ t += 8;
+ break;
+ case 1:
+ t += 0x108;
+ break;
+ default:
+ t += 0x108;
+ t <<= seg - 1;
+ }
+ /*printf("izracunan int %d in njegov hex %X \n", t,t);*/
+ return ((a_val & SIGN_BIT) ? t : -t);
+}
+
+#define BIAS (0x84) /* Bias for linear code. */
+
+/*
+ * linear2ulaw() - Convert a linear PCM value to u-law
+ *
+ * In order to simplify the encoding process, the original linear magnitude
+ * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
+ * (33 - 8191). The result can be seen in the following encoding table:
+ *
+ * Biased Linear Input Code Compressed Code
+ * ------------------------ ---------------
+ * 00000001wxyza 000wxyz
+ * 0000001wxyzab 001wxyz
+ * 000001wxyzabc 010wxyz
+ * 00001wxyzabcd 011wxyz
+ * 0001wxyzabcde 100wxyz
+ * 001wxyzabcdef 101wxyz
+ * 01wxyzabcdefg 110wxyz
+ * 1wxyzabcdefgh 111wxyz
+ *
+ * Each biased linear code has a leading 1 which identifies the segment
+ * number. The value of the segment number is equal to 7 minus the number
+ * of leading 0's. The quantization interval is directly available as the
+ * four bits wxyz. * The trailing bits (a - h) are ignored.
+ *
+ * Ordinarily the complement of the resulting code word is used for
+ * transmission, and so the code word is complemented before it is returned.
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+unsigned char
+linear2ulaw(
+ int pcm_val) /* 2's complement (16-bit range) */
+{
+ int mask;
+ int seg;
+ unsigned char uval;
+
+ /* Get the sign and the magnitude of the value. */
+ if (pcm_val < 0) {
+ pcm_val = BIAS - pcm_val;
+ mask = 0x7F;
+ } else {
+ pcm_val += BIAS;
+ mask = 0xFF;
+ }
+
+ /* Convert the scaled magnitude to segment number. */
+ seg = search(pcm_val, seg_end, 8);
+
+ /*
+ * Combine the sign, segment, quantization bits;
+ * and complement the code word.
+ */
+ if (seg >= 8) /* out of range, return maximum value. */
+ return (0x7F ^ mask);
+ else {
+ uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF);
+ return (uval ^ mask);
+ }
+
+}
+
+/*
+ * ulaw2linear() - Convert a u-law value to 16-bit linear PCM
+ *
+ * First, a biased linear code is derived from the code word. An unbiased
+ * output can then be obtained by subtracting 33 from the biased code.
+ *
+ * Note that this function expects to be passed the complement of the
+ * original code word. This is in keeping with ISDN conventions.
+ */
+int
+ulaw2linear(
+ unsigned char u_val)
+{
+ int t;
+
+ /* Complement to obtain normal u-law value. */
+ u_val = ~u_val;
+
+ /*
+ * Extract and bias the quantization bits. Then
+ * shift up by the segment number and subtract out the bias.
+ */
+ t = ((u_val & QUANT_MASK) << 3) + BIAS;
+ t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
+
+ return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
+}
+
+/* A-law to u-law conversion */
+/* unsigned char
+ * alaw2ulaw(
+ * unsigned char aval)
+ * {
+ * aval &= 0xff;
+ * return ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) :
+ * (0x7F ^ _a2u[aval ^ 0x55]));
+ * }
+ */
+
+/* u-law to A-law conversion */
+/* unsigned char
+ * ulaw2alaw(
+ * unsigned char uval)
+ * {
+ * uval &= 0xff;
+ * return ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) :
+ * (0x55 ^ (_u2a[0x7F ^ uval] - 1)));
+ * }
+ */
+
+/*
+ * 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:
+ */
diff --git a/wsutil/g711.h b/wsutil/g711.h
new file mode 100644
index 00000000..d5196da8
--- /dev/null
+++ b/wsutil/g711.h
@@ -0,0 +1,30 @@
+/** @file
+ *
+ * Definitions for routines for u-law, A-law and linear PCM conversions
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __G711_H__
+#define __G711_H__
+
+#include "ws_symbol_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+WS_DLL_PUBLIC unsigned char linear2alaw( int );
+WS_DLL_PUBLIC int alaw2linear( unsigned char );
+WS_DLL_PUBLIC unsigned char linear2ulaw( int );
+WS_DLL_PUBLIC int ulaw2linear( unsigned char );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __G711_H__ */
diff --git a/wsutil/glib-compat.h b/wsutil/glib-compat.h
new file mode 100644
index 00000000..08c87009
--- /dev/null
+++ b/wsutil/glib-compat.h
@@ -0,0 +1,46 @@
+/** @file
+*
+* Definitions to provide some functions that are not present in older
+* GLIB versions we support (currently down to 2.50)
+*
+* Wireshark - Network traffic analyzer
+* By Gerald Combs <gerald@wireshark.org>
+* Copyright 1998 Gerald Combs
+*
+* SPDX-License-Identifier: GPL-2.0-or-later
+*/
+#ifndef GLIB_COMPAT_H
+#define GLIB_COMPAT_H
+
+#include "ws_symbol_export.h"
+#include "ws_attributes.h"
+
+#include <glib.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#if !GLIB_CHECK_VERSION(2, 68, 0)
+static inline void *
+g_memdup2(gconstpointer mem, size_t byte_size)
+{
+ void * new_mem;
+
+ if (mem && byte_size != 0) {
+ new_mem = g_malloc(byte_size);
+ memcpy(new_mem, mem, byte_size);
+ }
+ else
+ new_mem = NULL;
+
+ return new_mem;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* GLIB_COMPAT_H */
diff --git a/wsutil/inet_addr.c b/wsutil/inet_addr.c
new file mode 100644
index 00000000..e0f1df62
--- /dev/null
+++ b/wsutil/inet_addr.c
@@ -0,0 +1,102 @@
+/* inet_addr.c
+ *
+ * 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"
+#define WS_LOG_DOMAIN LOG_DOMAIN_WSUTIL
+#include "inet_addr.h"
+
+#include <errno.h>
+#include <string.h>
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#include <sys/types.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h> /* needed to define AF_ values on UNIX */
+#endif
+
+#ifdef _WIN32
+#include <ws2tcpip.h> /* indirectly defines AF_ values on Windows */
+#define _NTOP_SRC_CAST_ (PVOID)
+#else
+#define _NTOP_SRC_CAST_
+#endif
+
+#include "str_util.h"
+
+/*
+ * We assume and require an inet_pton/inet_ntop that supports AF_INET
+ * and AF_INET6.
+ */
+
+static inline bool
+inet_pton_internal(int af, const char *src, void *dst, size_t dst_size,
+ const char *af_str)
+{
+ int ret = inet_pton(af, src, dst);
+ if (ret < 0) {
+ int err = errno;
+ ws_log(WS_LOG_DOMAIN, LOG_LEVEL_CRITICAL, "inet_pton: %s (%d): %s", af_str, af, g_strerror(err));
+ memset(dst, 0, dst_size);
+ errno = err;
+ return false;
+ }
+ /* ret == 0 invalid src representation, ret == 1 success. */
+ return ret == 1;
+}
+
+static inline const char *
+inet_ntop_internal(int af, const void *src, char *dst, size_t dst_size,
+ const char *af_str)
+{
+ /* Add a cast to ignore 64-to-32 bit narrowing warnings with some
+ * compilers (POSIX uses socklen_t instead of size_t). */
+ const char *ret = inet_ntop(af, _NTOP_SRC_CAST_ src, dst, (unsigned int)dst_size);
+ if (ret == NULL) {
+ int err = errno;
+ char errbuf[16];
+ ws_log(WS_LOG_DOMAIN, LOG_LEVEL_CRITICAL, "inet_ntop: %s (%d): %s", af_str, af, g_strerror(err));
+ /* set result to something that can't be confused with a valid conversion */
+ (void)g_strlcpy(dst, ws_strerrorname_r(err, errbuf, sizeof(errbuf)), dst_size);
+ errno = err;
+ return dst;
+ }
+ return dst;
+}
+
+const char *
+ws_inet_ntop4(const void *src, char *dst, size_t dst_size)
+{
+ return inet_ntop_internal(AF_INET, src, dst, dst_size, "AF_INET");
+}
+
+bool
+ws_inet_pton4(const char *src, ws_in4_addr *dst)
+{
+ return inet_pton_internal(AF_INET, src, dst, sizeof(*dst), "AF_INET");
+}
+
+const char *
+ws_inet_ntop6(const void *src, char *dst, size_t dst_size)
+{
+ return inet_ntop_internal(AF_INET6, src, dst, dst_size, "AF_INET6");
+}
+
+bool
+ws_inet_pton6(const char *src, ws_in6_addr *dst)
+{
+ return inet_pton_internal(AF_INET6, src, dst, sizeof(*dst), "AF_INET6");
+}
diff --git a/wsutil/inet_addr.h b/wsutil/inet_addr.h
new file mode 100644
index 00000000..7147c2ae
--- /dev/null
+++ b/wsutil/inet_addr.h
@@ -0,0 +1,75 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WS_INET_ADDR_H__
+#define __WS_INET_ADDR_H__
+
+#include <wireshark.h>
+
+#include "inet_ipv4.h"
+#include "inet_ipv6.h"
+
+/*
+ * These are the values specified by RFC 2133 and its successors for
+ * INET_ADDRSTRLEN and INET6_ADDRSTRLEN.
+ *
+ * On UN*X systems, INET_ADDRSTRLEN and INET6_ADDRSTRLEN are defined
+ * to the values from RFC 2133 and its successors.
+ *
+ * However, on Windows:
+ *
+ * There are APIs RtlIpv4AddressToStringEx(), which converts an
+ * IPv4 address *and transport-layer port* to the address in the
+ * standard text form, followed by a colon and the port number,
+ * and RtlIpv6AddressToStringEx(), which converts an IPv6 address
+ * *and scope ID and transport-layer port* to the address in the
+ * standard text form, followed by a percent sign and the scope
+ * ID (with the address and scope ID in square brackets), followed
+ * by a colon and the port number.
+ *
+ * Instead of defining INET_ADDRSTRLEN_EX as 22 and INET6_ADDRSTRLEN_EX
+ * as 65, and saying *those* were the buffer sizes to use for
+ * RtlIpv4AddressToStringEx() and RtlIpv6AddressToStringEx(), they
+ * defined INET_ADDRSTRLEN to be 22 and INET6_ADDRSTRLEN to be 65 - and
+ * recommend using those as the size for the buffers passed to
+ * RtlIpv4AddressToStringEx() and RtlIpv6AddressToStringEx().
+ *
+ * At least they document inet_ntop() as requiring a 16-byte or larger
+ * buffer for IPv4 addresses and a 46-byte or larger buffer for
+ * IPv6 addresses. For this reason, use hard-coded numeric constants rather than
+ * INET_ADDRSTRLEN and INET6_ADDRSTRLEN.
+ */
+#define WS_INET_ADDRSTRLEN 16
+#define WS_INET6_ADDRSTRLEN 46
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * To check for errors set errno to zero before calling ws_inet_ntop{4,6}.
+ * ENOSPC is set if the result exceeds the given buffer size.
+ */
+WS_DLL_PUBLIC WS_RETNONNULL const char *
+ws_inet_ntop4(const void *src, char *dst, size_t dst_size);
+
+WS_DLL_PUBLIC WS_RETNONNULL const char *
+ws_inet_ntop6(const void *src, char *dst, size_t dst_size);
+
+WS_DLL_PUBLIC bool
+ws_inet_pton4(const char *src, ws_in4_addr *dst);
+
+WS_DLL_PUBLIC bool
+ws_inet_pton6(const char *src, ws_in6_addr *dst);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/wsutil/inet_ipv4.h b/wsutil/inet_ipv4.h
new file mode 100644
index 00000000..a5a8fddd
--- /dev/null
+++ b/wsutil/inet_ipv4.h
@@ -0,0 +1,57 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __INET_IPV4_H__
+#define __INET_IPV4_H__
+
+#include <inttypes.h>
+#include <glib.h>
+
+typedef uint32_t ws_in4_addr; /* 32 bit IPv4 address, in network byte order */
+
+/*
+ * We define these in *network byte order*, unlike the C library. Therefore
+ * it uses a different prefix than INADDR_* to make the distinction more obvious.
+ */
+#define WS_IN4_LOOPBACK ((ws_in4_addr)GUINT32_TO_BE(0x7f000001))
+
+/**
+ * Unicast Local
+ * Returns true if the address is in the 224.0.0.0/24 local network
+ * control block
+ */
+#define in4_addr_is_local_network_control_block(addr) \
+ ((addr & 0xffffff00) == 0xe0000000)
+
+/**
+ * Multicast
+ * Returns true if the address is in the 224.0.0.0/4 network block
+ */
+#define in4_addr_is_multicast(addr) \
+ ((addr & 0xf0000000) == 0xe0000000)
+
+/**
+ * Private address
+ * Returns true if the address is in one of the three blocks reserved
+ * for private IPv4 addresses by section 3 of RFC 1918, namely:
+ * 10/8, 172.16/12, and 192.168/16
+ */
+#define in4_addr_is_private(addr) \
+ (((addr & 0xff000000) == 0x0a000000) || \
+ ((addr & 0xfff00000) == 0xac100000) || \
+ ((addr & 0xffff0000) == 0xc0a80000))
+
+/**
+ * Link-local address
+ * Returns true if the address is in the 169.254/16 network block
+ */
+#define in4_addr_is_link_local(addr) \
+ ((addr & 0xffff0000) == 0xa9fe0000)
+
+#endif
diff --git a/wsutil/inet_ipv6.h b/wsutil/inet_ipv6.h
new file mode 100644
index 00000000..0cd70394
--- /dev/null
+++ b/wsutil/inet_ipv6.h
@@ -0,0 +1,103 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __INET_IPV6_H__
+#define __INET_IPV6_H__
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#define IPv6_ADDR_SIZE 16
+
+#define IPv6_HDR_SIZE 40
+#define IPv6_FRAGMENT_HDR_SIZE 8
+
+typedef struct e_in6_addr {
+ uint8_t bytes[16]; /* 128 bit IPv6 address */
+} ws_in6_addr;
+
+/*
+ * Definition for internet protocol version 6.
+ * RFC 2460
+ */
+struct ws_ip6_hdr {
+ uint32_t ip6h_vc_flow; /* version, class, flow */
+ uint16_t ip6h_plen; /* payload length */
+ uint8_t ip6h_nxt; /* next header */
+ uint8_t ip6h_hlim; /* hop limit */
+ ws_in6_addr ip6h_src; /* source address */
+ ws_in6_addr ip6h_dst; /* destination address */
+};
+
+/*
+ * Extension Headers
+ */
+
+struct ip6_ext {
+ unsigned char ip6e_nxt;
+ unsigned char ip6e_len;
+};
+
+/* Routing header */
+struct ip6_rthdr {
+ uint8_t ip6r_nxt; /* next header */
+ uint8_t ip6r_len; /* length in units of 8 octets */
+ uint8_t ip6r_type; /* routing type */
+ uint8_t ip6r_segleft; /* segments left */
+ /* followed by routing type specific data */
+};
+
+/* Type 0 Routing header */
+struct ip6_rthdr0 {
+ uint8_t ip6r0_nxt; /* next header */
+ uint8_t ip6r0_len; /* length in units of 8 octets */
+ uint8_t ip6r0_type; /* always zero */
+ uint8_t ip6r0_segleft; /* segments left */
+ uint8_t ip6r0_reserved; /* reserved field */
+ uint8_t ip6r0_slmap[3]; /* strict/loose bit map */
+ /* followed by up to 127 addresses */
+ ws_in6_addr ip6r0_addr[1];
+};
+
+/* Fragment header */
+struct ip6_frag {
+ uint8_t ip6f_nxt; /* next header */
+ uint8_t ip6f_reserved; /* reserved field */
+ uint16_t ip6f_offlg; /* offset, reserved, and flag */
+ uint32_t ip6f_ident; /* identification */
+};
+
+#define IP6F_OFF_MASK 0xfff8 /* mask out offset from _offlg */
+#define IP6F_RESERVED_MASK 0x0006 /* reserved bits in ip6f_offlg */
+#define IP6F_MORE_FRAG 0x0001 /* more-fragments flag */
+
+
+/**
+ * Unicast Scope
+ * Note that we must check topmost 10 bits only, not 16 bits (see RFC2373).
+ */
+static inline bool in6_addr_is_linklocal(const ws_in6_addr *a)
+{
+ return (a->bytes[0] == 0xfe) && ((a->bytes[1] & 0xc0) == 0x80);
+}
+
+static inline bool in6_addr_is_sitelocal(const ws_in6_addr *a)
+{
+ return (a->bytes[0] == 0xfe) && ((a->bytes[1] & 0xc0) == 0xc0);
+}
+
+/**
+ * Multicast
+ */
+static inline bool in6_addr_is_multicast(const ws_in6_addr *a)
+{
+ return a->bytes[0] == 0xff;
+}
+
+#endif
diff --git a/wsutil/interface.c b/wsutil/interface.c
new file mode 100644
index 00000000..0fc990d4
--- /dev/null
+++ b/wsutil/interface.c
@@ -0,0 +1,171 @@
+/* interface.c
+ * Utility functions to get infos from interfaces
+ *
+ * Copyright 2016, Dario Lombardo
+ *
+ * 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 "interface.h"
+
+#include <string.h>
+#include <wsutil/inet_addr.h>
+
+#include <sys/types.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+ #include <sys/socket.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+ #include <netinet/in.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+ #include <arpa/inet.h>
+#endif
+
+#ifdef HAVE_IFADDRS_H
+ #include <ifaddrs.h>
+#endif
+
+#ifdef _WIN32
+ #include <winsock2.h>
+ #include <iphlpapi.h>
+ #include <ws2tcpip.h>
+#endif
+
+#define WORKING_BUFFER_SIZE 15000
+
+#ifdef HAVE_GETIFADDRS
+static GSList* local_interfaces_to_list_nix(void)
+{
+ GSList *interfaces = NULL;
+ struct ifaddrs *ifap;
+ struct ifaddrs *ifa;
+ int family;
+ char ip[WS_INET6_ADDRSTRLEN];
+
+ if (getifaddrs(&ifap)) {
+ goto end;
+ }
+
+ for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr == NULL)
+ continue;
+
+ family = ifa->ifa_addr->sa_family;
+
+ memset(ip, 0x0, WS_INET6_ADDRSTRLEN);
+
+ switch (family) {
+ case AF_INET:
+ {
+ struct sockaddr_in *addr4 = (struct sockaddr_in *)ifa->ifa_addr;
+ ws_inet_ntop4(&addr4->sin_addr, ip, sizeof(ip));
+ break;
+ }
+
+ case AF_INET6:
+ {
+ struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)ifa->ifa_addr;
+ ws_inet_ntop6(&addr6->sin6_addr, ip, sizeof(ip));
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ /* skip loopback addresses */
+ if (!g_strcmp0(ip, "127.0.0.1") || !g_strcmp0(ip, "::1"))
+ continue;
+
+ if (*ip) {
+ interfaces = g_slist_prepend(interfaces, g_strdup(ip));
+ }
+ }
+ freeifaddrs(ifap);
+end:
+ return interfaces;
+}
+#endif /* HAVE_GETIFADDRS */
+
+#ifdef _WIN32
+static GSList* local_interfaces_to_list_win(void)
+{
+ GSList *interfaces = NULL;
+ PIP_ADAPTER_ADDRESSES pAddresses = NULL;
+ ULONG outBufLen = WORKING_BUFFER_SIZE;
+ ULONG family = AF_UNSPEC;
+ PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
+ PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;
+ char ip[100];
+ unsigned iplen = 100;
+
+ pAddresses = (IP_ADAPTER_ADDRESSES *)g_malloc0(outBufLen);
+ if (pAddresses == NULL)
+ return NULL;
+
+ if (GetAdaptersAddresses(family, 0, NULL, pAddresses, &outBufLen) != NO_ERROR)
+ goto end;
+
+ pCurrAddresses = pAddresses;
+ while (pCurrAddresses) {
+
+ for (pUnicast = pCurrAddresses->FirstUnicastAddress; pUnicast != NULL; pUnicast = pUnicast->Next) {
+ if (pUnicast->Address.lpSockaddr->sa_family == AF_INET) {
+ SOCKADDR_IN* sa_in = (SOCKADDR_IN *)pUnicast->Address.lpSockaddr;
+ ws_inet_ntop4(&(sa_in->sin_addr), ip, iplen);
+ if (!g_strcmp0(ip, "127.0.0.1"))
+ continue;
+ if (*ip)
+ interfaces = g_slist_prepend(interfaces, g_strdup(ip));
+ }
+ if (pUnicast->Address.lpSockaddr->sa_family == AF_INET6) {
+ SOCKADDR_IN6* sa_in6 = (SOCKADDR_IN6 *)pUnicast->Address.lpSockaddr;
+ ws_inet_ntop6(&(sa_in6->sin6_addr), ip, iplen);
+ if (!g_strcmp0(ip, "::1"))
+ continue;
+ if (*ip)
+ interfaces = g_slist_prepend(interfaces, g_strdup(ip));
+ }
+ }
+ pCurrAddresses = pCurrAddresses->Next;
+ }
+end:
+ g_free(pAddresses);
+
+ return interfaces;
+}
+#endif /* _WIN32 */
+
+GSList* local_interfaces_to_list(void)
+{
+#if defined(_WIN32)
+ return local_interfaces_to_list_win();
+#elif defined(HAVE_GETIFADDRS)
+ return local_interfaces_to_list_nix();
+#else
+ return NULL;
+#endif
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 noexpandtab:
+ * :indentSize=4:tabSize=8:noTabs=false:
+ */
diff --git a/wsutil/interface.h b/wsutil/interface.h
new file mode 100644
index 00000000..5c431bfa
--- /dev/null
+++ b/wsutil/interface.h
@@ -0,0 +1,36 @@
+/** @file
+ * Utility functions to get infos from interfaces
+ *
+ * Copyright 2016, Dario Lombardo
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef _INTERFACE_H
+#define _INTERFACE_H
+
+#include <glib.h>
+#include "ws_symbol_export.h"
+
+/* Return a list of IPv4/IPv6 addresses for local interfaces */
+WS_DLL_PUBLIC
+GSList* local_interfaces_to_list(void);
+
+#endif
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 noexpandtab:
+ * :indentSize=4:tabSize=8:noTabs=false:
+ */
diff --git a/wsutil/introspection.c b/wsutil/introspection.c
new file mode 100644
index 00000000..c571c8d5
--- /dev/null
+++ b/wsutil/introspection.c
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2021, João Valverde <j@v6e.pt>
+ *
+ * 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 "introspection.h"
+#include <string.h>
+#include <stdlib.h>
+
+
+static int
+compare_enum(const void *needle, const void *memb)
+{
+ return strcmp(needle, ((const ws_enum_t *)memb)->symbol);
+}
+
+const ws_enum_t *
+ws_enums_bsearch(const ws_enum_t *enums, size_t count,
+ const char *needle)
+{
+ return bsearch(needle, enums, count, sizeof(ws_enum_t), compare_enum);
+}
diff --git a/wsutil/introspection.h b/wsutil/introspection.h
new file mode 100644
index 00000000..f3272df8
--- /dev/null
+++ b/wsutil/introspection.h
@@ -0,0 +1,29 @@
+/** @file
+ * Copyright 2021, João Valverde <j@v6e.pt>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef _INTROSPECTION_H_
+#define _INTROSPECTION_H_
+
+#include <stddef.h>
+#include <ws_symbol_export.h>
+
+typedef struct {
+ const char *symbol;
+ int value;
+} ws_enum_t;
+
+
+/** Performs a binary search for the magic constant "needle". */
+WS_DLL_PUBLIC
+const ws_enum_t *
+ws_enums_bsearch(const ws_enum_t *enums, size_t count,
+ const char *needle);
+
+#endif
diff --git a/wsutil/jsmn.c b/wsutil/jsmn.c
new file mode 100644
index 00000000..d5e3bb5f
--- /dev/null
+++ b/wsutil/jsmn.c
@@ -0,0 +1,332 @@
+/*
+Copyright (c) 2010 Serge A. Zaitsev
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#include "jsmn.h"
+
+/**
+ * Allocates a fresh unused token from the token pull.
+ */
+static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
+ jsmntok_t *tokens, size_t num_tokens) {
+ jsmntok_t *tok;
+ if (parser->toknext >= num_tokens) {
+ return NULL;
+ }
+ tok = &tokens[parser->toknext++];
+ tok->start = tok->end = -1;
+ tok->size = 0;
+#ifdef JSMN_PARENT_LINKS
+ tok->parent = -1;
+#endif
+ return tok;
+}
+
+/**
+ * Fills token type and boundaries.
+ */
+static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
+ int start, int end) {
+ token->type = type;
+ token->start = start;
+ token->end = end;
+ token->size = 0;
+}
+
+/**
+ * Fills next available token with JSON primitive.
+ */
+static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
+ size_t len, jsmntok_t *tokens, size_t num_tokens) {
+ jsmntok_t *token;
+ int start;
+
+ start = parser->pos;
+
+ for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
+ switch (js[parser->pos]) {
+#ifndef JSMN_STRICT
+ /* In strict mode primitive must be followed by "," or "}" or "]" */
+ case ':':
+#endif
+ case '\t' : case '\r' : case '\n' : case ' ' :
+ case ',' : case ']' : case '}' :
+ goto found;
+ }
+ if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
+ parser->pos = start;
+ return JSMN_ERROR_INVAL;
+ }
+ }
+#ifdef JSMN_STRICT
+ /* In strict mode primitive must be followed by a comma/object/array */
+ parser->pos = start;
+ return JSMN_ERROR_PART;
+#endif
+
+found:
+ if (tokens == NULL) {
+ parser->pos--;
+ return 0;
+ }
+ token = jsmn_alloc_token(parser, tokens, num_tokens);
+ if (token == NULL) {
+ parser->pos = start;
+ return JSMN_ERROR_NOMEM;
+ }
+ jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
+#ifdef JSMN_PARENT_LINKS
+ token->parent = parser->toksuper;
+#endif
+ parser->pos--;
+ return 0;
+}
+
+/**
+ * Fills next token with JSON string.
+ */
+static int jsmn_parse_string(jsmn_parser *parser, const char *js,
+ size_t len, jsmntok_t *tokens, size_t num_tokens) {
+ jsmntok_t *token;
+
+ int start = parser->pos;
+
+ parser->pos++;
+
+ /* Skip starting quote */
+ for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
+ char c = js[parser->pos];
+
+ /* Quote: end of string */
+ if (c == '\"') {
+ if (tokens == NULL) {
+ return 0;
+ }
+ token = jsmn_alloc_token(parser, tokens, num_tokens);
+ if (token == NULL) {
+ parser->pos = start;
+ return JSMN_ERROR_NOMEM;
+ }
+ jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
+#ifdef JSMN_PARENT_LINKS
+ token->parent = parser->toksuper;
+#endif
+ return 0;
+ }
+
+ /* Backslash: Quoted symbol expected */
+ if (c == '\\' && parser->pos + 1 < len) {
+ int i;
+ parser->pos++;
+ switch (js[parser->pos]) {
+ /* Allowed escaped symbols */
+ case '\"': case '/' : case '\\' : case 'b' :
+ case 'f' : case 'r' : case 'n' : case 't' :
+ break;
+ /* Allows escaped symbol \uXXXX */
+ case 'u':
+ parser->pos++;
+ for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
+ /* If it isn't a hex character we have an error */
+ if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
+ (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
+ (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
+ parser->pos = start;
+ return JSMN_ERROR_INVAL;
+ }
+ parser->pos++;
+ }
+ parser->pos--;
+ break;
+ /* Unexpected symbol */
+ default:
+ parser->pos = start;
+ return JSMN_ERROR_INVAL;
+ }
+ }
+ }
+ parser->pos = start;
+ return JSMN_ERROR_PART;
+}
+
+/**
+ * Parse JSON string and fill tokens.
+ */
+int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
+ jsmntok_t *tokens, unsigned int num_tokens) {
+ int r;
+ int i;
+ jsmntok_t *token;
+ int count = parser->toknext;
+
+ for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
+ char c;
+ jsmntype_t type;
+
+ c = js[parser->pos];
+ switch (c) {
+ case '{': case '[':
+ count++;
+ if (tokens == NULL) {
+ break;
+ }
+ token = jsmn_alloc_token(parser, tokens, num_tokens);
+ if (token == NULL)
+ return JSMN_ERROR_NOMEM;
+ if (parser->toksuper != -1) {
+ tokens[parser->toksuper].size++;
+#ifdef JSMN_PARENT_LINKS
+ token->parent = parser->toksuper;
+#endif
+ }
+ token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
+ token->start = parser->pos;
+ parser->toksuper = parser->toknext - 1;
+ break;
+ case '}': case ']':
+ if (tokens == NULL)
+ break;
+ type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
+#ifdef JSMN_PARENT_LINKS
+ if (parser->toknext < 1) {
+ return JSMN_ERROR_INVAL;
+ }
+ token = &tokens[parser->toknext - 1];
+ for (;;) {
+ if (token->start != -1 && token->end == -1) {
+ if (token->type != type) {
+ return JSMN_ERROR_INVAL;
+ }
+ token->end = parser->pos + 1;
+ parser->toksuper = token->parent;
+ break;
+ }
+ if (token->parent == -1) {
+ break;
+ }
+ token = &tokens[token->parent];
+ }
+#else
+ for (i = parser->toknext - 1; i >= 0; i--) {
+ token = &tokens[i];
+ if (token->start != -1 && token->end == -1) {
+ if (token->type != type) {
+ return JSMN_ERROR_INVAL;
+ }
+ parser->toksuper = -1;
+ token->end = parser->pos + 1;
+ break;
+ }
+ }
+ /* Error if unmatched closing bracket */
+ if (i == -1) return JSMN_ERROR_INVAL;
+ for (; i >= 0; i--) {
+ token = &tokens[i];
+ if (token->start != -1 && token->end == -1) {
+ parser->toksuper = i;
+ break;
+ }
+ }
+#endif
+ break;
+ case '\"':
+ r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
+ if (r < 0) return r;
+ count++;
+ if (parser->toksuper != -1 && tokens != NULL)
+ tokens[parser->toksuper].size++;
+ break;
+ case '\t' : case '\r' : case '\n' : case ' ':
+ break;
+ case ':':
+ parser->toksuper = parser->toknext - 1;
+ break;
+ case ',':
+ if (tokens != NULL && parser->toksuper != -1 &&
+ tokens[parser->toksuper].type != JSMN_ARRAY &&
+ tokens[parser->toksuper].type != JSMN_OBJECT) {
+#ifdef JSMN_PARENT_LINKS
+ parser->toksuper = tokens[parser->toksuper].parent;
+#else
+ for (i = parser->toknext - 1; i >= 0; i--) {
+ if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
+ if (tokens[i].start != -1 && tokens[i].end == -1) {
+ parser->toksuper = i;
+ break;
+ }
+ }
+ }
+#endif
+ }
+ break;
+#ifdef JSMN_STRICT
+ /* In strict mode primitives are: numbers and booleans */
+ case '-': case '0': case '1' : case '2': case '3' : case '4':
+ case '5': case '6': case '7' : case '8': case '9':
+ case 't': case 'f': case 'n' :
+ /* And they must not be keys of the object */
+ if (tokens != NULL && parser->toksuper != -1) {
+ jsmntok_t *t = &tokens[parser->toksuper];
+ if (t->type == JSMN_OBJECT ||
+ (t->type == JSMN_STRING && t->size != 0)) {
+ return JSMN_ERROR_INVAL;
+ }
+ }
+#else
+ /* In non-strict mode every unquoted value is a primitive */
+ default:
+#endif
+ r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
+ if (r < 0) return r;
+ count++;
+ if (parser->toksuper != -1 && tokens != NULL)
+ tokens[parser->toksuper].size++;
+ break;
+
+#ifdef JSMN_STRICT
+ /* Unexpected char in strict mode */
+ default:
+ return JSMN_ERROR_INVAL;
+#endif
+ }
+ }
+
+ if (tokens != NULL) {
+ for (i = parser->toknext - 1; i >= 0; i--) {
+ /* Unmatched opened object or array */
+ if (tokens[i].start != -1 && tokens[i].end == -1) {
+ return JSMN_ERROR_PART;
+ }
+ }
+ }
+
+ return count;
+}
+
+/**
+ * Creates a new parser based over a given buffer with an array of tokens
+ * available.
+ */
+void jsmn_init(jsmn_parser *parser) {
+ parser->pos = 0;
+ parser->toknext = 0;
+ parser->toksuper = -1;
+}
diff --git a/wsutil/jsmn.h b/wsutil/jsmn.h
new file mode 100644
index 00000000..228c4160
--- /dev/null
+++ b/wsutil/jsmn.h
@@ -0,0 +1,99 @@
+/** @file
+
+Copyright (c) 2010 Serge A. Zaitsev
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#ifndef __JSMN_H_
+#define __JSMN_H_
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * JSON type identifier. Basic types are:
+ * o Object
+ * o Array
+ * o String
+ * o Other primitive: number, boolean (true/false) or null
+ */
+typedef enum {
+ JSMN_UNDEFINED = 0,
+ JSMN_OBJECT = 1,
+ JSMN_ARRAY = 2,
+ JSMN_STRING = 3,
+ JSMN_PRIMITIVE = 4
+} jsmntype_t;
+
+enum jsmnerr {
+ /* Not enough tokens were provided */
+ JSMN_ERROR_NOMEM = -1,
+ /* Invalid character inside JSON string */
+ JSMN_ERROR_INVAL = -2,
+ /* The string is not a full JSON packet, more bytes expected */
+ JSMN_ERROR_PART = -3
+};
+
+/**
+ * JSON token description.
+ * type type (object, array, string etc.)
+ * start start position in JSON data string
+ * end end position in JSON data string
+ */
+typedef struct {
+ jsmntype_t type;
+ int start;
+ int end;
+ int size;
+#ifdef JSMN_PARENT_LINKS
+ int parent;
+#endif
+} jsmntok_t;
+
+/**
+ * JSON parser. Contains an array of token blocks available. Also stores
+ * the string being parsed now and current position in that string
+ */
+typedef struct {
+ unsigned int pos; /* offset in the JSON string */
+ unsigned int toknext; /* next token to allocate */
+ int toksuper; /* superior token node, e.g parent object or array */
+} jsmn_parser;
+
+/**
+ * Create JSON parser over an array of tokens
+ */
+void jsmn_init(jsmn_parser *parser);
+
+/**
+ * Run JSON parser. It parses a JSON data string into and array of tokens, each describing
+ * a single JSON object.
+ */
+int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
+ jsmntok_t *tokens, unsigned int num_tokens);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __JSMN_H_ */
diff --git a/wsutil/json_dumper.c b/wsutil/json_dumper.c
new file mode 100644
index 00000000..5cbc9494
--- /dev/null
+++ b/wsutil/json_dumper.c
@@ -0,0 +1,689 @@
+/* json_dumper.c
+ * Routines for serializing data as JSON.
+ *
+ * Copyright 2018, Peter Wu <peter@lekensteyn.nl>
+ * Copyright (C) 2016 Jakub Zawadzki
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <glib.h>
+
+#include "json_dumper.h"
+#define WS_LOG_DOMAIN LOG_DOMAIN_WSUTIL
+
+#include <math.h>
+
+#include <wsutil/wslog.h>
+
+/*
+ * json_dumper.state[current_depth] describes a nested element:
+ * - type: none/object/array/non-base64 value/base64 value
+ * - has_name: Whether the object member name was set.
+ *
+ * (A base64 value isn't really a nested element, but that's a
+ * convenient way of handling them, with a begin call that opens
+ * the string with a double-quote, one or more calls to convert
+ * raw bytes to base64 and add them to the value, and an end call
+ * that finishes the base64 encoding, adds any remaining raw bytes
+ * in base64 encoding, and closes the string with a double-quote.)
+ */
+enum json_dumper_element_type {
+ JSON_DUMPER_TYPE_NONE = 0,
+ JSON_DUMPER_TYPE_VALUE = 1,
+ JSON_DUMPER_TYPE_OBJECT = 2,
+ JSON_DUMPER_TYPE_ARRAY = 3,
+ JSON_DUMPER_TYPE_BASE64 = 4,
+};
+#define JSON_DUMPER_TYPE(state) ((enum json_dumper_element_type)((state) & 7))
+#define JSON_DUMPER_HAS_NAME (1 << 3)
+
+static const char *json_dumper_element_type_names[] = {
+ [JSON_DUMPER_TYPE_NONE] = "none",
+ [JSON_DUMPER_TYPE_VALUE] = "value",
+ [JSON_DUMPER_TYPE_OBJECT] = "object",
+ [JSON_DUMPER_TYPE_ARRAY] = "array",
+ [JSON_DUMPER_TYPE_BASE64] = "base64"
+};
+#define NUM_JSON_DUMPER_ELEMENT_TYPE_NAMES (sizeof json_dumper_element_type_names / sizeof json_dumper_element_type_names[0])
+
+#define JSON_DUMPER_FLAGS_ERROR (1 << 16) /* Output flag: an error occurred. */
+
+enum json_dumper_change {
+ JSON_DUMPER_BEGIN,
+ JSON_DUMPER_END,
+ JSON_DUMPER_SET_NAME,
+ JSON_DUMPER_SET_VALUE,
+ JSON_DUMPER_WRITE_BASE64,
+ JSON_DUMPER_FINISH,
+};
+
+/* JSON Dumper putc */
+static void
+jd_putc(const json_dumper *dumper, char c)
+{
+ if (dumper->output_file) {
+ fputc(c, dumper->output_file);
+ }
+
+ if (dumper->output_string) {
+ g_string_append_c(dumper->output_string, c);
+ }
+}
+
+/* JSON Dumper puts */
+static void
+jd_puts(const json_dumper *dumper, const char *s)
+{
+ if (dumper->output_file) {
+ fputs(s, dumper->output_file);
+ }
+
+ if (dumper->output_string) {
+ g_string_append(dumper->output_string, s);
+ }
+}
+
+static void
+jd_puts_len(const json_dumper *dumper, const char *s, size_t len)
+{
+ if (dumper->output_file) {
+ fwrite(s, 1, len, dumper->output_file);
+ }
+
+ if (dumper->output_string) {
+ g_string_append_len(dumper->output_string, s, len);
+ }
+}
+
+static void
+jd_vprintf(const json_dumper *dumper, const char *format, va_list args)
+{
+ if (dumper->output_file) {
+ vfprintf(dumper->output_file, format, args);
+ }
+
+ if (dumper->output_string) {
+ g_string_append_vprintf(dumper->output_string, format, args);
+ }
+}
+
+static void
+json_puts_string(const json_dumper *dumper, const char *str, bool dot_to_underscore)
+{
+ if (!str) {
+ jd_puts(dumper, "null");
+ return;
+ }
+
+ static const char json_cntrl[0x20][6] = {
+ "u0000", "u0001", "u0002", "u0003", "u0004", "u0005", "u0006", "u0007", "b", "t", "n", "u000b", "f", "r", "u000e", "u000f",
+ "u0010", "u0011", "u0012", "u0013", "u0014", "u0015", "u0016", "u0017", "u0018", "u0019", "u001a", "u001b", "u001c", "u001d", "u001e", "u001f"
+ };
+
+ jd_putc(dumper, '"');
+ for (int i = 0; str[i]; i++) {
+ if ((unsigned)str[i] < 0x20) {
+ jd_putc(dumper, '\\');
+ jd_puts(dumper, json_cntrl[(unsigned)str[i]]);
+ } else if (i > 0 && str[i - 1] == '<' && str[i] == '/') {
+ // Convert </script> to <\/script> to avoid breaking web pages.
+ jd_puts(dumper, "\\/");
+ } else {
+ if (str[i] == '\\' || str[i] == '"') {
+ jd_putc(dumper, '\\');
+ }
+ if (dot_to_underscore && str[i] == '.')
+ jd_putc(dumper, '_');
+ else
+ jd_putc(dumper, str[i]);
+ }
+ }
+ jd_putc(dumper, '"');
+}
+
+static inline uint8_t
+json_dumper_get_prev_state(json_dumper *dumper)
+{
+ unsigned depth = dumper->current_depth;
+ return depth != 0 ? dumper->state[depth - 1] : 0;
+}
+
+static inline uint8_t
+json_dumper_get_curr_state(json_dumper *dumper)
+{
+ unsigned depth = dumper->current_depth;
+ return dumper->state[depth];
+}
+
+/**
+ * Called when a programming error is encountered where the JSON manipulation
+ * state got corrupted. This could happen when pairing the wrong begin/end
+ * calls, when writing multiple values for the same object, etc.
+ */
+static void
+json_dumper_bad(json_dumper *dumper, const char *what)
+{
+ dumper->flags |= JSON_DUMPER_FLAGS_ERROR;
+ if ((dumper->flags & JSON_DUMPER_FLAGS_NO_DEBUG)) {
+ /* Console output can be slow, disable log calls to speed up fuzzing. */
+ /*
+ * XXX - should this call abort()? If that flag isn't set,
+ * ws_error() wou;d call it; is there any point in continuing
+ * to do anything if we get here when fuzzing?
+ */
+ return;
+ }
+
+ if (dumper->output_file) {
+ fflush(dumper->output_file);
+ }
+ char unknown_curr_type_name[10+1];
+ char unknown_prev_type_name[10+1];
+ const char *curr_type_name, *prev_type_name;
+ uint8_t curr_state = json_dumper_get_curr_state(dumper);
+ uint8_t curr_type = JSON_DUMPER_TYPE(curr_state);
+ if (curr_type < NUM_JSON_DUMPER_ELEMENT_TYPE_NAMES) {
+ curr_type_name = json_dumper_element_type_names[curr_type];
+ } else {
+ snprintf(unknown_curr_type_name, sizeof unknown_curr_type_name, "%u", curr_type);
+ curr_type_name = unknown_curr_type_name;
+ }
+ if (dumper->current_depth != 0) {
+ uint8_t prev_state = json_dumper_get_prev_state(dumper);
+ uint8_t prev_type = JSON_DUMPER_TYPE(prev_state);
+ if (prev_type < NUM_JSON_DUMPER_ELEMENT_TYPE_NAMES) {
+ prev_type_name = json_dumper_element_type_names[prev_type];
+ } else {
+ snprintf(unknown_prev_type_name, sizeof unknown_prev_type_name, "%u", prev_type);
+ prev_type_name = unknown_prev_type_name;
+ }
+ } else {
+ prev_type_name = "(none)";
+ }
+ ws_error("json_dumper error: %s: current stack depth %u, current type %s, previous_type %s",
+ what, dumper->current_depth, curr_type_name, prev_type_name);
+ /* NOTREACHED */
+}
+
+static inline bool
+json_dumper_stack_would_overflow(json_dumper *dumper)
+{
+ if (dumper->current_depth + 1 >= JSON_DUMPER_MAX_DEPTH) {
+ json_dumper_bad(dumper, "JSON dumper stack overflow");
+ return true;
+ }
+ return false;
+}
+
+static inline bool
+json_dumper_stack_would_underflow(json_dumper *dumper)
+{
+ if (dumper->current_depth == 0) {
+ json_dumper_bad(dumper, "JSON dumper stack underflow");
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Checks that the dumper has not already had an error. Fail, and
+ * return false, to tell our caller not to do any more work, if it
+ * has.
+ */
+static bool
+json_dumper_check_previous_error(json_dumper *dumper)
+{
+ if ((dumper->flags & JSON_DUMPER_FLAGS_ERROR)) {
+ json_dumper_bad(dumper, "previous corruption detected");
+ return false;
+ }
+ return true;
+}
+
+static void
+print_newline_indent(const json_dumper *dumper, unsigned depth)
+{
+ if ((dumper->flags & JSON_DUMPER_FLAGS_PRETTY_PRINT)) {
+ jd_putc(dumper, '\n');
+ for (unsigned i = 0; i < depth; i++) {
+ jd_puts(dumper, " ");
+ }
+ }
+}
+
+/**
+ * Prints commas, newlines and indentation (if necessary). Used for array
+ * values, object names and normal values (strings, etc.).
+ */
+static void
+prepare_token(json_dumper *dumper)
+{
+ if (dumper->current_depth == 0) {
+ // not part of an array or object.
+ return;
+ }
+ uint8_t prev_state = dumper->state[dumper->current_depth - 1];
+
+ // While processing the object value, reset the key state as it is consumed.
+ dumper->state[dumper->current_depth - 1] &= ~JSON_DUMPER_HAS_NAME;
+
+ switch (JSON_DUMPER_TYPE(prev_state)) {
+ case JSON_DUMPER_TYPE_OBJECT:
+ if ((prev_state & JSON_DUMPER_HAS_NAME)) {
+ // Object key already set, value follows. No indentation needed.
+ return;
+ }
+ break;
+ case JSON_DUMPER_TYPE_ARRAY:
+ break;
+ default:
+ // Initial values do not need indentation.
+ return;
+ }
+
+ uint8_t curr_state = json_dumper_get_curr_state(dumper);
+ if (curr_state != JSON_DUMPER_TYPE_NONE) {
+ jd_putc(dumper, ',');
+ }
+ print_newline_indent(dumper, dumper->current_depth);
+}
+
+/**
+ * Common code to open an object/array/base64 value, printing
+ * an opening character.
+ *
+ * It also makes various correctness checks.
+ */
+static bool
+json_dumper_begin_nested_element(json_dumper *dumper, enum json_dumper_element_type type)
+{
+ if (!json_dumper_check_previous_error(dumper)) {
+ return false;
+ }
+
+ /* Make sure we won't overflow the dumper stack */
+ if (json_dumper_stack_would_overflow(dumper)) {
+ return false;
+ }
+
+ prepare_token(dumper);
+ switch (type) {
+ case JSON_DUMPER_TYPE_OBJECT:
+ jd_putc(dumper, '{');
+ break;
+ case JSON_DUMPER_TYPE_ARRAY:
+ jd_putc(dumper, '[');
+ break;
+ case JSON_DUMPER_TYPE_BASE64:
+ dumper->base64_state = 0;
+ dumper->base64_save = 0;
+
+ jd_putc(dumper, '"');
+ break;
+ default:
+ json_dumper_bad(dumper, "beginning unknown nested element type");
+ return false;
+ }
+
+ dumper->state[dumper->current_depth] = type;
+ /*
+ * Guaranteed not to overflow, as json_dumper_stack_would_overflow()
+ * returned false.
+ */
+ ++dumper->current_depth;
+ dumper->state[dumper->current_depth] = JSON_DUMPER_TYPE_NONE;
+ return true;
+}
+
+/**
+ * Common code to close an object/array/base64 value, printing a
+ * closing character (and if necessary, it is preceded by newline
+ * and indentation).
+ *
+ * It also makes various correctness checks.
+ */
+static bool
+json_dumper_end_nested_element(json_dumper *dumper, enum json_dumper_element_type type)
+{
+ if (!json_dumper_check_previous_error(dumper)) {
+ return false;
+ }
+
+ uint8_t prev_state = json_dumper_get_prev_state(dumper);
+
+ switch (type) {
+ case JSON_DUMPER_TYPE_OBJECT:
+ if (JSON_DUMPER_TYPE(prev_state) != JSON_DUMPER_TYPE_OBJECT) {
+ json_dumper_bad(dumper, "ending non-object nested item type as object");
+ return false;
+ }
+ break;
+ case JSON_DUMPER_TYPE_ARRAY:
+ if (JSON_DUMPER_TYPE(prev_state) != JSON_DUMPER_TYPE_ARRAY) {
+ json_dumper_bad(dumper, "ending non-array nested item type as array");
+ return false;
+ }
+ break;
+ case JSON_DUMPER_TYPE_BASE64:
+ if (JSON_DUMPER_TYPE(prev_state) != JSON_DUMPER_TYPE_BASE64) {
+ json_dumper_bad(dumper, "ending non-base64 nested item type as base64");
+ return false;
+ }
+ break;
+ default:
+ json_dumper_bad(dumper, "ending unknown nested element type");
+ return false;
+ }
+
+ if (prev_state & JSON_DUMPER_HAS_NAME) {
+ json_dumper_bad(dumper, "finishing object with last item having name but no value");
+ return false;
+ }
+
+ /* Make sure we won't underflow the dumper stack */
+ if (json_dumper_stack_would_underflow(dumper)) {
+ return false;
+ }
+
+ // if the object/array was non-empty, add a newline and indentation.
+ if (dumper->state[dumper->current_depth]) {
+ print_newline_indent(dumper, dumper->current_depth - 1);
+ }
+
+ switch (type) {
+ case JSON_DUMPER_TYPE_OBJECT:
+ jd_putc(dumper, '}');
+ break;
+ case JSON_DUMPER_TYPE_ARRAY:
+ jd_putc(dumper, ']');
+ break;
+ case JSON_DUMPER_TYPE_BASE64:
+ {
+ char buf[4];
+ size_t wrote;
+
+ wrote = g_base64_encode_close(false, buf, &dumper->base64_state, &dumper->base64_save);
+ jd_puts_len(dumper, buf, wrote);
+
+ jd_putc(dumper, '"');
+ break;
+ }
+ default:
+ json_dumper_bad(dumper, "endning unknown nested element type");
+ return false;
+ }
+
+ /*
+ * Guaranteed not to underflow, as json_dumper_stack_would_underflow()
+ * returned false.
+ */
+ --dumper->current_depth;
+ return true;
+}
+
+void
+json_dumper_begin_object(json_dumper *dumper)
+{
+ json_dumper_begin_nested_element(dumper, JSON_DUMPER_TYPE_OBJECT);
+}
+
+void
+json_dumper_set_member_name(json_dumper *dumper, const char *name)
+{
+ if (!json_dumper_check_previous_error(dumper)) {
+ return;
+ }
+
+ uint8_t prev_state = json_dumper_get_prev_state(dumper);
+
+ /* Only object members, not array members, have names. */
+ if (JSON_DUMPER_TYPE(prev_state) != JSON_DUMPER_TYPE_OBJECT) {
+ json_dumper_bad(dumper, "setting name on non-object nested item type");
+ return;
+ }
+ /* An object member name can only be set once before its value is set. */
+ if (prev_state & JSON_DUMPER_HAS_NAME) {
+ json_dumper_bad(dumper, "setting name twice on an object member");
+ return;
+ }
+
+ prepare_token(dumper);
+ json_puts_string(dumper, name, dumper->flags & JSON_DUMPER_DOT_TO_UNDERSCORE);
+ jd_putc(dumper, ':');
+ if ((dumper->flags & JSON_DUMPER_FLAGS_PRETTY_PRINT)) {
+ jd_putc(dumper, ' ');
+ }
+
+ dumper->state[dumper->current_depth - 1] |= JSON_DUMPER_HAS_NAME;
+}
+
+void
+json_dumper_end_object(json_dumper *dumper)
+{
+ json_dumper_end_nested_element(dumper, JSON_DUMPER_TYPE_OBJECT);
+}
+
+void
+json_dumper_begin_array(json_dumper *dumper)
+{
+ json_dumper_begin_nested_element(dumper, JSON_DUMPER_TYPE_ARRAY);
+}
+
+void
+json_dumper_end_array(json_dumper *dumper)
+{
+ json_dumper_end_nested_element(dumper, JSON_DUMPER_TYPE_ARRAY);
+}
+
+static bool
+json_dumper_setting_value_ok(json_dumper *dumper)
+{
+ uint8_t prev_state = json_dumper_get_prev_state(dumper);
+
+ switch (JSON_DUMPER_TYPE(prev_state)) {
+ case JSON_DUMPER_TYPE_OBJECT:
+ /*
+ * This value is part of an object. As such, it must
+ * have a name.
+ */
+ if (!(prev_state & JSON_DUMPER_HAS_NAME)) {
+ json_dumper_bad(dumper, "setting value of object member without a name");
+ return false;
+ }
+ break;
+ case JSON_DUMPER_TYPE_ARRAY:
+ /*
+ * This value is part of an array. As such, it's not
+ * required to have a name (and shouldn't have a name;
+ * that's already been checked in json_dumper_set_member_name()).
+ */
+ break;
+ case JSON_DUMPER_TYPE_BASE64:
+ /*
+ * We're in the middle of constructing a base64-encoded
+ * value. Only json_dumper_write_base64() can be used
+ * for that; we can't add individual values to it.
+ */
+ json_dumper_bad(dumper, "attempt to set value of base64 item to something not base64-encoded");
+ return false;
+ case JSON_DUMPER_TYPE_NONE:
+ case JSON_DUMPER_TYPE_VALUE:
+ {
+ uint8_t curr_state = json_dumper_get_curr_state(dumper);
+ switch (JSON_DUMPER_TYPE(curr_state)) {
+ case JSON_DUMPER_TYPE_NONE:
+ /*
+ * We haven't put a value yet, so we can put one now.
+ */
+ break;
+ case JSON_DUMPER_TYPE_VALUE:
+ /*
+ * This value isn't part of an object or array,
+ * and we've already put one value.
+ */
+ json_dumper_bad(dumper, "value not in object or array immediately follows another value");
+ return false;
+ case JSON_DUMPER_TYPE_OBJECT:
+ case JSON_DUMPER_TYPE_ARRAY:
+ case JSON_DUMPER_TYPE_BASE64:
+ /*
+ * This should never be the case, no matter what
+ * our callers do:
+ *
+ * JSON_DUMPER_TYPE_OBJECT can be the previous
+ * type, meaning we're in the process of adding
+ * elements to an object, but it should never be
+ * the current type;
+ *
+ * JSON_DUMPER_TYPE_ARRAY can be the previous
+ * type, meaning we're in the process of adding
+ * elements to an array, but it should never be
+ * the current type;
+ *
+ * JSON_DUMPER_TYPE_BASE64 should only be the
+ * current type if we're in the middle of
+ * building a base64 value, in which case the
+ * previous type should also be JSON_DUMPER_TYPE_BASE64,
+ * but that's not the previous type.
+ */
+ json_dumper_bad(dumper, "internal error setting value - should not happen");
+ return false;
+ default:
+ json_dumper_bad(dumper, "internal error setting value, bad current state - should not happen");
+ return false;
+ }
+ break;
+ }
+ default:
+ json_dumper_bad(dumper, "internal error setting value, bad previous state - should not happen");
+ return false;
+ }
+ return true;
+}
+
+void
+json_dumper_value_string(json_dumper *dumper, const char *value)
+{
+ if (!json_dumper_check_previous_error(dumper)) {
+ return;
+ }
+ if (!json_dumper_setting_value_ok(dumper)) {
+ return;
+ }
+
+ prepare_token(dumper);
+ json_puts_string(dumper, value, false);
+
+ dumper->state[dumper->current_depth] = JSON_DUMPER_TYPE_VALUE;
+}
+
+void
+json_dumper_value_double(json_dumper *dumper, double value)
+{
+ if (!json_dumper_check_previous_error(dumper)) {
+ return;
+ }
+
+ if (!json_dumper_setting_value_ok(dumper)) {
+ return;
+ }
+
+ prepare_token(dumper);
+ char buffer[G_ASCII_DTOSTR_BUF_SIZE] = { 0 };
+ if (isfinite(value) && g_ascii_dtostr(buffer, G_ASCII_DTOSTR_BUF_SIZE, value) && buffer[0]) {
+ jd_puts(dumper, buffer);
+ } else {
+ jd_puts(dumper, "null");
+ }
+
+ dumper->state[dumper->current_depth] = JSON_DUMPER_TYPE_VALUE;
+}
+
+void
+json_dumper_value_va_list(json_dumper *dumper, const char *format, va_list ap)
+{
+ if (!json_dumper_check_previous_error(dumper)) {
+ return;
+ }
+
+ if (!json_dumper_setting_value_ok(dumper)) {
+ return;
+ }
+
+ prepare_token(dumper);
+ jd_vprintf(dumper, format, ap);
+
+ dumper->state[dumper->current_depth] = JSON_DUMPER_TYPE_VALUE;
+}
+
+void
+json_dumper_value_anyf(json_dumper *dumper, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ json_dumper_value_va_list(dumper, format, ap);
+ va_end(ap);
+}
+
+bool
+json_dumper_finish(json_dumper *dumper)
+{
+ if (!json_dumper_check_previous_error(dumper)) {
+ return false;
+ }
+
+ if (dumper->current_depth != 0) {
+ json_dumper_bad(dumper, "JSON dumper stack not empty at finish");
+ return false;
+ }
+
+ jd_putc(dumper, '\n');
+ dumper->state[0] = JSON_DUMPER_TYPE_NONE;
+ return true;
+}
+
+void
+json_dumper_begin_base64(json_dumper *dumper)
+{
+ json_dumper_begin_nested_element(dumper, JSON_DUMPER_TYPE_BASE64);
+}
+
+void
+json_dumper_write_base64(json_dumper* dumper, const unsigned char *data, size_t len)
+{
+ if (!json_dumper_check_previous_error(dumper)) {
+ return;
+ }
+
+ uint8_t prev_state = json_dumper_get_prev_state(dumper);
+
+ if (JSON_DUMPER_TYPE(prev_state) != JSON_DUMPER_TYPE_BASE64) {
+ json_dumper_bad(dumper, "writing base64 data to a non-base64 value");
+ return;
+ }
+
+ #define CHUNK_SIZE 1024
+ char buf[(CHUNK_SIZE / 3 + 1) * 4 + 4];
+
+ while (len > 0) {
+ size_t chunk_size = len < CHUNK_SIZE ? len : CHUNK_SIZE;
+ size_t output_size = g_base64_encode_step(data, chunk_size, false, buf, &dumper->base64_state, &dumper->base64_save);
+ jd_puts_len(dumper, buf, output_size);
+ data += chunk_size;
+ len -= chunk_size;
+ }
+
+ dumper->state[dumper->current_depth] = JSON_DUMPER_TYPE_BASE64;
+}
+
+void
+json_dumper_end_base64(json_dumper *dumper)
+{
+ json_dumper_end_nested_element(dumper, JSON_DUMPER_TYPE_BASE64);
+}
diff --git a/wsutil/json_dumper.h b/wsutil/json_dumper.h
new file mode 100644
index 00000000..184960ae
--- /dev/null
+++ b/wsutil/json_dumper.h
@@ -0,0 +1,140 @@
+/** @file
+ * Routines for serializing data as JSON.
+ *
+ * Copyright 2018, Peter Wu <peter@lekensteyn.nl>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __JSON_DUMPER_H__
+#define __JSON_DUMPER_H__
+
+#include "ws_symbol_export.h"
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Example:
+ *
+ * json_dumper dumper = {
+ * .output_file = stdout, // or .output_string = g_string_new(NULL)
+ * .flags = JSON_DUMPER_FLAGS_PRETTY_PRINT,
+ * };
+ * json_dumper_begin_object(&dumper);
+ * json_dumper_set_member_name(&dumper, "key");
+ * json_dumper_value_string(&dumper, "value");
+ * json_dumper_set_member_name(&dumper, "array");
+ * json_dumper_begin_array(&dumper);
+ * json_dumper_value_anyf(&dumper, "true");
+ * json_dumper_value_double(&dumper, 1.0);
+ * json_dumper_begin_base64(&dumper);
+ * json_dumper_write_base64(&dumper, (const unsigned char *)"abcd", 4);
+ * json_dumper_write_base64(&dumper, (const unsigned char *)"1234", 4);
+ * json_dumper_end_base64(&dumper);
+ * json_dumper_begin_object(&dumper);
+ * json_dumper_end_object(&dumper);
+ * json_dumper_begin_array(&dumper);
+ * json_dumper_end_array(&dumper);
+ * json_dumper_end_array(&dumper);
+ * json_dumper_end_object(&dumper);
+ * json_dumper_finish(&dumper);
+ */
+
+/** Maximum object/array nesting depth. */
+#define JSON_DUMPER_MAX_DEPTH 1100
+typedef struct json_dumper {
+ FILE *output_file; /**< Output file. If it is not NULL, JSON will be dumped in the file. */
+ GString *output_string; /**< Output GLib strings. If it is not NULL, JSON will be dumped in the string. */
+#define JSON_DUMPER_FLAGS_PRETTY_PRINT (1 << 0) /* Enable pretty printing. */
+#define JSON_DUMPER_DOT_TO_UNDERSCORE (1 << 1) /* Convert dots to underscores in keys */
+#define JSON_DUMPER_FLAGS_NO_DEBUG (1 << 17) /* Disable fatal ws_error messsges on error(intended for speeding up fuzzing). */
+ int flags;
+ /* for internal use, initialize with zeroes. */
+ unsigned current_depth;
+ int base64_state;
+ int base64_save;
+ uint8_t state[JSON_DUMPER_MAX_DEPTH];
+} json_dumper;
+
+WS_DLL_PUBLIC void
+json_dumper_begin_object(json_dumper *dumper);
+
+WS_DLL_PUBLIC void
+json_dumper_set_member_name(json_dumper *dumper, const char *name);
+
+WS_DLL_PUBLIC void
+json_dumper_end_object(json_dumper *dumper);
+
+WS_DLL_PUBLIC void
+json_dumper_begin_array(json_dumper *dumper);
+
+WS_DLL_PUBLIC void
+json_dumper_end_array(json_dumper *dumper);
+
+WS_DLL_PUBLIC void
+json_dumper_value_string(json_dumper *dumper, const char *value);
+
+WS_DLL_PUBLIC void
+json_dumper_value_double(json_dumper *dumper, double value);
+
+/**
+ * Dump number, "true", "false" or "null" values.
+ */
+WS_DLL_PUBLIC void
+json_dumper_value_anyf(json_dumper *dumper, const char *format, ...)
+G_GNUC_PRINTF(2, 3);
+
+/**
+ * Dump literal values (like json_dumper_value_anyf), but taking a va_list
+ * as parameter. String values MUST be properly quoted by the caller, no
+ * escaping occurs. Do not use with untrusted data.
+ */
+WS_DLL_PUBLIC void
+json_dumper_value_va_list(json_dumper *dumper, const char *format, va_list ap);
+
+WS_DLL_PUBLIC void
+json_dumper_begin_base64(json_dumper *dumper);
+
+WS_DLL_PUBLIC void
+json_dumper_end_base64(json_dumper *dumper);
+
+WS_DLL_PUBLIC void
+json_dumper_write_base64(json_dumper *dumper, const unsigned char *data, size_t len);
+
+/**
+ * Finishes dumping data. Returns true if everything is okay and false if
+ * something went wrong (open/close mismatch, missing values, etc.).
+ */
+WS_DLL_PUBLIC bool
+json_dumper_finish(json_dumper *dumper);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __JSON_DUMPER_H__ */
+
+/*
+ * 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/wsutil/mpeg-audio.c b/wsutil/mpeg-audio.c
new file mode 100644
index 00000000..8e80a13f
--- /dev/null
+++ b/wsutil/mpeg-audio.c
@@ -0,0 +1,122 @@
+/* mpeg-audio.c
+ *
+ * MPEG Audio header dissection
+ * Written by Shaun Jackman <sjackman@gmail.com>
+ * Copyright 2007 Shaun Jackman
+ *
+ * Wiretap Library
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "mpeg-audio.h"
+
+static const int mpa_versions[4] = { 2, -1, 1, 0 };
+static const int mpa_layers[4] = { -1, 2, 1, 0 };
+
+static const unsigned int mpa_samples_data[3][3] = {
+ { 384, 1152, 1152 },
+ { 384, 1152, 576 },
+ { 384, 1152, 576 },
+};
+
+static const unsigned int mpa_bitrates[3][3][16] = { /* kb/s */
+ {
+ { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448 },
+ { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384 },
+ { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 },
+ },
+ {
+ { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256 },
+ { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 },
+ { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 },
+ },
+ {
+ { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256 },
+ { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 },
+ { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 },
+ },
+};
+
+static const unsigned int mpa_frequencies[3][4] = {
+ { 44100, 48000, 32000 },
+ { 22050, 24000, 16000 },
+ { 11025, 12000, 8000 },
+};
+
+static const unsigned int mpa_padding_data[3] = { 4, 1, 1 };
+
+int
+mpa_version(const struct mpa *mpa)
+{
+ return mpa_versions[mpa->version];
+}
+
+int
+mpa_layer(const struct mpa *mpa)
+{
+ return mpa_layers[mpa->layer];
+}
+
+unsigned
+mpa_samples(const struct mpa *mpa)
+{
+ return mpa_samples_data[mpa_versions[mpa->version]][mpa_layer(mpa)];
+}
+
+unsigned
+mpa_bitrate(const struct mpa *mpa)
+{
+ return (1000 * (mpa_bitrates[mpa_versions[mpa->version]][mpa_layers[mpa->layer]][mpa->bitrate]));
+}
+
+unsigned
+mpa_frequency(const struct mpa *mpa)
+{
+ return(mpa_frequencies[mpa_versions[mpa->version]][mpa->frequency]);
+}
+
+unsigned
+mpa_padding(const struct mpa *mpa)
+{
+ return(mpa->padding ? mpa_padding_data[mpa_layers[mpa->layer]] : 0);
+}
+
+/* Decode an ID3v2 synchsafe integer.
+ * See https://id3.org/id3v2.4.0-structure section 6.2.
+ */
+uint32_t
+decode_synchsafe_int(uint32_t input)
+{
+ uint32_t value;
+
+ /* High-order byte */
+ value = (input >> 24) & 0x7f;
+ /* Shift the result left to make room for the next 7 bits */
+ value <<= 7;
+
+ /* Now OR in the 2nd byte */
+ value |= (input >> 16) & 0x7f;
+ value <<= 7;
+
+ /* ... and the 3rd */
+ value |= (input >> 8) & 0x7f;
+ value <<= 7;
+
+ /* For the 4th byte don't do the shift */
+ value |= input & 0x7f;
+
+ return value;
+}
+
+/*
+ * 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:
+ */
diff --git a/wsutil/mpeg-audio.h b/wsutil/mpeg-audio.h
new file mode 100644
index 00000000..10ef36a4
--- /dev/null
+++ b/wsutil/mpeg-audio.h
@@ -0,0 +1,95 @@
+/** @file
+ *
+ * MPEG Audio header dissection
+ * Written by Shaun Jackman <sjackman@gmail.com>
+ * Copyright 2007 Shaun Jackman
+ *
+ * Wiretap Library
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef MPA_H
+#define MPA_H 1
+
+#include <stdint.h>
+#include "ws_symbol_export.h"
+
+struct mpa {
+ unsigned int emphasis :2;
+ unsigned int original :1;
+ unsigned int copyright :1;
+ unsigned int modeext :2;
+ unsigned int mode :2;
+ unsigned int priv :1;
+ unsigned int padding :1;
+ unsigned int frequency :2;
+ unsigned int bitrate :4;
+ unsigned int protection :1;
+ unsigned int layer :2;
+ unsigned int version :2;
+ unsigned int sync :11;
+};
+
+#define MPA_UNMARSHAL_SYNC(n) ((n) >> 21 & 0x7ff)
+#define MPA_UNMARSHAL_VERSION(n) ((n) >> 19 & 0x3)
+#define MPA_UNMARSHAL_LAYER(n) ((n) >> 17 & 0x3)
+#define MPA_UNMARSHAL_PROTECTION(n) ((n) >> 16 & 0x1)
+#define MPA_UNMARSHAL_BITRATE(n) ((n) >> 12 & 0xf)
+#define MPA_UNMARSHAL_FREQUENCY(n) ((n) >> 10 & 0x3)
+#define MPA_UNMARSHAL_PADDING(n) ((n) >> 9 & 0x1)
+#define MPA_UNMARSHAL_PRIVATE(n) ((n) >> 8 & 0x1)
+#define MPA_UNMARSHAL_MODE(n) ((n) >> 6 & 0x3)
+#define MPA_UNMARSHAL_MODEEXT(n) ((n) >> 4 & 0x3)
+#define MPA_UNMARSHAL_COPYRIGHT(n) ((n) >> 3 & 0x1)
+#define MPA_UNMARSHAL_ORIGINAL(n) ((n) >> 2 & 0x1)
+#define MPA_UNMARSHAL_EMPHASIS(n) ((n) >> 0 & 0x3)
+
+#define MPA_UNMARSHAL(mpa, n) do { \
+ (mpa)->sync = MPA_UNMARSHAL_SYNC(n); \
+ (mpa)->version = MPA_UNMARSHAL_VERSION(n); \
+ (mpa)->layer = MPA_UNMARSHAL_LAYER(n); \
+ (mpa)->protection = MPA_UNMARSHAL_PROTECTION(n); \
+ (mpa)->bitrate = MPA_UNMARSHAL_BITRATE(n); \
+ (mpa)->frequency = MPA_UNMARSHAL_FREQUENCY(n); \
+ (mpa)->padding = MPA_UNMARSHAL_PADDING(n); \
+ (mpa)->priv = MPA_UNMARSHAL_PRIVATE(n); \
+ (mpa)->mode = MPA_UNMARSHAL_MODE(n); \
+ (mpa)->modeext = MPA_UNMARSHAL_MODEEXT(n); \
+ (mpa)->copyright = MPA_UNMARSHAL_COPYRIGHT(n); \
+ (mpa)->original = MPA_UNMARSHAL_ORIGINAL(n); \
+ (mpa)->emphasis = MPA_UNMARSHAL_EMPHASIS(n); \
+ } while (0)
+
+WS_DLL_PUBLIC
+int mpa_version(const struct mpa *);
+WS_DLL_PUBLIC
+int mpa_layer(const struct mpa *);
+WS_DLL_PUBLIC
+unsigned int mpa_samples(const struct mpa *);
+WS_DLL_PUBLIC
+unsigned int mpa_bitrate(const struct mpa *);
+WS_DLL_PUBLIC
+unsigned int mpa_frequency(const struct mpa *);
+WS_DLL_PUBLIC
+unsigned int mpa_padding(const struct mpa *);
+WS_DLL_PUBLIC
+uint32_t decode_synchsafe_int(uint32_t);
+
+#define MPA_DATA_BYTES(mpa) (mpa_bitrate(mpa) * mpa_samples(mpa) \
+ / mpa_frequency(mpa) / 8)
+#define MPA_BYTES(mpa) (MPA_DATA_BYTES(mpa) + mpa_padding(mpa))
+#define MPA_DURATION_NS(mpa) \
+ (1000000000 / mpa_frequency(mpa) * mpa_samples(mpa))
+
+enum { MPA_SYNC = 0x7ff };
+
+#define MPA_SYNC_VALID(mpa) ((mpa)->sync == MPA_SYNC)
+#define MPA_VERSION_VALID(mpa) (mpa_version(mpa) >= 0)
+#define MPA_LAYER_VALID(mpa) (mpa_layer(mpa) >= 0)
+#define MPA_BITRATE_VALID(mpa) (mpa_bitrate(mpa) > 0)
+#define MPA_FREQUENCY_VALID(mpa) (mpa_frequency(mpa) > 0)
+#define MPA_VALID(mpa) (MPA_SYNC_VALID(mpa) \
+ && MPA_VERSION_VALID(mpa) && MPA_LAYER_VALID(mpa) \
+ && MPA_BITRATE_VALID(mpa) && MPA_FREQUENCY_VALID(mpa))
+
+#endif
diff --git a/wsutil/nstime.c b/wsutil/nstime.c
new file mode 100644
index 00000000..1f58dbb4
--- /dev/null
+++ b/wsutil/nstime.c
@@ -0,0 +1,653 @@
+/* nstime.c
+ * Routines for manipulating nstime_t structures
+ *
+ * Copyright (c) 2005 MX Telecom Ltd. <richardv@mxtelecom.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "nstime.h"
+
+#include <stdio.h>
+#include <string.h>
+#include "epochs.h"
+#include "time_util.h"
+#include "to_str.h"
+#include "strtoi.h"
+
+/* this is #defined so that we can clearly see that we have the right number of
+ zeros, rather than as a guard against the number of nanoseconds in a second
+ changing ;) */
+#define NS_PER_S 1000000000
+
+/* set the given nstime_t to zero */
+void nstime_set_zero(nstime_t *nstime)
+{
+ nstime->secs = 0;
+ nstime->nsecs = 0;
+}
+
+/* is the given nstime_t currently zero? */
+bool nstime_is_zero(const nstime_t *nstime)
+{
+ return nstime->secs == 0 && nstime->nsecs == 0;
+}
+
+/* set the given nstime_t to (0,maxint) to mark it as "unset"
+ * That way we can find the first frame even when a timestamp
+ * is zero (fix for bug 1056)
+ */
+void nstime_set_unset(nstime_t *nstime)
+{
+ nstime->secs = 0;
+ nstime->nsecs = INT_MAX;
+}
+
+/* is the given nstime_t currently (0,maxint)? */
+bool nstime_is_unset(const nstime_t *nstime)
+{
+ if(nstime->secs == 0 && nstime->nsecs == INT_MAX) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+/** function: nstime_copy
+ *
+ * a = b
+ */
+void nstime_copy(nstime_t *a, const nstime_t *b)
+{
+ a->secs = b->secs;
+ a->nsecs = b->nsecs;
+}
+
+/*
+ * function: nstime_delta
+ * delta = b - a
+ */
+
+void nstime_delta(nstime_t *delta, const nstime_t *b, const nstime_t *a )
+{
+ if (b->secs == a->secs) {
+ /* The seconds part of b is the same as the seconds part of a, so if
+ the nanoseconds part of the first time is less than the nanoseconds
+ part of a, b is before a. The nanoseconds part of the delta should
+ just be the difference between the nanoseconds part of b and the
+ nanoseconds part of a; don't adjust the seconds part of the delta,
+ as it's OK if the nanoseconds part is negative, and an overflow
+ can never result. */
+ delta->secs = 0;
+ delta->nsecs = b->nsecs - a->nsecs;
+ } else if (b->secs < a->secs) {
+ /* The seconds part of b is less than the seconds part of a, so b is
+ before a.
+
+ Both the "seconds" and "nanoseconds" value of the delta
+ should have the same sign, so if the difference between the
+ nanoseconds values would be *positive*, subtract 1,000,000,000
+ from it, and add one to the seconds value. */
+ delta->secs = b->secs - a->secs;
+ delta->nsecs = b->nsecs - a->nsecs;
+ if(delta->nsecs > 0) {
+ delta->nsecs -= NS_PER_S;
+ delta->secs ++;
+ }
+ } else {
+ delta->secs = b->secs - a->secs;
+ delta->nsecs = b->nsecs - a->nsecs;
+ if(delta->nsecs < 0) {
+ delta->nsecs += NS_PER_S;
+ delta->secs --;
+ }
+ }
+}
+
+/*
+ * function: nstime_sum
+ * sum = a + b
+ */
+
+void nstime_sum(nstime_t *sum, const nstime_t *a, const nstime_t *b)
+{
+ sum->secs = a->secs + b->secs;
+ sum->nsecs = a->nsecs + b->nsecs;
+ if(sum->nsecs>=NS_PER_S || (sum->nsecs>0 && sum->secs<0)){
+ sum->nsecs-=NS_PER_S;
+ sum->secs++;
+ } else if(sum->nsecs<=-NS_PER_S || (sum->nsecs<0 && sum->secs>0)) {
+ sum->nsecs+=NS_PER_S;
+ sum->secs--;
+ }
+}
+
+/*
+ * function: nstime_cmp
+ *
+ * a > b : > 0
+ * a = b : 0
+ * a < b : < 0
+ */
+
+int nstime_cmp (const nstime_t *a, const nstime_t *b )
+{
+ if (G_UNLIKELY(nstime_is_unset(a))) {
+ if (G_UNLIKELY(nstime_is_unset(b))) {
+ return 0; /* "no time stamp" is "equal" to "no time stamp" */
+ } else {
+ return -1; /* and is less than all time stamps */
+ }
+ } else {
+ if (G_UNLIKELY(nstime_is_unset(b))) {
+ return 1;
+ }
+ }
+ if (a->secs == b->secs) {
+ return a->nsecs - b->nsecs;
+ } else {
+ return (int) (a->secs - b->secs);
+ }
+}
+
+unsigned nstime_hash(const nstime_t *nstime)
+{
+ int64_t val1 = (int64_t)nstime->secs;
+
+ return g_int64_hash(&val1) ^ g_int_hash(&nstime->nsecs);
+}
+
+/*
+ * function: nstime_to_msec
+ * converts nstime to double, time base is milli seconds
+ */
+
+double nstime_to_msec(const nstime_t *nstime)
+{
+ return ((double)nstime->secs*1000 + (double)nstime->nsecs/1000000);
+}
+
+/*
+ * function: nstime_to_sec
+ * converts nstime to double, time base is seconds
+ */
+
+double nstime_to_sec(const nstime_t *nstime)
+{
+ return ((double)nstime->secs + (double)nstime->nsecs/NS_PER_S);
+}
+
+/*
+ * This code is based on the Samba code:
+ *
+ * Unix SMB/Netbios implementation.
+ * Version 1.9.
+ * time handling functions
+ * Copyright (C) Andrew Tridgell 1992-1998
+ */
+
+#ifndef TIME_T_MIN
+#define TIME_T_MIN ((time_t) ((time_t)0 < (time_t) -1 ? (time_t) 0 \
+ : (time_t) (~0ULL << (sizeof (time_t) * CHAR_BIT - 1))))
+#endif
+#ifndef TIME_T_MAX
+#define TIME_T_MAX ((time_t) (~ (time_t) 0 - TIME_T_MIN))
+#endif
+
+static bool
+common_filetime_to_nstime(nstime_t *nstime, uint64_t ftsecs, int nsecs)
+{
+ int64_t secs;
+
+ /*
+ * Shift the seconds from the Windows epoch to the UN*X epoch.
+ * ftsecs's value should fit in a 64-bit signed variable, as
+ * ftsecs is derived from a 64-bit fractions-of-a-second value,
+ * and is far from the maximum 64-bit signed value, and
+ * EPOCH_DELTA_1601_01_01_00_00_00_UTC is also far from the
+ * maximum 64-bit signed value, so the difference between them
+ * should also fit in a 64-bit signed value.
+ */
+ secs = (int64_t)ftsecs - EPOCH_DELTA_1601_01_01_00_00_00_UTC;
+
+ if (!(TIME_T_MIN <= secs && secs <= TIME_T_MAX)) {
+ /* The result won't fit in a time_t */
+ return false;
+ }
+
+ /*
+ * Get the time as seconds and nanoseconds.
+ */
+ nstime->secs = (time_t) secs;
+ nstime->nsecs = nsecs;
+ return true;
+}
+
+/*
+ * function: filetime_to_nstime
+ * converts a Windows FILETIME value to an nstime_t
+ * returns true if the conversion succeeds, false if it doesn't
+ * (for example, with a 32-bit time_t, the time overflows or
+ * underflows time_t)
+ */
+bool
+filetime_to_nstime(nstime_t *nstime, uint64_t filetime)
+{
+ uint64_t ftsecs;
+ int nsecs;
+
+ /*
+ * Split into seconds and tenths of microseconds, and
+ * then convert tenths of microseconds to nanoseconds.
+ */
+ ftsecs = filetime / 10000000;
+ nsecs = (int)((filetime % 10000000)*100);
+
+ return common_filetime_to_nstime(nstime, ftsecs, nsecs);
+}
+
+/*
+ * function: nsfiletime_to_nstime
+ * converts a Windows FILETIME-like value, but given in nanoseconds
+ * rather than 10ths of microseconds, to an nstime_t
+ * returns true if the conversion succeeds, false if it doesn't
+ * (for example, with a 32-bit time_t, the time overflows or
+ * underflows time_t)
+ */
+bool
+nsfiletime_to_nstime(nstime_t *nstime, uint64_t nsfiletime)
+{
+ uint64_t ftsecs;
+ int nsecs;
+
+ /* Split into seconds and nanoseconds. */
+ ftsecs = nsfiletime / NS_PER_S;
+ nsecs = (int)(nsfiletime % NS_PER_S);
+
+ return common_filetime_to_nstime(nstime, ftsecs, nsecs);
+}
+
+/*
+ * function: iso8601_to_nstime
+ * parses a character string for a date and time given in
+ * ISO 8601 date-time format (eg: 2014-04-07T05:41:56.782+00:00)
+ * and converts to an nstime_t
+ * returns pointer to the first character after the last character
+ * parsed on success, or NULL on failure
+ *
+ * NB. ISO 8601 is actually a lot more flexible than the above format,
+ * much to a developer's chagrin. The "basic format" is distinguished from
+ * the "extended format" by lacking the - and : separators. This function
+ * supports both the basic and extended format (as well as both simultaneously)
+ * with several common options and extensions. Time resolution is supported
+ * up to nanoseconds (9 fractional digits) or down to whole minutes (omitting
+ * the seconds component in the latter case). The T separator can be replaced
+ * by a space in either format (a common extension not in ISO 8601 but found
+ * in, e.g., RFC 3339) or omitted entirely in the basic format.
+ *
+ * Many standards that use ISO 8601 implement profiles with additional
+ * constraints, such as requiring that the seconds field be present, only
+ * allowing "." as the decimal separator, or limiting the number of fractional
+ * digits. Callers that wish to check constraints not yet enforced by a
+ * profile supported by the function must do so themselves.
+ *
+ * Future improvements could parse other ISO 8601 formats, such as
+ * YYYY-Www-D, YYYY-DDD, etc. For a relatively easy introduction to
+ * these formats, see wikipedia: https://en.wikipedia.org/wiki/ISO_8601
+ */
+const char *
+iso8601_to_nstime(nstime_t *nstime, const char *ptr, iso8601_fmt_e format)
+{
+ struct tm tm;
+ int n_scanned = 0;
+ int n_chars = 0;
+ unsigned frac = 0;
+ int off_hr = 0;
+ int off_min = 0;
+ char sign = '\0';
+ bool has_separator = false;
+ bool have_offset = false;
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_isdst = -1;
+ nstime_set_unset(nstime);
+
+ /* Verify that we start with a four digit year and then look for the
+ * separator. */
+ for (n_scanned = 0; n_scanned < 4; n_scanned++) {
+ if (!g_ascii_isdigit(*ptr)) {
+ return NULL;
+ }
+ tm.tm_year *= 10;
+ tm.tm_year += *ptr++ - '0';
+ }
+ if (*ptr == '-') {
+ switch (format) {
+ case ISO8601_DATETIME_BASIC:
+ return NULL;
+
+ case ISO8601_DATETIME:
+ case ISO8601_DATETIME_AUTO:
+ default:
+ has_separator = true;
+ ptr++;
+ };
+ } else if (g_ascii_isdigit(*ptr)) {
+ switch (format) {
+ case ISO8601_DATETIME:
+ return NULL;
+
+ case ISO8601_DATETIME_BASIC:
+ case ISO8601_DATETIME_AUTO:
+ default:
+ has_separator = false;
+ };
+ } else {
+ return NULL;
+ }
+
+ tm.tm_year -= 1900; /* struct tm expects number of years since 1900 */
+
+ /* Note: sscanf is known to be inconsistent across platforms with respect
+ to whether a %n is counted as a return value or not (XXX: Is this
+ still true, despite the express comments of C99 §7.19.6.2 12?), so we
+ use '<'/'>='
+ */
+ /* XXX: sscanf allows an optional sign indicator before each integer
+ * converted (whether with %d or %u), so this will convert some bogus
+ * strings. Either checking afterwards or doing the whole thing by hand
+ * as with the year above is the only correct way. (strptime certainly
+ * can't handle the basic format.)
+ */
+ n_scanned = sscanf(ptr, has_separator ? "%2u-%2u%n" : "%2u%2u%n",
+ &tm.tm_mon,
+ &tm.tm_mday,
+ &n_chars);
+ if (n_scanned >= 2) {
+ /* Got year, month, and day */
+ tm.tm_mon--; /* struct tm expects 0-based month */
+ ptr += n_chars;
+ }
+ else {
+ return NULL;
+ }
+
+ if (*ptr == 'T' || *ptr == ' ') {
+ /* The 'T' between date and time is optional if the meaning is
+ unambiguous. We also allow for ' ' here per RFC 3339 to support
+ formats such as editcap's -A/-B options. */
+ ptr++;
+ }
+ else if (has_separator) {
+ /* Allow no separator between date and time iff we have no
+ separator between units. (Some extended formats may negotiate
+ no separator here, so this could be changed.) */
+ return NULL;
+ }
+
+ /* Now we're on to the time part. We'll require a minimum of hours and
+ minutes. */
+
+ n_scanned = sscanf(ptr, has_separator ? "%2u:%2u%n" : "%2u%2u%n",
+ &tm.tm_hour,
+ &tm.tm_min,
+ &n_chars);
+ if (n_scanned >= 2) {
+ ptr += n_chars;
+ }
+ else {
+ /* didn't get hours and minutes */
+ return NULL;
+ }
+
+ /* Test for (whole) seconds */
+ if ((has_separator && *ptr == ':') ||
+ (!has_separator && g_ascii_isdigit(*ptr))) {
+ /* Looks like we should have them */
+ if (1 > sscanf(ptr, has_separator ? ":%2u%n" : "%2u%n",
+ &tm.tm_sec, &n_chars)) {
+ /* Couldn't get them */
+ return NULL;
+ }
+ ptr += n_chars;
+
+ /* Now let's test for fractional seconds */
+ if (*ptr == '.' || *ptr == ',') {
+ /* Get fractional seconds */
+ ptr++;
+ if (1 <= sscanf(ptr, "%u%n", &frac, &n_chars)) {
+ /* normalize frac to nanoseconds */
+ if ((frac >= 1000000000) || (frac == 0)) {
+ frac = 0;
+ } else {
+ switch (n_chars) { /* including leading zeros */
+ case 1: frac *= 100000000; break;
+ case 2: frac *= 10000000; break;
+ case 3: frac *= 1000000; break;
+ case 4: frac *= 100000; break;
+ case 5: frac *= 10000; break;
+ case 6: frac *= 1000; break;
+ case 7: frac *= 100; break;
+ case 8: frac *= 10; break;
+ default: break;
+ }
+ }
+ ptr += n_chars;
+ }
+ /* If we didn't get frac, it's still its default of 0 */
+ }
+ }
+ else {
+ /* No seconds. ISO 8601 allows decimal fractions of a minute here,
+ * but that's pretty rare in practice. Could be added later if needed.
+ */
+ tm.tm_sec = 0;
+ }
+
+ /* Validate what we got so far. mktime() doesn't care about strange
+ values but we should at least start with something valid */
+ if (!tm_is_valid(&tm)) {
+ return NULL;
+ }
+
+ /* Check for a time zone offset */
+ if (*ptr == '-' || *ptr == '+' || *ptr == 'Z') {
+ /* Just in case somewhere decides to observe a timezone of -00:30 or
+ * some such. */
+ sign = *ptr;
+ /* We have a UTC-relative offset */
+ if (*ptr == 'Z') {
+ off_hr = off_min = 0;
+ have_offset = true;
+ ptr++;
+ }
+ else {
+ off_hr = off_min = 0;
+ n_scanned = sscanf(ptr, "%3d%n", &off_hr, &n_chars);
+ if (n_scanned >= 1) {
+ /* Definitely got hours */
+ have_offset = true;
+ ptr += n_chars;
+ n_scanned = sscanf(ptr, *ptr == ':' ? ":%2d%n" : "%2d%n", &off_min, &n_chars);
+ if (n_scanned >= 1) {
+ /* Got minutes too */
+ ptr += n_chars;
+ }
+ }
+ else {
+ /* Didn't get a valid offset, treat as if there's none at all */
+ have_offset = false;
+ }
+ }
+ }
+ if (have_offset) {
+ nstime->secs = mktime_utc(&tm);
+ if (sign == '+') {
+ nstime->secs -= (off_hr * 3600) + (off_min * 60);
+ } else if (sign == '-') {
+ /* -00:00 is illegal according to ISO 8601, but RFC 3339 allows
+ * it under a convention where -00:00 means "time in UTC is known,
+ * local timezone is unknown." This has the same value as an
+ * offset of Z or +00:00, but semantically implies that UTC is
+ * not the preferred time zone, which is immaterial to us.
+ */
+ /* Add the time, but reverse the sign of off_hr, which includes
+ * the negative sign.
+ */
+ nstime->secs += ((-off_hr) * 3600) + (off_min * 60);
+ }
+ }
+ else {
+ /* No UTC offset given; ISO 8601 says this means local time */
+ nstime->secs = mktime(&tm);
+ }
+ nstime->nsecs = frac;
+ return ptr;
+}
+
+/*
+ * function: unix_epoch_to_nstime
+ * parses a character string for a date and time given in
+ * a floating point number containing a Unix epoch date-time
+ * format (e.g. 1600000000.000 for Sun Sep 13 05:26:40 AM PDT 2020)
+ * and converts to an nstime_t
+ * returns pointer to the first character after the last character
+ * parsed on success, or NULL on failure
+ *
+ * Reference: https://en.wikipedia.org/wiki/Unix_time
+ */
+const char *
+unix_epoch_to_nstime(nstime_t *nstime, const char *ptr)
+{
+ int64_t secs;
+ const char *ptr_new;
+
+ int n_chars = 0;
+ unsigned frac = 0;
+
+ nstime_set_unset(nstime);
+
+ /*
+ * Extract the seconds as a 64-bit signed number, as time_t
+ * might be 64-bit.
+ */
+ if (!ws_strtoi64(ptr, &ptr_new, &secs)) {
+ return NULL;
+ }
+
+ /* For now, reject times before the Epoch. */
+ if (secs < 0) {
+ return NULL;
+ }
+
+ /* Make sure it fits. */
+ nstime->secs = (time_t) secs;
+ if (nstime->secs != secs) {
+ return NULL;
+ }
+
+ /* Now let's test for fractional seconds */
+ if (*ptr_new == '.' || *ptr_new == ',') {
+ /* Get fractional seconds */
+ ptr_new++;
+ if (1 <= sscanf(ptr_new, "%u%n", &frac, &n_chars)) {
+ /* normalize frac to nanoseconds */
+ if ((frac >= 1000000000) || (frac == 0)) {
+ frac = 0;
+ } else {
+ switch (n_chars) { /* including leading zeros */
+ case 1: frac *= 100000000; break;
+ case 2: frac *= 10000000; break;
+ case 3: frac *= 1000000; break;
+ case 4: frac *= 100000; break;
+ case 5: frac *= 10000; break;
+ case 6: frac *= 1000; break;
+ case 7: frac *= 100; break;
+ case 8: frac *= 10; break;
+ default: break;
+ }
+ }
+ ptr_new += n_chars;
+ }
+ /* If we didn't get frac, it's still its default of 0 */
+ }
+ else {
+ frac = 0;
+ }
+ nstime->nsecs = frac;
+ return ptr_new;
+}
+
+size_t nstime_to_iso8601(char *buf, size_t buf_size, const nstime_t *nstime)
+{
+ struct tm *tm;
+#ifndef _WIN32
+ struct tm tm_time;
+#endif
+ size_t len;
+
+#ifdef _WIN32
+ /*
+ * Do not use gmtime_s(), as it will call and
+ * exception handler if the time we're providing
+ * is < 0, and that will, by default, exit.
+ * ("Programmers not bothering to check return
+ * values? Try new Microsoft Visual Studio,
+ * with Parameter Validation(R)! Kill insufficiently
+ * careful programs - *and* the processes running them -
+ * fast!")
+ *
+ * We just want to report this as an unrepresentable
+ * time. It fills in a per-thread structure, which
+ * is sufficiently thread-safe for our purposes.
+ */
+ tm = gmtime(&nstime->secs);
+#else
+ /*
+ * Use gmtime_r(), because the Single UNIX Specification
+ * does *not* guarantee that gmtime() is thread-safe.
+ * Perhaps it is on all platforms on which we run, but
+ * this way we don't have to check.
+ */
+ tm = gmtime_r(&nstime->secs, &tm_time);
+#endif
+ if (tm == NULL) {
+ return 0;
+ }
+
+ /* Some platforms (MinGW-w64) do not support %F or %T. */
+ /* Returns number of bytes, excluding terminaning null, placed in
+ * buf, or zero if there is not enough space for the whole string. */
+ len = strftime(buf, buf_size, "%Y-%m-%dT%H:%M:%S", tm);
+ if (len == 0) {
+ return 0;
+ }
+ ws_assert(len < buf_size);
+ buf += len;
+ buf_size -= len;
+ len += snprintf(buf, buf_size, ".%09dZ", nstime->nsecs);
+ return len;
+}
+
+void nstime_to_unix(char *buf, size_t buf_size, const nstime_t *nstime)
+{
+ display_signed_time(buf, buf_size, nstime, WS_TSPREC_NSEC);
+}
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wsutil/nstime.h b/wsutil/nstime.h
new file mode 100644
index 00000000..acc78b53
--- /dev/null
+++ b/wsutil/nstime.h
@@ -0,0 +1,186 @@
+/* nstime.h
+ * Definition of data structure to hold time values with nanosecond resolution
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __NSTIME_H__
+#define __NSTIME_H__
+
+#include <wireshark.h>
+#include <time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/** @file
+ * Definition of data structure to hold time values with nanosecond resolution
+ */
+
+/** data structure to hold time values with nanosecond resolution*/
+typedef struct {
+ time_t secs;
+ int nsecs;
+} nstime_t;
+
+/* Macros that expand to nstime_t initializers */
+
+/* Initialize to zero */
+#define NSTIME_INIT_ZERO {0, 0}
+
+/* Initialize to unset */
+#define NSTIME_INIT_UNSET {0, INT_MAX}
+
+/* Initialize to a specified number of seconds and nanoseconds */
+#define NSTIME_INIT_SECS_NSECS(secs, nsecs) {secs, nsecs}
+
+/* Initialize to a specified number of seconds and microseconds */
+#define NSTIME_INIT_SECS_USECS(secs, usecs) {secs, usecs*1000}
+
+/* Initialize to a specified number of seconds and milliseconds */
+#define NSTIME_INIT_SECS_MSECS(secs, msecs) {secs, msecs*1000000}
+
+/* Initialize to a specified number of seconds */
+#define NSTIME_INIT_SECS(secs) {secs, 0}
+
+/* Initialize to the maximum possible value */
+#define NSTIME_INIT_MAX {sizeof(time_t) > sizeof(int) ? LONG_MAX : INT_MAX, INT_MAX}
+
+/* functions */
+
+/** set the given nstime_t to zero */
+WS_DLL_PUBLIC void nstime_set_zero(nstime_t *nstime);
+
+/** is the given nstime_t currently zero? */
+WS_DLL_PUBLIC bool nstime_is_zero(const nstime_t *nstime);
+
+/** set the given nstime_t to (0,maxint) to mark it as "unset"
+ * That way we can find the first frame even when a timestamp
+ * is zero (fix for bug 1056)
+ */
+WS_DLL_PUBLIC void nstime_set_unset(nstime_t *nstime);
+
+/* is the given nstime_t currently (0,maxint)? */
+WS_DLL_PUBLIC bool nstime_is_unset(const nstime_t *nstime);
+
+/** duplicate the current time
+ *
+ * a = b
+ */
+WS_DLL_PUBLIC void nstime_copy(nstime_t *a, const nstime_t *b);
+
+/** calculate the delta between two times (can be negative!)
+ *
+ * delta = b-a
+ *
+ * Note that it is acceptable for two or more of the arguments to point at the
+ * same structure.
+ */
+WS_DLL_PUBLIC void nstime_delta(nstime_t *delta, const nstime_t *b, const nstime_t *a );
+
+/** calculate the sum of two times
+ *
+ * sum = a+b
+ *
+ * Note that it is acceptable for two or more of the arguments to point at the
+ * same structure.
+ */
+WS_DLL_PUBLIC void nstime_sum(nstime_t *sum, const nstime_t *a, const nstime_t *b );
+
+/** sum += a */
+#define nstime_add(sum, a) nstime_sum(sum, sum, a)
+
+/** sum -= a */
+#define nstime_subtract(sum, a) nstime_delta(sum, sum, a)
+
+/** compare two times are return a value similar to memcmp() or strcmp().
+ *
+ * a > b : > 0
+ * a = b : 0
+ * a < b : < 0
+ */
+WS_DLL_PUBLIC int nstime_cmp (const nstime_t *a, const nstime_t *b );
+
+WS_DLL_PUBLIC unsigned nstime_hash(const nstime_t *nstime);
+
+/** converts nstime to double, time base is milli seconds */
+WS_DLL_PUBLIC double nstime_to_msec(const nstime_t *nstime);
+
+/** converts nstime to double, time base is seconds */
+WS_DLL_PUBLIC double nstime_to_sec(const nstime_t *nstime);
+
+/** converts Windows FILETIME to nstime, returns true on success,
+ false on failure */
+WS_DLL_PUBLIC bool filetime_to_nstime(nstime_t *nstime, uint64_t filetime);
+
+/** converts time like Windows FILETIME, but expressed in nanoseconds
+ rather than tenths of microseconds, to nstime, returns true on success,
+ false on failure */
+WS_DLL_PUBLIC bool nsfiletime_to_nstime(nstime_t *nstime, uint64_t nsfiletime);
+
+typedef enum {
+ ISO8601_DATETIME, /** e.g. 2014-07-04T12:34:56.789+00:00 */
+ ISO8601_DATETIME_BASIC, /** ISO8601 Basic format, i.e. no - : separators */
+ ISO8601_DATETIME_AUTO, /** Autodetect the presence of separators */
+} iso8601_fmt_e;
+
+/** parse an ISO 8601 format datetime string to nstime, returns pointer
+ to the first character after the last character, NULL on failure
+ Note that nstime is set to unset in the case of failure */
+WS_DLL_PUBLIC const char * iso8601_to_nstime(nstime_t *nstime, const char *ptr, iso8601_fmt_e format);
+
+/** parse an Unix epoch timestamp format datetime string to nstime, returns
+ pointer to the first character after the last character, NULL on failure
+ Note that nstime is set to unset in the case of failure */
+WS_DLL_PUBLIC const char * unix_epoch_to_nstime(nstime_t *nstime, const char *ptr);
+
+#define NSTIME_ISO8601_BUFSIZE sizeof("YYYY-MM-DDTHH:MM:SS.123456789Z")
+
+WS_DLL_PUBLIC size_t nstime_to_iso8601(char *buf, size_t buf_size, const nstime_t *nstime);
+
+/* 64 bit signed number plus nanosecond fractional part */
+#define NSTIME_UNIX_BUFSIZE (20+10+1)
+
+WS_DLL_PUBLIC void nstime_to_unix(char *buf, size_t buf_size, const nstime_t *nstime);
+
+/*
+ * Timestamp precision values.
+ *
+ * The value is the number of digits of precision after the integral part.
+ */
+typedef enum {
+ WS_TSPREC_SEC = 0,
+ WS_TSPREC_100_MSEC = 1,
+ WS_TSPREC_10_MSEC = 2,
+ WS_TSPREC_MSEC = 3,
+ WS_TSPREC_100_USEC = 4,
+ WS_TSPREC_10_USEC = 5,
+ WS_TSPREC_USEC = 6,
+ WS_TSPREC_100_NSEC = 7,
+ WS_TSPREC_10_NSEC = 8,
+ WS_TSPREC_NSEC = 9
+} ws_tsprec_e;
+
+/*
+ * Maximum time stamp precision supported.
+ * Note that going beyond nanosecond precision would require expanding
+ * the fractional part of an nstime_t to 64 bits, and changing code
+ * that currently only handles second to nanosecond precision.
+ */
+#define WS_TSPREC_MAX 9
+
+/*
+ * Total number of valid precision values.
+ */
+#define NUM_WS_TSPREC_VALS (WS_TSPREC_MAX + 1)
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __NSTIME_H__ */
diff --git a/wsutil/os_version_info.c b/wsutil/os_version_info.c
new file mode 100644
index 00000000..69a80c8f
--- /dev/null
+++ b/wsutil/os_version_info.c
@@ -0,0 +1,806 @@
+/* os_version_info.c
+ * Routines to report operating system version information
+ *
+ * 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 <wsutil/os_version_info.h>
+
+#include <string.h>
+#include <errno.h>
+
+#ifdef HAVE_SYS_UTSNAME_H
+#include <sys/utsname.h>
+#endif
+
+#ifdef HAVE_MACOS_FRAMEWORKS
+#include <CoreFoundation/CoreFoundation.h>
+#include <wsutil/cfutils.h>
+#endif
+
+#include <wsutil/unicode-utils.h>
+
+/*
+ * Handles the rather elaborate process of getting OS version information
+ * from macOS (we want the macOS version, not the Darwin version, the latter
+ * being easy to get with uname()).
+ */
+#ifdef HAVE_MACOS_FRAMEWORKS
+
+/*
+ * Fetch a string, as a UTF-8 C string, from a dictionary, given a key.
+ */
+static char *
+get_string_from_dictionary(CFPropertyListRef dict, CFStringRef key)
+{
+ CFStringRef cfstring;
+
+ cfstring = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)dict,
+ (const void *)key);
+ if (cfstring == NULL)
+ return NULL;
+ if (CFGetTypeID(cfstring) != CFStringGetTypeID()) {
+ /* It isn't a string. Punt. */
+ return NULL;
+ }
+ return CFString_to_C_string(cfstring);
+}
+
+/*
+ * Get the macOS version information, and append it to the GString.
+ * Return true if we succeed, false if we fail.
+ *
+ * XXX - this gives the OS name as "Mac OS X" even if Apple called/calls
+ * it "OS X" or "macOS".
+ */
+static bool
+get_macos_version_info(GString *str)
+{
+ static const UInt8 server_version_plist_path[] =
+ "/System/Library/CoreServices/ServerVersion.plist";
+ static const UInt8 system_version_plist_path[] =
+ "/System/Library/CoreServices/SystemVersion.plist";
+ CFURLRef version_plist_file_url;
+ CFReadStreamRef version_plist_stream;
+ CFDictionaryRef version_dict;
+ char *string;
+
+ /*
+ * On macOS, report the macOS version number as the OS, and put
+ * the Darwin information in parentheses.
+ *
+ * Alas, Gestalt() is deprecated in Mountain Lion, so the build
+ * fails if you treat deprecation warnings as fatal. I don't
+ * know of any replacement API, so we fall back on reading
+ * /System/Library/CoreServices/ServerVersion.plist if it
+ * exists, otherwise /System/Library/CoreServices/SystemVersion.plist,
+ * and using ProductUserVisibleVersion. We also get the build
+ * version from ProductBuildVersion and the product name from
+ * ProductName.
+ */
+ version_plist_file_url = CFURLCreateFromFileSystemRepresentation(NULL,
+ server_version_plist_path, sizeof server_version_plist_path - 1,
+ false);
+ if (version_plist_file_url == NULL)
+ return false;
+ version_plist_stream = CFReadStreamCreateWithFile(NULL,
+ version_plist_file_url);
+ CFRelease(version_plist_file_url);
+ if (version_plist_stream == NULL)
+ return false;
+ if (!CFReadStreamOpen(version_plist_stream)) {
+ CFRelease(version_plist_stream);
+
+ /*
+ * Try SystemVersion.plist.
+ */
+ version_plist_file_url = CFURLCreateFromFileSystemRepresentation(NULL,
+ system_version_plist_path, sizeof system_version_plist_path - 1,
+ false);
+ if (version_plist_file_url == NULL)
+ return false;
+ version_plist_stream = CFReadStreamCreateWithFile(NULL,
+ version_plist_file_url);
+ CFRelease(version_plist_file_url);
+ if (version_plist_stream == NULL)
+ return false;
+ if (!CFReadStreamOpen(version_plist_stream)) {
+ CFRelease(version_plist_stream);
+ return false;
+ }
+ }
+#ifdef HAVE_CFPROPERTYLISTCREATEWITHSTREAM
+ version_dict = (CFDictionaryRef)CFPropertyListCreateWithStream(NULL,
+ version_plist_stream, 0, kCFPropertyListImmutable,
+ NULL, NULL);
+#else
+ version_dict = (CFDictionaryRef)CFPropertyListCreateFromStream(NULL,
+ version_plist_stream, 0, kCFPropertyListImmutable,
+ NULL, NULL);
+#endif
+ if (version_dict == NULL) {
+ CFRelease(version_plist_stream);
+ return false;
+ }
+ if (CFGetTypeID(version_dict) != CFDictionaryGetTypeID()) {
+ /* This is *supposed* to be a dictionary. Punt. */
+ CFRelease(version_dict);
+ CFReadStreamClose(version_plist_stream);
+ CFRelease(version_plist_stream);
+ return false;
+ }
+ /* Get the product name string. */
+ string = get_string_from_dictionary(version_dict,
+ CFSTR("ProductName"));
+ if (string == NULL) {
+ CFRelease(version_dict);
+ CFReadStreamClose(version_plist_stream);
+ CFRelease(version_plist_stream);
+ return false;
+ }
+ g_string_append_printf(str, "%s", string);
+ g_free(string);
+
+ /* Get the OS version string. */
+ string = get_string_from_dictionary(version_dict,
+ CFSTR("ProductUserVisibleVersion"));
+ if (string == NULL) {
+ CFRelease(version_dict);
+ CFReadStreamClose(version_plist_stream);
+ CFRelease(version_plist_stream);
+ return false;
+ }
+ g_string_append_printf(str, " %s", string);
+ g_free(string);
+
+ /* Get the build string */
+ string = get_string_from_dictionary(version_dict,
+ CFSTR("ProductBuildVersion"));
+ if (string == NULL) {
+ CFRelease(version_dict);
+ CFReadStreamClose(version_plist_stream);
+ CFRelease(version_plist_stream);
+ return false;
+ }
+ g_string_append_printf(str, ", build %s", string);
+ g_free(string);
+ CFRelease(version_dict);
+ CFReadStreamClose(version_plist_stream);
+ CFRelease(version_plist_stream);
+ return true;
+}
+#endif
+
+#ifdef _WIN32
+typedef LONG (WINAPI * RtlGetVersionProc) (OSVERSIONINFOEX *);
+#ifndef STATUS_SUCCESS
+#define STATUS_SUCCESS 0
+#endif
+#include <stdlib.h>
+
+/*
+ * Determine whether it's 32-bit or 64-bit Windows based on the
+ * instruction set; this only tests for the instruction sets
+ * that we currently support for Windows, it doesn't bother with MIPS,
+ * PowerPC, Alpha, or IA-64, nor does it bother wieth 32-bit ARM.
+ */
+static void
+add_os_bitsize(GString *str, SYSTEM_INFO *system_info)
+{
+ switch (system_info->wProcessorArchitecture) {
+ case PROCESSOR_ARCHITECTURE_AMD64:
+#ifdef PROCESSOR_ARCHITECTURE_ARM64
+ case PROCESSOR_ARCHITECTURE_ARM64:
+#endif
+ g_string_append(str, "64-bit ");
+ break;
+ case PROCESSOR_ARCHITECTURE_INTEL:
+ g_string_append(str, "32-bit ");
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Test whether the OS an "NT Workstation" version, meaning "not server".
+ */
+static bool
+is_nt_workstation(OSVERSIONINFOEX *win_version_info)
+{
+ return win_version_info->wProductType == VER_NT_WORKSTATION;
+}
+#endif // _WIN32
+
+/*
+ * Get the OS version, and append it to the GString
+ */
+void
+get_os_version_info(GString *str)
+{
+#if defined(_WIN32)
+
+ OSVERSIONINFOEX win_version_info = {0};
+ RtlGetVersionProc RtlGetVersionP = 0;
+ LONG version_status = STATUS_ENTRYPOINT_NOT_FOUND; // Any nonzero value should work.
+
+ /*
+ * We want the major and minor Windows version along with other
+ * information. GetVersionEx provides this, but is deprecated.
+ * We use RtlGetVersion instead, which requires a bit of extra
+ * effort.
+ */
+
+ HMODULE ntdll_module = LoadLibrary(_T("ntdll.dll"));
+ if (ntdll_module) {
+DIAG_OFF(cast-function-type)
+ RtlGetVersionP = (RtlGetVersionProc) GetProcAddress(ntdll_module, "RtlGetVersion");
+DIAG_ON(cast-function-type)
+ }
+
+ if (RtlGetVersionP) {
+ win_version_info.dwOSVersionInfoSize = sizeof(win_version_info);
+ version_status = RtlGetVersionP(&win_version_info);
+ }
+
+ if (ntdll_module) {
+ FreeLibrary(ntdll_module);
+ }
+
+ if (version_status != STATUS_SUCCESS) {
+ /*
+ * XXX - get the failure reason.
+ */
+ g_string_append(str, "unknown Windows version");
+ return;
+ }
+
+ SYSTEM_INFO system_info;
+ memset(&system_info, '\0', sizeof system_info);
+ /*
+ * Look for and use the GetNativeSystemInfo() function to get the
+ * correct processor architecture even when running 32-bit Wireshark
+ * in WOW64 (x86 emulation on 64-bit Windows).
+ *
+ * However, the documentation for GetNativeSystemInfo() says
+ *
+ * If the function is called from an x86 or x64 application
+ * running on a 64-bit system that does not have an Intel64
+ * or x64 processor (such as ARM64), it will return information
+ * as if the system is x86 only if x86 emulation is supported
+ * (or x64 if x64 emulation is also supported).
+ *
+ * so it appears that it will *not* return the correct processor
+ * architecture if running x86-64 Wireshark on ARM64 with
+ * x86-64 emulation - it will presumably say "x86-64", not "ARM64".
+ *
+ * So we use it to say "32-bit" or "64-bit", but we don't use
+ * it to say "N-bit x86" or "N-bit ARM".
+ *
+ * It Would Be Nice if there were some way to report that
+ * Wireshark is running in emulation on an ARM64 system;
+ * that might be important if, for example, a user is
+ * reporting a capture problem, as there currently isn't
+ * a version of Npcap that can support x86-64 programs on
+ * an ARM64 system.
+ */
+ GetNativeSystemInfo(&system_info);
+
+ switch (win_version_info.dwPlatformId) {
+
+ case VER_PLATFORM_WIN32s:
+ /* Shyeah, right. */
+ g_string_append_printf(str, "Windows 3.1 with Win32s");
+ break;
+
+ case VER_PLATFORM_WIN32_WINDOWS:
+ /*
+ * Windows OT.
+ *
+ * https://nsis-dev.github.io/NSIS-Forums/html/t-128527.html
+ *
+ * claims that
+ *
+ * HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion
+ *
+ * has a key ProductName, at least in Windows M3, the
+ * value of that key appears to be an OS product name.
+ */
+ switch (win_version_info.dwMajorVersion) {
+
+ case 4:
+ /* 3 cheers for Microsoft marketing! */
+ switch (win_version_info.dwMinorVersion) {
+
+ case 0:
+ g_string_append_printf(str, "Windows 95");
+ break;
+
+ case 10:
+ g_string_append_printf(str, "Windows 98");
+ break;
+
+ case 90:
+ g_string_append_printf(str, "Windows Me");
+ break;
+
+ default:
+ g_string_append_printf(str, "Windows OT, unknown version %lu.%lu",
+ win_version_info.dwMajorVersion, win_version_info.dwMinorVersion);
+ break;
+ }
+ break;
+
+ default:
+ g_string_append_printf(str, "Windows OT, unknown version %lu.%lu",
+ win_version_info.dwMajorVersion, win_version_info.dwMinorVersion);
+ break;
+ }
+ break;
+
+ case VER_PLATFORM_WIN32_NT:
+ /*
+ * Windows NT.
+ *
+ * https://stackoverflow.com/a/19778234/16139739
+ *
+ * claims that
+ *
+ * HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion
+ *
+ * has a key ProductName that is "present for Windows XP
+ * and aboeve[sic]". The value of that key gives a
+ * "product name"...
+ *
+ * ...at least until Windows 11, which it insists is
+ * Windows 10. So we don't bother with it. (It may
+ * indicate whether it's Home or Pro or..., but that's
+ * not worth the effort of fixing the "Windows 11 is
+ * Windows 10" nonsense.)
+ *
+ * https://patents.google.com/patent/EP1517235A2/en
+ *
+ * is a Microsoft patent that mentions the
+ * BrandingFormatString() routine, and seems to suggest
+ * that it dates back to at least Windows XP.
+ *
+ * https://dennisbabkin.com/blog/?t=how-to-tell-the-real-version-of-windows-your-app-is-running-on
+ *
+ * says that routine is in an undocumented winbrand.dll DLL,
+ * but is used by Microsoft's own code to put the OS
+ * product name into messages. It, unlike ProductName,
+ * appears to make a distinction between Windows 10 and
+ * Windows 11, and, when handed the string "%WINDOWS_LONG%",
+ * gives the same edition decoration that I suspect
+ * ProductName does.
+ */
+ switch (win_version_info.dwMajorVersion) {
+
+ case 3:
+ case 4:
+ /* NT 3.x and 4.x. */
+ g_string_append_printf(str, "Windows NT %lu.%lu",
+ win_version_info.dwMajorVersion, win_version_info.dwMinorVersion);
+ break;
+
+ case 5:
+ /*
+ * W2K, WXP, and their server versions.
+ * 3 cheers for Microsoft marketing!
+ */
+ switch (win_version_info.dwMinorVersion) {
+
+ case 0:
+ g_string_append_printf(str, "Windows 2000");
+ break;
+
+ case 1:
+ g_string_append_printf(str, "Windows XP");
+ break;
+
+ case 2:
+ if (is_nt_workstation(&win_version_info) &&
+ (system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)) {
+ g_string_append_printf(str, "Windows XP Professional x64 Edition");
+ } else {
+ g_string_append_printf(str, "Windows Server 2003");
+ if (system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
+ g_string_append_printf(str, " x64 Edition");
+ }
+ break;
+
+ default:
+ g_string_append_printf(str, "Windows NT, unknown version %lu.%lu",
+ win_version_info.dwMajorVersion, win_version_info.dwMinorVersion);
+ break;
+ }
+ break;
+
+ case 6: {
+ /*
+ * Vista, W7, W8, W8.1, and their server versions.
+ */
+ add_os_bitsize(str, &system_info);
+ switch (win_version_info.dwMinorVersion) {
+ case 0:
+ g_string_append_printf(str, is_nt_workstation(&win_version_info) ? "Windows Vista" : "Windows Server 2008");
+ break;
+ case 1:
+ g_string_append_printf(str, is_nt_workstation(&win_version_info) ? "Windows 7" : "Windows Server 2008 R2");
+ break;
+ case 2:
+ g_string_append_printf(str, is_nt_workstation(&win_version_info) ? "Windows 8" : "Windows Server 2012");
+ break;
+ case 3:
+ g_string_append_printf(str, is_nt_workstation(&win_version_info) ? "Windows 8.1" : "Windows Server 2012 R2");
+ break;
+ default:
+ g_string_append_printf(str, "Windows NT, unknown version %lu.%lu",
+ win_version_info.dwMajorVersion, win_version_info.dwMinorVersion);
+ break;
+ }
+ break;
+ } /* case 6 */
+
+ case 10: {
+ /*
+ * W10, W11, and their server versions.
+ */
+ TCHAR ReleaseId[10];
+ DWORD ridSize = _countof(ReleaseId);
+
+ add_os_bitsize(str, &system_info);
+ switch (win_version_info.dwMinorVersion) {
+ case 0:
+ /* List of BuildNumber from https://en.wikipedia.org/wiki/List_of_Microsoft_Windows_versions
+ * and https://docs.microsoft.com/en-us/windows/release-health/windows11-release-information */
+ if (is_nt_workstation(&win_version_info)) {
+ if (win_version_info.dwBuildNumber < 10240) {
+ /* XXX - W10 builds before 10240? */
+ g_string_append_printf(str, "Windows");
+ } else if (win_version_info.dwBuildNumber < 22000){
+ /* W10 builds sstart at 10240 and end before 22000 */
+ g_string_append_printf(str, "Windows 10");
+ } else {
+ /* Builds 22000 and later are W11 (until there's W12...). */
+ g_string_append_printf(str, "Windows 11");
+ }
+ } else {
+ switch (win_version_info.dwBuildNumber) {
+ case 14393:
+ g_string_append_printf(str, "Windows Server 2016");
+ break;
+ case 17763:
+ g_string_append_printf(str, "Windows Server 2019");
+ break;
+ case 20348:
+ g_string_append_printf(str, "Windows Server 2022");
+ break;
+ default:
+ g_string_append_printf(str, "Windows Server");
+ break;
+ }
+ }
+
+ /*
+ * Windows 10 and 11 have had multiple
+ * releases, with different build numbers.
+ *
+ * The build number *could* be used to
+ * determine the release string, but
+ * that would require a table of releases
+ * and strings, and that would have to
+ * get updated whenever a new release
+ * comes out, and that seems to happen
+ * twice a year these days.
+ *
+ * The good news is that, under
+ *
+ * HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion
+ *
+ * there are two keys, DisplayVersion and
+ * ReleaseId. If DisplayVersion is present,
+ * it's a string that gives the release
+ * string; if not, ReleaseId gives the
+ * release string.
+ *
+ * The DisplayVersion value is currently
+ * of the form YYHN, where YY is the
+ * last two digits of the year, H stands
+ * for "half", and N is the half of the
+ * year in which it came out.
+ *
+ * The ReleaseId is just a numeric string
+ * and for all the YYHN releases, it's
+ * stuck at the same value.
+ *
+ * Note further that
+ *
+ * https://github.com/nvaccess/nvda/blob/master/source/winVersion.py
+ *
+ * has a comment claiming that
+ *
+ * From Version 1511 (build 10586), release
+ * Id/display version comes from Windows
+ * Registry.
+ * However there are builds with no release
+ * name (Version 1507/10240) or releases
+ * with different builds.
+ * Look these up first before asking
+ * Windows Registry.
+ *
+ * "Look these up first" means "look them
+ * up in a table that goes from
+ *
+ * 10240: Windows 10 1507
+ *
+ * to
+ *
+ * 22621: Windows 11 22H2
+ *
+ * and also includes
+ *
+ * 20348: Windows Server 2022
+ *
+ * I'm not sure why any Windows 10 builds
+ * after 10240 are in the table; what does
+ * "releases with different builds" mean?
+ * does it mean that those particular
+ * builds have bogus ReleaseId or
+ * DisplayVersion values? Those builds
+ * appear to be official release builds
+ * for W10/W11, according to the table
+ * in
+ *
+ * https://en.wikipedia.org/wiki/Windows_NT
+ *
+ * so, if those are all necessary, why
+ * should ReleaseId or DisplayVersion be
+ * trusted at all?
+ *
+ * As for the Windows Server 2022 entry,
+ * is that just becuase that script doesn't
+ * bother checking for "workstation" vs.
+ * "server"?
+ */
+ if (RegGetValue(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+ L"DisplayVersion", RRF_RT_REG_SZ, NULL, &ReleaseId, &ridSize) == ERROR_SUCCESS) {
+ g_string_append_printf(str, " (%s)", utf_16to8(ReleaseId));
+ }
+ else if (RegGetValue(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+ L"ReleaseId", RRF_RT_REG_SZ, NULL, &ReleaseId, &ridSize) == ERROR_SUCCESS) {
+ g_string_append_printf(str, " (%s)", utf_16to8(ReleaseId));
+ }
+ break;
+ default:
+ g_string_append_printf(str, "Windows NT, unknown version %lu.%lu",
+ win_version_info.dwMajorVersion, win_version_info.dwMinorVersion);
+ break;
+ }
+ break;
+ } /* case 10 */
+
+ default:
+ g_string_append_printf(str, "Windows NT, unknown version %lu.%lu",
+ win_version_info.dwMajorVersion, win_version_info.dwMinorVersion);
+ break;
+ } /* info.dwMajorVersion */
+ break;
+
+ default:
+ g_string_append_printf(str, "Unknown Windows platform %lu version %lu.%lu",
+ win_version_info.dwPlatformId, win_version_info.dwMajorVersion, win_version_info.dwMinorVersion);
+ break;
+ }
+ if (win_version_info.szCSDVersion[0] != '\0')
+ g_string_append_printf(str, " %s", utf_16to8(win_version_info.szCSDVersion));
+ g_string_append_printf(str, ", build %lu", win_version_info.dwBuildNumber);
+#elif defined(HAVE_SYS_UTSNAME_H)
+ struct utsname name;
+ /*
+ * We have <sys/utsname.h>, so we assume we have "uname()".
+ */
+ if (uname(&name) < 0) {
+ g_string_append_printf(str, "unknown OS version (uname failed - %s)",
+ g_strerror(errno));
+ return;
+ }
+
+ if (strcmp(name.sysname, "AIX") == 0) {
+ /*
+ * Yay, IBM! Thanks for doing something different
+ * from most of the other UNIXes out there, and
+ * making "name.version" apparently be the major
+ * version number and "name.release" be the minor
+ * version number.
+ */
+ g_string_append_printf(str, "%s %s.%s", name.sysname, name.version,
+ name.release);
+ } else {
+ /*
+ * XXX - get "version" on any other platforms?
+ *
+ * On Digital/Tru64 UNIX, it's something unknown.
+ * On Solaris, it's some kind of build information.
+ * On HP-UX, it appears to be some sort of subrevision
+ * thing.
+ * On *BSD and Darwin/macOS, it's a long string giving
+ * a build date, config file name, etc., etc., etc..
+ */
+#ifdef HAVE_MACOS_FRAMEWORKS
+ /*
+ * On macOS, report the macOS version number as the OS
+ * version if we can, and put the Darwin information
+ * in parentheses.
+ */
+ if (get_macos_version_info(str)) {
+ /* Success - append the Darwin information. */
+ g_string_append_printf(str, " (%s %s)", name.sysname, name.release);
+ } else {
+ /* Failure - just use the Darwin information. */
+ g_string_append_printf(str, "%s %s", name.sysname, name.release);
+ }
+#else /* HAVE_MACOS_FRAMEWORKS */
+ /*
+ * XXX - on Linux, are there any APIs to get the distribution
+ * name and version number? I think some distributions have
+ * that.
+ *
+ * At least on Linux Standard Base-compliant distributions,
+ * there's an "lsb_release" command. However:
+ *
+ * http://forums.fedoraforum.org/showthread.php?t=220885
+ *
+ * seems to suggest that if you don't have the redhat-lsb
+ * package installed, you don't have lsb_release, and that
+ * /etc/fedora-release has the release information on
+ * Fedora.
+ *
+ * http://linux.die.net/man/1/lsb_release
+ *
+ * suggests that there's an /etc/distrib-release file, but
+ * it doesn't indicate whether "distrib" is literally
+ * "distrib" or is the name for the distribution, and
+ * also speaks of an /etc/debian_version file.
+ *
+ * "lsb_release" apparently parses /etc/lsb-release, which
+ * has shell-style assignments, assigning to, among other
+ * values, DISTRIB_ID (distributor/distribution name),
+ * DISTRIB_RELEASE (release number of the distribution),
+ * DISTRIB_DESCRIPTION (*might* be name followed by version,
+ * but the manpage for lsb_release seems to indicate that's
+ * not guaranteed), and DISTRIB_CODENAME (code name, e.g.
+ * "licentious" for the Ubuntu Licentious Lemur release).
+ * the lsb_release man page also speaks of the distrib-release
+ * file, but Debian doesn't have one, and Ubuntu 7's
+ * lsb_release command doesn't look for one.
+ *
+ * I've seen references to /etc/redhat-release as well.
+ *
+ * See also
+ *
+ * http://bugs.python.org/issue1322
+ *
+ * http://www.novell.com/coolsolutions/feature/11251.html
+ *
+ * http://linuxmafia.com/faq/Admin/release-files.html
+ *
+ * and the Lib/Platform.py file in recent Python 2.x
+ * releases.
+ *
+ * And then there's /etc/os-release:
+ *
+ * https://0pointer.de/blog/projects/os-release
+ *
+ * which, apparently, is something that all distributions
+ * with systemd have, which seems to mean "most distributions"
+ * these days. It also has a list of several of the assorted
+ * *other* such files that various distributions have.
+ *
+ * Maybe look at what pre-version-43 systemd does? 43
+ * removed support for the old files, but I guess that
+ * means older versions *did* support them:
+ *
+ * https://lists.freedesktop.org/archives/systemd-devel/2012-February/004475.html
+ *
+ * At least on my Ubuntu 7 system, /etc/debian_version
+ * doesn't contain anything interesting (just some Debian
+ * codenames). It does have /etc/lsb-release. My Ubuntu
+ * 22.04 system has /etc/lsb-release and /etc/os-release.
+ *
+ * My Fedora 9 system has /etc/fedora-release, with
+ * /etc/redhat-release and /etc/system-release as symlinks
+ * to it. They all just contain a one-line relase
+ * description. My Fedora 38 system has that, plus
+ * /etc/os-release.
+ *
+ * A quick Debian 3.1a installation I did has only
+ * /etc/debian_version. My Debian 11.3 system has
+ * /etc/os-release.
+ *
+ * See
+ *
+ * https://gist.github.com/natefoo/814c5bf936922dad97ff
+ *
+ * for descriptions of what some versions of some
+ * distributions offer.
+ *
+ * So maybe have a table of files to try, with each
+ * entry having a pathname, a pointer to a file parser
+ * routine, and a pointer to a string giving a
+ * parameter name passed to that routine, with entries
+ * for:
+ *
+ * /etc/os-release, regular parser, "PRETTY_NAME"
+ * /etc/lsb-release, regular parser, "DISTRIB_DESCRIPTION"
+ * /etc/system-release, first line parser, NULL
+ * /etc/redhat-release, first line parser, NULL
+ * /etc/fedora-release, first line parser, NULL
+ * /etc/centos-release, first line parser, NULL
+ * /etc/debian_version, first line parser, "Debian"
+ * /etc/SuSE-release, first line parser, NULL
+ * /etc/slackware-version:, first line parser, NULL
+ * /etc/gentoo-release, first line parser, NULL
+ * /etc/antix-version, first line parser, NULL
+ *
+ * Each line is tried in order. If the open fails, go to
+ * the next one. If the open succeeds but the parser
+ * fails, close the file and go on to the next one.
+ *
+ * The regular parser parses files of the form
+ * <param>="value". It's passed the value of <param>
+ * for which to look; if not found, it fails.
+ *
+ * The first line parser reads the first line of the file.
+ * If a string is passed to it, it constructs a distribution
+ * name string by concatenating the parameter, a space,
+ * and the contents of that line (iwth the newline removed),
+ * otherwise it constructs it from the contents of the line.
+ *
+ * Fall back on just "Linux" if nothing works.
+ *
+ * Then use the uname() information to indicate what
+ * kernel version the machine is running.
+ *
+ * XXX - for Gentoo, PRETTY_NAME might not give a version,
+ * so fall back on /etc/gentoo-release? Gentoo is
+ * a rolling-release distribution, so what *is* the
+ * significance of the contnets of /etc/gentoo-release?
+ *
+ * XXX - MX appears to be a Debian-based distribution
+ * whose /etc/os-release gives its Debian version and
+ * whose /etc/mx-version and /etc/antix-version give
+ * the MX version. Are there any other Debian derivatives
+ * that do this? (The Big One calls itself "Ubuntu"
+ * in PRETTY_NAME.)
+ *
+ * XXX - use ID_LIKE in /etc/os-release to check for,
+ * for example, Debian-like distributions, e.g. when
+ * suggesting how to give dumpcap capture privileges?
+ */
+ g_string_append_printf(str, "%s %s", name.sysname, name.release);
+#endif /* HAVE_MACOS_FRAMEWORKS */
+ }
+#else
+ g_string_append(str, "an unknown OS");
+#endif
+}
+
+/*
+ * 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:
+ */
diff --git a/wsutil/os_version_info.h b/wsutil/os_version_info.h
new file mode 100644
index 00000000..f496b303
--- /dev/null
+++ b/wsutil/os_version_info.h
@@ -0,0 +1,29 @@
+/** @file
+ * Declarations of outines to report operating system version information
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WSUTIL_OS_VERSION_INFO_H__
+#define __WSUTIL_OS_VERSION_INFO_H__
+
+#include <wireshark.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * Get the OS version, and append it to a GString.
+ */
+WS_DLL_PUBLIC void get_os_version_info(GString *str);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WSUTIL_OS_VERSION_INFO_H__ */
diff --git a/wsutil/path_config.h.in b/wsutil/path_config.h.in
new file mode 100644
index 00000000..6539a5ab
--- /dev/null
+++ b/wsutil/path_config.h.in
@@ -0,0 +1,10 @@
+#ifndef __PATH_CONFIG_H__
+#define __PATH_CONFIG_H__
+
+#define INSTALL_PREFIX "@PATH_INSTALL_PREFIX@"
+#define DATA_DIR "@PATH_DATA_DIR@"
+#define DOC_DIR "@PATH_DOC_DIR@"
+#define PLUGIN_DIR "@PATH_PLUGIN_DIR@"
+#define EXTCAP_DIR "@PATH_EXTCAP_DIR@"
+
+#endif
diff --git a/wsutil/pint.h b/wsutil/pint.h
new file mode 100644
index 00000000..97b6da23
--- /dev/null
+++ b/wsutil/pint.h
@@ -0,0 +1,385 @@
+/** @file
+ *
+ * Definitions for extracting and translating integers safely and portably
+ * via pointers.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __PINT_H__
+#define __PINT_H__
+
+#include <inttypes.h>
+
+#include <glib.h>
+
+/* Routines that take a possibly-unaligned pointer to a 16-bit, 24-bit,
+ * 32-bit, 40-bit, ... 64-bit integral quantity, in a particular byte
+ * order, and fetch the value and return it in host byte order.
+ *
+ * The pntohN() routines fetch big-endian values; the pletohN() routines
+ * fetch little-endian values.
+ */
+
+/* On most architectures, accesses of 16, 32, and 64 bit quantities can be
+ * heavily optimized. gcc and clang recognize portable versions below and,
+ * at -Os and higher, optimize them appropriately (for gcc, that includes
+ * for z/Architecture, PPC64, MIPS, etc.). Older versions don't do as good
+ * of a job with 16 bit accesses, though.
+ *
+ * Unfortunately, MSVC and icc (both the "classic" version and the new
+ * LLVM-based Intel C Compiler) do not, according to Matt Godbolt's Compiler
+ * Explorer (https://godbolt.org) as of the end of 2022. They *do* recognize
+ * and optimize a memcpy based approach (which avoids unaligned accesses on,
+ * say, ARM32), though that requires byteswapping appropriately.
+ */
+
+#if (defined(_MSC_VER) && !defined(__clang__)) || defined(__INTEL_COMPILER) || defined(__INTEL_LLVM_COMPILER)
+/* MSVC or Intel C Compiler (Classic or new LLVM version), but not
+ * clang-cl on Windows.
+ */
+/* Unfortunately, C23 did not fully accept the N3022 Modern Bit Utilities
+ * proposal, so a standard bytereverse function has been deferred for some
+ * future version:
+ * https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3048.htm
+ * https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3022.htm
+ *
+ * So choose byteswap intrinsics we know we have.
+ */
+#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__INTEL_LLVM_COMPILER) && !defined(__clang__)
+/* Intel and clang-cl both define _MSC_VER when compiling on Windows for
+ * greater compatiblity (just as they define __GNUC__ on other platforms).
+ * However, at least on some versions, while including the MSVC <stdlib.h>
+ * provides access to the _byteswap_ intrinsics, they are not actually
+ * optimized into a single x86 BSWAP function, unlike the gcc-style intrinsics
+ * (which both support.) See: https://stackoverflow.com/q/72327906
+ */
+#include <stdlib.h> // For MSVC _byteswap intrinsics
+#define pint_bswap16(x) _byteswap_ushort(x)
+#define pint_bswap32(x) _byteswap_ulong(x)
+/* Hopefully MSVC never decides that a long is 64 bit. */
+#define pint_bswap64(x) _byteswap_uint64(x)
+#elif defined(__INTEL_COMPILER)
+/* The (deprecated) Intel C++ Compiler Classic has these byteswap intrinsics.
+ * It also has the GCC-style intrinsics, though __builtin_bswap16 wasn't
+ * added until some point after icc 13.0 but at least by 16.0, reflecting
+ * that it wasn't added to gcc until 4.8.
+ */
+#define pint_bswap16(x) _bswap16(x)
+#define pint_bswap32(x) _bswap32(x)
+#define pint_bswap64(x) _bswap64(x)
+#else
+/* GCC-style _bswap intrinsics */
+/* The new LLVM-based Intel C++ Compiler doesn't have the above intrinsics,
+ * but it always has all the GCC intrinsics.
+ */
+/* __builtin_bswap32 and __builtin_bswap64 intrinsics have been supported
+ * for a long time on gcc (4.1), and clang (pre 3.0), versions that predate
+ * C11 and C+11 support, which we require, so we could assume we have them.
+ *
+ * __builtin_bswap16 was added a bit later, gcc 4.8, and clang 3.2. While
+ * those versions or later are required for full C11 and C++11 support,
+ * some earlier versions claim to support C11 and C++11 in ways that might
+ * allow them to get past CMake. We don't use this codepath for those
+ * compilers because they heavily optimize the portable versions, though.
+ */
+#define pint_bswap16(x) __builtin_bswap16(x)
+#define pint_bswap32(x) __builtin_bswap32(x)
+#define pint_bswap64(x) __builtin_bswap64(x)
+#endif
+
+static inline uint16_t pntoh16(const void *p)
+{
+ uint16_t ret;
+ memcpy(&ret, p, sizeof(ret));
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ ret = pint_bswap16(ret);
+#endif
+ return ret;
+}
+
+static inline uint32_t pntoh32(const void *p)
+{
+ uint32_t ret;
+ memcpy(&ret, p, sizeof(ret));
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ ret = pint_bswap32(ret);
+#endif
+ return ret;
+}
+
+static inline uint64_t pntoh64(const void *p)
+{
+ uint64_t ret;
+ memcpy(&ret, p, sizeof(ret));
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ ret = pint_bswap64(ret);
+#endif
+ return ret;
+}
+
+static inline uint16_t pletoh16(const void *p)
+{
+ uint16_t ret;
+ memcpy(&ret, p, sizeof(ret));
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+ ret = pint_bswap16(ret);
+#endif
+ return ret;
+}
+
+static inline uint32_t pletoh32(const void *p)
+{
+ uint32_t ret;
+ memcpy(&ret, p, sizeof(ret));
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+ ret = pint_bswap32(ret);
+#endif
+ return ret;
+}
+
+static inline uint64_t pletoh64(const void *p)
+{
+ uint64_t ret;
+ memcpy(&ret, p, sizeof(ret));
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+ ret = pint_bswap64(ret);
+#endif
+ return ret;
+}
+
+static inline void phton16(uint8_t *p, uint16_t v)
+{
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ v = pint_bswap16(v);
+#endif
+ memcpy(p, &v, sizeof(v));
+}
+
+static inline void phton32(uint8_t *p, uint32_t v)
+{
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ v = pint_bswap32(v);
+#endif
+ memcpy(p, &v, sizeof(v));
+}
+
+static inline void phton64(uint8_t *p, uint64_t v) {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ v = pint_bswap64(v);
+#endif
+ memcpy(p, &v, sizeof(v));
+}
+
+static inline void phtole32(uint8_t *p, uint32_t v)
+{
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+ v = pint_bswap32(v);
+#endif
+ memcpy(p, &v, sizeof(v));
+}
+
+static inline void phtole64(uint8_t *p, uint64_t v) {
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+ v = pint_bswap64(v);
+#endif
+ memcpy(p, &v, sizeof(v));
+}
+
+#else
+/* Portable functions */
+static inline uint16_t pntoh16(const void *p)
+{
+ return (uint16_t)*((const uint8_t *)(p)+0)<<8|
+ (uint16_t)*((const uint8_t *)(p)+1)<<0;
+}
+
+static inline uint32_t pntoh32(const void *p)
+{
+ return (uint32_t)*((const uint8_t *)(p)+0)<<24|
+ (uint32_t)*((const uint8_t *)(p)+1)<<16|
+ (uint32_t)*((const uint8_t *)(p)+2)<<8|
+ (uint32_t)*((const uint8_t *)(p)+3)<<0;
+}
+
+static inline uint64_t pntoh64(const void *p)
+{
+ return (uint64_t)*((const uint8_t *)(p)+0)<<56|
+ (uint64_t)*((const uint8_t *)(p)+1)<<48|
+ (uint64_t)*((const uint8_t *)(p)+2)<<40|
+ (uint64_t)*((const uint8_t *)(p)+3)<<32|
+ (uint64_t)*((const uint8_t *)(p)+4)<<24|
+ (uint64_t)*((const uint8_t *)(p)+5)<<16|
+ (uint64_t)*((const uint8_t *)(p)+6)<<8|
+ (uint64_t)*((const uint8_t *)(p)+7)<<0;
+}
+
+static inline uint16_t pletoh16(const void *p)
+{
+ return (uint16_t)*((const uint8_t *)(p)+1)<<8|
+ (uint16_t)*((const uint8_t *)(p)+0)<<0;
+}
+
+static inline uint32_t pletoh32(const void *p)
+{
+ return (uint32_t)*((const uint8_t *)(p)+3)<<24|
+ (uint32_t)*((const uint8_t *)(p)+2)<<16|
+ (uint32_t)*((const uint8_t *)(p)+1)<<8|
+ (uint32_t)*((const uint8_t *)(p)+0)<<0;
+}
+
+static inline uint64_t pletoh64(const void *p)
+{
+ return (uint64_t)*((const uint8_t *)(p)+7)<<56|
+ (uint64_t)*((const uint8_t *)(p)+6)<<48|
+ (uint64_t)*((const uint8_t *)(p)+5)<<40|
+ (uint64_t)*((const uint8_t *)(p)+4)<<32|
+ (uint64_t)*((const uint8_t *)(p)+3)<<24|
+ (uint64_t)*((const uint8_t *)(p)+2)<<16|
+ (uint64_t)*((const uint8_t *)(p)+1)<<8|
+ (uint64_t)*((const uint8_t *)(p)+0)<<0;
+}
+
+/* Pointer routines to put items out in a particular byte order.
+ * These will work regardless of the byte alignment of the pointer.
+ */
+
+static inline void phton16(uint8_t *p, uint16_t v)
+{
+ p[0] = (uint8_t)(v >> 8);
+ p[1] = (uint8_t)(v >> 0);
+}
+
+static inline void phton32(uint8_t *p, uint32_t v)
+{
+ p[0] = (uint8_t)(v >> 24);
+ p[1] = (uint8_t)(v >> 16);
+ p[2] = (uint8_t)(v >> 8);
+ p[3] = (uint8_t)(v >> 0);
+}
+
+static inline void phton64(uint8_t *p, uint64_t v) {
+ p[0] = (uint8_t)(v >> 56);
+ p[1] = (uint8_t)(v >> 48);
+ p[2] = (uint8_t)(v >> 40);
+ p[3] = (uint8_t)(v >> 32);
+ p[4] = (uint8_t)(v >> 24);
+ p[5] = (uint8_t)(v >> 16);
+ p[6] = (uint8_t)(v >> 8);
+ p[7] = (uint8_t)(v >> 0);
+}
+
+static inline void phtole32(uint8_t *p, uint32_t v) {
+ p[0] = (uint8_t)(v >> 0);
+ p[1] = (uint8_t)(v >> 8);
+ p[2] = (uint8_t)(v >> 16);
+ p[3] = (uint8_t)(v >> 24);
+}
+
+static inline void phtole64(uint8_t *p, uint64_t v) {
+ p[0] = (uint8_t)(v >> 0);
+ p[1] = (uint8_t)(v >> 8);
+ p[2] = (uint8_t)(v >> 16);
+ p[3] = (uint8_t)(v >> 24);
+ p[4] = (uint8_t)(v >> 32);
+ p[5] = (uint8_t)(v >> 40);
+ p[6] = (uint8_t)(v >> 48);
+ p[7] = (uint8_t)(v >> 56);
+}
+#endif
+
+static inline uint32_t pntoh24(const void *p)
+{
+ return (uint32_t)*((const uint8_t *)(p)+0)<<16|
+ (uint32_t)*((const uint8_t *)(p)+1)<<8|
+ (uint32_t)*((const uint8_t *)(p)+2)<<0;
+}
+
+static inline uint64_t pntoh40(const void *p)
+{
+ return (uint64_t)*((const uint8_t *)(p)+0)<<32|
+ (uint64_t)*((const uint8_t *)(p)+1)<<24|
+ (uint64_t)*((const uint8_t *)(p)+2)<<16|
+ (uint64_t)*((const uint8_t *)(p)+3)<<8|
+ (uint64_t)*((const uint8_t *)(p)+4)<<0;
+}
+
+static inline uint64_t pntoh48(const void *p)
+{
+ return (uint64_t)*((const uint8_t *)(p)+0)<<40|
+ (uint64_t)*((const uint8_t *)(p)+1)<<32|
+ (uint64_t)*((const uint8_t *)(p)+2)<<24|
+ (uint64_t)*((const uint8_t *)(p)+3)<<16|
+ (uint64_t)*((const uint8_t *)(p)+4)<<8|
+ (uint64_t)*((const uint8_t *)(p)+5)<<0;
+}
+
+static inline uint64_t pntoh56(const void *p)
+{
+ return (uint64_t)*((const uint8_t *)(p)+0)<<48|
+ (uint64_t)*((const uint8_t *)(p)+1)<<40|
+ (uint64_t)*((const uint8_t *)(p)+2)<<32|
+ (uint64_t)*((const uint8_t *)(p)+3)<<24|
+ (uint64_t)*((const uint8_t *)(p)+4)<<16|
+ (uint64_t)*((const uint8_t *)(p)+5)<<8|
+ (uint64_t)*((const uint8_t *)(p)+6)<<0;
+}
+
+static inline uint32_t pletoh24(const void *p)
+{
+ return (uint32_t)*((const uint8_t *)(p)+2)<<16|
+ (uint32_t)*((const uint8_t *)(p)+1)<<8|
+ (uint32_t)*((const uint8_t *)(p)+0)<<0;
+}
+
+static inline uint64_t pletoh40(const void *p)
+{
+ return (uint64_t)*((const uint8_t *)(p)+4)<<32|
+ (uint64_t)*((const uint8_t *)(p)+3)<<24|
+ (uint64_t)*((const uint8_t *)(p)+2)<<16|
+ (uint64_t)*((const uint8_t *)(p)+1)<<8|
+ (uint64_t)*((const uint8_t *)(p)+0)<<0;
+}
+
+static inline uint64_t pletoh48(const void *p)
+{
+ return (uint64_t)*((const uint8_t *)(p)+5)<<40|
+ (uint64_t)*((const uint8_t *)(p)+4)<<32|
+ (uint64_t)*((const uint8_t *)(p)+3)<<24|
+ (uint64_t)*((const uint8_t *)(p)+2)<<16|
+ (uint64_t)*((const uint8_t *)(p)+1)<<8|
+ (uint64_t)*((const uint8_t *)(p)+0)<<0;
+}
+
+static inline uint64_t pletoh56(const void *p)
+{
+ return (uint64_t)*((const uint8_t *)(p)+6)<<48|
+ (uint64_t)*((const uint8_t *)(p)+5)<<40|
+ (uint64_t)*((const uint8_t *)(p)+4)<<32|
+ (uint64_t)*((const uint8_t *)(p)+3)<<24|
+ (uint64_t)*((const uint8_t *)(p)+2)<<16|
+ (uint64_t)*((const uint8_t *)(p)+1)<<8|
+ (uint64_t)*((const uint8_t *)(p)+0)<<0;
+}
+
+/* Subtract two guint32s with respect to wraparound */
+#define guint32_wraparound_diff(higher, lower) ((higher>lower)?(higher-lower):(higher+0xffffffff-lower+1))
+
+#endif /* PINT_H */
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wsutil/please_report_bug.c b/wsutil/please_report_bug.c
new file mode 100644
index 00000000..82520fb2
--- /dev/null
+++ b/wsutil/please_report_bug.c
@@ -0,0 +1,33 @@
+/* please_report_bug.c
+ * Routines returning strings to use when reporting a bug.
+ * They ask the user to report a bug to the Wireshark developers.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "please_report_bug.h"
+
+/*
+ * Long message, to use in alert boxes and printed messages.
+ */
+const char *
+please_report_bug(void)
+{
+ return
+ "Please report this to the Wireshark developers as a bug.\n"
+ "https://gitlab.com/wireshark/wireshark/issues\n"
+ "(This is not a crash; please do not say, in your report, that it is a crash.)";
+}
+
+/*
+ * Short message, to use in status bar messages.
+ */
+const char *
+please_report_bug_short(void)
+{
+ return "Please report this to the Wireshark developers.";
+}
diff --git a/wsutil/please_report_bug.h b/wsutil/please_report_bug.h
new file mode 100644
index 00000000..e99b26ca
--- /dev/null
+++ b/wsutil/please_report_bug.h
@@ -0,0 +1,35 @@
+/** @file
+ * Declarations of routines returning strings to use when reporting a bug.
+ * They ask the user to report a bug to the Wireshark developers.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __PLEASE_REPORT_BUG_H__
+#define __PLEASE_REPORT_BUG_H__
+
+#include "ws_symbol_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * Long message, to use in alert boxes and printed messages.
+ */
+WS_DLL_PUBLIC const char *please_report_bug(void);
+
+/*
+ * Short message, to use in status bar messages.
+ */
+WS_DLL_PUBLIC const char *please_report_bug_short(void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __PLEASE_REPORT_BUG_H__ */
diff --git a/wsutil/plugins.c b/wsutil/plugins.c
new file mode 100644
index 00000000..2798fdc3
--- /dev/null
+++ b/wsutil/plugins.c
@@ -0,0 +1,333 @@
+/* plugins.c
+ * plugin routines
+ *
+ * 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"
+#define WS_LOG_DOMAIN LOG_DOMAIN_PLUGINS
+#include "plugins.h"
+
+#include <time.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <gmodule.h>
+
+#include <wsutil/filesystem.h>
+#include <wsutil/privileges.h>
+#include <wsutil/file_util.h>
+#include <wsutil/report_message.h>
+#include <wsutil/wslog.h>
+
+typedef struct _plugin {
+ GModule *handle; /* handle returned by g_module_open */
+ char *name; /* plugin name */
+ const char *version; /* plugin version */
+ const char *type_name; /* user-facing name (what it does). Should these be capitalized? */
+} plugin;
+
+#define TYPE_DIR_EPAN "epan"
+#define TYPE_DIR_WIRETAP "wiretap"
+#define TYPE_DIR_CODECS "codecs"
+
+#define TYPE_NAME_DISSECTOR "dissector"
+#define TYPE_NAME_FILE_TYPE "file type"
+#define TYPE_NAME_CODEC "codec"
+
+
+static GSList *plugins_module_list = NULL;
+
+
+static inline const char *
+type_to_dir(plugin_type_e type)
+{
+ switch (type) {
+ case WS_PLUGIN_EPAN:
+ return TYPE_DIR_EPAN;
+ case WS_PLUGIN_WIRETAP:
+ return TYPE_DIR_WIRETAP;
+ case WS_PLUGIN_CODEC:
+ return TYPE_DIR_CODECS;
+ default:
+ ws_error("Unknown plugin type: %u. Aborting.", (unsigned) type);
+ break;
+ }
+ ws_assert_not_reached();
+}
+
+static inline const char *
+type_to_name(plugin_type_e type)
+{
+ switch (type) {
+ case WS_PLUGIN_EPAN:
+ return TYPE_NAME_DISSECTOR;
+ case WS_PLUGIN_WIRETAP:
+ return TYPE_NAME_FILE_TYPE;
+ case WS_PLUGIN_CODEC:
+ return TYPE_NAME_CODEC;
+ default:
+ ws_error("Unknown plugin type: %u. Aborting.", (unsigned) type);
+ break;
+ }
+ ws_assert_not_reached();
+}
+
+static void
+free_plugin(void * data)
+{
+ plugin *p = (plugin *)data;
+ g_module_close(p->handle);
+ g_free(p->name);
+ g_free(p);
+}
+
+static int
+compare_plugins(gconstpointer a, gconstpointer b)
+{
+ return g_strcmp0((*(plugin *const *)a)->name, (*(plugin *const *)b)->name);
+}
+
+static bool
+pass_plugin_version_compatibility(GModule *handle, const char *name)
+{
+ void * symb;
+ int major, minor;
+
+ if(!g_module_symbol(handle, "plugin_want_major", &symb)) {
+ report_failure("The plugin '%s' has no \"plugin_want_major\" symbol", name);
+ return false;
+ }
+ major = *(int *)symb;
+
+ if(!g_module_symbol(handle, "plugin_want_minor", &symb)) {
+ report_failure("The plugin '%s' has no \"plugin_want_minor\" symbol", name);
+ return false;
+ }
+ minor = *(int *)symb;
+
+ if (major != VERSION_MAJOR || minor != VERSION_MINOR) {
+ report_failure("The plugin '%s' was compiled for Wireshark version %d.%d",
+ name, major, minor);
+ return false;
+ }
+
+ return true;
+}
+
+// GLib and Qt allow ".dylib" and ".so" on macOS. Should we do the same?
+#ifdef _WIN32
+#define MODULE_SUFFIX ".dll"
+#else
+#define MODULE_SUFFIX ".so"
+#endif
+
+static void
+scan_plugins_dir(GHashTable *plugins_module, const char *dirpath, plugin_type_e type, bool append_type)
+{
+ GDir *dir;
+ const char *name; /* current file name */
+ char *plugin_folder;
+ char *plugin_file; /* current file full path */
+ GModule *handle; /* handle returned by g_module_open */
+ void * symbol;
+ const char *plug_version;
+ plugin *new_plug;
+
+ if (append_type)
+ plugin_folder = g_build_filename(dirpath, type_to_dir(type), (char *)NULL);
+ else
+ plugin_folder = g_strdup(dirpath);
+
+ dir = g_dir_open(plugin_folder, 0, NULL);
+ if (dir == NULL) {
+ g_free(plugin_folder);
+ return;
+ }
+
+ ws_debug("Scanning plugins folder \"%s\"", plugin_folder);
+
+ while ((name = g_dir_read_name(dir)) != NULL) {
+ /* Skip anything but files with .dll or .so. */
+ if (!g_str_has_suffix(name, MODULE_SUFFIX))
+ continue;
+
+ /*
+ * Check if the same name is already registered.
+ */
+ if (g_hash_table_lookup(plugins_module, name)) {
+ /* Yes, it is. */
+ report_warning("The plugin '%s' was found "
+ "in multiple directories", name);
+ continue;
+ }
+
+ plugin_file = g_build_filename(plugin_folder, name, (char *)NULL);
+ handle = g_module_open(plugin_file, G_MODULE_BIND_LOCAL);
+ if (handle == NULL) {
+ /* g_module_error() provides file path. */
+ report_failure("Couldn't load plugin '%s': %s", name,
+ g_module_error());
+ g_free(plugin_file);
+ continue;
+ }
+
+ if (!g_module_symbol(handle, "plugin_version", &symbol))
+ {
+ report_failure("The plugin '%s' has no \"plugin_version\" symbol", name);
+ g_module_close(handle);
+ g_free(plugin_file);
+ continue;
+ }
+ plug_version = (const char *)symbol;
+
+ if (!pass_plugin_version_compatibility(handle, name)) {
+ g_module_close(handle);
+ g_free(plugin_file);
+ continue;
+ }
+
+ /* Search for the entry point for the plugin registration function */
+ if (!g_module_symbol(handle, "plugin_register", &symbol)) {
+ report_failure("The plugin '%s' has no \"plugin_register\" symbol", name);
+ g_module_close(handle);
+ g_free(plugin_file);
+ continue;
+ }
+
+DIAG_OFF_PEDANTIC
+ /* Found it, call the plugin registration function. */
+ ((plugin_register_func)symbol)();
+DIAG_ON_PEDANTIC
+
+ new_plug = g_new(plugin, 1);
+ new_plug->handle = handle;
+ new_plug->name = g_strdup(name);
+ new_plug->version = plug_version;
+ new_plug->type_name = type_to_name(type);
+
+ /* Add it to the list of plugins. */
+ g_hash_table_replace(plugins_module, new_plug->name, new_plug);
+ ws_info("Registered plugin: %s (%s)", new_plug->name, plugin_file);
+ g_free(plugin_file);
+ }
+ ws_dir_close(dir);
+ g_free(plugin_folder);
+}
+
+/*
+ * Scan for plugins.
+ */
+plugins_t *
+plugins_init(plugin_type_e type)
+{
+ if (!g_module_supported())
+ return NULL; /* nothing to do */
+
+ GHashTable *plugins_module = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, free_plugin);
+
+ /*
+ * Scan the global plugin directory.
+ */
+ scan_plugins_dir(plugins_module, get_plugins_dir_with_version(), type, true);
+
+ /*
+ * If the program wasn't started with special privileges,
+ * scan the users plugin directory. (Even if we relinquish
+ * them, plugins aren't safe unless we've *permanently*
+ * relinquished them, and we can't do that in Wireshark as,
+ * if we need privileges to start capturing, we'd need to
+ * reclaim them before each time we start capturing.)
+ */
+ if (!started_with_special_privs()) {
+ scan_plugins_dir(plugins_module, get_plugins_pers_dir_with_version(), type, true);
+ }
+
+ plugins_module_list = g_slist_prepend(plugins_module_list, plugins_module);
+
+ return plugins_module;
+}
+
+WS_DLL_PUBLIC void
+plugins_get_descriptions(plugin_description_callback callback, void *callback_data)
+{
+ GPtrArray *plugins_array = g_ptr_array_new();
+ GHashTableIter iter;
+ void * value;
+
+ for (GSList *l = plugins_module_list; l != NULL; l = l->next) {
+ g_hash_table_iter_init (&iter, (GHashTable *)l->data);
+ while (g_hash_table_iter_next (&iter, NULL, &value)) {
+ g_ptr_array_add(plugins_array, value);
+ }
+ }
+
+ g_ptr_array_sort(plugins_array, compare_plugins);
+
+ for (unsigned i = 0; i < plugins_array->len; i++) {
+ plugin *plug = (plugin *)plugins_array->pdata[i];
+ callback(plug->name, plug->version, plug->type_name, g_module_name(plug->handle), callback_data);
+ }
+
+ g_ptr_array_free(plugins_array, true);
+}
+
+static void
+print_plugin_description(const char *name, const char *version,
+ const char *description, const char *filename,
+ void *user_data _U_)
+{
+ printf("%-16s\t%s\t%s\t%s\n", name, version, description, filename);
+}
+
+void
+plugins_dump_all(void)
+{
+ plugins_get_descriptions(print_plugin_description, NULL);
+}
+
+int
+plugins_get_count(void)
+{
+ unsigned count = 0;
+
+ for (GSList *l = plugins_module_list; l != NULL; l = l->next) {
+ count += g_hash_table_size((GHashTable *)l->data);
+ }
+ return count;
+}
+
+void
+plugins_cleanup(plugins_t *plugins)
+{
+ if (!plugins)
+ return;
+
+ plugins_module_list = g_slist_remove(plugins_module_list, plugins);
+ g_hash_table_destroy((GHashTable *)plugins);
+}
+
+bool
+plugins_supported(void)
+{
+ return g_module_supported();
+}
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wsutil/plugins.h b/wsutil/plugins.h
new file mode 100644
index 00000000..96f3ac03
--- /dev/null
+++ b/wsutil/plugins.h
@@ -0,0 +1,63 @@
+/** @file
+ * definitions for plugins structures
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __PLUGINS_H__
+#define __PLUGINS_H__
+
+#include <wireshark.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef void (*plugin_register_func)(void);
+
+typedef void plugins_t;
+
+typedef enum {
+ WS_PLUGIN_EPAN,
+ WS_PLUGIN_WIRETAP,
+ WS_PLUGIN_CODEC
+} plugin_type_e;
+
+WS_DLL_PUBLIC plugins_t *plugins_init(plugin_type_e type);
+
+typedef void (*plugin_description_callback)(const char *name, const char *version,
+ const char *types, const char *filename,
+ void *user_data);
+
+WS_DLL_PUBLIC void plugins_get_descriptions(plugin_description_callback callback, void *user_data);
+
+WS_DLL_PUBLIC void plugins_dump_all(void);
+
+WS_DLL_PUBLIC int plugins_get_count(void);
+
+WS_DLL_PUBLIC void plugins_cleanup(plugins_t *plugins);
+
+WS_DLL_PUBLIC bool plugins_supported(void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __PLUGINS_H__ */
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wsutil/pow2.h b/wsutil/pow2.h
new file mode 100644
index 00000000..1555f3c4
--- /dev/null
+++ b/wsutil/pow2.h
@@ -0,0 +1,29 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WS_POW2_H__
+#define __WS_POW2_H__
+
+/*
+ * Macros to calculate pow2^M, for various power-of-2 values and positive
+ * integer values of M. That's (2^N)^M, i.e. 2^(N*M).
+ *
+ * The first argument is the type of the desired result; the second
+ * argument is M.
+ */
+#define pow2(type, m) (((type)1U) << (m))
+#define pow4(type, m) (((type)1U) << (2*(m)))
+#define pow8(type, m) (((type)1U) << (3*(m)))
+#define pow16(type, m) (((type)1U) << (4*(m)))
+#define pow32(type, m) (((type)1U) << (5*(m)))
+#define pow64(type, m) (((type)1U) << (6*(m)))
+#define pow128(type, m) (((type)1U) << (7*(m)))
+#define pow256(type, m) (((type)1U) << (8*(m)))
+
+#endif /* __WS_POW2_H__ */
diff --git a/wsutil/privileges.c b/wsutil/privileges.c
new file mode 100644
index 00000000..2ca30203
--- /dev/null
+++ b/wsutil/privileges.c
@@ -0,0 +1,288 @@
+/* privileges.c
+ * Routines for handling privileges, e.g. set-UID and set-GID on UNIX.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2006 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#define WS_LOG_DOMAIN LOG_DOMAIN_WSUTIL
+
+#if defined(HAVE_SETRESUID) || defined(HAVE_SETREGUID)
+#define _GNU_SOURCE /* Otherwise [sg]etres[gu]id won't be defined on Linux */
+#endif
+#include "privileges.h"
+
+#include <wsutil/ws_assert.h>
+#include <wsutil/wslog.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#include <wchar.h>
+#include <tchar.h>
+
+/*
+ * Called when the program starts, to save whatever credential information
+ * we'll need later, and to do whatever other specialized platform-dependent
+ * initialization we want.
+ */
+void
+init_process_policies(void)
+{
+ /*
+ * If we have SetProcessDEPPolicy(), turn "data execution
+ * prevention" on - i.e., if the MMU lets you set execute
+ * permission on a per-page basis, turn execute permission
+ * off on most data pages. SetProcessDEPPolicy() fails on
+ * 64-bit Windows (it's *always* on there), but if it fails,
+ * we don't care (we did our best), so we don't check for
+ * errors.
+ *
+ */
+ SetProcessDEPPolicy(PROCESS_DEP_ENABLE);
+}
+
+/*
+ * For now, we say the program wasn't started with special privileges.
+ * There are ways of running programs with credentials other than those
+ * for the session in which it's run, but I don't know whether that'd be
+ * done with Wireshark/TShark or not.
+ */
+bool
+started_with_special_privs(void)
+{
+ return false;
+}
+
+/*
+ * For now, we say the program isn't running with special privileges.
+ * There are ways of running programs with credentials other than those
+ * for the session in which it's run, but I don't know whether that'd be
+ * done with Wireshark/TShark or not.
+ */
+bool
+running_with_special_privs(void)
+{
+ return false;
+}
+
+/*
+ * For now, we don't do anything when asked to relinquish special privileges.
+ */
+void
+relinquish_special_privs_perm(void)
+{
+}
+
+/*
+ * Get the current username. String must be g_free()d after use.
+ */
+char *
+get_cur_username(void) {
+ char *username;
+ username = g_strdup("UNKNOWN");
+ return username;
+}
+
+/*
+ * Get the current group. String must be g_free()d after use.
+ */
+char *
+get_cur_groupname(void) {
+ char *groupname;
+ groupname = g_strdup("UNKNOWN");
+ return groupname;
+}
+
+#else /* _WIN32 */
+
+#include <sys/types.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+
+#include <string.h>
+#include <errno.h>
+
+static uid_t ruid, euid;
+static gid_t rgid, egid;
+static bool init_process_policies_called = false;
+
+/*
+ * Called when the program starts, to save whatever credential information
+ * we'll need later, and to do whatever other specialized platform-dependent
+ * initialization we want.
+ *
+ * The credential information we'll need later on UNIX is the real and
+ * effective UID and GID.
+ *
+ * XXX - do any UN*Xes have opt-in "no execute on data pages by default"
+ * permission? This would be the place to request it.
+ */
+void
+init_process_policies(void)
+{
+ ruid = getuid();
+ euid = geteuid();
+ rgid = getgid();
+ egid = getegid();
+
+ init_process_policies_called = true;
+}
+
+/*
+ * "Started with special privileges" means "started out set-UID or set-GID",
+ * or run as the root user or group.
+ */
+bool
+started_with_special_privs(void)
+{
+ ws_assert(init_process_policies_called);
+#ifdef HAVE_ISSETUGID
+ return issetugid();
+#else
+ return (ruid != euid || rgid != egid || ruid == 0 || rgid == 0);
+#endif
+}
+
+/*
+ * Return true if the real, effective, or saved (if we can check it) user
+ * ID or group are 0.
+ */
+bool
+running_with_special_privs(void)
+{
+#ifdef HAVE_SETRESUID
+ uid_t ru, eu, su;
+#endif
+#ifdef HAVE_SETRESGID
+ gid_t rg, eg, sg;
+#endif
+
+#ifdef HAVE_SETRESUID
+ getresuid(&ru, &eu, &su);
+ if (ru == 0 || eu == 0 || su == 0)
+ return true;
+#else
+ if (getuid() == 0 || geteuid() == 0)
+ return true;
+#endif
+#ifdef HAVE_SETRESGID
+ getresgid(&rg, &eg, &sg);
+ if (rg == 0 || eg == 0 || sg == 0)
+ return true;
+#else
+ if (getgid() == 0 || getegid() == 0)
+ return true;
+#endif
+ return false;
+}
+
+/*
+ * Permanently relinquish set-UID and set-GID privileges.
+ * If error, abort since we probably shouldn't continue
+ * with elevated privileges.
+ * Note that if this error occurs when dumpcap is called from
+ * wireshark or tshark, the message seen will be
+ * "Child dumpcap process died:". This is obscure but we'll
+ * consider it acceptable since it should be highly unlikely
+ * that this error will occur.
+ */
+
+static void
+setxid_fail(const char *str)
+{
+ ws_error("Attempt to relinquish privileges failed [%s()] - aborting: %s\n",
+ str, g_strerror(errno));
+}
+
+void
+relinquish_special_privs_perm(void)
+{
+ /*
+ * If we were started with special privileges, set the
+ * real and effective group and user IDs to the original
+ * values of the real and effective group and user IDs.
+ * If we're not, don't bother - doing so seems to mung
+ * our group set, at least in Mac OS X 10.5.
+ *
+ * (Set the effective UID last - that takes away our
+ * rights to set anything else.)
+ */
+ if (started_with_special_privs()) {
+#ifdef HAVE_SETRESGID
+ if (setresgid(rgid, rgid, rgid) == -1) {setxid_fail("setresgid");}
+#else
+ if (setgid(rgid) == -1) {setxid_fail("setgid"); }
+ if (setegid(rgid) == -1) {setxid_fail("setegid");}
+#endif
+
+#ifdef HAVE_SETRESUID
+ if (setresuid(ruid, ruid, ruid) == -1) {setxid_fail("setresuid");}
+#else
+ if (setuid(ruid) == -1) {setxid_fail("setuid"); }
+ if (seteuid(ruid) == -1) {setxid_fail("seteuid");}
+#endif
+ }
+}
+
+/*
+ * Get the current username. String must be g_free()d after use.
+ */
+char *
+get_cur_username(void) {
+ char *username;
+ struct passwd *pw = getpwuid(getuid());
+
+ if (pw) {
+ username = g_strdup(pw->pw_name);
+ } else {
+ username = g_strdup("UNKNOWN");
+ }
+ endpwent();
+ return username;
+}
+
+/*
+ * Get the current group. String must be g_free()d after use.
+ */
+char *
+get_cur_groupname(void) {
+ char *groupname;
+ struct group *gr = getgrgid(getgid());
+
+ if (gr) {
+ groupname = g_strdup(gr->gr_name);
+ } else {
+ groupname = g_strdup("UNKNOWN");
+ }
+ endgrent();
+ return groupname;
+}
+
+#endif /* _WIN32 */
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * ex: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wsutil/privileges.h b/wsutil/privileges.h
new file mode 100644
index 00000000..c936228c
--- /dev/null
+++ b/wsutil/privileges.h
@@ -0,0 +1,66 @@
+/** @file
+ * Declarations of routines for handling privileges.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2006 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __PRIVILEGES_H__
+#define __PRIVILEGES_H__
+
+#include <wireshark.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Called when the program starts, to enable security features and save
+ * whatever credential information we'll need later.
+ */
+WS_DLL_PUBLIC void init_process_policies(void);
+
+/**
+ * Was this program started with special privileges? get_credential_info()
+ * MUST be called before calling this.
+ * @return true if the program was started with special privileges,
+ * false otherwise.
+ */
+WS_DLL_PUBLIC bool started_with_special_privs(void);
+
+/**
+ * Is this program running with special privileges? get_credential_info()
+ * MUST be called before calling this.
+ * @return true if the program is running with special privileges,
+ * false otherwise.
+ */
+WS_DLL_PUBLIC bool running_with_special_privs(void);
+
+/**
+ * Permanently relinquish special privileges. get_credential_info()
+ * MUST be called before calling this.
+ */
+WS_DLL_PUBLIC void relinquish_special_privs_perm(void);
+
+/**
+ * Get the current username. String must be g_free()d after use.
+ * @return A freshly g_alloc()ed string containing the username,
+ * or "UNKNOWN" on failure.
+ */
+WS_DLL_PUBLIC char *get_cur_username(void);
+
+/**
+ * Get the current group. String must be g_free()d after use.
+ * @return A freshly g_alloc()ed string containing the group,
+ * or "UNKNOWN" on failure.
+ */
+WS_DLL_PUBLIC char *get_cur_groupname(void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __PRIVILEGES_H__ */
diff --git a/wsutil/processes.h b/wsutil/processes.h
new file mode 100644
index 00000000..8e08a171
--- /dev/null
+++ b/wsutil/processes.h
@@ -0,0 +1,56 @@
+/** @file
+ *
+ * Process utility definitions
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef _WSUTIL_PROCESSES_H_
+#define _WSUTIL_PROCESSES_H_
+
+#include "ws_symbol_export.h"
+
+#ifdef _WIN32
+/*
+ * On Windows, a process ID is a HANDLE.
+ * Include <windows.h> to make sure HANDLE is defined.
+ */
+#include <windows.h>
+#else
+/*
+ * On UN*X, a process ID is a pid_t.
+ * Include <sys/types.h> to make sure pid_t is defined.
+ */
+#include <sys/types.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifdef _WIN32
+/*
+ * On Windows, a process ID is a HANDLE.
+ */
+typedef HANDLE ws_process_id;
+
+#define WS_INVALID_PID INVALID_HANDLE_VALUE
+
+#else
+/*
+ * On UN*X, a process ID is a pid_t.
+ */
+typedef pid_t ws_process_id;
+
+#define WS_INVALID_PID -1
+#endif
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _WSUTIL_PROCESSES_H_ */
diff --git a/wsutil/regex.c b/wsutil/regex.c
new file mode 100644
index 00000000..bb27189b
--- /dev/null
+++ b/wsutil/regex.c
@@ -0,0 +1,203 @@
+/*
+ * 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 "regex.h"
+
+#include <wsutil/str_util.h>
+#include <pcre2.h>
+
+
+struct _ws_regex {
+ pcre2_code *code;
+ char *pattern;
+};
+
+#define ERROR_MAXLEN_IN_CODE_UNITS 128
+
+static char *
+get_error_msg(int errorcode)
+{
+ char *buffer;
+
+ /*
+ * We have to provide a buffer and we don't know how long the
+ * error message is or even the maximum size. From pcre2api(3):
+ * "None of the messages are very long; a
+ * buffer size of 120 code units is ample."
+ */
+ /* Code unit = one byte */
+ buffer = g_malloc(ERROR_MAXLEN_IN_CODE_UNITS);
+ /* Message is returned with a trailing zero. */
+ pcre2_get_error_message(errorcode, buffer, ERROR_MAXLEN_IN_CODE_UNITS);
+ /* One more at the end for good luck. */
+ buffer[ERROR_MAXLEN_IN_CODE_UNITS-1] = '\0';
+ return buffer;
+}
+
+
+static pcre2_code *
+compile_pcre2(const char *patt, ssize_t size, char **errmsg, unsigned flags)
+{
+ pcre2_code *code;
+ int errorcode;
+ PCRE2_SIZE length;
+ PCRE2_SIZE erroroffset;
+ uint32_t options = 0;
+
+ if (size < 0)
+ length = PCRE2_ZERO_TERMINATED;
+ else
+ length = (PCRE2_SIZE)size;
+
+ if (flags & WS_REGEX_NEVER_UTF)
+ options |= PCRE2_NEVER_UTF;
+ if (flags & WS_REGEX_CASELESS)
+ options |= PCRE2_CASELESS;
+
+ /* By default UTF-8 is off. */
+ code = pcre2_compile_8((PCRE2_SPTR)patt,
+ length,
+ options,
+ &errorcode,
+ &erroroffset,
+ NULL);
+
+ if (code == NULL) {
+ *errmsg = get_error_msg(errorcode);
+ return NULL;
+ }
+
+ return code;
+}
+
+
+ws_regex_t *
+ws_regex_compile_ex(const char *patt, ssize_t size, char **errmsg, unsigned flags)
+{
+ ws_return_val_if(!patt, NULL);
+
+ pcre2_code *code = compile_pcre2(patt, size, errmsg, flags);
+ if (code == NULL)
+ return NULL;
+
+ ws_regex_t *re = g_new(ws_regex_t, 1);
+ re->code = code;
+ re->pattern = ws_escape_string_len(NULL, patt, size, false);
+ return re;
+}
+
+
+ws_regex_t *
+ws_regex_compile(const char *patt, char **errmsg)
+{
+ return ws_regex_compile_ex(patt, -1, errmsg, 0);
+}
+
+
+static bool
+match_pcre2(pcre2_code *code, const char *subject, ssize_t subj_length,
+ pcre2_match_data *match_data)
+{
+ PCRE2_SIZE length;
+ int rc;
+
+ if (subj_length < 0)
+ length = PCRE2_ZERO_TERMINATED;
+ else
+ length = (PCRE2_SIZE)subj_length;
+
+ rc = pcre2_match(code,
+ subject,
+ length,
+ 0, /* start at offset zero of the subject */
+ 0, /* default options */
+ match_data,
+ NULL);
+
+ if (rc < 0) {
+ /* No match */
+ if (rc != PCRE2_ERROR_NOMATCH) {
+ /* Error. Should not happen with UTF-8 disabled. Some huge
+ * subject strings could hit some internal limit. */
+ char *msg = get_error_msg(rc);
+ ws_debug("Unexpected pcre2_match() error: %s.", msg);
+ g_free(msg);
+ }
+ return false;
+ }
+
+ /* Matched */
+ return true;
+}
+
+
+bool
+ws_regex_matches(const ws_regex_t *re, const char *subj)
+{
+ return ws_regex_matches_length(re, subj, -1);
+}
+
+
+bool
+ws_regex_matches_length(const ws_regex_t *re,
+ const char *subj, ssize_t subj_length)
+{
+ bool matched;
+ pcre2_match_data *match_data;
+
+ ws_return_val_if(!re, false);
+ ws_return_val_if(!subj, false);
+
+ /* We don't use the matched substring but pcre2_match requires
+ * at least one pair of offsets. */
+ match_data = pcre2_match_data_create(1, NULL);
+ matched = match_pcre2(re->code, subj, subj_length, match_data);
+ pcre2_match_data_free(match_data);
+ return matched;
+}
+
+
+bool
+ws_regex_matches_pos(const ws_regex_t *re,
+ const char *subj, ssize_t subj_length,
+ size_t pos_vect[2])
+{
+ bool matched;
+ pcre2_match_data *match_data;
+
+ ws_return_val_if(!re, false);
+ ws_return_val_if(!subj, false);
+
+ match_data = pcre2_match_data_create(1, NULL);
+ matched = match_pcre2(re->code, subj, subj_length, match_data);
+ if (matched && pos_vect) {
+ PCRE2_SIZE *ovect = pcre2_get_ovector_pointer(match_data);
+ pos_vect[0] = ovect[0];
+ pos_vect[1] = ovect[1];
+ }
+ pcre2_match_data_free(match_data);
+ return matched;
+}
+
+
+void
+ws_regex_free(ws_regex_t *re)
+{
+ pcre2_code_free(re->code);
+ g_free(re->pattern);
+ g_free(re);
+}
+
+
+const char *
+ws_regex_pattern(const ws_regex_t *re)
+{
+ return re->pattern;
+}
diff --git a/wsutil/regex.h b/wsutil/regex.h
new file mode 100644
index 00000000..0484eab3
--- /dev/null
+++ b/wsutil/regex.h
@@ -0,0 +1,63 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WSUTIL_REGEX_H__
+#define __WSUTIL_REGEX_H__
+
+#include <wireshark.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _ws_regex;
+typedef struct _ws_regex ws_regex_t;
+
+WS_DLL_PUBLIC ws_regex_t *
+ws_regex_compile(const char *patt, char **errmsg);
+
+#define WS_REGEX_CASELESS (1U << 0)
+/* By default UTF-8 is off. This option also prevents it from being
+ * turned on using a pattern option. */
+#define WS_REGEX_NEVER_UTF (1U << 1)
+
+WS_DLL_PUBLIC ws_regex_t *
+ws_regex_compile_ex(const char *patt, ssize_t size, char **errmsg, unsigned flags);
+
+/** Matches a null-terminated subject string. */
+WS_DLL_PUBLIC bool
+ws_regex_matches(const ws_regex_t *re, const char *subj);
+
+/** Matches a subject string length in 8 bit code units. */
+WS_DLL_PUBLIC bool
+ws_regex_matches_length(const ws_regex_t *re,
+ const char *subj, ssize_t subj_length);
+
+/** Returns start and end position of the matched substring.
+ *
+ * pos_vect[0] is first codepoint in the matched substring.
+ * pos_vect[1] is the next to last codepoint in the matched substring.
+ * pos_vect[1] - pos_vect[0] is the matched substring length.
+ */
+WS_DLL_PUBLIC bool
+ws_regex_matches_pos(const ws_regex_t *re,
+ const char *subj, ssize_t subj_length,
+ size_t pos_vect[2]);
+
+WS_DLL_PUBLIC void
+ws_regex_free(ws_regex_t *re);
+
+WS_DLL_PUBLIC const char *
+ws_regex_pattern(const ws_regex_t *re);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __WSUTIL_REGEX_H__ */
diff --git a/wsutil/report_message.c b/wsutil/report_message.c
new file mode 100644
index 00000000..6f797fab
--- /dev/null
+++ b/wsutil/report_message.c
@@ -0,0 +1,162 @@
+/* report_message.c
+ * Routines for code that can run in GUI and command-line environments to
+ * use to report errors and warnings to the user (e.g., I/O errors, or
+ * problems with preference settings) if the message should be shown as
+ * a GUI error in a GUI environment.
+ *
+ * The application using libwsutil will register error-reporting
+ * routines, and the routines defined here will call the registered
+ * routines. That way, these routines can be called by code that
+ * doesn't itself know whether to pop up a dialog or print something
+ * to the standard error.
+ *
+ * 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 "report_message.h"
+
+static const char *friendly_program_name;
+static const struct report_message_routines *routines;
+
+void
+init_report_message(const char *friendly_program_name_arg,
+ const struct report_message_routines *routines_arg)
+{
+ friendly_program_name = friendly_program_name_arg;
+ routines = routines_arg;
+}
+
+/*
+ * Report a general error.
+ */
+void
+report_failure(const char *msg_format, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg_format);
+ (*routines->vreport_failure)(msg_format, ap);
+ va_end(ap);
+}
+
+/*
+ * Report a general warning.
+ */
+void
+report_warning(const char *msg_format, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg_format);
+ (*routines->vreport_warning)(msg_format, ap);
+ va_end(ap);
+}
+
+/*
+ * Report an error when trying to open or create a file.
+ * "err" is assumed to be an error code from Wiretap; positive values are
+ * UNIX-style errnos, so this can be used for open failures not from
+ * Wiretap as long as the failure code is just an errno.
+ */
+void
+report_open_failure(const char *filename, int err,
+ bool for_writing)
+{
+ (*routines->report_open_failure)(filename, err, for_writing);
+}
+
+/*
+ * Report an error when trying to read a file.
+ * "err" is assumed to be a UNIX-style errno.
+ */
+void
+report_read_failure(const char *filename, int err)
+{
+ (*routines->report_read_failure)(filename, err);
+}
+
+/*
+ * Report an error when trying to write a file.
+ * "err" is assumed to be a UNIX-style errno.
+ */
+void
+report_write_failure(const char *filename, int err)
+{
+ (*routines->report_write_failure)(filename, err);
+}
+
+/*
+ * Report an error from opening a capture file for reading.
+ */
+void
+report_cfile_open_failure(const char *filename, int err, char *err_info)
+{
+ (*routines->report_cfile_open_failure)(filename, err, err_info);
+}
+
+/*
+ * Report an error from opening a capture file for writing.
+ */
+void
+report_cfile_dump_open_failure(const char *filename,
+ int err, char *err_info, int file_type_subtype)
+{
+ (*routines->report_cfile_dump_open_failure)(filename,
+ err, err_info, file_type_subtype);
+}
+
+/*
+ * Report an error from attempting to read from a capture file.
+ */
+void
+report_cfile_read_failure(const char *filename, int err, char *err_info)
+{
+ (*routines->report_cfile_read_failure)(filename, err, err_info);
+}
+
+/*
+ * Report an error from attempting to write to a capture file.
+ */
+void
+report_cfile_write_failure(const char *in_filename, const char *out_filename,
+ int err, char *err_info, uint32_t framenum, int file_type_subtype)
+{
+ (*routines->report_cfile_write_failure)(in_filename, out_filename,
+ err, err_info, framenum, file_type_subtype);
+}
+
+/*
+ * Report an error from closing a capture file open for writing.
+ */
+void
+report_cfile_close_failure(const char *filename, int err, char *err_info)
+{
+ (*routines->report_cfile_close_failure)(filename, err, err_info);
+}
+
+/*
+ * Return the "friendly" program name.
+ */
+const char *
+get_friendly_program_name(void)
+{
+ return friendly_program_name;
+}
+
+/*
+ * 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:
+ */
diff --git a/wsutil/report_message.h b/wsutil/report_message.h
new file mode 100644
index 00000000..7be1808b
--- /dev/null
+++ b/wsutil/report_message.h
@@ -0,0 +1,120 @@
+/** @file
+ * Declarations of routines for code that can run in GUI and command-line
+ * environments to use to report errors and warnings to the user (e.g.,
+ * I/O errors, or problems with preference settings) if the message should
+ * be shown as a GUI error in a GUI environment.
+ *
+ * The application using libwsutil will register message-reporting
+ * routines, and the routines declared here will call the registered
+ * routines. That way, these routines can be called by code that
+ * doesn't itself know whether to pop up a dialog or print something
+ * to the standard error.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __REPORT_MESSAGE_H__
+#define __REPORT_MESSAGE_H__
+
+#include <wireshark.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * Initialize the report message routines
+ */
+struct report_message_routines {
+ void (*vreport_failure)(const char *, va_list);
+ void (*vreport_warning)(const char *, va_list);
+ void (*report_open_failure)(const char *, int, gboolean);
+ void (*report_read_failure)(const char *, int);
+ void (*report_write_failure)(const char *, int);
+ void (*report_cfile_open_failure)(const char *, int, char *);
+ void (*report_cfile_dump_open_failure)(const char *, int, char *, int);
+ void (*report_cfile_read_failure)(const char *, int, char *);
+ void (*report_cfile_write_failure)(const char *, const char *,
+ int, char *, uint32_t, int);
+ void (*report_cfile_close_failure)(const char *, int, char *);
+};
+
+WS_DLL_PUBLIC void init_report_message(const char *friendly_program_name,
+ const struct report_message_routines *routines);
+
+/*
+ * Report a general error.
+ */
+WS_DLL_PUBLIC void report_failure(const char *msg_format, ...) G_GNUC_PRINTF(1, 2);
+
+/*
+ * Report a general warning.
+ */
+WS_DLL_PUBLIC void report_warning(const char *msg_format, ...) G_GNUC_PRINTF(1, 2);
+
+/*
+ * Report an error when trying to open a file.
+ * "err" is assumed to be an error code from Wiretap; positive values are
+ * UNIX-style errnos, so this can be used for open failures not from
+ * Wiretap as long as the failure code is just an errno.
+ */
+WS_DLL_PUBLIC void report_open_failure(const char *filename, int err,
+ bool for_writing);
+
+/*
+ * Report an error when trying to read a file.
+ * "err" is assumed to be a UNIX-style errno.
+ */
+WS_DLL_PUBLIC void report_read_failure(const char *filename, int err);
+
+/*
+ * Report an error when trying to write a file.
+ * "err" is assumed to be a UNIX-style errno.
+ */
+WS_DLL_PUBLIC void report_write_failure(const char *filename, int err);
+
+/*
+ * Report an error from opening a capture file for reading.
+ */
+WS_DLL_PUBLIC void report_cfile_open_failure(const char *filename,
+ int err, char *err_info);
+
+/*
+ * Report an error from opening a capture file for writing.
+ */
+WS_DLL_PUBLIC void report_cfile_dump_open_failure(const char *filename,
+ int err, char *err_info, int file_type_subtype);
+
+/*
+ * Report an error from attempting to read from a capture file.
+ */
+WS_DLL_PUBLIC void report_cfile_read_failure(const char *filename,
+ int err, char *err_info);
+
+/*
+ * Report an error from attempting to write to a capture file.
+ */
+WS_DLL_PUBLIC void report_cfile_write_failure(const char *in_filename,
+ const char *out_filename, int err, char *err_info, uint32_t framenum,
+ int file_type_subtype);
+
+/*
+ * Report an error from closing a capture file open for writing.
+ */
+WS_DLL_PUBLIC void report_cfile_close_failure(const char *filename,
+ int err, char *err_info);
+
+/*
+ * Return the "friendly" program name.
+ */
+WS_DLL_PUBLIC const char *get_friendly_program_name(void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __REPORT_MESSAGE_H__ */
diff --git a/wsutil/rsa.c b/wsutil/rsa.c
new file mode 100644
index 00000000..22afe08f
--- /dev/null
+++ b/wsutil/rsa.c
@@ -0,0 +1,376 @@
+/* rsa.c
+ *
+ * Functions for RSA private key reading and use
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2007 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#define WS_LOG_DOMAIN LOG_DOMAIN_WSUTIL
+
+#include "rsa.h"
+#include "filesystem.h"
+#include "file_util.h"
+#include <errno.h>
+#include <wsutil/wslog.h>
+
+
+#ifdef HAVE_LIBGNUTLS
+
+#include <gnutls/abstract.h>
+#include <gnutls/pkcs12.h>
+
+/* RSA private key file processing {{{ */
+#define RSA_PARS 6
+gcry_sexp_t
+rsa_privkey_to_sexp(gnutls_x509_privkey_t priv_key, char **err)
+{
+ gnutls_datum_t rsa_datum[RSA_PARS]; /* m, e, d, p, q, u */
+ size_t tmp_size;
+ gcry_error_t gret;
+ gcry_sexp_t rsa_priv_key = NULL;
+ int i;
+ gcry_mpi_t rsa_params[RSA_PARS];
+ *err = NULL;
+
+ /* RSA get parameter */
+ if (gnutls_x509_privkey_export_rsa_raw(priv_key,
+ &rsa_datum[0],
+ &rsa_datum[1],
+ &rsa_datum[2],
+ &rsa_datum[3],
+ &rsa_datum[4],
+ &rsa_datum[5]) != 0) {
+ *err = g_strdup("can't export rsa param (is a rsa private key file ?!?)");
+ return NULL;
+ }
+
+ /* convert each rsa parameter to mpi format*/
+ for(i=0; i<RSA_PARS; i++) {
+ gret = gcry_mpi_scan(&rsa_params[i], GCRYMPI_FMT_USG, rsa_datum[i].data, rsa_datum[i].size,&tmp_size);
+ /* these buffers were allocated by gnutls_x509_privkey_export_rsa_raw() */
+ g_free(rsa_datum[i].data);
+ if (gret != 0) {
+ *err = ws_strdup_printf("can't convert m rsa param to int (size %d)", rsa_datum[i].size);
+ return NULL;
+ }
+ }
+
+ /* libgcrypt expects p < q, and gnutls might not return it as such, depending on gnutls version and its crypto backend */
+ if (gcry_mpi_cmp(rsa_params[3], rsa_params[4]) > 0)
+ {
+ /* p, q = q, p */
+ gcry_mpi_swap(rsa_params[3], rsa_params[4]);
+ /* due to swapping p and q, u = p^-1 mod p which happens to be needed. */
+ }
+ /* libgcrypt expects u = p^-1 mod q (for OpenPGP), but the u parameter
+ * says u = q^-1 mod p. Recompute u = p^-1 mod q. Do this unconditionally as
+ * at least GnuTLS 2.12.23 computes an invalid value. */
+ gcry_mpi_invm(rsa_params[5], rsa_params[3], rsa_params[4]);
+
+ if (gcry_sexp_build( &rsa_priv_key, NULL,
+ "(private-key(rsa((n%m)(e%m)(d%m)(p%m)(q%m)(u%m))))", rsa_params[0],
+ rsa_params[1], rsa_params[2], rsa_params[3], rsa_params[4],
+ rsa_params[5]) != 0) {
+ *err = g_strdup("can't build rsa private key s-exp");
+ return NULL;
+ }
+
+ for (i=0; i< 6; i++)
+ gcry_mpi_release(rsa_params[i]);
+ return rsa_priv_key;
+}
+
+gnutls_x509_privkey_t
+rsa_load_pem_key(FILE *fp, char **err)
+{
+ /* gnutls makes our work much harder, since we have to work internally with
+ * s-exp formatted data, but PEM loader exports only in "gnutls_datum_t"
+ * format, and a datum -> s-exp conversion function does not exist.
+ */
+ gnutls_x509_privkey_t priv_key;
+ gnutls_datum_t key;
+ ws_statb64 statbuf;
+ int ret;
+ unsigned bytes;
+ *err = NULL;
+
+ if (ws_fstat64(ws_fileno(fp), &statbuf) == -1) {
+ *err = ws_strdup_printf("can't ws_fstat64 file: %s", g_strerror(errno));
+ return NULL;
+ }
+ if (S_ISDIR(statbuf.st_mode)) {
+ *err = g_strdup("file is a directory");
+ errno = EISDIR;
+ return NULL;
+ }
+ if (S_ISFIFO(statbuf.st_mode)) {
+ *err = g_strdup("file is a named pipe");
+ errno = EINVAL;
+ return NULL;
+ }
+ if (!S_ISREG(statbuf.st_mode)) {
+ *err = g_strdup("file is not a regular file");
+ errno = EINVAL;
+ return NULL;
+ }
+ /* XXX - check for a too-big size */
+ /* load all file contents into a datum buffer*/
+ key.data = (unsigned char *)g_malloc((size_t)statbuf.st_size);
+ key.size = (int)statbuf.st_size;
+ bytes = (unsigned) fread(key.data, 1, key.size, fp);
+ if (bytes < key.size) {
+ if (bytes == 0 && ferror(fp)) {
+ *err = ws_strdup_printf("can't read from file %d bytes, got error %s",
+ key.size, g_strerror(errno));
+ } else {
+ *err = ws_strdup_printf("can't read from file %d bytes, got %d",
+ key.size, bytes);
+ }
+ g_free(key.data);
+ return NULL;
+ }
+
+ /* init private key data*/
+ gnutls_x509_privkey_init(&priv_key);
+
+ /* import PEM data*/
+ if ((ret = gnutls_x509_privkey_import(priv_key, &key, GNUTLS_X509_FMT_PEM)) != GNUTLS_E_SUCCESS) {
+ *err = ws_strdup_printf("can't import pem data: %s", gnutls_strerror(ret));
+ g_free(key.data);
+ gnutls_x509_privkey_deinit(priv_key);
+ return NULL;
+ }
+
+ if (gnutls_x509_privkey_get_pk_algorithm(priv_key) != GNUTLS_PK_RSA) {
+ *err = g_strdup("private key public key algorithm isn't RSA");
+ g_free(key.data);
+ gnutls_x509_privkey_deinit(priv_key);
+ return NULL;
+ }
+
+ g_free(key.data);
+
+ return priv_key;
+}
+
+static const char *
+BAGTYPE(gnutls_pkcs12_bag_type_t x) {
+ switch (x) {
+ case GNUTLS_BAG_EMPTY: return "Empty";
+ case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY: return "PKCS#8 Encrypted key";
+ case GNUTLS_BAG_PKCS8_KEY: return "PKCS#8 Key";
+ case GNUTLS_BAG_CERTIFICATE: return "Certificate";
+ case GNUTLS_BAG_CRL: return "CRL";
+ case GNUTLS_BAG_ENCRYPTED: return "Encrypted";
+ case GNUTLS_BAG_UNKNOWN: return "Unknown";
+ default: return "<undefined>";
+ }
+}
+
+gnutls_x509_privkey_t
+rsa_load_pkcs12(FILE *fp, const char *cert_passwd, char **err)
+{
+ int i, j, ret;
+ int rest;
+ unsigned char *p;
+ gnutls_datum_t data;
+ gnutls_pkcs12_bag_t bag = NULL;
+ size_t len;
+
+ gnutls_pkcs12_t rsa_p12 = NULL;
+
+ gnutls_x509_privkey_t priv_key = NULL;
+ *err = NULL;
+
+ rest = 4096;
+ data.data = (unsigned char *)g_malloc(rest);
+ data.size = rest;
+ p = data.data;
+ while ((len = fread(p, 1, rest, fp)) > 0) {
+ p += len;
+ rest -= (int) len;
+ if (!rest) {
+ rest = 1024;
+ data.data = (unsigned char *)g_realloc(data.data, data.size + rest);
+ p = data.data + data.size;
+ data.size += rest;
+ }
+ }
+ data.size -= rest;
+ if (!feof(fp)) {
+ *err = g_strdup("Error during certificate reading.");
+ g_free(data.data);
+ return NULL;
+ }
+
+ ret = gnutls_pkcs12_init(&rsa_p12);
+ if (ret < 0) {
+ *err = ws_strdup_printf("gnutls_pkcs12_init(&st_p12) - %s", gnutls_strerror(ret));
+ g_free(data.data);
+ return NULL;
+ }
+
+ /* load PKCS#12 in DER or PEM format */
+ ret = gnutls_pkcs12_import(rsa_p12, &data, GNUTLS_X509_FMT_DER, 0);
+ if (ret < 0) {
+ ret = gnutls_pkcs12_import(rsa_p12, &data, GNUTLS_X509_FMT_PEM, 0);
+ if (ret < 0) {
+ *err = ws_strdup_printf("could not load PKCS#12 in DER or PEM format: %s", gnutls_strerror(ret));
+ }
+ }
+ g_free(data.data);
+ if (ret < 0) {
+ gnutls_pkcs12_deinit(rsa_p12);
+ return NULL;
+ }
+
+ ws_debug("grsa_privkey_to_sexp: PKCS#12 imported");
+
+ /* TODO: Use gnutls_pkcs12_simple_parse, since 3.1.0 (August 2012) */
+ for (i=0; ; i++) {
+ gnutls_pkcs12_bag_type_t bag_type;
+
+ ret = gnutls_pkcs12_bag_init(&bag);
+ if (ret < 0) {
+ *err = ws_strdup_printf("gnutls_pkcs12_bag_init failed: %s",
+ gnutls_strerror(ret));
+ goto done;
+ }
+
+ ret = gnutls_pkcs12_get_bag(rsa_p12, i, bag);
+ if (ret < 0) {
+ *err = ws_strdup_printf("gnutls_pkcs12_get_bag failed: %s",
+ gnutls_strerror(ret));
+ goto done;
+ }
+
+ for (j=0; j<gnutls_pkcs12_bag_get_count(bag); j++) {
+
+ ret = gnutls_pkcs12_bag_get_type(bag, j);
+ if (ret < 0) {
+ *err = ws_strdup_printf("gnutls_pkcs12_bag_get_type failed: %s",
+ gnutls_strerror(ret));
+ goto done;
+ }
+ bag_type = (gnutls_pkcs12_bag_type_t)ret;
+ if (bag_type >= GNUTLS_BAG_UNKNOWN) {
+ *err = ws_strdup_printf("gnutls_pkcs12_bag_get_type returned unknown bag type %u",
+ ret);
+ goto done;
+ }
+ ws_debug("Bag %d/%d: %s", i, j, BAGTYPE(bag_type));
+ if (bag_type == GNUTLS_BAG_ENCRYPTED) {
+ ret = gnutls_pkcs12_bag_decrypt(bag, cert_passwd);
+ if (ret == 0) {
+ ret = gnutls_pkcs12_bag_get_type(bag, j);
+ if (ret < 0) {
+ *err = ws_strdup_printf("gnutls_pkcs12_bag_get_type failed: %s",
+ gnutls_strerror(ret));
+ goto done;
+ }
+ bag_type = (gnutls_pkcs12_bag_type_t)ret;
+ if (bag_type >= GNUTLS_BAG_UNKNOWN) {
+ *err = ws_strdup_printf("gnutls_pkcs12_bag_get_type returned unknown bag type %u",
+ ret);
+ goto done;
+ }
+ ws_debug("Bag %d/%d decrypted: %s", i, j, BAGTYPE(bag_type));
+ }
+ }
+
+ ret = gnutls_pkcs12_bag_get_data(bag, j, &data);
+ if (ret < 0) {
+ *err = ws_strdup_printf("gnutls_pkcs12_bag_get_data failed: %s",
+ gnutls_strerror(ret));
+ goto done;
+ }
+
+ switch (bag_type) {
+
+ case GNUTLS_BAG_PKCS8_KEY:
+ case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY:
+ {
+ gnutls_x509_privkey_t rsa_pkey;
+
+ ret = gnutls_x509_privkey_init(&rsa_pkey);
+ if (ret < 0) {
+ *err = ws_strdup_printf("gnutls_x509_privkey_init failed: %s", gnutls_strerror(ret));
+ goto done;
+ }
+ ret = gnutls_x509_privkey_import_pkcs8(rsa_pkey, &data, GNUTLS_X509_FMT_DER, cert_passwd,
+ (bag_type==GNUTLS_BAG_PKCS8_KEY) ? GNUTLS_PKCS_PLAIN : 0);
+ if (ret < 0) {
+ *err = ws_strdup_printf("Can not decrypt private key - %s", gnutls_strerror(ret));
+ gnutls_x509_privkey_deinit(rsa_pkey);
+ goto done;
+ }
+
+ if (gnutls_x509_privkey_get_pk_algorithm(rsa_pkey) != GNUTLS_PK_RSA) {
+ *err = g_strdup("private key public key algorithm isn't RSA");
+ gnutls_x509_privkey_deinit(rsa_pkey);
+ goto done;
+ }
+
+ /* Private key found, return it. */
+ priv_key = rsa_pkey;
+ goto done;
+ }
+
+ default: ;
+ }
+ } /* j */
+
+ gnutls_pkcs12_bag_deinit(bag);
+ bag = NULL;
+ } /* i */
+
+done:
+ if (bag) {
+ gnutls_pkcs12_bag_deinit(bag);
+ }
+ if (!priv_key) {
+ /*
+ * We failed. If we didn't fail with an error, we failed because
+ * we found no PKCS8 key and fell out of the loop; report that
+ * error.
+ */
+ if (*err == NULL)
+ *err = g_strdup("no PKCS8 key found");
+ }
+ gnutls_pkcs12_deinit(rsa_p12);
+
+ return priv_key;
+}
+
+void
+rsa_private_key_free(void * key)
+{
+ gcry_sexp_release((gcry_sexp_t) key);
+}
+
+#else /* ! defined(HAVE_LIBGNUTLS) */
+
+void
+rsa_private_key_free(void * key _U_)
+{
+}
+
+#endif /* HAVE_LIBGNUTLS */
+
+/*
+ * 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/wsutil/rsa.h b/wsutil/rsa.h
new file mode 100644
index 00000000..83817048
--- /dev/null
+++ b/wsutil/rsa.h
@@ -0,0 +1,44 @@
+/** @file
+ *
+ * Functions for RSA private key reading and use
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2007 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __RSA_H__
+#define __RSA_H__
+
+#include <wireshark.h>
+#include <gcrypt.h>
+
+#ifdef HAVE_LIBGNUTLS
+#include <stdio.h>
+#include <gnutls/abstract.h>
+WS_DLL_PUBLIC gcry_sexp_t rsa_privkey_to_sexp(gnutls_x509_privkey_t priv_key, char **err);
+
+/**
+ * Load an RSA private key from specified file
+ * @param fp the file that contain the key data
+ * @param [out] err error message upon failure; NULL upon success
+ * @return a pointer to the loaded key on success, or NULL upon failure
+ */
+WS_DLL_PUBLIC gnutls_x509_privkey_t rsa_load_pem_key(FILE* fp, char **err);
+
+/**
+ * Load a RSA private key from a PKCS#12 file (DER or PEM format)
+ * @param fp the file that contains the key data
+ * @param cert_passwd password to decrypt the PKCS#12 file
+ * @param [out] err error message upon failure; NULL upon success
+ * @return a pointer to the loaded key on success; NULL upon failure
+ */
+WS_DLL_PUBLIC gnutls_x509_privkey_t rsa_load_pkcs12(FILE* fp, const char *cert_passwd, char** err);
+#endif
+
+WS_DLL_PUBLIC void rsa_private_key_free(void * key);
+
+
+#endif /* __RSA_H__ */
diff --git a/wsutil/safe-math.h b/wsutil/safe-math.h
new file mode 100644
index 00000000..f40ee16e
--- /dev/null
+++ b/wsutil/safe-math.h
@@ -0,0 +1,1064 @@
+/* Overflow-safe math functions
+ * Portable Snippets - https://github.com/nemequ/portable-snippets
+ * Created by Evan Nemerson <evan@nemerson.com>
+ *
+ * To the extent possible under law, the authors have waived all
+ * copyright and related or neighboring rights to this code. For
+ * details, see the Creative Commons Zero 1.0 Universal license at
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#if !defined(PSNIP_SAFE_H)
+#define PSNIP_SAFE_H
+
+#if !defined(PSNIP_SAFE_FORCE_PORTABLE)
+# if defined(__has_builtin)
+# if __has_builtin(__builtin_add_overflow) && !defined(__ibmxl__)
+# define PSNIP_SAFE_HAVE_BUILTIN_OVERFLOW
+# endif
+# elif defined(__GNUC__) && (__GNUC__ >= 5) && !defined(__INTEL_COMPILER)
+# define PSNIP_SAFE_HAVE_BUILTIN_OVERFLOW
+# endif
+# if defined(__has_include)
+# if __has_include(<intsafe.h>)
+# define PSNIP_SAFE_HAVE_INTSAFE_H
+# endif
+# elif defined(_WIN32)
+# define PSNIP_SAFE_HAVE_INTSAFE_H
+# endif
+#endif /* !defined(PSNIP_SAFE_FORCE_PORTABLE) */
+
+#if defined(__GNUC__)
+# define PSNIP_SAFE_LIKELY(expr) __builtin_expect(!!(expr), 1)
+# define PSNIP_SAFE_UNLIKELY(expr) __builtin_expect(!!(expr), 0)
+#else
+# define PSNIP_SAFE_LIKELY(expr) !!(expr)
+# define PSNIP_SAFE_UNLIKELY(expr) !!(expr)
+#endif /* defined(__GNUC__) */
+
+#if !defined(PSNIP_SAFE_STATIC_INLINE)
+# if defined(__GNUC__)
+# define PSNIP_SAFE__COMPILER_ATTRIBUTES __attribute__((__unused__))
+# else
+# define PSNIP_SAFE__COMPILER_ATTRIBUTES
+# endif
+
+# if defined(HEDLEY_INLINE)
+# define PSNIP_SAFE__INLINE HEDLEY_INLINE
+# elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+# define PSNIP_SAFE__INLINE inline
+# elif defined(__GNUC_STDC_INLINE__)
+# define PSNIP_SAFE__INLINE __inline__
+# elif defined(_MSC_VER) && _MSC_VER >= 1200
+# define PSNIP_SAFE__INLINE __inline
+# else
+# define PSNIP_SAFE__INLINE
+# endif
+
+# define PSNIP_SAFE__FUNCTION PSNIP_SAFE__COMPILER_ATTRIBUTES static PSNIP_SAFE__INLINE
+#endif
+
+#if !defined(__cplusplus) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+# define psnip_safe_bool _Bool
+#else
+# define psnip_safe_bool int
+#endif
+
+#if !defined(PSNIP_SAFE_NO_FIXED)
+/* For maximum portability include the exact-int module from
+ portable snippets. */
+# if \
+ !defined(psnip_int64_t) || !defined(psnip_uint64_t) || \
+ !defined(psnip_int32_t) || !defined(psnip_uint32_t) || \
+ !defined(psnip_int16_t) || !defined(psnip_uint16_t) || \
+ !defined(psnip_int8_t) || !defined(psnip_uint8_t)
+# include <stdint.h>
+# if !defined(psnip_int64_t)
+# define psnip_int64_t int64_t
+# endif
+# if !defined(psnip_uint64_t)
+# define psnip_uint64_t uint64_t
+# endif
+# if !defined(psnip_int32_t)
+# define psnip_int32_t int32_t
+# endif
+# if !defined(psnip_uint32_t)
+# define psnip_uint32_t uint32_t
+# endif
+# if !defined(psnip_int16_t)
+# define psnip_int16_t int16_t
+# endif
+# if !defined(psnip_uint16_t)
+# define psnip_uint16_t uint16_t
+# endif
+# if !defined(psnip_int8_t)
+# define psnip_int8_t int8_t
+# endif
+# if !defined(psnip_uint8_t)
+# define psnip_uint8_t uint8_t
+# endif
+# endif
+#endif /* !defined(PSNIP_SAFE_NO_FIXED) */
+#include <limits.h>
+#include <stdlib.h>
+
+#if !defined(PSNIP_SAFE_SIZE_MAX)
+# if defined(__SIZE_MAX__)
+# define PSNIP_SAFE_SIZE_MAX __SIZE_MAX__
+# elif defined(PSNIP_EXACT_INT_HAVE_STDINT)
+# include <stdint.h>
+# endif
+#endif
+
+#if defined(PSNIP_SAFE_SIZE_MAX)
+# define PSNIP_SAFE__SIZE_MAX_RT PSNIP_SAFE_SIZE_MAX
+#else
+# define PSNIP_SAFE__SIZE_MAX_RT (~((size_t) 0))
+#endif
+
+#if defined(PSNIP_SAFE_HAVE_INTSAFE_H)
+/* In VS 10, stdint.h and intsafe.h both define (U)INTN_MIN/MAX, which
+ triggers warning C4005 (level 1). */
+# if defined(_MSC_VER) && (_MSC_VER == 1600)
+# pragma warning(push)
+# pragma warning(disable:4005)
+# endif
+# include <intsafe.h>
+# if defined(_MSC_VER) && (_MSC_VER == 1600)
+# pragma warning(pop)
+# endif
+#endif /* defined(PSNIP_SAFE_HAVE_INTSAFE_H) */
+
+/* If there is a type larger than the one we're concerned with it's
+ * likely much faster to simply promote the operands, perform the
+ * requested operation, verify that the result falls within the
+ * original type, then cast the result back to the original type. */
+
+#if !defined(PSNIP_SAFE_NO_PROMOTIONS)
+
+#define PSNIP_SAFE_DEFINE_LARGER_BINARY_OP(T, name, op_name, op) \
+ PSNIP_SAFE__FUNCTION psnip_safe_##name##_larger \
+ psnip_safe_larger_##name##_##op_name (T a, T b) { \
+ return ((psnip_safe_##name##_larger) a) op ((psnip_safe_##name##_larger) b); \
+ }
+
+#define PSNIP_SAFE_DEFINE_LARGER_UNARY_OP(T, name, op_name, op) \
+ PSNIP_SAFE__FUNCTION psnip_safe_##name##_larger \
+ psnip_safe_larger_##name##_##op_name (T value) { \
+ return (op ((psnip_safe_##name##_larger) value)); \
+ }
+
+#define PSNIP_SAFE_DEFINE_LARGER_SIGNED_OPS(T, name) \
+ PSNIP_SAFE_DEFINE_LARGER_BINARY_OP(T, name, add, +) \
+ PSNIP_SAFE_DEFINE_LARGER_BINARY_OP(T, name, sub, -) \
+ PSNIP_SAFE_DEFINE_LARGER_BINARY_OP(T, name, mul, *) \
+ PSNIP_SAFE_DEFINE_LARGER_BINARY_OP(T, name, div, /) \
+ PSNIP_SAFE_DEFINE_LARGER_BINARY_OP(T, name, mod, %) \
+ PSNIP_SAFE_DEFINE_LARGER_UNARY_OP (T, name, neg, -)
+
+#define PSNIP_SAFE_DEFINE_LARGER_UNSIGNED_OPS(T, name) \
+ PSNIP_SAFE_DEFINE_LARGER_BINARY_OP(T, name, add, +) \
+ PSNIP_SAFE_DEFINE_LARGER_BINARY_OP(T, name, sub, -) \
+ PSNIP_SAFE_DEFINE_LARGER_BINARY_OP(T, name, mul, *) \
+ PSNIP_SAFE_DEFINE_LARGER_BINARY_OP(T, name, div, /) \
+ PSNIP_SAFE_DEFINE_LARGER_BINARY_OP(T, name, mod, %)
+
+#define PSNIP_SAFE_IS_LARGER(ORIG_MAX, DEST_MAX) ((DEST_MAX / ORIG_MAX) >= ORIG_MAX)
+
+#if defined(__GNUC__) && ((__GNUC__ >= 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__SIZEOF_INT128__) && !defined(__ibmxl__)
+#define PSNIP_SAFE_HAVE_128
+typedef __int128 psnip_safe_int128_t;
+typedef unsigned __int128 psnip_safe_uint128_t;
+#endif /* defined(__GNUC__) */
+
+#if !defined(PSNIP_SAFE_NO_FIXED)
+#define PSNIP_SAFE_HAVE_INT8_LARGER
+#define PSNIP_SAFE_HAVE_UINT8_LARGER
+typedef psnip_int16_t psnip_safe_int8_larger;
+typedef psnip_uint16_t psnip_safe_uint8_larger;
+
+#define PSNIP_SAFE_HAVE_INT16_LARGER
+typedef psnip_int32_t psnip_safe_int16_larger;
+typedef psnip_uint32_t psnip_safe_uint16_larger;
+
+#define PSNIP_SAFE_HAVE_INT32_LARGER
+typedef psnip_int64_t psnip_safe_int32_larger;
+typedef psnip_uint64_t psnip_safe_uint32_larger;
+
+#if defined(PSNIP_SAFE_HAVE_128)
+#define PSNIP_SAFE_HAVE_INT64_LARGER
+typedef psnip_safe_int128_t psnip_safe_int64_larger;
+typedef psnip_safe_uint128_t psnip_safe_uint64_larger;
+#endif /* defined(PSNIP_SAFE_HAVE_128) */
+#endif /* !defined(PSNIP_SAFE_NO_FIXED) */
+
+#define PSNIP_SAFE_HAVE_LARGER_SCHAR
+#if PSNIP_SAFE_IS_LARGER(SCHAR_MAX, SHRT_MAX)
+typedef short psnip_safe_schar_larger;
+#elif PSNIP_SAFE_IS_LARGER(SCHAR_MAX, INT_MAX)
+typedef int psnip_safe_schar_larger;
+#elif PSNIP_SAFE_IS_LARGER(SCHAR_MAX, LONG_MAX)
+typedef long psnip_safe_schar_larger;
+#elif PSNIP_SAFE_IS_LARGER(SCHAR_MAX, LLONG_MAX)
+typedef long long psnip_safe_schar_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(SCHAR_MAX, 0x7fff)
+typedef psnip_int16_t psnip_safe_schar_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(SCHAR_MAX, 0x7fffffffLL)
+typedef psnip_int32_t psnip_safe_schar_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(SCHAR_MAX, 0x7fffffffffffffffLL)
+typedef psnip_int64_t psnip_safe_schar_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && defined(PSNIP_SAFE_HAVE_128) && (SCHAR_MAX <= 0x7fffffffffffffffLL)
+typedef psnip_safe_int128_t psnip_safe_schar_larger;
+#else
+#undef PSNIP_SAFE_HAVE_LARGER_SCHAR
+#endif
+
+#define PSNIP_SAFE_HAVE_LARGER_UCHAR
+#if PSNIP_SAFE_IS_LARGER(UCHAR_MAX, USHRT_MAX)
+typedef unsigned short psnip_safe_uchar_larger;
+#elif PSNIP_SAFE_IS_LARGER(UCHAR_MAX, UINT_MAX)
+typedef unsigned int psnip_safe_uchar_larger;
+#elif PSNIP_SAFE_IS_LARGER(UCHAR_MAX, ULONG_MAX)
+typedef unsigned long psnip_safe_uchar_larger;
+#elif PSNIP_SAFE_IS_LARGER(UCHAR_MAX, ULLONG_MAX)
+typedef unsigned long long psnip_safe_uchar_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(UCHAR_MAX, 0xffffU)
+typedef psnip_uint16_t psnip_safe_uchar_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(UCHAR_MAX, 0xffffffffUL)
+typedef psnip_uint32_t psnip_safe_uchar_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(UCHAR_MAX, 0xffffffffffffffffULL)
+typedef psnip_uint64_t psnip_safe_uchar_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && defined(PSNIP_SAFE_HAVE_128) && (UCHAR_MAX <= 0xffffffffffffffffULL)
+typedef psnip_safe_uint128_t psnip_safe_uchar_larger;
+#else
+#undef PSNIP_SAFE_HAVE_LARGER_UCHAR
+#endif
+
+#if CHAR_MIN == 0 && defined(PSNIP_SAFE_HAVE_LARGER_UCHAR)
+#define PSNIP_SAFE_HAVE_LARGER_CHAR
+typedef psnip_safe_uchar_larger psnip_safe_char_larger;
+#elif CHAR_MIN < 0 && defined(PSNIP_SAFE_HAVE_LARGER_SCHAR)
+#define PSNIP_SAFE_HAVE_LARGER_CHAR
+typedef psnip_safe_schar_larger psnip_safe_char_larger;
+#endif
+
+#define PSNIP_SAFE_HAVE_LARGER_SHRT
+#if PSNIP_SAFE_IS_LARGER(SHRT_MAX, INT_MAX)
+typedef int psnip_safe_short_larger;
+#elif PSNIP_SAFE_IS_LARGER(SHRT_MAX, LONG_MAX)
+typedef long psnip_safe_short_larger;
+#elif PSNIP_SAFE_IS_LARGER(SHRT_MAX, LLONG_MAX)
+typedef long long psnip_safe_short_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(SHRT_MAX, 0x7fff)
+typedef psnip_int16_t psnip_safe_short_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(SHRT_MAX, 0x7fffffffLL)
+typedef psnip_int32_t psnip_safe_short_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(SHRT_MAX, 0x7fffffffffffffffLL)
+typedef psnip_int64_t psnip_safe_short_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && defined(PSNIP_SAFE_HAVE_128) && (SHRT_MAX <= 0x7fffffffffffffffLL)
+typedef psnip_safe_int128_t psnip_safe_short_larger;
+#else
+#undef PSNIP_SAFE_HAVE_LARGER_SHRT
+#endif
+
+#define PSNIP_SAFE_HAVE_LARGER_USHRT
+#if PSNIP_SAFE_IS_LARGER(USHRT_MAX, UINT_MAX)
+typedef unsigned int psnip_safe_ushort_larger;
+#elif PSNIP_SAFE_IS_LARGER(USHRT_MAX, ULONG_MAX)
+typedef unsigned long psnip_safe_ushort_larger;
+#elif PSNIP_SAFE_IS_LARGER(USHRT_MAX, ULLONG_MAX)
+typedef unsigned long long psnip_safe_ushort_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(USHRT_MAX, 0xffff)
+typedef psnip_uint16_t psnip_safe_ushort_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(USHRT_MAX, 0xffffffffUL)
+typedef psnip_uint32_t psnip_safe_ushort_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(USHRT_MAX, 0xffffffffffffffffULL)
+typedef psnip_uint64_t psnip_safe_ushort_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && defined(PSNIP_SAFE_HAVE_128) && (USHRT_MAX <= 0xffffffffffffffffULL)
+typedef psnip_safe_uint128_t psnip_safe_ushort_larger;
+#else
+#undef PSNIP_SAFE_HAVE_LARGER_USHRT
+#endif
+
+#define PSNIP_SAFE_HAVE_LARGER_INT
+#if PSNIP_SAFE_IS_LARGER(INT_MAX, LONG_MAX)
+typedef long psnip_safe_int_larger;
+#elif PSNIP_SAFE_IS_LARGER(INT_MAX, LLONG_MAX)
+typedef long long psnip_safe_int_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(INT_MAX, 0x7fff)
+typedef psnip_int16_t psnip_safe_int_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(INT_MAX, 0x7fffffffLL)
+typedef psnip_int32_t psnip_safe_int_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(INT_MAX, 0x7fffffffffffffffLL)
+typedef psnip_int64_t psnip_safe_int_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && defined(PSNIP_SAFE_HAVE_128) && (INT_MAX <= 0x7fffffffffffffffLL)
+typedef psnip_safe_int128_t psnip_safe_int_larger;
+#else
+#undef PSNIP_SAFE_HAVE_LARGER_INT
+#endif
+
+#define PSNIP_SAFE_HAVE_LARGER_UINT
+#if PSNIP_SAFE_IS_LARGER(UINT_MAX, ULONG_MAX)
+typedef unsigned long psnip_safe_uint_larger;
+#elif PSNIP_SAFE_IS_LARGER(UINT_MAX, ULLONG_MAX)
+typedef unsigned long long psnip_safe_uint_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(UINT_MAX, 0xffff)
+typedef psnip_uint16_t psnip_safe_uint_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(UINT_MAX, 0xffffffffUL)
+typedef psnip_uint32_t psnip_safe_uint_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(UINT_MAX, 0xffffffffffffffffULL)
+typedef psnip_uint64_t psnip_safe_uint_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && defined(PSNIP_SAFE_HAVE_128) && (UINT_MAX <= 0xffffffffffffffffULL)
+typedef psnip_safe_uint128_t psnip_safe_uint_larger;
+#else
+#undef PSNIP_SAFE_HAVE_LARGER_UINT
+#endif
+
+#define PSNIP_SAFE_HAVE_LARGER_LONG
+#if PSNIP_SAFE_IS_LARGER(LONG_MAX, LLONG_MAX)
+typedef long long psnip_safe_long_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(LONG_MAX, 0x7fff)
+typedef psnip_int16_t psnip_safe_long_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(LONG_MAX, 0x7fffffffLL)
+typedef psnip_int32_t psnip_safe_long_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(LONG_MAX, 0x7fffffffffffffffLL)
+typedef psnip_int64_t psnip_safe_long_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && defined(PSNIP_SAFE_HAVE_128) && (LONG_MAX <= 0x7fffffffffffffffLL)
+typedef psnip_safe_int128_t psnip_safe_long_larger;
+#else
+#undef PSNIP_SAFE_HAVE_LARGER_LONG
+#endif
+
+#define PSNIP_SAFE_HAVE_LARGER_ULONG
+#if PSNIP_SAFE_IS_LARGER(ULONG_MAX, ULLONG_MAX)
+typedef unsigned long long psnip_safe_ulong_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(ULONG_MAX, 0xffff)
+typedef psnip_uint16_t psnip_safe_ulong_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(ULONG_MAX, 0xffffffffUL)
+typedef psnip_uint32_t psnip_safe_ulong_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(ULONG_MAX, 0xffffffffffffffffULL)
+typedef psnip_uint64_t psnip_safe_ulong_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && defined(PSNIP_SAFE_HAVE_128) && (ULONG_MAX <= 0xffffffffffffffffULL)
+typedef psnip_safe_uint128_t psnip_safe_ulong_larger;
+#else
+#undef PSNIP_SAFE_HAVE_LARGER_ULONG
+#endif
+
+#define PSNIP_SAFE_HAVE_LARGER_LLONG
+#if !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(LLONG_MAX, 0x7fff)
+typedef psnip_int16_t psnip_safe_llong_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(LLONG_MAX, 0x7fffffffLL)
+typedef psnip_int32_t psnip_safe_llong_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(LLONG_MAX, 0x7fffffffffffffffLL)
+typedef psnip_int64_t psnip_safe_llong_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && defined(PSNIP_SAFE_HAVE_128) && (LLONG_MAX <= 0x7fffffffffffffffLL)
+typedef psnip_safe_int128_t psnip_safe_llong_larger;
+#else
+#undef PSNIP_SAFE_HAVE_LARGER_LLONG
+#endif
+
+#define PSNIP_SAFE_HAVE_LARGER_ULLONG
+#if !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(ULLONG_MAX, 0xffff)
+typedef psnip_uint16_t psnip_safe_ullong_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(ULLONG_MAX, 0xffffffffUL)
+typedef psnip_uint32_t psnip_safe_ullong_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(ULLONG_MAX, 0xffffffffffffffffULL)
+typedef psnip_uint64_t psnip_safe_ullong_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && defined(PSNIP_SAFE_HAVE_128) && (ULLONG_MAX <= 0xffffffffffffffffULL)
+typedef psnip_safe_uint128_t psnip_safe_ullong_larger;
+#else
+#undef PSNIP_SAFE_HAVE_LARGER_ULLONG
+#endif
+
+#if defined(PSNIP_SAFE_SIZE_MAX)
+#define PSNIP_SAFE_HAVE_LARGER_SIZE
+#if PSNIP_SAFE_IS_LARGER(PSNIP_SAFE_SIZE_MAX, USHRT_MAX)
+typedef unsigned short psnip_safe_size_larger;
+#elif PSNIP_SAFE_IS_LARGER(PSNIP_SAFE_SIZE_MAX, UINT_MAX)
+typedef unsigned int psnip_safe_size_larger;
+#elif PSNIP_SAFE_IS_LARGER(PSNIP_SAFE_SIZE_MAX, ULONG_MAX)
+typedef unsigned long psnip_safe_size_larger;
+#elif PSNIP_SAFE_IS_LARGER(PSNIP_SAFE_SIZE_MAX, ULLONG_MAX)
+typedef unsigned long long psnip_safe_size_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(PSNIP_SAFE_SIZE_MAX, 0xffff)
+typedef psnip_uint16_t psnip_safe_size_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(PSNIP_SAFE_SIZE_MAX, 0xffffffffUL)
+typedef psnip_uint32_t psnip_safe_size_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && PSNIP_SAFE_IS_LARGER(PSNIP_SAFE_SIZE_MAX, 0xffffffffffffffffULL)
+typedef psnip_uint64_t psnip_safe_size_larger;
+#elif !defined(PSNIP_SAFE_NO_FIXED) && defined(PSNIP_SAFE_HAVE_128) && (PSNIP_SAFE_SIZE_MAX <= 0xffffffffffffffffULL)
+typedef psnip_safe_uint128_t psnip_safe_size_larger;
+#else
+#undef PSNIP_SAFE_HAVE_LARGER_SIZE
+#endif
+#endif
+
+#if defined(PSNIP_SAFE_HAVE_LARGER_SCHAR)
+PSNIP_SAFE_DEFINE_LARGER_SIGNED_OPS(signed char, schar)
+#endif
+
+#if defined(PSNIP_SAFE_HAVE_LARGER_UCHAR)
+PSNIP_SAFE_DEFINE_LARGER_UNSIGNED_OPS(unsigned char, uchar)
+#endif
+
+#if defined(PSNIP_SAFE_HAVE_LARGER_CHAR)
+#if CHAR_MIN == 0
+PSNIP_SAFE_DEFINE_LARGER_UNSIGNED_OPS(char, char)
+#else
+PSNIP_SAFE_DEFINE_LARGER_SIGNED_OPS(char, char)
+#endif
+#endif
+
+#if defined(PSNIP_SAFE_HAVE_LARGER_SHORT)
+PSNIP_SAFE_DEFINE_LARGER_SIGNED_OPS(short, short)
+#endif
+
+#if defined(PSNIP_SAFE_HAVE_LARGER_USHORT)
+PSNIP_SAFE_DEFINE_LARGER_UNSIGNED_OPS(unsigned short, ushort)
+#endif
+
+#if defined(PSNIP_SAFE_HAVE_LARGER_INT)
+PSNIP_SAFE_DEFINE_LARGER_SIGNED_OPS(int, int)
+#endif
+
+#if defined(PSNIP_SAFE_HAVE_LARGER_UINT)
+PSNIP_SAFE_DEFINE_LARGER_UNSIGNED_OPS(unsigned int, uint)
+#endif
+
+#if defined(PSNIP_SAFE_HAVE_LARGER_LONG)
+PSNIP_SAFE_DEFINE_LARGER_SIGNED_OPS(long, long)
+#endif
+
+#if defined(PSNIP_SAFE_HAVE_LARGER_ULONG)
+PSNIP_SAFE_DEFINE_LARGER_UNSIGNED_OPS(unsigned long, ulong)
+#endif
+
+#if defined(PSNIP_SAFE_HAVE_LARGER_LLONG)
+PSNIP_SAFE_DEFINE_LARGER_SIGNED_OPS(long long, llong)
+#endif
+
+#if defined(PSNIP_SAFE_HAVE_LARGER_ULLONG)
+PSNIP_SAFE_DEFINE_LARGER_UNSIGNED_OPS(unsigned long long, ullong)
+#endif
+
+#if defined(PSNIP_SAFE_HAVE_LARGER_SIZE)
+PSNIP_SAFE_DEFINE_LARGER_UNSIGNED_OPS(size_t, size)
+#endif
+
+#if !defined(PSNIP_SAFE_NO_FIXED)
+PSNIP_SAFE_DEFINE_LARGER_SIGNED_OPS(psnip_int8_t, int8)
+PSNIP_SAFE_DEFINE_LARGER_UNSIGNED_OPS(psnip_uint8_t, uint8)
+PSNIP_SAFE_DEFINE_LARGER_SIGNED_OPS(psnip_int16_t, int16)
+PSNIP_SAFE_DEFINE_LARGER_UNSIGNED_OPS(psnip_uint16_t, uint16)
+PSNIP_SAFE_DEFINE_LARGER_SIGNED_OPS(psnip_int32_t, int32)
+PSNIP_SAFE_DEFINE_LARGER_UNSIGNED_OPS(psnip_uint32_t, uint32)
+#if defined(PSNIP_SAFE_HAVE_128)
+PSNIP_SAFE_DEFINE_LARGER_SIGNED_OPS(psnip_int64_t, int64)
+PSNIP_SAFE_DEFINE_LARGER_UNSIGNED_OPS(psnip_uint64_t, uint64)
+#endif
+#endif
+
+#endif /* !defined(PSNIP_SAFE_NO_PROMOTIONS) */
+
+#define PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(T, name, op_name) \
+ PSNIP_SAFE__FUNCTION psnip_safe_bool \
+ psnip_safe_##name##_##op_name(T* res, T a, T b) { \
+ return !__builtin_##op_name##_overflow(a, b, res); \
+ }
+
+#define PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(T, name, op_name, min, max) \
+ PSNIP_SAFE__FUNCTION psnip_safe_bool \
+ psnip_safe_##name##_##op_name(T* res, T a, T b) { \
+ const psnip_safe_##name##_larger r = psnip_safe_larger_##name##_##op_name(a, b); \
+ *res = (T) r; \
+ return (r >= min) && (r <= max); \
+ }
+
+#define PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(T, name, op_name, max) \
+ PSNIP_SAFE__FUNCTION psnip_safe_bool \
+ psnip_safe_##name##_##op_name(T* res, T a, T b) { \
+ const psnip_safe_##name##_larger r = psnip_safe_larger_##name##_##op_name(a, b); \
+ *res = (T) r; \
+ return (r <= max); \
+ }
+
+#define PSNIP_SAFE_DEFINE_SIGNED_ADD(T, name, min, max) \
+ PSNIP_SAFE__FUNCTION psnip_safe_bool \
+ psnip_safe_##name##_add (T* res, T a, T b) { \
+ psnip_safe_bool r = !( ((b > 0) && (a > (max - b))) || \
+ ((b < 0) && (a < (min - b))) ); \
+ if(PSNIP_SAFE_LIKELY(r)) \
+ *res = a + b; \
+ return r; \
+ }
+
+#define PSNIP_SAFE_DEFINE_UNSIGNED_ADD(T, name, max) \
+ PSNIP_SAFE__FUNCTION psnip_safe_bool \
+ psnip_safe_##name##_add (T* res, T a, T b) { \
+ *res = (T) (a + b); \
+ return !PSNIP_SAFE_UNLIKELY((b > 0) && (a > (max - b))); \
+ }
+
+#define PSNIP_SAFE_DEFINE_SIGNED_SUB(T, name, min, max) \
+ PSNIP_SAFE__FUNCTION psnip_safe_bool \
+ psnip_safe_##name##_sub (T* res, T a, T b) { \
+ psnip_safe_bool r = !((b > 0 && a < (min + b)) || \
+ (b < 0 && a > (max + b))); \
+ if(PSNIP_SAFE_LIKELY(r)) \
+ *res = a - b; \
+ return r; \
+ }
+
+#define PSNIP_SAFE_DEFINE_UNSIGNED_SUB(T, name, max) \
+ PSNIP_SAFE__FUNCTION psnip_safe_bool \
+ psnip_safe_##name##_sub (T* res, T a, T b) { \
+ *res = a - b; \
+ return !PSNIP_SAFE_UNLIKELY(b > a); \
+ }
+
+#define PSNIP_SAFE_DEFINE_SIGNED_MUL(T, name, min, max) \
+ PSNIP_SAFE__FUNCTION psnip_safe_bool \
+ psnip_safe_##name##_mul (T* res, T a, T b) { \
+ psnip_safe_bool r = 1; \
+ if (a > 0) { \
+ if (b > 0) { \
+ if (a > (max / b)) { \
+ r = 0; \
+ } \
+ } else { \
+ if (b < (min / a)) { \
+ r = 0; \
+ } \
+ } \
+ } else { \
+ if (b > 0) { \
+ if (a < (min / b)) { \
+ r = 0; \
+ } \
+ } else { \
+ if ( (a != 0) && (b < (max / a))) { \
+ r = 0; \
+ } \
+ } \
+ } \
+ if(PSNIP_SAFE_LIKELY(r)) \
+ *res = a * b; \
+ return r; \
+ }
+
+#define PSNIP_SAFE_DEFINE_UNSIGNED_MUL(T, name, max) \
+ PSNIP_SAFE__FUNCTION psnip_safe_bool \
+ psnip_safe_##name##_mul (T* res, T a, T b) { \
+ *res = (T) (a * b); \
+ return !PSNIP_SAFE_UNLIKELY((a > 0) && (b > 0) && (a > (max / b))); \
+ }
+
+#define PSNIP_SAFE_DEFINE_SIGNED_DIV(T, name, min, max) \
+ PSNIP_SAFE__FUNCTION psnip_safe_bool \
+ psnip_safe_##name##_div (T* res, T a, T b) { \
+ if (PSNIP_SAFE_UNLIKELY(b == 0)) { \
+ *res = 0; \
+ return 0; \
+ } else if (PSNIP_SAFE_UNLIKELY(a == min && b == -1)) { \
+ *res = min; \
+ return 0; \
+ } else { \
+ *res = (T) (a / b); \
+ return 1; \
+ } \
+ }
+
+#define PSNIP_SAFE_DEFINE_UNSIGNED_DIV(T, name, max) \
+ PSNIP_SAFE__FUNCTION psnip_safe_bool \
+ psnip_safe_##name##_div (T* res, T a, T b) { \
+ if (PSNIP_SAFE_UNLIKELY(b == 0)) { \
+ *res = 0; \
+ return 0; \
+ } else { \
+ *res = a / b; \
+ return 1; \
+ } \
+ }
+
+#define PSNIP_SAFE_DEFINE_SIGNED_MOD(T, name, min, max) \
+ PSNIP_SAFE__FUNCTION psnip_safe_bool \
+ psnip_safe_##name##_mod (T* res, T a, T b) { \
+ if (PSNIP_SAFE_UNLIKELY(b == 0)) { \
+ *res = 0; \
+ return 0; \
+ } else if (PSNIP_SAFE_UNLIKELY(a == min && b == -1)) { \
+ *res = min; \
+ return 0; \
+ } else { \
+ *res = (T) (a % b); \
+ return 1; \
+ } \
+ }
+
+#define PSNIP_SAFE_DEFINE_UNSIGNED_MOD(T, name, max) \
+ PSNIP_SAFE__FUNCTION psnip_safe_bool \
+ psnip_safe_##name##_mod (T* res, T a, T b) { \
+ if (PSNIP_SAFE_UNLIKELY(b == 0)) { \
+ *res = 0; \
+ return 0; \
+ } else { \
+ *res = a % b; \
+ return 1; \
+ } \
+ }
+
+#define PSNIP_SAFE_DEFINE_SIGNED_NEG(T, name, min, max) \
+ PSNIP_SAFE__FUNCTION psnip_safe_bool \
+ psnip_safe_##name##_neg (T* res, T value) { \
+ psnip_safe_bool r = value != min; \
+ *res = PSNIP_SAFE_LIKELY(r) ? -value : max; \
+ return r; \
+ }
+
+#define PSNIP_SAFE_DEFINE_INTSAFE(T, name, op, isf) \
+ PSNIP_SAFE__FUNCTION psnip_safe_bool \
+ psnip_safe_##name##_##op (T* res, T a, T b) { \
+ return isf(a, b, res) == S_OK; \
+ }
+
+#if CHAR_MIN == 0
+#if defined(PSNIP_SAFE_HAVE_BUILTIN_OVERFLOW)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(char, char, add)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(char, char, sub)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(char, char, mul)
+#elif defined(PSNIP_SAFE_HAVE_LARGER_CHAR)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(char, char, add, CHAR_MAX)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(char, char, sub, CHAR_MAX)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(char, char, mul, CHAR_MAX)
+#else
+PSNIP_SAFE_DEFINE_UNSIGNED_ADD(char, char, CHAR_MAX)
+PSNIP_SAFE_DEFINE_UNSIGNED_SUB(char, char, CHAR_MAX)
+PSNIP_SAFE_DEFINE_UNSIGNED_MUL(char, char, CHAR_MAX)
+#endif
+PSNIP_SAFE_DEFINE_UNSIGNED_DIV(char, char, CHAR_MAX)
+PSNIP_SAFE_DEFINE_UNSIGNED_MOD(char, char, CHAR_MAX)
+#else /* CHAR_MIN != 0 */
+#if defined(PSNIP_SAFE_HAVE_BUILTIN_OVERFLOW)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(char, char, add)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(char, char, sub)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(char, char, mul)
+#elif defined(PSNIP_SAFE_HAVE_LARGER_CHAR)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(char, char, add, CHAR_MIN, CHAR_MAX)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(char, char, sub, CHAR_MIN, CHAR_MAX)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(char, char, mul, CHAR_MIN, CHAR_MAX)
+#else
+PSNIP_SAFE_DEFINE_SIGNED_ADD(char, char, CHAR_MIN, CHAR_MAX)
+PSNIP_SAFE_DEFINE_SIGNED_SUB(char, char, CHAR_MIN, CHAR_MAX)
+PSNIP_SAFE_DEFINE_SIGNED_MUL(char, char, CHAR_MIN, CHAR_MAX)
+#endif
+PSNIP_SAFE_DEFINE_SIGNED_DIV(char, char, CHAR_MIN, CHAR_MAX)
+PSNIP_SAFE_DEFINE_SIGNED_MOD(char, char, CHAR_MIN, CHAR_MAX)
+PSNIP_SAFE_DEFINE_SIGNED_NEG(char, char, CHAR_MIN, CHAR_MAX)
+#endif
+
+#if defined(PSNIP_SAFE_HAVE_BUILTIN_OVERFLOW)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(signed char, schar, add)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(signed char, schar, sub)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(signed char, schar, mul)
+#elif defined(PSNIP_SAFE_HAVE_LARGER_SCHAR)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(signed char, schar, add, SCHAR_MIN, SCHAR_MAX)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(signed char, schar, sub, SCHAR_MIN, SCHAR_MAX)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(signed char, schar, mul, SCHAR_MIN, SCHAR_MAX)
+#else
+PSNIP_SAFE_DEFINE_SIGNED_ADD(signed char, schar, SCHAR_MIN, SCHAR_MAX)
+PSNIP_SAFE_DEFINE_SIGNED_SUB(signed char, schar, SCHAR_MIN, SCHAR_MAX)
+PSNIP_SAFE_DEFINE_SIGNED_MUL(signed char, schar, SCHAR_MIN, SCHAR_MAX)
+#endif
+PSNIP_SAFE_DEFINE_SIGNED_DIV(signed char, schar, SCHAR_MIN, SCHAR_MAX)
+PSNIP_SAFE_DEFINE_SIGNED_MOD(signed char, schar, SCHAR_MIN, SCHAR_MAX)
+PSNIP_SAFE_DEFINE_SIGNED_NEG(signed char, schar, SCHAR_MIN, SCHAR_MAX)
+
+#if defined(PSNIP_SAFE_HAVE_BUILTIN_OVERFLOW)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(unsigned char, uchar, add)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(unsigned char, uchar, sub)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(unsigned char, uchar, mul)
+#elif defined(PSNIP_SAFE_HAVE_LARGER_UCHAR)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(unsigned char, uchar, add, UCHAR_MAX)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(unsigned char, uchar, sub, UCHAR_MAX)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(unsigned char, uchar, mul, UCHAR_MAX)
+#else
+PSNIP_SAFE_DEFINE_UNSIGNED_ADD(unsigned char, uchar, UCHAR_MAX)
+PSNIP_SAFE_DEFINE_UNSIGNED_SUB(unsigned char, uchar, UCHAR_MAX)
+PSNIP_SAFE_DEFINE_UNSIGNED_MUL(unsigned char, uchar, UCHAR_MAX)
+#endif
+PSNIP_SAFE_DEFINE_UNSIGNED_DIV(unsigned char, uchar, UCHAR_MAX)
+PSNIP_SAFE_DEFINE_UNSIGNED_MOD(unsigned char, uchar, UCHAR_MAX)
+
+#if defined(PSNIP_SAFE_HAVE_BUILTIN_OVERFLOW)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(short, short, add)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(short, short, sub)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(short, short, mul)
+#elif defined(PSNIP_SAFE_HAVE_LARGER_SHORT)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(short, short, add, SHRT_MIN, SHRT_MAX)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(short, short, sub, SHRT_MIN, SHRT_MAX)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(short, short, mul, SHRT_MIN, SHRT_MAX)
+#else
+PSNIP_SAFE_DEFINE_SIGNED_ADD(short, short, SHRT_MIN, SHRT_MAX)
+PSNIP_SAFE_DEFINE_SIGNED_SUB(short, short, SHRT_MIN, SHRT_MAX)
+PSNIP_SAFE_DEFINE_SIGNED_MUL(short, short, SHRT_MIN, SHRT_MAX)
+#endif
+PSNIP_SAFE_DEFINE_SIGNED_DIV(short, short, SHRT_MIN, SHRT_MAX)
+PSNIP_SAFE_DEFINE_SIGNED_MOD(short, short, SHRT_MIN, SHRT_MAX)
+PSNIP_SAFE_DEFINE_SIGNED_NEG(short, short, SHRT_MIN, SHRT_MAX)
+
+#if defined(PSNIP_SAFE_HAVE_BUILTIN_OVERFLOW)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(unsigned short, ushort, add)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(unsigned short, ushort, sub)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(unsigned short, ushort, mul)
+#elif defined(PSNIP_SAFE_HAVE_INTSAFE_H)
+PSNIP_SAFE_DEFINE_INTSAFE(unsigned short, ushort, add, UShortAdd)
+PSNIP_SAFE_DEFINE_INTSAFE(unsigned short, ushort, sub, UShortSub)
+PSNIP_SAFE_DEFINE_INTSAFE(unsigned short, ushort, mul, UShortMult)
+#elif defined(PSNIP_SAFE_HAVE_LARGER_USHORT)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(unsigned short, ushort, add, USHRT_MAX)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(unsigned short, ushort, sub, USHRT_MAX)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(unsigned short, ushort, mul, USHRT_MAX)
+#else
+PSNIP_SAFE_DEFINE_UNSIGNED_ADD(unsigned short, ushort, USHRT_MAX)
+PSNIP_SAFE_DEFINE_UNSIGNED_SUB(unsigned short, ushort, USHRT_MAX)
+PSNIP_SAFE_DEFINE_UNSIGNED_MUL(unsigned short, ushort, USHRT_MAX)
+#endif
+PSNIP_SAFE_DEFINE_UNSIGNED_DIV(unsigned short, ushort, USHRT_MAX)
+PSNIP_SAFE_DEFINE_UNSIGNED_MOD(unsigned short, ushort, USHRT_MAX)
+
+#if defined(PSNIP_SAFE_HAVE_BUILTIN_OVERFLOW)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(int, int, add)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(int, int, sub)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(int, int, mul)
+#elif defined(PSNIP_SAFE_HAVE_LARGER_INT)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(int, int, add, INT_MIN, INT_MAX)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(int, int, sub, INT_MIN, INT_MAX)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(int, int, mul, INT_MIN, INT_MAX)
+#else
+PSNIP_SAFE_DEFINE_SIGNED_ADD(int, int, INT_MIN, INT_MAX)
+PSNIP_SAFE_DEFINE_SIGNED_SUB(int, int, INT_MIN, INT_MAX)
+PSNIP_SAFE_DEFINE_SIGNED_MUL(int, int, INT_MIN, INT_MAX)
+#endif
+PSNIP_SAFE_DEFINE_SIGNED_DIV(int, int, INT_MIN, INT_MAX)
+PSNIP_SAFE_DEFINE_SIGNED_MOD(int, int, INT_MIN, INT_MAX)
+PSNIP_SAFE_DEFINE_SIGNED_NEG(int, int, INT_MIN, INT_MAX)
+
+#if defined(PSNIP_SAFE_HAVE_BUILTIN_OVERFLOW)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(unsigned int, uint, add)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(unsigned int, uint, sub)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(unsigned int, uint, mul)
+#elif defined(PSNIP_SAFE_HAVE_INTSAFE_H)
+PSNIP_SAFE_DEFINE_INTSAFE(unsigned int, uint, add, UIntAdd)
+PSNIP_SAFE_DEFINE_INTSAFE(unsigned int, uint, sub, UIntSub)
+PSNIP_SAFE_DEFINE_INTSAFE(unsigned int, uint, mul, UIntMult)
+#elif defined(PSNIP_SAFE_HAVE_LARGER_UINT)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(unsigned int, uint, add, UINT_MAX)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(unsigned int, uint, sub, UINT_MAX)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(unsigned int, uint, mul, UINT_MAX)
+#else
+PSNIP_SAFE_DEFINE_UNSIGNED_ADD(unsigned int, uint, UINT_MAX)
+PSNIP_SAFE_DEFINE_UNSIGNED_SUB(unsigned int, uint, UINT_MAX)
+PSNIP_SAFE_DEFINE_UNSIGNED_MUL(unsigned int, uint, UINT_MAX)
+#endif
+PSNIP_SAFE_DEFINE_UNSIGNED_DIV(unsigned int, uint, UINT_MAX)
+PSNIP_SAFE_DEFINE_UNSIGNED_MOD(unsigned int, uint, UINT_MAX)
+
+#if defined(PSNIP_SAFE_HAVE_BUILTIN_OVERFLOW)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(long, long, add)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(long, long, sub)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(long, long, mul)
+#elif defined(PSNIP_SAFE_HAVE_LARGER_LONG)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(long, long, add, LONG_MIN, LONG_MAX)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(long, long, sub, LONG_MIN, LONG_MAX)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(long, long, mul, LONG_MIN, LONG_MAX)
+#else
+PSNIP_SAFE_DEFINE_SIGNED_ADD(long, long, LONG_MIN, LONG_MAX)
+PSNIP_SAFE_DEFINE_SIGNED_SUB(long, long, LONG_MIN, LONG_MAX)
+PSNIP_SAFE_DEFINE_SIGNED_MUL(long, long, LONG_MIN, LONG_MAX)
+#endif
+PSNIP_SAFE_DEFINE_SIGNED_DIV(long, long, LONG_MIN, LONG_MAX)
+PSNIP_SAFE_DEFINE_SIGNED_MOD(long, long, LONG_MIN, LONG_MAX)
+PSNIP_SAFE_DEFINE_SIGNED_NEG(long, long, LONG_MIN, LONG_MAX)
+
+#if defined(PSNIP_SAFE_HAVE_BUILTIN_OVERFLOW)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(unsigned long, ulong, add)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(unsigned long, ulong, sub)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(unsigned long, ulong, mul)
+#elif defined(PSNIP_SAFE_HAVE_INTSAFE_H)
+PSNIP_SAFE_DEFINE_INTSAFE(unsigned long, ulong, add, ULongAdd)
+PSNIP_SAFE_DEFINE_INTSAFE(unsigned long, ulong, sub, ULongSub)
+PSNIP_SAFE_DEFINE_INTSAFE(unsigned long, ulong, mul, ULongMult)
+#elif defined(PSNIP_SAFE_HAVE_LARGER_ULONG)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(unsigned long, ulong, add, ULONG_MAX)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(unsigned long, ulong, sub, ULONG_MAX)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(unsigned long, ulong, mul, ULONG_MAX)
+#else
+PSNIP_SAFE_DEFINE_UNSIGNED_ADD(unsigned long, ulong, ULONG_MAX)
+PSNIP_SAFE_DEFINE_UNSIGNED_SUB(unsigned long, ulong, ULONG_MAX)
+PSNIP_SAFE_DEFINE_UNSIGNED_MUL(unsigned long, ulong, ULONG_MAX)
+#endif
+PSNIP_SAFE_DEFINE_UNSIGNED_DIV(unsigned long, ulong, ULONG_MAX)
+PSNIP_SAFE_DEFINE_UNSIGNED_MOD(unsigned long, ulong, ULONG_MAX)
+
+#if defined(PSNIP_SAFE_HAVE_BUILTIN_OVERFLOW)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(long long, llong, add)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(long long, llong, sub)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(long long, llong, mul)
+#elif defined(PSNIP_SAFE_HAVE_LARGER_LLONG)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(long long, llong, add, LLONG_MIN, LLONG_MAX)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(long long, llong, sub, LLONG_MIN, LLONG_MAX)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(long long, llong, mul, LLONG_MIN, LLONG_MAX)
+#else
+PSNIP_SAFE_DEFINE_SIGNED_ADD(long long, llong, LLONG_MIN, LLONG_MAX)
+PSNIP_SAFE_DEFINE_SIGNED_SUB(long long, llong, LLONG_MIN, LLONG_MAX)
+PSNIP_SAFE_DEFINE_SIGNED_MUL(long long, llong, LLONG_MIN, LLONG_MAX)
+#endif
+PSNIP_SAFE_DEFINE_SIGNED_DIV(long long, llong, LLONG_MIN, LLONG_MAX)
+PSNIP_SAFE_DEFINE_SIGNED_MOD(long long, llong, LLONG_MIN, LLONG_MAX)
+PSNIP_SAFE_DEFINE_SIGNED_NEG(long long, llong, LLONG_MIN, LLONG_MAX)
+
+#if defined(PSNIP_SAFE_HAVE_BUILTIN_OVERFLOW)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(unsigned long long, ullong, add)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(unsigned long long, ullong, sub)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(unsigned long long, ullong, mul)
+#elif defined(PSNIP_SAFE_HAVE_INTSAFE_H)
+PSNIP_SAFE_DEFINE_INTSAFE(unsigned long long, ullong, add, ULongLongAdd)
+PSNIP_SAFE_DEFINE_INTSAFE(unsigned long long, ullong, sub, ULongLongSub)
+PSNIP_SAFE_DEFINE_INTSAFE(unsigned long long, ullong, mul, ULongLongMult)
+#elif defined(PSNIP_SAFE_HAVE_LARGER_ULLONG)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(unsigned long long, ullong, add, ULLONG_MAX)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(unsigned long long, ullong, sub, ULLONG_MAX)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(unsigned long long, ullong, mul, ULLONG_MAX)
+#else
+PSNIP_SAFE_DEFINE_UNSIGNED_ADD(unsigned long long, ullong, ULLONG_MAX)
+PSNIP_SAFE_DEFINE_UNSIGNED_SUB(unsigned long long, ullong, ULLONG_MAX)
+PSNIP_SAFE_DEFINE_UNSIGNED_MUL(unsigned long long, ullong, ULLONG_MAX)
+#endif
+PSNIP_SAFE_DEFINE_UNSIGNED_DIV(unsigned long long, ullong, ULLONG_MAX)
+PSNIP_SAFE_DEFINE_UNSIGNED_MOD(unsigned long long, ullong, ULLONG_MAX)
+
+#if defined(PSNIP_SAFE_HAVE_BUILTIN_OVERFLOW)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(size_t, size, add)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(size_t, size, sub)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(size_t, size, mul)
+#elif defined(PSNIP_SAFE_HAVE_INTSAFE_H)
+PSNIP_SAFE_DEFINE_INTSAFE(size_t, size, add, SizeTAdd)
+PSNIP_SAFE_DEFINE_INTSAFE(size_t, size, sub, SizeTSub)
+PSNIP_SAFE_DEFINE_INTSAFE(size_t, size, mul, SizeTMult)
+#elif defined(PSNIP_SAFE_HAVE_LARGER_SIZE)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(size_t, size, add, PSNIP_SAFE__SIZE_MAX_RT)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(size_t, size, sub, PSNIP_SAFE__SIZE_MAX_RT)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(size_t, size, mul, PSNIP_SAFE__SIZE_MAX_RT)
+#else
+PSNIP_SAFE_DEFINE_UNSIGNED_ADD(size_t, size, PSNIP_SAFE__SIZE_MAX_RT)
+PSNIP_SAFE_DEFINE_UNSIGNED_SUB(size_t, size, PSNIP_SAFE__SIZE_MAX_RT)
+PSNIP_SAFE_DEFINE_UNSIGNED_MUL(size_t, size, PSNIP_SAFE__SIZE_MAX_RT)
+#endif
+PSNIP_SAFE_DEFINE_UNSIGNED_DIV(size_t, size, PSNIP_SAFE__SIZE_MAX_RT)
+PSNIP_SAFE_DEFINE_UNSIGNED_MOD(size_t, size, PSNIP_SAFE__SIZE_MAX_RT)
+
+#if !defined(PSNIP_SAFE_NO_FIXED)
+
+#if defined(PSNIP_SAFE_HAVE_BUILTIN_OVERFLOW)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(psnip_int8_t, int8, add)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(psnip_int8_t, int8, sub)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(psnip_int8_t, int8, mul)
+#elif defined(PSNIP_SAFE_HAVE_LARGER_INT8)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(psnip_int8_t, int8, add, (-0x7fLL-1), 0x7f)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(psnip_int8_t, int8, sub, (-0x7fLL-1), 0x7f)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(psnip_int8_t, int8, mul, (-0x7fLL-1), 0x7f)
+#else
+PSNIP_SAFE_DEFINE_SIGNED_ADD(psnip_int8_t, int8, (-0x7fLL-1), 0x7f)
+PSNIP_SAFE_DEFINE_SIGNED_SUB(psnip_int8_t, int8, (-0x7fLL-1), 0x7f)
+PSNIP_SAFE_DEFINE_SIGNED_MUL(psnip_int8_t, int8, (-0x7fLL-1), 0x7f)
+#endif
+PSNIP_SAFE_DEFINE_SIGNED_DIV(psnip_int8_t, int8, (-0x7fLL-1), 0x7f)
+PSNIP_SAFE_DEFINE_SIGNED_MOD(psnip_int8_t, int8, (-0x7fLL-1), 0x7f)
+PSNIP_SAFE_DEFINE_SIGNED_NEG(psnip_int8_t, int8, (-0x7fLL-1), 0x7f)
+
+#if defined(PSNIP_SAFE_HAVE_BUILTIN_OVERFLOW)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(psnip_uint8_t, uint8, add)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(psnip_uint8_t, uint8, sub)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(psnip_uint8_t, uint8, mul)
+#elif defined(PSNIP_SAFE_HAVE_LARGER_UINT8)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(psnip_uint8_t, uint8, add, 0xff)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(psnip_uint8_t, uint8, sub, 0xff)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(psnip_uint8_t, uint8, mul, 0xff)
+#else
+PSNIP_SAFE_DEFINE_UNSIGNED_ADD(psnip_uint8_t, uint8, 0xff)
+PSNIP_SAFE_DEFINE_UNSIGNED_SUB(psnip_uint8_t, uint8, 0xff)
+PSNIP_SAFE_DEFINE_UNSIGNED_MUL(psnip_uint8_t, uint8, 0xff)
+#endif
+PSNIP_SAFE_DEFINE_UNSIGNED_DIV(psnip_uint8_t, uint8, 0xff)
+PSNIP_SAFE_DEFINE_UNSIGNED_MOD(psnip_uint8_t, uint8, 0xff)
+
+#if defined(PSNIP_SAFE_HAVE_BUILTIN_OVERFLOW)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(psnip_int16_t, int16, add)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(psnip_int16_t, int16, sub)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(psnip_int16_t, int16, mul)
+#elif defined(PSNIP_SAFE_HAVE_LARGER_INT16)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(psnip_int16_t, int16, add, (-32767-1), 0x7fff)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(psnip_int16_t, int16, sub, (-32767-1), 0x7fff)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(psnip_int16_t, int16, mul, (-32767-1), 0x7fff)
+#else
+PSNIP_SAFE_DEFINE_SIGNED_ADD(psnip_int16_t, int16, (-32767-1), 0x7fff)
+PSNIP_SAFE_DEFINE_SIGNED_SUB(psnip_int16_t, int16, (-32767-1), 0x7fff)
+PSNIP_SAFE_DEFINE_SIGNED_MUL(psnip_int16_t, int16, (-32767-1), 0x7fff)
+#endif
+PSNIP_SAFE_DEFINE_SIGNED_DIV(psnip_int16_t, int16, (-32767-1), 0x7fff)
+PSNIP_SAFE_DEFINE_SIGNED_MOD(psnip_int16_t, int16, (-32767-1), 0x7fff)
+PSNIP_SAFE_DEFINE_SIGNED_NEG(psnip_int16_t, int16, (-32767-1), 0x7fff)
+
+#if defined(PSNIP_SAFE_HAVE_BUILTIN_OVERFLOW)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(psnip_uint16_t, uint16, add)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(psnip_uint16_t, uint16, sub)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(psnip_uint16_t, uint16, mul)
+#elif defined(PSNIP_SAFE_HAVE_INTSAFE_H) && defined(_WIN32)
+PSNIP_SAFE_DEFINE_INTSAFE(psnip_uint16_t, uint16, add, UShortAdd)
+PSNIP_SAFE_DEFINE_INTSAFE(psnip_uint16_t, uint16, sub, UShortSub)
+PSNIP_SAFE_DEFINE_INTSAFE(psnip_uint16_t, uint16, mul, UShortMult)
+#elif defined(PSNIP_SAFE_HAVE_LARGER_UINT16)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(psnip_uint16_t, uint16, add, 0xffff)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(psnip_uint16_t, uint16, sub, 0xffff)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(psnip_uint16_t, uint16, mul, 0xffff)
+#else
+PSNIP_SAFE_DEFINE_UNSIGNED_ADD(psnip_uint16_t, uint16, 0xffff)
+PSNIP_SAFE_DEFINE_UNSIGNED_SUB(psnip_uint16_t, uint16, 0xffff)
+PSNIP_SAFE_DEFINE_UNSIGNED_MUL(psnip_uint16_t, uint16, 0xffff)
+#endif
+PSNIP_SAFE_DEFINE_UNSIGNED_DIV(psnip_uint16_t, uint16, 0xffff)
+PSNIP_SAFE_DEFINE_UNSIGNED_MOD(psnip_uint16_t, uint16, 0xffff)
+
+#if defined(PSNIP_SAFE_HAVE_BUILTIN_OVERFLOW)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(psnip_int32_t, int32, add)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(psnip_int32_t, int32, sub)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(psnip_int32_t, int32, mul)
+#elif defined(PSNIP_SAFE_HAVE_LARGER_INT32)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(psnip_int32_t, int32, add, (-0x7fffffffLL-1), 0x7fffffffLL)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(psnip_int32_t, int32, sub, (-0x7fffffffLL-1), 0x7fffffffLL)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(psnip_int32_t, int32, mul, (-0x7fffffffLL-1), 0x7fffffffLL)
+#else
+PSNIP_SAFE_DEFINE_SIGNED_ADD(psnip_int32_t, int32, (-0x7fffffffLL-1), 0x7fffffffLL)
+PSNIP_SAFE_DEFINE_SIGNED_SUB(psnip_int32_t, int32, (-0x7fffffffLL-1), 0x7fffffffLL)
+PSNIP_SAFE_DEFINE_SIGNED_MUL(psnip_int32_t, int32, (-0x7fffffffLL-1), 0x7fffffffLL)
+#endif
+PSNIP_SAFE_DEFINE_SIGNED_DIV(psnip_int32_t, int32, (-0x7fffffffLL-1), 0x7fffffffLL)
+PSNIP_SAFE_DEFINE_SIGNED_MOD(psnip_int32_t, int32, (-0x7fffffffLL-1), 0x7fffffffLL)
+PSNIP_SAFE_DEFINE_SIGNED_NEG(psnip_int32_t, int32, (-0x7fffffffLL-1), 0x7fffffffLL)
+
+#if defined(PSNIP_SAFE_HAVE_BUILTIN_OVERFLOW)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(psnip_uint32_t, uint32, add)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(psnip_uint32_t, uint32, sub)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(psnip_uint32_t, uint32, mul)
+#elif defined(PSNIP_SAFE_HAVE_INTSAFE_H) && defined(_WIN32)
+PSNIP_SAFE_DEFINE_INTSAFE(psnip_uint32_t, uint32, add, UIntAdd)
+PSNIP_SAFE_DEFINE_INTSAFE(psnip_uint32_t, uint32, sub, UIntSub)
+PSNIP_SAFE_DEFINE_INTSAFE(psnip_uint32_t, uint32, mul, UIntMult)
+#elif defined(PSNIP_SAFE_HAVE_LARGER_UINT32)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(psnip_uint32_t, uint32, add, 0xffffffffUL)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(psnip_uint32_t, uint32, sub, 0xffffffffUL)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(psnip_uint32_t, uint32, mul, 0xffffffffUL)
+#else
+PSNIP_SAFE_DEFINE_UNSIGNED_ADD(psnip_uint32_t, uint32, 0xffffffffUL)
+PSNIP_SAFE_DEFINE_UNSIGNED_SUB(psnip_uint32_t, uint32, 0xffffffffUL)
+PSNIP_SAFE_DEFINE_UNSIGNED_MUL(psnip_uint32_t, uint32, 0xffffffffUL)
+#endif
+PSNIP_SAFE_DEFINE_UNSIGNED_DIV(psnip_uint32_t, uint32, 0xffffffffUL)
+PSNIP_SAFE_DEFINE_UNSIGNED_MOD(psnip_uint32_t, uint32, 0xffffffffUL)
+
+#if defined(PSNIP_SAFE_HAVE_BUILTIN_OVERFLOW)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(psnip_int64_t, int64, add)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(psnip_int64_t, int64, sub)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(psnip_int64_t, int64, mul)
+#elif defined(PSNIP_SAFE_HAVE_LARGER_INT64)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(psnip_int64_t, int64, add, (-0x7fffffffffffffffLL-1), 0x7fffffffffffffffLL)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(psnip_int64_t, int64, sub, (-0x7fffffffffffffffLL-1), 0x7fffffffffffffffLL)
+PSNIP_SAFE_DEFINE_PROMOTED_SIGNED_BINARY_OP(psnip_int64_t, int64, mul, (-0x7fffffffffffffffLL-1), 0x7fffffffffffffffLL)
+#else
+PSNIP_SAFE_DEFINE_SIGNED_ADD(psnip_int64_t, int64, (-0x7fffffffffffffffLL-1), 0x7fffffffffffffffLL)
+PSNIP_SAFE_DEFINE_SIGNED_SUB(psnip_int64_t, int64, (-0x7fffffffffffffffLL-1), 0x7fffffffffffffffLL)
+PSNIP_SAFE_DEFINE_SIGNED_MUL(psnip_int64_t, int64, (-0x7fffffffffffffffLL-1), 0x7fffffffffffffffLL)
+#endif
+PSNIP_SAFE_DEFINE_SIGNED_DIV(psnip_int64_t, int64, (-0x7fffffffffffffffLL-1), 0x7fffffffffffffffLL)
+PSNIP_SAFE_DEFINE_SIGNED_MOD(psnip_int64_t, int64, (-0x7fffffffffffffffLL-1), 0x7fffffffffffffffLL)
+PSNIP_SAFE_DEFINE_SIGNED_NEG(psnip_int64_t, int64, (-0x7fffffffffffffffLL-1), 0x7fffffffffffffffLL)
+
+#if defined(PSNIP_SAFE_HAVE_BUILTIN_OVERFLOW)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(psnip_uint64_t, uint64, add)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(psnip_uint64_t, uint64, sub)
+PSNIP_SAFE_DEFINE_BUILTIN_BINARY_OP(psnip_uint64_t, uint64, mul)
+#elif defined(PSNIP_SAFE_HAVE_INTSAFE_H) && defined(_WIN32)
+PSNIP_SAFE_DEFINE_INTSAFE(psnip_uint64_t, uint64, add, ULongLongAdd)
+PSNIP_SAFE_DEFINE_INTSAFE(psnip_uint64_t, uint64, sub, ULongLongSub)
+PSNIP_SAFE_DEFINE_INTSAFE(psnip_uint64_t, uint64, mul, ULongLongMult)
+#elif defined(PSNIP_SAFE_HAVE_LARGER_UINT64)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(psnip_uint64_t, uint64, add, 0xffffffffffffffffULL)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(psnip_uint64_t, uint64, sub, 0xffffffffffffffffULL)
+PSNIP_SAFE_DEFINE_PROMOTED_UNSIGNED_BINARY_OP(psnip_uint64_t, uint64, mul, 0xffffffffffffffffULL)
+#else
+PSNIP_SAFE_DEFINE_UNSIGNED_ADD(psnip_uint64_t, uint64, 0xffffffffffffffffULL)
+PSNIP_SAFE_DEFINE_UNSIGNED_SUB(psnip_uint64_t, uint64, 0xffffffffffffffffULL)
+PSNIP_SAFE_DEFINE_UNSIGNED_MUL(psnip_uint64_t, uint64, 0xffffffffffffffffULL)
+#endif
+PSNIP_SAFE_DEFINE_UNSIGNED_DIV(psnip_uint64_t, uint64, 0xffffffffffffffffULL)
+PSNIP_SAFE_DEFINE_UNSIGNED_MOD(psnip_uint64_t, uint64, 0xffffffffffffffffULL)
+
+#endif /* !defined(PSNIP_SAFE_NO_FIXED) */
+
+#define PSNIP_SAFE_C11_GENERIC_SELECTION(res, op) \
+ _Generic((*res), \
+ char: psnip_safe_char_##op, \
+ unsigned char: psnip_safe_uchar_##op, \
+ short: psnip_safe_short_##op, \
+ unsigned short: psnip_safe_ushort_##op, \
+ int: psnip_safe_int_##op, \
+ unsigned int: psnip_safe_uint_##op, \
+ long: psnip_safe_long_##op, \
+ unsigned long: psnip_safe_ulong_##op, \
+ long long: psnip_safe_llong_##op, \
+ unsigned long long: psnip_safe_ullong_##op)
+
+#define PSNIP_SAFE_C11_GENERIC_BINARY_OP(op, res, a, b) \
+ PSNIP_SAFE_C11_GENERIC_SELECTION(res, op)(res, a, b)
+#define PSNIP_SAFE_C11_GENERIC_UNARY_OP(op, res, v) \
+ PSNIP_SAFE_C11_GENERIC_SELECTION(res, op)(res, v)
+
+#if defined(PSNIP_SAFE_HAVE_BUILTIN_OVERFLOW)
+#define psnip_safe_add(res, a, b) !__builtin_add_overflow(a, b, res)
+#define psnip_safe_sub(res, a, b) !__builtin_sub_overflow(a, b, res)
+#define psnip_safe_mul(res, a, b) !__builtin_mul_overflow(a, b, res)
+#define psnip_safe_div(res, a, b) PSNIP_SAFE_C11_GENERIC_BINARY_OP(div, res, a, b)
+#define psnip_safe_mod(res, a, b) PSNIP_SAFE_C11_GENERIC_BINARY_OP(mod, res, a, b)
+#define psnip_safe_neg(res, v) PSNIP_SAFE_C11_GENERIC_UNARY_OP (neg, res, v)
+
+#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
+/* The are no fixed-length or size selections because they cause an
+ * error about _Generic specifying two compatible types. Hopefully
+ * this doesn't cause problems on exotic platforms, but if it does
+ * please let me know and I'll try to figure something out. */
+
+#define psnip_safe_add(res, a, b) PSNIP_SAFE_C11_GENERIC_BINARY_OP(add, res, a, b)
+#define psnip_safe_sub(res, a, b) PSNIP_SAFE_C11_GENERIC_BINARY_OP(sub, res, a, b)
+#define psnip_safe_mul(res, a, b) PSNIP_SAFE_C11_GENERIC_BINARY_OP(mul, res, a, b)
+#define psnip_safe_div(res, a, b) PSNIP_SAFE_C11_GENERIC_BINARY_OP(div, res, a, b)
+#define psnip_safe_mod(res, a, b) PSNIP_SAFE_C11_GENERIC_BINARY_OP(mod, res, a, b)
+#define psnip_safe_neg(res, v) PSNIP_SAFE_C11_GENERIC_UNARY_OP (neg, res, v)
+#endif
+
+#include <setjmp.h>
+
+#define ws_safe_op_jmp(op, res, a, b, env) \
+ do { \
+ if(!psnip_safe_##op(res, a, b)) { \
+ longjmp(env, 1); \
+ } \
+ } while (0)
+
+#define ws_safe_add_jmp(res, a, b, env) ws_safe_op_jmp(add, res, a, b, env)
+#define ws_safe_sub_jmp(res, a, b, env) ws_safe_op_jmp(sub, res, a, b, env)
+#define ws_safe_mul_jmp(res, a, b, env) ws_safe_op_jmp(mul, res, a, b, env)
+#define ws_safe_div_jmp(res, a, b, env) ws_safe_op_jmp(div, res, a, b, env)
+#define ws_safe_mod_jmp(res, a, b, env) ws_safe_op_jmp(mod, res, a, b, env)
+#define ws_safe_neg_jmp(res, a, b, env) ws_safe_op_jmp(neg, res, a, b, env)
+
+#endif /* !defined(PSNIP_SAFE_H) */
diff --git a/wsutil/sign_ext.h b/wsutil/sign_ext.h
new file mode 100644
index 00000000..67401edb
--- /dev/null
+++ b/wsutil/sign_ext.h
@@ -0,0 +1,71 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WSUTIL_SIGN_EXT_H__
+#define __WSUTIL_SIGN_EXT_H__
+
+#include <inttypes.h>
+
+#include <glib.h>
+
+#include <wsutil/ws_assert.h>
+
+/* sign extension routines */
+
+static inline uint32_t
+ws_sign_ext32(uint32_t val, int no_of_bits)
+{
+ ws_assert (no_of_bits >= 0 && no_of_bits <= 32);
+
+ if ((no_of_bits == 0) || (no_of_bits == 32))
+ return val;
+
+ /*
+ * Don't shift signed values left; that's not valid in C99, at
+ * least, if the value is negative or if the shift count is
+ * the number of bits in the value - 1, and we might get
+ * compile-time or run-time complaints about that.
+ */
+ if (val & (1U << (no_of_bits-1)))
+ val |= (0xFFFFFFFFU << no_of_bits);
+
+ return val;
+}
+
+static inline uint64_t
+ws_sign_ext64(uint64_t val, int no_of_bits)
+{
+ ws_assert (no_of_bits >= 0 && no_of_bits <= 64);
+
+ if ((no_of_bits == 0) || (no_of_bits == 64))
+ return val;
+
+ /*
+ * Don't shift signed values left; that's not valid in C99, at
+ * least, if the value is negative or if the shift count is
+ * the number of bits in the value - 1, and we might get
+ * compile-time or run-time complaints about that.
+ */
+ if (val & (G_GUINT64_CONSTANT(1) << (no_of_bits-1)))
+ val |= (G_GUINT64_CONSTANT(0xFFFFFFFFFFFFFFFF) << no_of_bits);
+
+ return val;
+}
+
+/*
+static inline uint64_t
+ws_sign_ext64(uint64_t val, int no_of_bits)
+{
+ int64_t sval = (val << (64 - no_of_bits));
+
+ return (uint64_t) (sval >> (64 - no_of_bits));
+}
+*/
+
+#endif /* __WSUTIL_SIGN_EXT_H__ */
diff --git a/wsutil/sober128.c b/wsutil/sober128.c
new file mode 100644
index 00000000..c84c114e
--- /dev/null
+++ b/wsutil/sober128.c
@@ -0,0 +1,488 @@
+/* This file is derived from sober128 implementation in corosync
+ cluster engine. corosync cluster engine borrows the implementation
+ from LibTomCrypt.
+
+ The latest version of the original code can be found at
+ http://www.libtom.net/LibTomCrypt/ according to which this code is in the
+ Public Domain
+ */
+
+/* About LibTomCrypt:
+ * ---------------------------------------------------------------------
+ * LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@iahu.ca, http://www.libtom.net/LibTomCrypt/
+ */
+
+#include "sober128.h"
+
+typedef unsigned long ulong32;
+typedef unsigned long long ulong64;
+
+/* ---- HELPER MACROS ---- */
+#define STORE32L(x, y) \
+ { (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255); \
+ (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); }
+
+#define LOAD32L(x, y) \
+ { x = ((unsigned long)((y)[3] & 255)<<24) | \
+ ((unsigned long)((y)[2] & 255)<<16) | \
+ ((unsigned long)((y)[1] & 255)<<8) | \
+ ((unsigned long)((y)[0] & 255)); }
+
+/* rotates the hard way */
+#define ROR(x, y) ( ((((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)((y)&31)) | ((unsigned long)(x)<<(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL)
+
+
+/* Id: s128multab.h 213 2003-12-16 04:27:12Z ggr $ */
+/* @(#)TuringMultab.h 1.3 (QUALCOMM) 02/09/03 */
+/* Multiplication table for Turing using 0xD02B4367 */
+
+static const ulong32 Multab[256] = {
+ 0x00000000, 0xD02B4367, 0xED5686CE, 0x3D7DC5A9,
+ 0x97AC41D1, 0x478702B6, 0x7AFAC71F, 0xAAD18478,
+ 0x631582EF, 0xB33EC188, 0x8E430421, 0x5E684746,
+ 0xF4B9C33E, 0x24928059, 0x19EF45F0, 0xC9C40697,
+ 0xC62A4993, 0x16010AF4, 0x2B7CCF5D, 0xFB578C3A,
+ 0x51860842, 0x81AD4B25, 0xBCD08E8C, 0x6CFBCDEB,
+ 0xA53FCB7C, 0x7514881B, 0x48694DB2, 0x98420ED5,
+ 0x32938AAD, 0xE2B8C9CA, 0xDFC50C63, 0x0FEE4F04,
+ 0xC154926B, 0x117FD10C, 0x2C0214A5, 0xFC2957C2,
+ 0x56F8D3BA, 0x86D390DD, 0xBBAE5574, 0x6B851613,
+ 0xA2411084, 0x726A53E3, 0x4F17964A, 0x9F3CD52D,
+ 0x35ED5155, 0xE5C61232, 0xD8BBD79B, 0x089094FC,
+ 0x077EDBF8, 0xD755989F, 0xEA285D36, 0x3A031E51,
+ 0x90D29A29, 0x40F9D94E, 0x7D841CE7, 0xADAF5F80,
+ 0x646B5917, 0xB4401A70, 0x893DDFD9, 0x59169CBE,
+ 0xF3C718C6, 0x23EC5BA1, 0x1E919E08, 0xCEBADD6F,
+ 0xCFA869D6, 0x1F832AB1, 0x22FEEF18, 0xF2D5AC7F,
+ 0x58042807, 0x882F6B60, 0xB552AEC9, 0x6579EDAE,
+ 0xACBDEB39, 0x7C96A85E, 0x41EB6DF7, 0x91C02E90,
+ 0x3B11AAE8, 0xEB3AE98F, 0xD6472C26, 0x066C6F41,
+ 0x09822045, 0xD9A96322, 0xE4D4A68B, 0x34FFE5EC,
+ 0x9E2E6194, 0x4E0522F3, 0x7378E75A, 0xA353A43D,
+ 0x6A97A2AA, 0xBABCE1CD, 0x87C12464, 0x57EA6703,
+ 0xFD3BE37B, 0x2D10A01C, 0x106D65B5, 0xC04626D2,
+ 0x0EFCFBBD, 0xDED7B8DA, 0xE3AA7D73, 0x33813E14,
+ 0x9950BA6C, 0x497BF90B, 0x74063CA2, 0xA42D7FC5,
+ 0x6DE97952, 0xBDC23A35, 0x80BFFF9C, 0x5094BCFB,
+ 0xFA453883, 0x2A6E7BE4, 0x1713BE4D, 0xC738FD2A,
+ 0xC8D6B22E, 0x18FDF149, 0x258034E0, 0xF5AB7787,
+ 0x5F7AF3FF, 0x8F51B098, 0xB22C7531, 0x62073656,
+ 0xABC330C1, 0x7BE873A6, 0x4695B60F, 0x96BEF568,
+ 0x3C6F7110, 0xEC443277, 0xD139F7DE, 0x0112B4B9,
+ 0xD31DD2E1, 0x03369186, 0x3E4B542F, 0xEE601748,
+ 0x44B19330, 0x949AD057, 0xA9E715FE, 0x79CC5699,
+ 0xB008500E, 0x60231369, 0x5D5ED6C0, 0x8D7595A7,
+ 0x27A411DF, 0xF78F52B8, 0xCAF29711, 0x1AD9D476,
+ 0x15379B72, 0xC51CD815, 0xF8611DBC, 0x284A5EDB,
+ 0x829BDAA3, 0x52B099C4, 0x6FCD5C6D, 0xBFE61F0A,
+ 0x7622199D, 0xA6095AFA, 0x9B749F53, 0x4B5FDC34,
+ 0xE18E584C, 0x31A51B2B, 0x0CD8DE82, 0xDCF39DE5,
+ 0x1249408A, 0xC26203ED, 0xFF1FC644, 0x2F348523,
+ 0x85E5015B, 0x55CE423C, 0x68B38795, 0xB898C4F2,
+ 0x715CC265, 0xA1778102, 0x9C0A44AB, 0x4C2107CC,
+ 0xE6F083B4, 0x36DBC0D3, 0x0BA6057A, 0xDB8D461D,
+ 0xD4630919, 0x04484A7E, 0x39358FD7, 0xE91ECCB0,
+ 0x43CF48C8, 0x93E40BAF, 0xAE99CE06, 0x7EB28D61,
+ 0xB7768BF6, 0x675DC891, 0x5A200D38, 0x8A0B4E5F,
+ 0x20DACA27, 0xF0F18940, 0xCD8C4CE9, 0x1DA70F8E,
+ 0x1CB5BB37, 0xCC9EF850, 0xF1E33DF9, 0x21C87E9E,
+ 0x8B19FAE6, 0x5B32B981, 0x664F7C28, 0xB6643F4F,
+ 0x7FA039D8, 0xAF8B7ABF, 0x92F6BF16, 0x42DDFC71,
+ 0xE80C7809, 0x38273B6E, 0x055AFEC7, 0xD571BDA0,
+ 0xDA9FF2A4, 0x0AB4B1C3, 0x37C9746A, 0xE7E2370D,
+ 0x4D33B375, 0x9D18F012, 0xA06535BB, 0x704E76DC,
+ 0xB98A704B, 0x69A1332C, 0x54DCF685, 0x84F7B5E2,
+ 0x2E26319A, 0xFE0D72FD, 0xC370B754, 0x135BF433,
+ 0xDDE1295C, 0x0DCA6A3B, 0x30B7AF92, 0xE09CECF5,
+ 0x4A4D688D, 0x9A662BEA, 0xA71BEE43, 0x7730AD24,
+ 0xBEF4ABB3, 0x6EDFE8D4, 0x53A22D7D, 0x83896E1A,
+ 0x2958EA62, 0xF973A905, 0xC40E6CAC, 0x14252FCB,
+ 0x1BCB60CF, 0xCBE023A8, 0xF69DE601, 0x26B6A566,
+ 0x8C67211E, 0x5C4C6279, 0x6131A7D0, 0xB11AE4B7,
+ 0x78DEE220, 0xA8F5A147, 0x958864EE, 0x45A32789,
+ 0xEF72A3F1, 0x3F59E096, 0x0224253F, 0xD20F6658,
+};
+
+/* Id: s128sbox.h 213 2003-12-16 04:27:12Z ggr $ */
+/* Sbox for SOBER-128 */
+/*
+ * This is really the combination of two SBoxes; the least significant
+ * 24 bits comes from:
+ * 8->32 Sbox generated by Millan et. al. at Queensland University of
+ * Technology. See: E. Dawson, W. Millan, L. Burnett, G. Carter,
+ * "On the Design of 8*32 S-boxes". Unpublished report, by the
+ * Information Systems Research Centre,
+ * Queensland University of Technology, 1999.
+ *
+ * The most significant 8 bits are the Skipjack "F table", which can be
+ * found at http://csrc.nist.gov/CryptoToolkit/skipjack/skipjack.pdf .
+ * In this optimised table, though, the intent is to XOR the word from
+ * the table selected by the high byte with the input word. Thus, the
+ * high byte is actually the Skipjack F-table entry XORED with its
+ * table index.
+ */
+static const ulong32 Sbox[256] = {
+ 0xa3aa1887, 0xd65e435c, 0x0b65c042, 0x800e6ef4,
+ 0xfc57ee20, 0x4d84fed3, 0xf066c502, 0xf354e8ae,
+ 0xbb2ee9d9, 0x281f38d4, 0x1f829b5d, 0x735cdf3c,
+ 0x95864249, 0xbc2e3963, 0xa1f4429f, 0xf6432c35,
+ 0xf7f40325, 0x3cc0dd70, 0x5f973ded, 0x9902dc5e,
+ 0xda175b42, 0x590012bf, 0xdc94d78c, 0x39aab26b,
+ 0x4ac11b9a, 0x8c168146, 0xc3ea8ec5, 0x058ac28f,
+ 0x52ed5c0f, 0x25b4101c, 0x5a2db082, 0x370929e1,
+ 0x2a1843de, 0xfe8299fc, 0x202fbc4b, 0x833915dd,
+ 0x33a803fa, 0xd446b2de, 0x46233342, 0x4fcee7c3,
+ 0x3ad607ef, 0x9e97ebab, 0x507f859b, 0xe81f2e2f,
+ 0xc55b71da, 0xd7e2269a, 0x1339c3d1, 0x7ca56b36,
+ 0xa6c9def2, 0xb5c9fc5f, 0x5927b3a3, 0x89a56ddf,
+ 0xc625b510, 0x560f85a7, 0xace82e71, 0x2ecb8816,
+ 0x44951e2a, 0x97f5f6af, 0xdfcbc2b3, 0xce4ff55d,
+ 0xcb6b6214, 0x2b0b83e3, 0x549ea6f5, 0x9de041af,
+ 0x792f1f17, 0xf73b99ee, 0x39a65ec0, 0x4c7016c6,
+ 0x857709a4, 0xd6326e01, 0xc7b280d9, 0x5cfb1418,
+ 0xa6aff227, 0xfd548203, 0x506b9d96, 0xa117a8c0,
+ 0x9cd5bf6e, 0xdcee7888, 0x61fcfe64, 0xf7a193cd,
+ 0x050d0184, 0xe8ae4930, 0x88014f36, 0xd6a87088,
+ 0x6bad6c2a, 0x1422c678, 0xe9204de7, 0xb7c2e759,
+ 0x0200248e, 0x013b446b, 0xda0d9fc2, 0x0414a895,
+ 0x3a6cc3a1, 0x56fef170, 0x86c19155, 0xcf7b8a66,
+ 0x551b5e69, 0xb4a8623e, 0xa2bdfa35, 0xc4f068cc,
+ 0x573a6acd, 0x6355e936, 0x03602db9, 0x0edf13c1,
+ 0x2d0bb16d, 0x6980b83c, 0xfeb23763, 0x3dd8a911,
+ 0x01b6bc13, 0xf55579d7, 0xf55c2fa8, 0x19f4196e,
+ 0xe7db5476, 0x8d64a866, 0xc06e16ad, 0xb17fc515,
+ 0xc46feb3c, 0x8bc8a306, 0xad6799d9, 0x571a9133,
+ 0x992466dd, 0x92eb5dcd, 0xac118f50, 0x9fafb226,
+ 0xa1b9cef3, 0x3ab36189, 0x347a19b1, 0x62c73084,
+ 0xc27ded5c, 0x6c8bc58f, 0x1cdde421, 0xed1e47fb,
+ 0xcdcc715e, 0xb9c0ff99, 0x4b122f0f, 0xc4d25184,
+ 0xaf7a5e6c, 0x5bbf18bc, 0x8dd7c6e0, 0x5fb7e420,
+ 0x521f523f, 0x4ad9b8a2, 0xe9da1a6b, 0x97888c02,
+ 0x19d1e354, 0x5aba7d79, 0xa2cc7753, 0x8c2d9655,
+ 0x19829da1, 0x531590a7, 0x19c1c149, 0x3d537f1c,
+ 0x50779b69, 0xed71f2b7, 0x463c58fa, 0x52dc4418,
+ 0xc18c8c76, 0xc120d9f0, 0xafa80d4d, 0x3b74c473,
+ 0xd09410e9, 0x290e4211, 0xc3c8082b, 0x8f6b334a,
+ 0x3bf68ed2, 0xa843cc1b, 0x8d3c0ff3, 0x20e564a0,
+ 0xf8f55a4f, 0x2b40f8e7, 0xfea7f15f, 0xcf00fe21,
+ 0x8a6d37d6, 0xd0d506f1, 0xade00973, 0xefbbde36,
+ 0x84670fa8, 0xfa31ab9e, 0xaedab618, 0xc01f52f5,
+ 0x6558eb4f, 0x71b9e343, 0x4b8d77dd, 0x8cb93da6,
+ 0x740fd52d, 0x425412f8, 0xc5a63360, 0x10e53ad0,
+ 0x5a700f1c, 0x8324ed0b, 0xe53dc1ec, 0x1a366795,
+ 0x6d549d15, 0xc5ce46d7, 0xe17abe76, 0x5f48e0a0,
+ 0xd0f07c02, 0x941249b7, 0xe49ed6ba, 0x37a47f78,
+ 0xe1cfffbd, 0xb007ca84, 0xbb65f4da, 0xb59f35da,
+ 0x33d2aa44, 0x417452ac, 0xc0d674a7, 0x2d61a46a,
+ 0xdc63152a, 0x3e12b7aa, 0x6e615927, 0xa14fb118,
+ 0xa151758d, 0xba81687b, 0xe152f0b3, 0x764254ed,
+ 0x34c77271, 0x0a31acab, 0x54f94aec, 0xb9e994cd,
+ 0x574d9e81, 0x5b623730, 0xce8a21e8, 0x37917f0b,
+ 0xe8a9b5d6, 0x9697adf8, 0xf3d30431, 0x5dcac921,
+ 0x76b35d46, 0xaa430a36, 0xc2194022, 0x22bca65e,
+ 0xdaec70ba, 0xdfaea8cc, 0x777bae8b, 0x242924d5,
+ 0x1f098a5a, 0x4b396b81, 0x55de2522, 0x435c1cb8,
+ 0xaeb8fe1d, 0x9db3c697, 0x5b164f83, 0xe0c16376,
+ 0xa319224c, 0xd0203b35, 0x433ac0fe, 0x1466a19a,
+ 0x45f0b24f, 0x51fda998, 0xc0d52d71, 0xfa0896a8,
+ 0xf9e6053f, 0xa4b0d300, 0xd499cbcc, 0xb95e3d40,
+};
+
+
+/* Implementation of SOBER-128 by Tom St Denis.
+ * Based on s128fast.c reference code supplied by Greg Rose of QUALCOMM.
+ */
+
+/* don't change these... */
+#define N 17
+#define FOLD N /* how many iterations of folding to do */
+#define INITKONST 0x6996c53a /* value of KONST to use during key loading */
+#define KEYP 15 /* where to insert key words */
+#define FOLDP 4 /* where to insert non-linear feedback */
+
+static ulong32 BYTE2WORD(const unsigned char *b)
+{
+ ulong32 t;
+ LOAD32L(t, b);
+ return t;
+}
+
+static void XORWORD(ulong32 w, unsigned char *b)
+{
+ ulong32 t;
+ LOAD32L(t, b);
+ t ^= w;
+ STORE32L(t, b);
+}
+
+/* give correct offset for the current position of the register,
+ * where logically R[0] is at position "zero".
+ */
+#define OFF(zero, i) (((zero)+(i)) % N)
+
+/* step the LFSR */
+/* After stepping, "zero" moves right one place */
+#define STEP(R,z) \
+ R[OFF(z,0)] = R[OFF(z,15)] ^ R[OFF(z,4)] ^ (R[OFF(z,0)] << 8) ^ Multab[(R[OFF(z,0)] >> 24) & 0xFF];
+
+static void cycle(ulong32 *R)
+{
+ ulong32 t;
+ int i;
+
+ STEP(R,0);
+ t = R[0];
+ for (i = 1; i < N; ++i) {
+ R[i-1] = R[i];
+ }
+ R[N-1] = t;
+}
+
+/* Return a non-linear function of some parts of the register.
+ */
+#define NLFUNC(c,z) \
+{ \
+ t = c->R[OFF(z,0)] + c->R[OFF(z,16)]; \
+ t ^= Sbox[(t >> 24) & 0xFF]; \
+ t = ROR(t, 8); \
+ t = ((t + c->R[OFF(z,1)]) ^ c->konst) + c->R[OFF(z,6)]; \
+ t ^= Sbox[(t >> 24) & 0xFF]; \
+ t = t + c->R[OFF(z,13)]; \
+}
+
+static ulong32 nltap(sober128_prng *c)
+{
+ ulong32 t;
+ NLFUNC(c, 0);
+ return t;
+}
+
+/* initialise to known state
+ */
+int sober128_start(sober128_prng *c)
+{
+ int i;
+
+ /* Register initialised to Fibonacci numbers */
+ c->R[0] = 1;
+ c->R[1] = 1;
+ for (i = 2; i < N; ++i) {
+ c->R[i] = c->R[i-1] + c->R[i-2];
+ }
+ c->konst = INITKONST;
+
+ /* next add_entropy will be the key */
+ c->flag = 1;
+ c->set = 0;
+
+ return 0;
+}
+
+/* Save the current register state
+ */
+static void s128_savestate(sober128_prng *c)
+{
+ int i;
+ for (i = 0; i < N; ++i) {
+ c->initR[i] = c->R[i];
+ }
+}
+
+/* initialise to previously saved register state
+ */
+static void s128_reloadstate(sober128_prng *c)
+{
+ int i;
+
+ for (i = 0; i < N; ++i) {
+ c->R[i] = c->initR[i];
+ }
+}
+
+/* Initialise "konst"
+ */
+static void s128_genkonst(sober128_prng *c)
+{
+ ulong32 newkonst;
+
+ do {
+ cycle(c->R);
+ newkonst = nltap(c);
+ } while ((newkonst & 0xFF000000) == 0);
+ c->konst = newkonst;
+}
+
+/* Load key material into the register
+ */
+#define ADDKEY(k) \
+ c->R[KEYP] += (k);
+
+#define XORNL(nl) \
+ c->R[FOLDP] ^= (nl);
+
+/* nonlinear diffusion of register for key */
+#define DROUND(z) STEP(c->R,z); NLFUNC(c,(z+1)); c->R[OFF((z+1),FOLDP)] ^= t;
+static void s128_diffuse(sober128_prng *c)
+{
+ ulong32 t;
+ /* relies on FOLD == N == 17! */
+ DROUND(0);
+ DROUND(1);
+ DROUND(2);
+ DROUND(3);
+ DROUND(4);
+ DROUND(5);
+ DROUND(6);
+ DROUND(7);
+ DROUND(8);
+ DROUND(9);
+ DROUND(10);
+ DROUND(11);
+ DROUND(12);
+ DROUND(13);
+ DROUND(14);
+ DROUND(15);
+ DROUND(16);
+}
+
+int sober128_add_entropy(const unsigned char *buf, unsigned long len, sober128_prng *c)
+{
+ ulong32 i, k;
+
+
+ if (c->flag == 1) {
+ /* this is the first call to the add_entropy so this input is the key */
+ /* len must be multiple of 4 bytes */
+ /* assert ((len & 3) == 0); */
+
+ for (i = 0; i < len/4; i++) {
+ k = BYTE2WORD(&buf[i*4]);
+ ADDKEY(k);
+ cycle(c->R);
+ XORNL(nltap(c));
+ }
+
+ /* also fold in the length of the key */
+ ADDKEY(len);
+
+ /* now diffuse */
+ s128_diffuse(c);
+
+ s128_genkonst(c);
+ s128_savestate(c);
+ c->nbuf = 0;
+ c->flag = 0;
+ c->set = 1;
+ } else {
+ /* ok we are adding an IV then... */
+ s128_reloadstate(c);
+
+ /* len must be multiple of 4 bytes */
+ /* assert ((len & 3) == 0); */
+
+ for (i = 0; i < len/4; i++) {
+ k = BYTE2WORD(&buf[i*4]);
+ ADDKEY(k);
+ cycle(c->R);
+ XORNL(nltap(c));
+ }
+
+ /* also fold in the length of the key */
+ ADDKEY(len);
+
+ /* now diffuse */
+ s128_diffuse(c);
+ c->nbuf = 0;
+ }
+
+ return 0;
+}
+
+/* XOR pseudo-random bytes into buffer
+ */
+#define SROUND(z) STEP(c->R,z); NLFUNC(c,(z+1)); XORWORD(t, buf+(z*4));
+
+unsigned long sober128_read(unsigned char *buf, unsigned long nbytes, sober128_prng *c)
+{
+ ulong32 t, tlen;
+
+ tlen = nbytes;
+
+ /* handle any previously buffered bytes */
+ while (c->nbuf != 0 && nbytes != 0) {
+ *buf++ ^= c->sbuf & 0xFF;
+ c->sbuf >>= 8;
+ c->nbuf -= 8;
+ --nbytes;
+ }
+
+#ifndef SMALL_CODE
+ /* do lots at a time, if there's enough to do */
+ while (nbytes >= N*4) {
+ SROUND(0);
+ SROUND(1);
+ SROUND(2);
+ SROUND(3);
+ SROUND(4);
+ SROUND(5);
+ SROUND(6);
+ SROUND(7);
+ SROUND(8);
+ SROUND(9);
+ SROUND(10);
+ SROUND(11);
+ SROUND(12);
+ SROUND(13);
+ SROUND(14);
+ SROUND(15);
+ SROUND(16);
+ buf += 4*N;
+ nbytes -= 4*N;
+ }
+#endif
+
+ /* do small or odd size buffers the slow way */
+ while (4 <= nbytes) {
+ cycle(c->R);
+ t = nltap(c);
+ XORWORD(t, buf);
+ buf += 4;
+ nbytes -= 4;
+ }
+
+ /* handle any trailing bytes */
+ if (nbytes != 0) {
+ cycle(c->R);
+ c->sbuf = nltap(c);
+ c->nbuf = 32;
+ while (c->nbuf != 0 && nbytes != 0) {
+ *buf++ ^= c->sbuf & 0xFF;
+ c->sbuf >>= 8;
+ c->nbuf -= 8;
+ --nbytes;
+ }
+ }
+
+ return tlen;
+}
+
+/*
+ * 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/wsutil/sober128.h b/wsutil/sober128.h
new file mode 100644
index 00000000..0238f0ef
--- /dev/null
+++ b/wsutil/sober128.h
@@ -0,0 +1,48 @@
+/** @file
+ This file is derived from sober128 implementation in corosync
+ cluster engine. corosync cluster engine borrows the implementation
+ from LibTomCrypt.
+
+ The latest version of the original code can be found at
+ http://www.libtom.net/LibTomCrypt/ according to which this code is in the
+ Public Domain
+*/
+
+/* About LibTomCrypt:
+ * ---------------------------------------------------------------------
+ * LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@iahu.ca, http://www.libtom.net/LibTomCrypt/
+ */
+
+#ifndef _SOBER127_H
+#define _SOBER127_H
+
+#include "ws_symbol_export.h"
+
+typedef struct _sober128_prng {
+ unsigned long R[17], /* Working storage for the shift register */
+ initR[17], /* saved register contents */
+ konst, /* key dependent constant */
+ sbuf; /* partial word encryption buffer */
+
+ int nbuf, /* number of part-word stream bits buffered */
+ flag, /* first add_entropy call or not? */
+ set; /* did we call add_entropy to set key? */
+
+} sober128_prng;
+
+WS_DLL_PUBLIC
+int sober128_start(sober128_prng *prng);
+WS_DLL_PUBLIC
+int sober128_add_entropy(const unsigned char *buf, unsigned long len, sober128_prng *prng);
+WS_DLL_PUBLIC
+unsigned long sober128_read(unsigned char *buf, unsigned long len, sober128_prng *prng);
+
+#endif /* sober128.h */
diff --git a/wsutil/socket.c b/wsutil/socket.c
new file mode 100644
index 00000000..88297f80
--- /dev/null
+++ b/wsutil/socket.c
@@ -0,0 +1,150 @@
+/* socket.c
+ * Socket wrappers
+ *
+ * Copyright 2019, Gerald Combs
+ *
+ * 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 "socket.h"
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include <wsutil/inet_addr.h>
+
+#ifdef _WIN32
+#include <wsutil/win32-utils.h>
+#define in_port_t uint16_t
+#endif
+
+char *
+ws_init_sockets(void)
+{
+ char *errmsg = NULL;
+#ifdef _WIN32
+ int err;
+ WORD wVersionRequested;
+ WSADATA wsaData;
+
+ wVersionRequested = MAKEWORD(2, 2);
+ err = WSAStartup(wVersionRequested, &wsaData);
+ if (err != 0) {
+ errmsg = ws_strdup_printf("Couldn't initialize Windows Sockets: %s",
+ win32strerror(err));
+ }
+#endif
+ return errmsg;
+}
+
+void
+ws_cleanup_sockets(void)
+{
+#ifdef _WIN32
+ /* XXX - any reason to check the error return? */
+ WSACleanup();
+#endif
+}
+
+int
+ws_socket_ptoa(struct sockaddr_storage *dst, const char *src,
+ uint16_t def_port)
+{
+ int ret = -1, af = -1;
+ char *addr_src, *p;
+ char *addr_str = NULL, *port_str = NULL;
+ union {
+ ws_in4_addr ip4;
+ ws_in6_addr ip6;
+ } addr;
+ char *endptr;
+ long num;
+ in_port_t port;
+
+ addr_src = g_strdup(src);
+
+ /* Is it an IPv6/IPv4 literal address enclosed in braces? */
+ if (*addr_src == '[') {
+ addr_str = addr_src + 1;
+ if ((p = strchr(addr_str, ']')) == NULL) {
+ errno = EINVAL;
+ goto out;
+ }
+ *p++ = '\0';
+ if (*p == ':') {
+ port_str = p + 1;
+ }
+ else if (*p != '\0') {
+ errno = EINVAL;
+ goto out;
+ }
+ if (ws_inet_pton6(addr_str, &addr.ip6)) {
+ af = AF_INET6;
+ }
+ else if (ws_inet_pton4(addr_str, &addr.ip4)) {
+ af = AF_INET;
+ }
+ else {
+ errno = EINVAL;
+ goto out;
+ }
+ }
+ else {
+ /* It is an IPv4 dotted decimal. */
+ addr_str = addr_src;
+ if ((p = strchr(addr_str, ':')) != NULL) {
+ *p++ = '\0';
+ port_str = p;
+ }
+ if (ws_inet_pton4(addr_str, &addr.ip4)) {
+ af = AF_INET;
+ }
+ else {
+ errno = EINVAL;
+ goto out;
+ }
+ }
+
+ if (port_str != NULL && *port_str != '\0') {
+ num = strtol(port_str, &endptr, 10);
+ /* We want the entire string to be a valid decimal representation. */
+ if (endptr == port_str || *endptr != '\0' || num < 0 || num > UINT16_MAX) {
+ errno = EINVAL;
+ goto out;
+ }
+ port = g_htons(num);
+ }
+ else {
+ port = g_htons(def_port);
+ }
+
+ /* sockaddr_storage is guaranteed to fit any sockaddr type. */
+ if (af == AF_INET6) {
+ struct sockaddr_in6 *sa = (struct sockaddr_in6 *)dst;
+ memset(sa, 0, sizeof(struct sockaddr_in6));
+ sa->sin6_family = AF_INET6;
+ sa->sin6_port = port;
+ memcpy(&sa->sin6_addr, &addr.ip6, sizeof(struct in6_addr));
+ ret = 0;
+ }
+ else if (af == AF_INET) {
+ struct sockaddr_in *sa = (struct sockaddr_in *)dst;
+ memset(sa, 0, sizeof(struct sockaddr_in));
+ sa->sin_family = AF_INET;
+ sa->sin_port = port;
+ memcpy(&sa->sin_addr, &addr.ip4, sizeof(struct in_addr));
+ ret = 0;
+ }
+ else {
+ ws_assert_not_reached();
+ }
+
+out:
+ g_free(addr_src);
+ return ret;
+}
diff --git a/wsutil/socket.h b/wsutil/socket.h
new file mode 100644
index 00000000..7393db13
--- /dev/null
+++ b/wsutil/socket.h
@@ -0,0 +1,97 @@
+/** @file
+ * Socket wrappers
+ *
+ * Copyright 2016, Dario Lombardo
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef __SOCKET_H__
+#define __SOCKET_H__
+
+#include <wireshark.h>
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ #include <windows.h>
+ #include <ws2tcpip.h>
+ #include <winsock2.h>
+ #include <process.h>
+
+ #define socket_handle_t SOCKET
+ #define socklen_t int
+#else
+ /*
+ * UN*X, or Windows pretending to be UN*X with the aid of Cygwin.
+ */
+ #ifdef HAVE_UNISTD_H
+ /*
+ * For close().
+ */
+ #include <unistd.h>
+ #endif
+ #ifdef HAVE_SYS_SOCKET_H
+ #include <sys/socket.h>
+ #endif
+
+ #define closesocket(socket) close(socket)
+ #define socket_handle_t int
+#ifndef INVALID_SOCKET
+ #define INVALID_SOCKET (-1)
+#endif
+ #define SOCKET_ERROR (-1)
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+ #include <arpa/inet.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+ #include <netinet/in.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Initialize sockets.
+ *
+ * Returns NULL on success, a g_malloc()ed error message on failure.
+ */
+WS_DLL_PUBLIC char *ws_init_sockets(void);
+
+/*
+ * Clean up sockets.
+ */
+WS_DLL_PUBLIC void ws_cleanup_sockets(void);
+
+/*
+ * Convert the strings ipv4_address:port or [ipv6_address]:port to a
+ * sockaddr object. Ports are optional. Receives default port
+ * in host byte order.
+ */
+WS_DLL_PUBLIC int
+ws_socket_ptoa(struct sockaddr_storage *dst, const char *src,
+ uint16_t def_port);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SOCKET_H__ */
+
+/*
+ * 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:
+ */
diff --git a/wsutil/str_util.c b/wsutil/str_util.c
new file mode 100644
index 00000000..4243b22b
--- /dev/null
+++ b/wsutil/str_util.c
@@ -0,0 +1,1309 @@
+/* str_util.c
+ * String utility routines
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#define _GNU_SOURCE
+#include "config.h"
+#include "str_util.h"
+
+#include <string.h>
+
+#include <ws_codepoints.h>
+
+#include <wsutil/to_str.h>
+
+
+static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+char *
+wmem_strconcat(wmem_allocator_t *allocator, const char *first, ...)
+{
+ size_t len;
+ va_list args;
+ char *s;
+ char *concat;
+ char *ptr;
+
+ if (!first)
+ return NULL;
+
+ len = 1 + strlen(first);
+ va_start(args, first);
+ while ((s = va_arg(args, char*))) {
+ len += strlen(s);
+ }
+ va_end(args);
+
+ ptr = concat = (char *)wmem_alloc(allocator, len);
+
+ ptr = g_stpcpy(ptr, first);
+ va_start(args, first);
+ while ((s = va_arg(args, char*))) {
+ ptr = g_stpcpy(ptr, s);
+ }
+ va_end(args);
+
+ return concat;
+}
+
+char *
+wmem_strjoin(wmem_allocator_t *allocator,
+ const char *separator, const char *first, ...)
+{
+ size_t len;
+ va_list args;
+ size_t separator_len;
+ char *s;
+ char *concat;
+ char *ptr;
+
+ if (!first)
+ return NULL;
+
+ if (separator == NULL) {
+ separator = "";
+ }
+
+ separator_len = strlen (separator);
+
+ len = 1 + strlen(first); /* + 1 for null byte */
+ va_start(args, first);
+ while ((s = va_arg(args, char*))) {
+ len += (separator_len + strlen(s));
+ }
+ va_end(args);
+
+ ptr = concat = (char *)wmem_alloc(allocator, len);
+ ptr = g_stpcpy(ptr, first);
+ va_start(args, first);
+ while ((s = va_arg(args, char*))) {
+ ptr = g_stpcpy(ptr, separator);
+ ptr = g_stpcpy(ptr, s);
+ }
+ va_end(args);
+
+ return concat;
+
+}
+
+char *
+wmem_strjoinv(wmem_allocator_t *allocator,
+ const char *separator, char **str_array)
+{
+ char *string = NULL;
+
+ ws_return_val_if(!str_array, NULL);
+
+ if (separator == NULL) {
+ separator = "";
+ }
+
+ if (str_array[0]) {
+ int i;
+ char *ptr;
+ size_t len, separator_len;
+
+ separator_len = strlen(separator);
+
+ /* Get first part of length. Plus one for null byte. */
+ len = 1 + strlen(str_array[0]);
+ /* Get the full length, including the separators. */
+ for (i = 1; str_array[i] != NULL; i++) {
+ len += separator_len;
+ len += strlen(str_array[i]);
+ }
+
+ /* Allocate and build the string. */
+ string = (char *)wmem_alloc(allocator, len);
+ ptr = g_stpcpy(string, str_array[0]);
+ for (i = 1; str_array[i] != NULL; i++) {
+ ptr = g_stpcpy(ptr, separator);
+ ptr = g_stpcpy(ptr, str_array[i]);
+ }
+ } else {
+ string = wmem_strdup(allocator, "");
+ }
+
+ return string;
+
+}
+
+char **
+wmem_strsplit(wmem_allocator_t *allocator, const char *src,
+ const char *delimiter, int max_tokens)
+{
+ char *splitted;
+ char *s;
+ unsigned tokens;
+ unsigned sep_len;
+ unsigned i;
+ char **vec;
+
+ if (!src || !delimiter || !delimiter[0])
+ return NULL;
+
+ /* An empty string results in an empty vector. */
+ if (!src[0]) {
+ vec = wmem_new0(allocator, char *);
+ return vec;
+ }
+
+ splitted = wmem_strdup(allocator, src);
+ sep_len = (unsigned)strlen(delimiter);
+
+ if (max_tokens < 1)
+ max_tokens = INT_MAX;
+
+ /* Calculate the number of fields. */
+ s = splitted;
+ tokens = 1;
+ while (tokens < (unsigned)max_tokens && (s = strstr(s, delimiter))) {
+ s += sep_len;
+ tokens++;
+ }
+
+ vec = wmem_alloc_array(allocator, char *, tokens + 1);
+
+ /* Populate the array of string tokens. */
+ s = splitted;
+ vec[0] = s;
+ tokens = 1;
+ while (tokens < (unsigned)max_tokens && (s = strstr(s, delimiter))) {
+ for (i = 0; i < sep_len; i++)
+ s[i] = '\0';
+ s += sep_len;
+ vec[tokens] = s;
+ tokens++;
+
+ }
+
+ vec[tokens] = NULL;
+
+ return vec;
+}
+
+/*
+ * wmem_ascii_strdown:
+ * based on g_ascii_strdown.
+ */
+char*
+wmem_ascii_strdown(wmem_allocator_t *allocator, const char *str, ssize_t len)
+{
+ char *result, *s;
+
+ g_return_val_if_fail (str != NULL, NULL);
+
+ if (len < 0)
+ len = strlen (str);
+
+ result = wmem_strndup(allocator, str, len);
+ for (s = result; *s; s++)
+ *s = g_ascii_tolower (*s);
+
+ return result;
+}
+
+int
+ws_xton(char ch)
+{
+ switch (ch) {
+ case '0': return 0;
+ case '1': return 1;
+ case '2': return 2;
+ case '3': return 3;
+ case '4': return 4;
+ case '5': return 5;
+ case '6': return 6;
+ case '7': return 7;
+ case '8': return 8;
+ case '9': return 9;
+ case 'a': case 'A': return 10;
+ case 'b': case 'B': return 11;
+ case 'c': case 'C': return 12;
+ case 'd': case 'D': return 13;
+ case 'e': case 'E': return 14;
+ case 'f': case 'F': return 15;
+ default: return -1;
+ }
+}
+
+/* Convert all ASCII letters to lower case, in place. */
+char *
+ascii_strdown_inplace(char *str)
+{
+ char *s;
+
+ for (s = str; *s; s++)
+ /* What 'g_ascii_tolower (char c)' does, this should be slightly more efficient */
+ *s = g_ascii_isupper (*s) ? *s - 'A' + 'a' : *s;
+
+ return (str);
+}
+
+/* Convert all ASCII letters to upper case, in place. */
+char *
+ascii_strup_inplace(char *str)
+{
+ char *s;
+
+ for (s = str; *s; s++)
+ /* What 'g_ascii_toupper (char c)' does, this should be slightly more efficient */
+ *s = g_ascii_islower (*s) ? *s - 'a' + 'A' : *s;
+
+ return (str);
+}
+
+/* Check if an entire string is printable. */
+bool
+isprint_string(const char *str)
+{
+ unsigned pos;
+
+ /* Loop until we reach the end of the string (a null) */
+ for(pos = 0; str[pos] != '\0'; pos++){
+ if(!g_ascii_isprint(str[pos])){
+ /* The string contains a non-printable character */
+ return false;
+ }
+ }
+
+ /* The string contains only printable characters */
+ return true;
+}
+
+/* Check if an entire UTF-8 string is printable. */
+bool
+isprint_utf8_string(const char *str, const unsigned length)
+{
+ const char *strend = str + length;
+
+ if (!g_utf8_validate(str, length, NULL)) {
+ return false;
+ }
+
+ while (str < strend) {
+ /* This returns false for G_UNICODE_CONTROL | G_UNICODE_FORMAT |
+ * G_UNICODE_UNASSIGNED | G_UNICODE_SURROGATE
+ * XXX: Could it be ok to have certain format characters, e.g.
+ * U+00AD SOFT HYPHEN? If so, format_text() should be changed too.
+ */
+ if (!g_unichar_isprint(g_utf8_get_char(str))) {
+ return false;
+ }
+ str = g_utf8_next_char(str);
+ }
+
+ return true;
+}
+
+/* Check if an entire string is digits. */
+bool
+isdigit_string(const unsigned char *str)
+{
+ unsigned pos;
+
+ /* Loop until we reach the end of the string (a null) */
+ for(pos = 0; str[pos] != '\0'; pos++){
+ if(!g_ascii_isdigit(str[pos])){
+ /* The string contains a non-digit character */
+ return false;
+ }
+ }
+
+ /* The string contains only digits */
+ return true;
+}
+
+const char *
+ws_ascii_strcasestr(const char *haystack, const char *needle)
+{
+ /* Do not use strcasestr() here, even if a system has it, as it is
+ * locale-dependent (and has different results for e.g. Turkic languages.)
+ * FreeBSD, NetBSD, macOS have a strcasestr_l() that could be used.
+ */
+ size_t hlen = strlen(haystack);
+ size_t nlen = strlen(needle);
+
+ while (hlen-- >= nlen) {
+ if (!g_ascii_strncasecmp(haystack, needle, nlen))
+ return haystack;
+ haystack++;
+ }
+ return NULL;
+}
+
+#define FORMAT_SIZE_UNIT_MASK 0x00ff
+#define FORMAT_SIZE_PFX_MASK 0xff00
+
+static const char *thousands_grouping_fmt = NULL;
+
+DIAG_OFF(format)
+static void test_printf_thousands_grouping(void) {
+ /* test whether wmem_strbuf works with "'" flag character */
+ wmem_strbuf_t *buf = wmem_strbuf_new(NULL, NULL);
+ wmem_strbuf_append_printf(buf, "%'d", 22);
+ if (g_strcmp0(wmem_strbuf_get_str(buf), "22") == 0) {
+ thousands_grouping_fmt = "%'"PRId64;
+ } else {
+ /* Don't use */
+ thousands_grouping_fmt = "%"PRId64;
+ }
+ wmem_strbuf_destroy(buf);
+}
+DIAG_ON(format)
+
+/* Given a size, return its value in a human-readable format */
+/* This doesn't handle fractional values. We might want to make size a double. */
+char *
+format_size_wmem(wmem_allocator_t *allocator, int64_t size,
+ format_size_units_e unit, uint16_t flags)
+{
+ wmem_strbuf_t *human_str = wmem_strbuf_new(allocator, NULL);
+ int power = 1000;
+ int pfx_off = 0;
+ bool is_small = false;
+ static const char *prefix[] = {" T", " G", " M", " k", " Ti", " Gi", " Mi", " Ki"};
+ char *ret_val;
+
+ if (thousands_grouping_fmt == NULL)
+ test_printf_thousands_grouping();
+
+ if (flags & FORMAT_SIZE_PREFIX_IEC) {
+ pfx_off = 4;
+ power = 1024;
+ }
+
+ if (size / power / power / power / power >= 10) {
+ wmem_strbuf_append_printf(human_str, thousands_grouping_fmt, size / power / power / power / power);
+ wmem_strbuf_append(human_str, prefix[pfx_off]);
+ } else if (size / power / power / power >= 10) {
+ wmem_strbuf_append_printf(human_str, thousands_grouping_fmt, size / power / power / power);
+ wmem_strbuf_append(human_str, prefix[pfx_off+1]);
+ } else if (size / power / power >= 10) {
+ wmem_strbuf_append_printf(human_str, thousands_grouping_fmt, size / power / power);
+ wmem_strbuf_append(human_str, prefix[pfx_off+2]);
+ } else if (size / power >= 10) {
+ wmem_strbuf_append_printf(human_str, thousands_grouping_fmt, size / power);
+ wmem_strbuf_append(human_str, prefix[pfx_off+3]);
+ } else {
+ wmem_strbuf_append_printf(human_str, thousands_grouping_fmt, size);
+ is_small = true;
+ }
+
+ switch (unit) {
+ case FORMAT_SIZE_UNIT_NONE:
+ break;
+ case FORMAT_SIZE_UNIT_BYTES:
+ wmem_strbuf_append(human_str, is_small ? " bytes" : "B");
+ break;
+ case FORMAT_SIZE_UNIT_BITS:
+ wmem_strbuf_append(human_str, is_small ? " bits" : "b");
+ break;
+ case FORMAT_SIZE_UNIT_BITS_S:
+ wmem_strbuf_append(human_str, is_small ? " bits/s" : "bps");
+ break;
+ case FORMAT_SIZE_UNIT_BYTES_S:
+ wmem_strbuf_append(human_str, is_small ? " bytes/s" : "Bps");
+ break;
+ case FORMAT_SIZE_UNIT_PACKETS:
+ wmem_strbuf_append(human_str, is_small ? " packets" : "packets");
+ break;
+ case FORMAT_SIZE_UNIT_PACKETS_S:
+ wmem_strbuf_append(human_str, is_small ? " packets/s" : "packets/s");
+ break;
+ default:
+ ws_assert_not_reached();
+ }
+
+ ret_val = wmem_strbuf_finalize(human_str);
+ return g_strchomp(ret_val);
+}
+
+char
+printable_char_or_period(char c)
+{
+ return g_ascii_isprint(c) ? c : '.';
+}
+
+/*
+ * This is used by the display filter engine and must be compatible
+ * with display filter syntax.
+ */
+static inline bool
+escape_char(char c, char *p)
+{
+ int r = -1;
+ ws_assert(p);
+
+ /*
+ * Backslashes and double-quotes must
+ * be escaped. Whitespace is also escaped.
+ */
+ switch (c) {
+ case '\a': r = 'a'; break;
+ case '\b': r = 'b'; break;
+ case '\f': r = 'f'; break;
+ case '\n': r = 'n'; break;
+ case '\r': r = 'r'; break;
+ case '\t': r = 't'; break;
+ case '\v': r = 'v'; break;
+ case '"': r = '"'; break;
+ case '\\': r = '\\'; break;
+ case '\0': r = '0'; break;
+ }
+
+ if (r != -1) {
+ *p = r;
+ return true;
+ }
+ return false;
+}
+
+static inline bool
+escape_null(char c, char *p)
+{
+ ws_assert(p);
+ if (c == '\0') {
+ *p = '0';
+ return true;
+ }
+ return false;
+}
+
+static char *
+escape_string_len(wmem_allocator_t *alloc, const char *string, ssize_t len,
+ bool (*escape_func)(char c, char *p), bool add_quotes)
+{
+ char c, r;
+ wmem_strbuf_t *buf;
+ size_t alloc_size;
+ ssize_t i;
+
+ if (len < 0)
+ len = strlen(string);
+
+ alloc_size = len;
+ if (add_quotes)
+ alloc_size += 2;
+
+ buf = wmem_strbuf_new_sized(alloc, alloc_size);
+
+ if (add_quotes)
+ wmem_strbuf_append_c(buf, '"');
+
+ for (i = 0; i < len; i++) {
+ c = string[i];
+ if ((escape_func(c, &r))) {
+ wmem_strbuf_append_c(buf, '\\');
+ wmem_strbuf_append_c(buf, r);
+ }
+ else {
+ /* Other UTF-8 bytes are passed through. */
+ wmem_strbuf_append_c(buf, c);
+ }
+ }
+
+ if (add_quotes)
+ wmem_strbuf_append_c(buf, '"');
+
+ return wmem_strbuf_finalize(buf);
+}
+
+char *
+ws_escape_string_len(wmem_allocator_t *alloc, const char *string, ssize_t len, bool add_quotes)
+{
+ return escape_string_len(alloc, string, len, escape_char, add_quotes);
+}
+
+char *
+ws_escape_string(wmem_allocator_t *alloc, const char *string, bool add_quotes)
+{
+ return escape_string_len(alloc, string, -1, escape_char, add_quotes);
+}
+
+char *ws_escape_null(wmem_allocator_t *alloc, const char *string, size_t len, bool add_quotes)
+{
+ return escape_string_len(alloc, string, len, escape_null, add_quotes);
+}
+
+const char *
+ws_strerrorname_r(int errnum, char *buf, size_t buf_size)
+{
+#ifdef HAVE_STRERRORNAME_NP
+ const char *errstr = strerrorname_np(errnum);
+ if (errstr != NULL) {
+ (void)g_strlcpy(buf, errstr, buf_size);
+ return buf;
+ }
+#endif
+ snprintf(buf, buf_size, "Errno(%d)", errnum);
+ return buf;
+}
+
+char *
+ws_strdup_underline(wmem_allocator_t *allocator, long offset, size_t len)
+{
+ if (offset < 0)
+ return NULL;
+
+ wmem_strbuf_t *buf = wmem_strbuf_new_sized(allocator, offset + len);
+
+ for (int i = 0; i < offset; i++) {
+ wmem_strbuf_append_c(buf, ' ');
+ }
+ wmem_strbuf_append_c(buf, '^');
+
+ for (size_t l = len; l > 1; l--) {
+ wmem_strbuf_append_c(buf, '~');
+ }
+
+ return wmem_strbuf_finalize(buf);
+}
+
+#define INITIAL_FMTBUF_SIZE 128
+
+/*
+ * Declare, and initialize, the variables used for an output buffer.
+ */
+#define FMTBUF_VARS \
+ char *fmtbuf = (char*)wmem_alloc(allocator, INITIAL_FMTBUF_SIZE); \
+ unsigned fmtbuf_len = INITIAL_FMTBUF_SIZE; \
+ unsigned column = 0
+
+/*
+ * Expand the buffer to be large enough to add nbytes bytes, plus a
+ * terminating '\0'.
+ */
+#define FMTBUF_EXPAND(nbytes) \
+ /* \
+ * Is there enough room for those bytes and also enough room for \
+ * a terminating '\0'? \
+ */ \
+ if (column+(nbytes+1) >= fmtbuf_len) { \
+ /* \
+ * Double the buffer's size if it's not big enough. \
+ * The size of the buffer starts at 128, so doubling its size \
+ * adds at least another 128 bytes, which is more than enough \
+ * for one more character plus a terminating '\0'. \
+ */ \
+ fmtbuf_len *= 2; \
+ fmtbuf = (char *)wmem_realloc(allocator, fmtbuf, fmtbuf_len); \
+ }
+
+/*
+ * Put a byte into the buffer; space must have been ensured for it.
+ */
+#define FMTBUF_PUTCHAR(b) \
+ fmtbuf[column] = (b); \
+ column++
+
+/*
+ * Add the one-byte argument, as an octal escape sequence, to the end
+ * of the buffer.
+ */
+#define FMTBUF_PUTBYTE_OCTAL(b) \
+ FMTBUF_PUTCHAR((((b)>>6)&03) + '0'); \
+ FMTBUF_PUTCHAR((((b)>>3)&07) + '0'); \
+ FMTBUF_PUTCHAR((((b)>>0)&07) + '0')
+
+/*
+ * Add the one-byte argument, as a hex escape sequence, to the end
+ * of the buffer.
+ */
+#define FMTBUF_PUTBYTE_HEX(b) \
+ FMTBUF_PUTCHAR('\\'); \
+ FMTBUF_PUTCHAR('x'); \
+ FMTBUF_PUTCHAR(hex[((b) >> 4) & 0xF]); \
+ FMTBUF_PUTCHAR(hex[((b) >> 0) & 0xF])
+
+/*
+ * Put the trailing '\0' at the end of the buffer.
+ */
+#define FMTBUF_ENDSTR \
+ fmtbuf[column] = '\0'
+
+static char *
+format_text_internal(wmem_allocator_t *allocator,
+ const unsigned char *string, size_t len,
+ bool replace_space)
+{
+ FMTBUF_VARS;
+ const unsigned char *stringend = string + len;
+ unsigned char c;
+
+ while (string < stringend) {
+ /*
+ * Get the first byte of this character.
+ */
+ c = *string++;
+ if (g_ascii_isprint(c)) {
+ /*
+ * Printable ASCII, so not part of a multi-byte UTF-8 sequence.
+ * Make sure there's enough room for one more byte, and add
+ * the character.
+ */
+ FMTBUF_EXPAND(1);
+ FMTBUF_PUTCHAR(c);
+ } else if (replace_space && g_ascii_isspace(c)) {
+ /*
+ * ASCII, so not part of a multi-byte UTF-8 sequence, but
+ * not printable, but is a space character; show it as a
+ * blank.
+ *
+ * Make sure there's enough room for one more byte, and add
+ * the blank.
+ */
+ FMTBUF_EXPAND(1);
+ FMTBUF_PUTCHAR(' ');
+ } else if (c < 128) {
+ /*
+ * ASCII, so not part of a multi-byte UTF-8 sequence, but not
+ * printable.
+ *
+ * That requires a minimum of 2 bytes, one for the backslash
+ * and one for a letter, so make sure we have enough room
+ * for that, plus a trailing '\0'.
+ */
+ FMTBUF_EXPAND(2);
+ FMTBUF_PUTCHAR('\\');
+ switch (c) {
+
+ case '\a':
+ FMTBUF_PUTCHAR('a');
+ break;
+
+ case '\b':
+ FMTBUF_PUTCHAR('b'); /* BS */
+ break;
+
+ case '\f':
+ FMTBUF_PUTCHAR('f'); /* FF */
+ break;
+
+ case '\n':
+ FMTBUF_PUTCHAR('n'); /* NL */
+ break;
+
+ case '\r':
+ FMTBUF_PUTCHAR('r'); /* CR */
+ break;
+
+ case '\t':
+ FMTBUF_PUTCHAR('t'); /* tab */
+ break;
+
+ case '\v':
+ FMTBUF_PUTCHAR('v');
+ break;
+
+ default:
+ /*
+ * We've already put the backslash, but this
+ * will put 3 more characters for the octal
+ * number; make sure we have enough room for
+ * that, plus the trailing '\0'.
+ */
+ FMTBUF_EXPAND(3);
+ FMTBUF_PUTBYTE_OCTAL(c);
+ break;
+ }
+ } else {
+ /*
+ * We've fetched the first byte of a multi-byte UTF-8
+ * sequence into c.
+ */
+ int utf8_len;
+ unsigned char mask;
+ gunichar uc;
+ unsigned char first;
+
+ if ((c & 0xe0) == 0xc0) {
+ /* Starts a 2-byte UTF-8 sequence; 1 byte left */
+ utf8_len = 1;
+ mask = 0x1f;
+ } else if ((c & 0xf0) == 0xe0) {
+ /* Starts a 3-byte UTF-8 sequence; 2 bytes left */
+ utf8_len = 2;
+ mask = 0x0f;
+ } else if ((c & 0xf8) == 0xf0) {
+ /* Starts a 4-byte UTF-8 sequence; 3 bytes left */
+ utf8_len = 3;
+ mask = 0x07;
+ } else if ((c & 0xfc) == 0xf8) {
+ /* Starts an old-style 5-byte UTF-8 sequence; 4 bytes left */
+ utf8_len = 4;
+ mask = 0x03;
+ } else if ((c & 0xfe) == 0xfc) {
+ /* Starts an old-style 6-byte UTF-8 sequence; 5 bytes left */
+ utf8_len = 5;
+ mask = 0x01;
+ } else {
+ /* 0xfe or 0xff or a continuation byte - not valid */
+ utf8_len = -1;
+ }
+ if (utf8_len > 0) {
+ /* Try to construct the Unicode character */
+ uc = c & mask;
+ for (int i = 0; i < utf8_len; i++) {
+ if (string >= stringend) {
+ /*
+ * Ran out of octets, so the character is
+ * incomplete. Put in a REPLACEMENT CHARACTER
+ * instead, and then continue the loop, which
+ * will terminate.
+ */
+ uc = UNICODE_REPLACEMENT_CHARACTER;
+ break;
+ }
+ c = *string;
+ if ((c & 0xc0) != 0x80) {
+ /*
+ * Not valid UTF-8 continuation character; put in
+ * a replacement character, and then re-process
+ * this octet as the beginning of a new character.
+ */
+ uc = UNICODE_REPLACEMENT_CHARACTER;
+ break;
+ }
+ string++;
+ uc = (uc << 6) | (c & 0x3f);
+ }
+
+ /*
+ * If this isn't a valid Unicode character, put in
+ * a REPLACEMENT CHARACTER.
+ */
+ if (!g_unichar_validate(uc))
+ uc = UNICODE_REPLACEMENT_CHARACTER;
+ } else {
+ /* 0xfe or 0xff; put it a REPLACEMENT CHARACTER */
+ uc = UNICODE_REPLACEMENT_CHARACTER;
+ }
+
+ /*
+ * OK, is it a printable Unicode character?
+ */
+ if (g_unichar_isprint(uc)) {
+ /*
+ * Yes - put it into the string as UTF-8.
+ * This means that if it was an overlong
+ * encoding, this will put out the right
+ * sized encoding.
+ */
+ if (uc < 0x80) {
+ first = 0;
+ utf8_len = 1;
+ } else if (uc < 0x800) {
+ first = 0xc0;
+ utf8_len = 2;
+ } else if (uc < 0x10000) {
+ first = 0xe0;
+ utf8_len = 3;
+ } else if (uc < 0x200000) {
+ first = 0xf0;
+ utf8_len = 4;
+ } else if (uc < 0x4000000) {
+ /*
+ * This should never happen, as Unicode doesn't
+ * go that high.
+ */
+ first = 0xf8;
+ utf8_len = 5;
+ } else {
+ /*
+ * This should never happen, as Unicode doesn't
+ * go that high.
+ */
+ first = 0xfc;
+ utf8_len = 6;
+ }
+ FMTBUF_EXPAND(utf8_len);
+ for (int i = utf8_len - 1; i > 0; i--) {
+ fmtbuf[column + i] = (uc & 0x3f) | 0x80;
+ uc >>= 6;
+ }
+ fmtbuf[column] = uc | first;
+ column += utf8_len;
+ } else if (replace_space && g_unichar_isspace(uc)) {
+ /*
+ * Not printable, but is a space character; show it
+ * as a blank.
+ *
+ * Make sure there's enough room for one more byte,
+ * and add the blank.
+ */
+ FMTBUF_EXPAND(1);
+ FMTBUF_PUTCHAR(' ');
+ } else if (c < 128) {
+ /*
+ * ASCII, but not printable.
+ * Yes, this could happen with an overlong encoding.
+ *
+ * That requires a minimum of 2 bytes, one for the
+ * backslash and one for a letter, so make sure we
+ * have enough room for that, plus a trailing '\0'.
+ */
+ FMTBUF_EXPAND(2);
+ FMTBUF_PUTCHAR('\\');
+ switch (c) {
+
+ case '\a':
+ FMTBUF_PUTCHAR('a');
+ break;
+
+ case '\b':
+ FMTBUF_PUTCHAR('b'); /* BS */
+ break;
+
+ case '\f':
+ FMTBUF_PUTCHAR('f'); /* FF */
+ break;
+
+ case '\n':
+ FMTBUF_PUTCHAR('n'); /* NL */
+ break;
+
+ case '\r':
+ FMTBUF_PUTCHAR('r'); /* CR */
+ break;
+
+ case '\t':
+ FMTBUF_PUTCHAR('t'); /* tab */
+ break;
+
+ case '\v':
+ FMTBUF_PUTCHAR('v');
+ break;
+
+ default:
+ /*
+ * We've already put the backslash, but this
+ * will put 3 more characters for the octal
+ * number; make sure we have enough room for
+ * that, plus the trailing '\0'.
+ */
+ FMTBUF_EXPAND(3);
+ FMTBUF_PUTBYTE_OCTAL(c);
+ break;
+ }
+ } else {
+ /*
+ * Unicode, but not printable, and not ASCII;
+ * put it out as \uxxxx or \Uxxxxxxxx.
+ */
+ if (uc <= 0xFFFF) {
+ FMTBUF_EXPAND(6);
+ FMTBUF_PUTCHAR('\\');
+ FMTBUF_PUTCHAR('u');
+ FMTBUF_PUTCHAR(hex[(uc >> 12) & 0xF]);
+ FMTBUF_PUTCHAR(hex[(uc >> 8) & 0xF]);
+ FMTBUF_PUTCHAR(hex[(uc >> 4) & 0xF]);
+ FMTBUF_PUTCHAR(hex[(uc >> 0) & 0xF]);
+ } else {
+ FMTBUF_EXPAND(10);
+ FMTBUF_PUTCHAR('\\');
+ FMTBUF_PUTCHAR('U');
+ FMTBUF_PUTCHAR(hex[(uc >> 28) & 0xF]);
+ FMTBUF_PUTCHAR(hex[(uc >> 24) & 0xF]);
+ FMTBUF_PUTCHAR(hex[(uc >> 20) & 0xF]);
+ FMTBUF_PUTCHAR(hex[(uc >> 16) & 0xF]);
+ FMTBUF_PUTCHAR(hex[(uc >> 12) & 0xF]);
+ FMTBUF_PUTCHAR(hex[(uc >> 8) & 0xF]);
+ FMTBUF_PUTCHAR(hex[(uc >> 4) & 0xF]);
+ FMTBUF_PUTCHAR(hex[(uc >> 0) & 0xF]);
+ }
+ }
+ }
+ }
+
+ FMTBUF_ENDSTR;
+
+ return fmtbuf;
+}
+
+/*
+ * Given a wmem scope, a not-necessarily-null-terminated string,
+ * expected to be in UTF-8 but possibly containing invalid sequences
+ * (as it may have come from packet data), and the length of the string,
+ * generate a valid UTF-8 string from it, allocated in the specified
+ * wmem scope, that:
+ *
+ * shows printable Unicode characters as themselves;
+ *
+ * shows non-printable ASCII characters as C-style escapes (octal
+ * if not one of the standard ones such as LF -> '\n');
+ *
+ * shows non-printable Unicode-but-not-ASCII characters as
+ * their universal character names;
+ *
+ * shows illegal UTF-8 sequences as a sequence of bytes represented
+ * as C-style hex escapes (XXX: Does not actually do this. Some illegal
+ * sequences, such as overlong encodings, the sequences reserved for
+ * UTF-16 surrogate halves (paired or unpaired), and values outside
+ * Unicode (i.e., the old sequences for code points above U+10FFFF)
+ * will be decoded in a permissive way. Other illegal sequences,
+ * such 0xFE and 0xFF and the presence of a continuation byte where
+ * not expected (or vice versa its absence), are replaced with
+ * REPLACEMENT CHARACTER.)
+ *
+ * and return a pointer to it.
+ */
+char *
+format_text(wmem_allocator_t *allocator,
+ const char *string, size_t len)
+{
+ return format_text_internal(allocator, string, len, false);
+}
+
+/** Given a wmem scope and a null-terminated string, expected to be in
+ * UTF-8 but possibly containing invalid sequences (as it may have come
+ * from packet data), and the length of the string, generate a valid
+ * UTF-8 string from it, allocated in the specified wmem scope, that:
+ *
+ * shows printable Unicode characters as themselves;
+ *
+ * shows non-printable ASCII characters as C-style escapes (octal
+ * if not one of the standard ones such as LF -> '\n');
+ *
+ * shows non-printable Unicode-but-not-ASCII characters as
+ * their universal character names;
+ *
+ * shows illegal UTF-8 sequences as a sequence of bytes represented
+ * as C-style hex escapes;
+ *
+ * and return a pointer to it.
+ */
+char *
+format_text_string(wmem_allocator_t* allocator, const char *string)
+{
+ return format_text_internal(allocator, string, strlen(string), false);
+}
+
+/*
+ * Given a string, generate a string from it that shows non-printable
+ * characters as C-style escapes except a whitespace character
+ * (space, tab, carriage return, new line, vertical tab, or formfeed)
+ * which will be replaced by a space, and return a pointer to it.
+ */
+char *
+format_text_wsp(wmem_allocator_t* allocator, const char *string, size_t len)
+{
+ return format_text_internal(allocator, string, len, true);
+}
+
+/*
+ * Given a string, generate a string from it that shows non-printable
+ * characters as the chr parameter passed, except a whitespace character
+ * (space, tab, carriage return, new line, vertical tab, or formfeed)
+ * which will be replaced by a space, and return a pointer to it.
+ *
+ * This does *not* treat the input string as UTF-8.
+ *
+ * This is useful for displaying binary data that frequently but not always
+ * contains text; otherwise the number of C escape codes makes it unreadable.
+ */
+char *
+format_text_chr(wmem_allocator_t *allocator, const char *string, size_t len, char chr)
+{
+ wmem_strbuf_t *buf;
+
+ buf = wmem_strbuf_new_sized(allocator, len + 1);
+ for (const char *p = string; p < string + len; p++) {
+ if (g_ascii_isprint(*p)) {
+ wmem_strbuf_append_c(buf, *p);
+ }
+ else if (g_ascii_isspace(*p)) {
+ wmem_strbuf_append_c(buf, ' ');
+ }
+ else {
+ wmem_strbuf_append_c(buf, chr);
+ }
+ }
+ return wmem_strbuf_finalize(buf);
+}
+
+char *
+format_char(wmem_allocator_t *allocator, char c)
+{
+ char *buf;
+ char r;
+
+ if (g_ascii_isprint(c)) {
+ buf = wmem_alloc_array(allocator, char, 2);
+ buf[0] = c;
+ buf[1] = '\0';
+ return buf;
+ }
+ if (escape_char(c, &r)) {
+ buf = wmem_alloc_array(allocator, char, 3);
+ buf[0] = '\\';
+ buf[1] = r;
+ buf[2] = '\0';
+ return buf;
+ }
+ buf = wmem_alloc_array(allocator, char, 5);
+ buf[0] = '\\';
+ buf[1] = 'x';
+ buf[2] = hex[((uint8_t)c >> 4) & 0xF];
+ buf[3] = hex[((uint8_t)c >> 0) & 0xF];
+ buf[4] = '\0';
+ return buf;
+}
+
+char*
+ws_utf8_truncate(char *string, size_t len)
+{
+ char* last_char;
+
+ /* Ensure that it is null terminated */
+ string[len] = '\0';
+ last_char = g_utf8_find_prev_char(string, string + len);
+ if (last_char != NULL && g_utf8_get_char_validated(last_char, -1) == (gunichar)-2) {
+ /* The last UTF-8 character was truncated into a partial sequence. */
+ *last_char = '\0';
+ }
+ return string;
+}
+
+/* ASCII/EBCDIC conversion tables from
+ * https://web.archive.org/web/20060813174742/http://www.room42.com/store/computer_center/code_tables.shtml
+ */
+#if 0
+static const uint8_t ASCII_translate_EBCDIC [ 256 ] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+ 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+ 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, 0x4D,
+ 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61,
+ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
+ 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F,
+ 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8,
+ 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6,
+ 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
+ 0xE8, 0xE9, 0xAD, 0xE0, 0xBD, 0x5F, 0x6D,
+ 0x7D, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+ 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96,
+ 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
+ 0xA8, 0xA9, 0xC0, 0x6A, 0xD0, 0xA1, 0x4B,
+ 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
+ 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
+ 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
+ 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
+ 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
+ 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
+ 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
+ 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
+ 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
+ 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
+ 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
+ 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
+ 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
+ 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
+ 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
+ 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B
+};
+
+void
+ASCII_to_EBCDIC(uint8_t *buf, unsigned bytes)
+{
+ unsigned i;
+ uint8_t *bufptr;
+
+ bufptr = buf;
+
+ for (i = 0; i < bytes; i++, bufptr++) {
+ *bufptr = ASCII_translate_EBCDIC[*bufptr];
+ }
+}
+
+uint8_t
+ASCII_to_EBCDIC1(uint8_t c)
+{
+ return ASCII_translate_EBCDIC[c];
+}
+#endif
+
+static const uint8_t EBCDIC_translate_ASCII [ 256 ] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
+ 0x2E, 0x2E, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x2E, 0x3F,
+ 0x20, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
+ 0x2E, 0x2E, 0x2E, 0x2E, 0x3C, 0x28, 0x2B, 0x7C,
+ 0x26, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
+ 0x2E, 0x2E, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0x5E,
+ 0x2D, 0x2F, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
+ 0x2E, 0x2E, 0x7C, 0x2C, 0x25, 0x5F, 0x3E, 0x3F,
+ 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
+ 0x2E, 0x2E, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22,
+ 0x2E, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
+ 0x2E, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70,
+ 0x71, 0x72, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
+ 0x2E, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+ 0x79, 0x7A, 0x2E, 0x2E, 0x2E, 0x5B, 0x2E, 0x2E,
+ 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
+ 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x5D, 0x2E, 0x2E,
+ 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
+ 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
+ 0x51, 0x52, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
+ 0x5C, 0x2E, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+ 0x59, 0x5A, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E
+};
+
+void
+EBCDIC_to_ASCII(uint8_t *buf, unsigned bytes)
+{
+ unsigned i;
+ uint8_t *bufptr;
+
+ bufptr = buf;
+
+ for (i = 0; i < bytes; i++, bufptr++) {
+ *bufptr = EBCDIC_translate_ASCII[*bufptr];
+ }
+}
+
+uint8_t
+EBCDIC_to_ASCII1(uint8_t c)
+{
+ return EBCDIC_translate_ASCII[c];
+}
+
+/*
+ * This routine is based on a routine created by Dan Lasley
+ * <DLASLEY@PROMUS.com>.
+ *
+ * It was modified for Wireshark by Gilbert Ramirez and others.
+ */
+
+#define MAX_OFFSET_LEN 8 /* max length of hex offset of bytes */
+#define BYTES_PER_LINE 16 /* max byte values printed on a line */
+#define HEX_DUMP_LEN (BYTES_PER_LINE*3)
+ /* max number of characters hex dump takes -
+ 2 digits plus trailing blank */
+#define DATA_DUMP_LEN (HEX_DUMP_LEN + 2 + 2 + BYTES_PER_LINE)
+ /* number of characters those bytes take;
+ 3 characters per byte of hex dump,
+ 2 blanks separating hex from ASCII,
+ 2 optional ASCII dump delimiters,
+ 1 character per byte of ASCII dump */
+#define MAX_LINE_LEN (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN)
+ /* number of characters per line;
+ offset, 2 blanks separating offset
+ from data dump, data dump */
+
+bool
+hex_dump_buffer(bool (*print_line)(void *, const char *), void *fp,
+ const unsigned char *cp, unsigned length,
+ hex_dump_enc encoding,
+ unsigned ascii_option)
+{
+ register unsigned int ad, i, j, k, l;
+ unsigned char c;
+ char line[MAX_LINE_LEN + 1];
+ unsigned int use_digits;
+
+ static char binhex[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+ /*
+ * How many of the leading digits of the offset will we supply?
+ * We always supply at least 4 digits, but if the maximum offset
+ * won't fit in 4 digits, we use as many digits as will be needed.
+ */
+ if (((length - 1) & 0xF0000000) != 0)
+ use_digits = 8; /* need all 8 digits */
+ else if (((length - 1) & 0x0F000000) != 0)
+ use_digits = 7; /* need 7 digits */
+ else if (((length - 1) & 0x00F00000) != 0)
+ use_digits = 6; /* need 6 digits */
+ else if (((length - 1) & 0x000F0000) != 0)
+ use_digits = 5; /* need 5 digits */
+ else
+ use_digits = 4; /* we'll supply 4 digits */
+
+ ad = 0;
+ i = 0;
+ j = 0;
+ k = 0;
+ while (i < length) {
+ if ((i & 15) == 0) {
+ /*
+ * Start of a new line.
+ */
+ j = 0;
+ l = use_digits;
+ do {
+ l--;
+ c = (ad >> (l*4)) & 0xF;
+ line[j++] = binhex[c];
+ } while (l != 0);
+ line[j++] = ' ';
+ line[j++] = ' ';
+ memset(line+j, ' ', DATA_DUMP_LEN);
+
+ /*
+ * Offset in line of ASCII dump.
+ */
+ k = j + HEX_DUMP_LEN + 2;
+ if (ascii_option == HEXDUMP_ASCII_DELIMIT)
+ line[k++] = '|';
+ }
+ c = *cp++;
+ line[j++] = binhex[c>>4];
+ line[j++] = binhex[c&0xf];
+ j++;
+ if (ascii_option != HEXDUMP_ASCII_EXCLUDE ) {
+ if (encoding == HEXDUMP_ENC_EBCDIC) {
+ c = EBCDIC_to_ASCII1(c);
+ }
+ line[k++] = ((c >= ' ') && (c < 0x7f)) ? c : '.';
+ }
+ i++;
+ if (((i & 15) == 0) || (i == length)) {
+ /*
+ * We'll be starting a new line, or
+ * we're finished printing this buffer;
+ * dump out the line we've constructed,
+ * and advance the offset.
+ */
+ if (ascii_option == HEXDUMP_ASCII_DELIMIT)
+ line[k++] = '|';
+ line[k] = '\0';
+ if (!print_line(fp, line))
+ return false;
+ ad += 16;
+ }
+ }
+ return true;
+}
+
+/*
+ * 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/wsutil/str_util.h b/wsutil/str_util.h
new file mode 100644
index 00000000..7f1362f4
--- /dev/null
+++ b/wsutil/str_util.h
@@ -0,0 +1,392 @@
+/** @file
+ * String utility definitions
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __STR_UTIL_H__
+#define __STR_UTIL_H__
+
+#include <wireshark.h>
+#include <wsutil/wmem/wmem.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+WS_DLL_PUBLIC
+char *
+wmem_strconcat(wmem_allocator_t *allocator, const char *first, ...)
+G_GNUC_MALLOC G_GNUC_NULL_TERMINATED;
+
+WS_DLL_PUBLIC
+char *
+wmem_strjoin(wmem_allocator_t *allocator,
+ const char *separator, const char *first, ...)
+G_GNUC_MALLOC G_GNUC_NULL_TERMINATED;
+
+/**
+ * As g_strjoinv, with the returned string wmem allocated.
+ * Joins a number of strings together to form one long string,
+ * with the optional separator inserted between each of them.
+ *
+ * @param allocator The wmem scope to use to allocate the returned string
+ * @param separator A string to insert between each of the strings, or NULL.
+ * @param str_array A NULL-terminated array of strings to join
+ *
+ * @note If str_array has no items, the return value is an empty string.
+ * str_array should not be NULL (NULL is returned with an warning.)
+ * NULL as a separator is equivalent to the empty string.
+ */
+WS_DLL_PUBLIC
+char *
+wmem_strjoinv(wmem_allocator_t *allocator,
+ const char *separator, char **str_array)
+G_GNUC_MALLOC;
+
+/**
+ * Splits a string into a maximum of max_tokens pieces, using the given
+ * delimiter. If max_tokens is reached, the remainder of string is appended
+ * to the last token. Successive tokens are not folded and will instead result
+ * in an empty string as element.
+ *
+ * If src or delimiter are NULL, or if delimiter is empty, this will return
+ * NULL.
+ *
+ * Do not use with a NULL allocator, use g_strsplit instead.
+ */
+WS_DLL_PUBLIC
+char **
+wmem_strsplit(wmem_allocator_t *allocator, const char *src,
+ const char *delimiter, int max_tokens);
+
+/**
+ * wmem_ascii_strdown:
+ * Based on g_ascii_strdown
+ * @param allocator An enumeration of the different types of available allocators.
+ * @param str a string.
+ * @param len length of str in bytes, or -1 if str is nul-terminated.
+ *
+ * Converts all upper case ASCII letters to lower case ASCII letters.
+ *
+ * Return value: a newly-allocated string, with all the upper case
+ * characters in str converted to lower case, with
+ * semantics that exactly match g_ascii_tolower(). (Note
+ * that this is unlike the old g_strdown(), which modified
+ * the string in place.)
+ **/
+WS_DLL_PUBLIC
+char*
+wmem_ascii_strdown(wmem_allocator_t *allocator, const char *str, ssize_t len);
+
+/** Convert all upper-case ASCII letters to their ASCII lower-case
+ * equivalents, in place, with a simple non-locale-dependent
+ * ASCII mapping (A-Z -> a-z).
+ * All other characters are left unchanged, as the mapping to
+ * lower case may be locale-dependent.
+ *
+ * The string is assumed to be in a character encoding, such as
+ * an ISO 8859 or other EUC encoding, or UTF-8, in which all
+ * bytes in the range 0x00 through 0x7F are ASCII characters and
+ * non-ASCII characters are constructed from one or more bytes in
+ * the range 0x80 through 0xFF.
+ *
+ * @param str The string to be lower-cased.
+ * @return ptr to the string
+ */
+WS_DLL_PUBLIC
+char *ascii_strdown_inplace(char *str);
+
+/** Convert all lower-case ASCII letters to their ASCII upper-case
+ * equivalents, in place, with a simple non-locale-dependent
+ * ASCII mapping (a-z -> A-Z).
+ * All other characters are left unchanged, as the mapping to
+ * lower case may be locale-dependent.
+ *
+ * The string is assumed to be in a character encoding, such as
+ * an ISO 8859 or other EUC encoding, or UTF-8, in which all
+ * bytes in the range 0x00 through 0x7F are ASCII characters and
+ * non-ASCII characters are constructed from one or more bytes in
+ * the range 0x80 through 0xFF.
+ *
+ * @param str The string to be upper-cased.
+ * @return ptr to the string
+ */
+WS_DLL_PUBLIC
+char *ascii_strup_inplace(char *str);
+
+/** Check if an entire string consists of printable characters
+ *
+ * @param str The string to be checked
+ * @return true if the entire string is printable, otherwise false
+ */
+WS_DLL_PUBLIC
+bool isprint_string(const char *str);
+
+/** Given a not-necessarily-null-terminated string, expected to be in
+ * UTF-8 but possibly containing invalid sequences (as it may have come
+ * from packet data), and the length of the string, deterimine if the
+ * string is valid UTF-8 consisting entirely of printable characters.
+ *
+ * This means that it:
+ *
+ * does not contain an illegal UTF-8 sequence (including overlong encodings,
+ * the sequences reserved for UTF-16 surrogate halves, and the values for
+ * code points above U+10FFFF that are no longer in Unicode)
+ *
+ * does not contain a non-printable Unicode character such as control
+ * characters (including internal NULL bytes)
+ *
+ * does not end in a partial sequence that could begin a valid character;
+ *
+ * does not start with a partial sequence that could end a valid character;
+ *
+ * and thus guarantees that the result of format_text() would be the same as
+ * that of wmem_strndup() with the same parameters.
+ *
+ * @param str The string to be checked
+ * @param length The number of bytes to validate
+ * @return true if the entire string is valid and printable UTF-8,
+ * otherwise false
+ */
+WS_DLL_PUBLIC
+bool isprint_utf8_string(const char *str, const unsigned length);
+
+/** Check if an entire string consists of digits
+ *
+ * @param str The string to be checked
+ * @return true if the entire string is digits, otherwise false
+ */
+WS_DLL_PUBLIC
+bool isdigit_string(const unsigned char *str);
+
+/** Finds the first occurrence of string 'needle' in string 'haystack'.
+ * The matching is done ignoring the case of ASCII characters in a
+ * non-locale-dependent way.
+ *
+ * The string is assumed to be in a character encoding, such as
+ * an ISO 8859 or other EUC encoding, or UTF-8, in which all
+ * bytes in the range 0x00 through 0x7F are ASCII characters and
+ * non-ASCII characters are constructed from one or more bytes in
+ * the range 0x80 through 0xFF.
+ *
+ * @param haystack The string possibly containing the substring
+ * @param needle The substring to be searched
+ * @return A pointer into 'haystack' where 'needle' is first found.
+ * Otherwise it returns NULL.
+ */
+WS_DLL_PUBLIC
+const char *ws_ascii_strcasestr(const char *haystack, const char *needle);
+
+WS_DLL_PUBLIC
+char *ws_escape_string(wmem_allocator_t *alloc, const char *string, bool add_quotes);
+
+WS_DLL_PUBLIC
+char *ws_escape_string_len(wmem_allocator_t *alloc, const char *string, ssize_t len, bool add_quotes);
+
+/* Replace null bytes with "\0". */
+WS_DLL_PUBLIC
+char *ws_escape_null(wmem_allocator_t *alloc, const char *string, size_t len, bool add_quotes);
+
+WS_DLL_PUBLIC
+int ws_xton(char ch);
+
+typedef enum {
+ FORMAT_SIZE_UNIT_NONE, /**< No unit will be appended. You must supply your own. */
+ FORMAT_SIZE_UNIT_BYTES, /**< "bytes" for un-prefixed sizes, "B" otherwise. */
+ FORMAT_SIZE_UNIT_BITS, /**< "bits" for un-prefixed sizes, "b" otherwise. */
+ FORMAT_SIZE_UNIT_BITS_S, /**< "bits/s" for un-prefixed sizes, "bps" otherwise. */
+ FORMAT_SIZE_UNIT_BYTES_S, /**< "bytes/s" for un-prefixed sizes, "Bps" otherwise. */
+ FORMAT_SIZE_UNIT_PACKETS, /**< "packets" */
+ FORMAT_SIZE_UNIT_PACKETS_S, /**< "packets/s" */
+} format_size_units_e;
+
+#define FORMAT_SIZE_PREFIX_SI (1 << 0) /**< SI (power of 1000) prefixes will be used. */
+#define FORMAT_SIZE_PREFIX_IEC (1 << 1) /**< IEC (power of 1024) prefixes will be used. */
+
+/** Given a size, return its value in a human-readable format
+ *
+ * Prefixes up to "T/Ti" (tera, tebi) are currently supported.
+ *
+ * @param size The size value
+ * @param flags Flags to control the output (unit of measurement,
+ * SI vs IEC, etc). Unit and prefix flags may be ORed together.
+ * @return A newly-allocated string representing the value.
+ */
+WS_DLL_PUBLIC
+char *format_size_wmem(wmem_allocator_t *allocator, int64_t size,
+ format_size_units_e unit, uint16_t flags);
+
+#define format_size(size, unit, flags) \
+ format_size_wmem(NULL, size, unit, flags)
+
+WS_DLL_PUBLIC
+char printable_char_or_period(char c);
+
+WS_DLL_PUBLIC WS_RETNONNULL
+const char *ws_strerrorname_r(int errnum, char *buf, size_t buf_size);
+
+WS_DLL_PUBLIC
+char *ws_strdup_underline(wmem_allocator_t *allocator, long offset, size_t len);
+
+/** Given a wmem scope, a not-necessarily-null-terminated string,
+ * expected to be in UTF-8 but possibly containing invalid sequences
+ * (as it may have come from packet data), and the length of the string,
+ * generate a valid UTF-8 string from it, allocated in the specified
+ * wmem scope, that:
+ *
+ * shows printable Unicode characters as themselves;
+ *
+ * shows non-printable ASCII characters as C-style escapes (octal
+ * if not one of the standard ones such as LF -> '\n');
+ *
+ * shows non-printable Unicode-but-not-ASCII characters as
+ * their universal character names;
+ *
+ * Replaces illegal UTF-8 sequences with U+FFFD (replacement character) ;
+ *
+ * and return a pointer to it.
+ *
+ * @param allocator The wmem scope
+ * @param string A pointer to the input string
+ * @param len The length of the input string
+ * @return A pointer to the formatted string
+ *
+ * @see tvb_format_text()
+ */
+WS_DLL_PUBLIC
+char *format_text(wmem_allocator_t* allocator, const char *string, size_t len);
+
+/** Same as format_text() but accepts a nul-terminated string.
+ *
+ * @param allocator The wmem scope
+ * @param string A pointer to the input string
+ * @return A pointer to the formatted string
+ *
+ * @see tvb_format_text()
+ */
+WS_DLL_PUBLIC
+char *format_text_string(wmem_allocator_t* allocator, const char *string);
+
+/**
+ * Same as format_text() but replaces any whitespace characters
+ * (space, tab, carriage return, new line, vertical tab, or formfeed)
+ * with a space.
+ *
+ * @param allocator The wmem scope
+ * @param line A pointer to the input string
+ * @param len The length of the input string
+ * @return A pointer to the formatted string
+ *
+ */
+WS_DLL_PUBLIC
+char *format_text_wsp(wmem_allocator_t* allocator, const char *line, size_t len);
+
+/**
+ * Given a string, generate a string from it that shows non-printable
+ * characters as the chr parameter passed, except a whitespace character
+ * (space, tab, carriage return, new line, vertical tab, or formfeed)
+ * which will be replaced by a space, and return a pointer to it.
+ *
+ * This does *not* treat the input string as UTF-8.
+ *
+ * This is useful for displaying binary data that frequently but not always
+ * contains text; otherwise the number of C escape codes makes it unreadable.
+ *
+ * @param allocator The wmem scope
+ * @param string A pointer to the input string
+ * @param len The length of the input string
+ * @param chr The character to use to replace non-printable characters
+ * @return A pointer to the formatted string
+ *
+ */
+WS_DLL_PUBLIC
+char *format_text_chr(wmem_allocator_t *allocator,
+ const char *string, size_t len, char chr);
+
+/** Given a wmem scope and an 8-bit character
+ * generate a valid UTF-8 string from it, allocated in the specified
+ * wmem scope, that:
+ *
+ * shows printable Unicode characters as themselves;
+ *
+ * shows non-printable ASCII characters as C-style escapes (hex
+ * if not one of the standard ones such as LF -> '\n');
+ *
+ * and return a pointer to it.
+ *
+ * @param allocator The wmem scope
+ * @param c A character to format
+ * @return A pointer to the formatted string
+ */
+WS_DLL_PUBLIC
+char *format_char(wmem_allocator_t *allocator, char c);
+
+/**
+ * Truncate a UTF-8 string in place so that it is no larger than len bytes,
+ * ensuring that the string is null terminated and ends with a complete
+ * character instead of a partial sequence (e.g., possibly truncating up
+ * to 3 additional bytes if the terminal character is 4 bytes long).
+ *
+ * The buffer holding the string must be large enough (at least len + 1
+ * including the null terminator), and the first len bytes of the buffer
+ * must be a valid UTF-8 string, except for possibly ending in a partial
+ * sequence or not being null terminated. This is a convenience function
+ * that for speed does not check either of those conditions.
+ *
+ * A common use case is when a valid UTF-8 string has been copied into a
+ * buffer of length len+1 via snprintf, strlcpy, or strlcat and truncated,
+ * to ensure that the final UTF-8 character is not a partial sequence.
+ *
+ * @param string A pointer to the input string
+ * @param len The maximum length to truncate to
+ * @return ptr to the string
+ */
+WS_DLL_PUBLIC
+char* ws_utf8_truncate(char *string, size_t len);
+
+WS_DLL_PUBLIC
+void EBCDIC_to_ASCII(uint8_t *buf, unsigned bytes);
+
+WS_DLL_PUBLIC
+uint8_t EBCDIC_to_ASCII1(uint8_t c);
+
+/* Types of character encodings */
+typedef enum {
+ HEXDUMP_ENC_ASCII = 0, /* ASCII */
+ HEXDUMP_ENC_EBCDIC = 1 /* EBCDIC */
+} hex_dump_enc;
+
+/*
+ * Hexdump options for ASCII:
+ */
+
+#define HEXDUMP_ASCII_MASK (0x0003U)
+#define HEXDUMP_ASCII_OPTION(option) ((option) & HEXDUMP_ASCII_MASK)
+
+#define HEXDUMP_ASCII_INCLUDE (0x0000U) /* include ASCII section no delimiters (legacy tshark behavior) */
+#define HEXDUMP_ASCII_DELIMIT (0x0001U) /* include ASCII section with delimiters, useful for reliable detection of last hexdata */
+#define HEXDUMP_ASCII_EXCLUDE (0x0002U) /* exclude ASCII section from hexdump reports, if we really don't want or need it */
+
+WS_DLL_PUBLIC
+bool hex_dump_buffer(bool (*print_line)(void *, const char *), void *fp,
+ const unsigned char *cp, unsigned length,
+ hex_dump_enc encoding,
+ unsigned ascii_option);
+
+/* To pass one of two strings, singular or plural */
+#define plurality(d,s,p) ((d) == 1 ? (s) : (p))
+
+#define true_or_false(val) ((val) ? "TRUE" : "FALSE")
+
+#define string_or_null(val) ((val) ? (val) : "[NULL]")
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __STR_UTIL_H__ */
diff --git a/wsutil/strnatcmp.c b/wsutil/strnatcmp.c
new file mode 100644
index 00000000..6a9a6a5f
--- /dev/null
+++ b/wsutil/strnatcmp.c
@@ -0,0 +1,189 @@
+/* strnatcmp.c
+ *
+ * Original code downloaded from: http://sourcefrog.net/projects/natsort/
+
+ strnatcmp.c -- Perform 'natural order' comparisons of strings in C.
+ Copyright (C) 2000, 2004 by Martin Pool <mbp sourcefrog net>
+
+ SPDX-License-Identifier: Zlib
+ */
+
+
+/* partial change history:
+ *
+ * 2004-10-10 mbp: Lift out character type dependencies into macros.
+ *
+ * Eric Sosman pointed out that ctype functions take a parameter whose
+ * value must be that of an unsigned int, even on platforms that have
+ * negative chars in their default char type.
+ */
+
+/*
+ * Modified 2014-10-29 to use the g_ascii_XXX() routines; this avoids
+ * locale-dependent behavior. The routine names were changed to
+ * ws_ascii_XXX() to reflect this.
+ */
+
+#include "strnatcmp.h"
+
+#include <glib.h>
+
+/* These are defined as macros to make it easier to adapt this code to
+ * different characters types or comparison functions. */
+static int
+nat_isdigit(nat_char a)
+{
+ return g_ascii_isdigit(a);
+}
+
+
+static int
+nat_isspace(nat_char a)
+{
+ return g_ascii_isspace(a);
+}
+
+
+static nat_char
+nat_toupper(nat_char a)
+{
+ return g_ascii_toupper(a);
+}
+
+
+static int
+compare_right(nat_char const *a, nat_char const *b)
+{
+ int bias = 0;
+
+ /* The longest run of digits wins. That aside, the greatest
+ value wins, but we can't know that it will until we've scanned
+ both numbers to know that they have the same magnitude, so we
+ remember it in BIAS. */
+ for (;; a++, b++) {
+ if (!nat_isdigit(*a) && !nat_isdigit(*b))
+ return bias;
+ else if (!nat_isdigit(*a))
+ return -1;
+ else if (!nat_isdigit(*b))
+ return +1;
+ else if (*a < *b) {
+ if (!bias)
+ bias = -1;
+ } else if (*a > *b) {
+ if (!bias)
+ bias = +1;
+ } else if (!*a && !*b)
+ return bias;
+ }
+
+ return 0;
+}
+
+
+static int
+compare_left(nat_char const *a, nat_char const *b)
+{
+ /* Compare two left-aligned numbers: the first to have a
+ different value wins. */
+ for (;; a++, b++) {
+ if (!nat_isdigit(*a) && !nat_isdigit(*b))
+ return 0;
+ else if (!nat_isdigit(*a))
+ return -1;
+ else if (!nat_isdigit(*b))
+ return +1;
+ else if (*a < *b)
+ return -1;
+ else if (*a > *b)
+ return +1;
+ }
+
+ return 0;
+}
+
+
+static int strnatcmp0(nat_char const *a, nat_char const *b, int fold_case)
+{
+ int ai, bi;
+ nat_char ca, cb;
+ int fractional, result;
+
+ if (!a || !b) {
+ if (!a && !b)
+ return 0;
+ if (!a)
+ return -1;
+ return +1;
+ }
+ ai = bi = 0;
+ while (1) {
+ ca = a[ai]; cb = b[bi];
+
+ /* skip over leading spaces or zeros */
+ while (nat_isspace(ca))
+ ca = a[++ai];
+
+ while (nat_isspace(cb))
+ cb = b[++bi];
+
+ /* process run of digits */
+ if (nat_isdigit(ca) && nat_isdigit(cb)) {
+ fractional = (ca == '0' || cb == '0');
+
+ if (fractional) {
+ if ((result = compare_left(a+ai, b+bi)) != 0)
+ return result;
+ } else {
+ if ((result = compare_right(a+ai, b+bi)) != 0)
+ return result;
+ }
+ }
+
+ if (!ca && !cb) {
+ /* The strings compare the same. Perhaps the caller
+ will want to call strcmp to break the tie. */
+ return 0;
+ }
+
+ if (fold_case) {
+ ca = nat_toupper(ca);
+ cb = nat_toupper(cb);
+ }
+
+ if (ca < cb)
+ return -1;
+ else if (ca > cb)
+ return +1;
+
+ ++ai; ++bi;
+ }
+}
+
+
+int ws_ascii_strnatcmp(nat_char const *a, nat_char const *b)
+{
+ return strnatcmp0(a, b, 0);
+}
+
+
+/* Compare, recognizing numeric string and ignoring case. */
+int ws_ascii_strnatcasecmp(nat_char const *a, nat_char const *b)
+{
+ return strnatcmp0(a, b, 1);
+}
+
+
+/*
+ * 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/wsutil/strnatcmp.h b/wsutil/strnatcmp.h
new file mode 100644
index 00000000..0ebe1f6f
--- /dev/null
+++ b/wsutil/strnatcmp.h
@@ -0,0 +1,33 @@
+/** @file
+ *
+ * Original code downloaded from: http://sourcefrog.net/projects/natsort/
+
+ strnatcmp.c -- Perform 'natural order' comparisons of strings in C.
+ Copyright (C) 2000, 2004 by Martin Pool <mbp sourcefrog net>
+
+ SPDX-License-Identifier: Zlib
+ */
+
+#ifndef STRNATCMP_H
+#define STRNATCMP_H
+
+#include "ws_symbol_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* CUSTOMIZATION SECTION
+ *
+ * You can change this typedef, but must then also change the inline
+ * functions in strnatcmp.c */
+typedef char nat_char;
+
+WS_DLL_PUBLIC int ws_ascii_strnatcmp(nat_char const *a, nat_char const *b);
+WS_DLL_PUBLIC int ws_ascii_strnatcasecmp(nat_char const *a, nat_char const *b);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* STRNATCMP_H */
diff --git a/wsutil/strtoi.c b/wsutil/strtoi.c
new file mode 100644
index 00000000..b64763a5
--- /dev/null
+++ b/wsutil/strtoi.c
@@ -0,0 +1,306 @@
+/* strtoi.c
+ * Utilities to convert strings to integers
+ *
+ * Copyright 2016, Dario Lombardo
+ *
+ * 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 <errno.h>
+
+#include <glib.h>
+
+#include "strtoi.h"
+#include <wsutil/ws_assert.h>
+
+bool ws_strtoi64(const char* str, const char** endptr, int64_t* cint)
+{
+ char* end;
+ int64_t val;
+
+ ws_assert(cint);
+
+ if (!str) {
+ errno = EINVAL;
+ return false;
+ }
+
+ errno = 0;
+ val = g_ascii_strtoll(str, &end, 10);
+ if ((val == 0 && end == str) || (endptr == NULL && *end != '\0')) {
+ *cint = 0;
+ if (endptr != NULL)
+ *endptr = end;
+ errno = EINVAL;
+ return false;
+ }
+ if ((val == INT64_MAX || val == INT64_MIN) && errno == ERANGE) {
+ /*
+ * Return the value, so our caller knows whether to
+ * report the value as "too small" or "too large".
+ */
+ *cint = val;
+ if (endptr != NULL)
+ *endptr = end;
+ /* errno is already set */
+ return false;
+ }
+ if (endptr != NULL)
+ *endptr = end;
+ *cint = val;
+ return true;
+}
+
+#define DEFINE_WS_STRTOI_BITS(bits) \
+bool ws_strtoi##bits(const char* str, const char** endptr, int##bits##_t* cint) \
+{ \
+ int64_t val = 0; \
+ if (!ws_strtoi64(str, endptr, &val)) { \
+ /* \
+ * For ERANGE, return either INT##bits##_MIN or \
+ * INT##bits##_MAX so our caller knows whether \
+ * to report the value as "too small" or "too \
+ * large". \
+ * \
+ * For other errors, return 0, for parallelism \
+ * with ws_strtoi64(). \
+ */ \
+ if (errno == ERANGE) { \
+ if (val < 0) \
+ *cint = INT##bits##_MIN; \
+ else \
+ *cint = INT##bits##_MAX; \
+ } else \
+ *cint = 0; \
+ return false; \
+ } \
+ if (val < INT##bits##_MIN) { \
+ /* \
+ * Return INT##bits##_MIN so our caller knows whether to \
+ * report the value as "too small" or "too large". \
+ */ \
+ *cint = INT##bits##_MIN; \
+ errno = ERANGE; \
+ return false; \
+ } \
+ if (val > INT##bits##_MAX) { \
+ /* \
+ * Return INT##bits##_MAX so our caller knows whether to \
+ * report the value as "too small" or "too large". \
+ */ \
+ *cint = INT##bits##_MAX; \
+ errno = ERANGE; \
+ return false; \
+ } \
+ *cint = (int##bits##_t)val; \
+ return true; \
+}
+
+DEFINE_WS_STRTOI_BITS(32)
+DEFINE_WS_STRTOI_BITS(16)
+DEFINE_WS_STRTOI_BITS(8)
+
+bool ws_strtoi(const char* str, const char** endptr, int* cint)
+{
+ int64_t val = 0;
+ if (!ws_strtoi64(str, endptr, &val)) {
+ /*
+ * For ERANGE, return either INT_MIN or
+ * INT_MAX so our caller knows whether
+ * to report the value as "too small" or "too
+ * large".
+ *
+ * For other errors, return 0, for parallelism
+ * with ws_strtoi64().
+ */
+ if (errno == ERANGE) {
+ if (val < 0)
+ *cint = INT_MIN;
+ else
+ *cint = INT_MAX;
+ } else
+ *cint = 0;
+ return false;
+ }
+ if (val < INT_MIN) {
+ /*
+ * Return INT_MIN so our caller knows whether to
+ * report the value as "too small" or "too large".
+ */
+ *cint = INT_MIN;
+ errno = ERANGE;
+ return false;
+ }
+ if (val > INT_MAX) {
+ /*
+ * Return INT_MAX so our caller knows whether to
+ * report the value as "too small" or "too large".
+ */
+ *cint = INT_MAX;
+ errno = ERANGE;
+ return false;
+ }
+ *cint = (int)val;
+ return true;
+}
+
+bool ws_basestrtou64(const char* str, const char** endptr, uint64_t* cint, int base)
+{
+ char* end;
+ uint64_t val;
+
+ ws_assert(cint);
+
+ if (!str) {
+ errno = EINVAL;
+ return false;
+ }
+
+ if (str[0] == '-' || str[0] == '+') {
+ /*
+ * Unsigned numbers don't have a sign.
+ */
+ *cint = 0;
+ if (endptr != NULL)
+ *endptr = str;
+ errno = EINVAL;
+ return false;
+ }
+ errno = 0;
+ val = g_ascii_strtoull(str, &end, base);
+ if ((val == 0 && end == str) || (endptr == NULL && *end != '\0')) {
+ *cint = 0;
+ if (endptr != NULL)
+ *endptr = end;
+ errno = EINVAL;
+ return false;
+ }
+ if (val == UINT64_MAX && errno == ERANGE) {
+ /*
+ * Return the value, because ws_strtoi64() does.
+ */
+ *cint = val;
+ if (endptr != NULL)
+ *endptr = end;
+ /* errno is already set */
+ return false;
+ }
+ if (endptr != NULL)
+ *endptr = end;
+ *cint = val;
+ return true;
+}
+
+bool ws_strtou64(const char* str, const char** endptr, uint64_t* cint)
+{
+ return ws_basestrtou64(str, endptr, cint, 10);
+}
+
+bool ws_hexstrtou64(const char* str, const char** endptr, uint64_t* cint)
+{
+ return ws_basestrtou64(str, endptr, cint, 16);
+}
+
+#define DEFINE_WS_STRTOU_BITS(bits) \
+bool ws_basestrtou##bits(const char* str, const char** endptr, uint##bits##_t* cint, int base) \
+{ \
+ uint64_t val; \
+ if (!ws_basestrtou64(str, endptr, &val, base)) { \
+ /* \
+ * For ERANGE, return UINT##bits##_MAX for parallelism \
+ * with ws_strtoi##bits(). \
+ * \
+ * For other errors, return 0, for parallelism \
+ * with ws_basestrtou64(). \
+ */ \
+ if (errno == ERANGE) \
+ *cint = UINT##bits##_MAX; \
+ else \
+ *cint = 0; \
+ return false; \
+ } \
+ if (val > UINT##bits##_MAX) { \
+ /* \
+ * Return UINT##bits##_MAX for parallelism with \
+ * ws_strtoi##bits(). \
+ */ \
+ *cint = UINT##bits##_MAX; \
+ errno = ERANGE; \
+ return false; \
+ } \
+ *cint = (uint##bits##_t)val; \
+ return true; \
+} \
+\
+bool ws_strtou##bits(const char* str, const char** endptr, uint##bits##_t* cint) \
+{ \
+ return ws_basestrtou##bits(str, endptr, cint, 10); \
+} \
+\
+bool ws_hexstrtou##bits(const char* str, const char** endptr, uint##bits##_t* cint) \
+{ \
+ return ws_basestrtou##bits(str, endptr, cint, 16); \
+}
+
+DEFINE_WS_STRTOU_BITS(32)
+DEFINE_WS_STRTOU_BITS(16)
+DEFINE_WS_STRTOU_BITS(8)
+
+bool ws_basestrtou(const char* str, const char** endptr, unsigned* cint, int base)
+{
+ uint64_t val;
+ if (!ws_basestrtou64(str, endptr, &val, base)) {
+ /*
+ * For ERANGE, return UINT_MAX for parallelism
+ * with ws_strtoi().
+ *
+ * For other errors, return 0, for parallelism
+ * with ws_basestrtou64().
+ */
+ if (errno == ERANGE)
+ *cint = UINT_MAX;
+ else
+ *cint = 0;
+ return false;
+ }
+ if (val > UINT_MAX) {
+ /*
+ * Return UINT_MAX for parallelism with
+ * ws_strtoi().
+ */
+ *cint = UINT_MAX;
+ errno = ERANGE;
+ return false;
+ }
+ *cint = (unsigned)val;
+ return true;
+}
+
+bool ws_strtou(const char* str, const char** endptr, unsigned* cint)
+{
+ return ws_basestrtou(str, endptr, cint, 10);
+}
+\
+bool ws_hexstrtou(const char* str, const char** endptr, unsigned* cint)
+{
+ return ws_basestrtou(str, endptr, cint, 16);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 noexpandtab:
+ * :indentSize=4:tabSize=8:noTabs=false:
+ */
diff --git a/wsutil/strtoi.h b/wsutil/strtoi.h
new file mode 100644
index 00000000..04fe802e
--- /dev/null
+++ b/wsutil/strtoi.h
@@ -0,0 +1,108 @@
+/** @file
+ * Utilities to convert strings to integers
+ *
+ * Copyright 2016, Dario Lombardo
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef _WS_STRTOI_H
+#define _WS_STRTOI_H
+
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include "ws_symbol_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * \brief Convert a decimal string to a signed/unsigned int, with error checks.
+ * \param str The string to convert
+ * \param endptr A pointer that will store a pointer to the first invalid
+ * character in str, allowing a number to be parsed even if there is trailing
+ * whitespace. If NULL, then the string is assumed to contain only valid
+ * characters (or it will error out).
+ * \param cint The converted integer
+ * \return true if the conversion succeeds, false otherwise.
+ * On error, errno is set to EINVAL for unrecognized input and ERANGE
+ * if the resulting number does not fit in the type.
+ */
+WS_DLL_PUBLIC bool ws_strtoi64(const char* str, const char** endptr, int64_t* cint);
+WS_DLL_PUBLIC bool ws_strtoi32(const char* str, const char** endptr, int32_t* cint);
+WS_DLL_PUBLIC bool ws_strtoi16(const char* str, const char** endptr, int16_t* cint);
+WS_DLL_PUBLIC bool ws_strtoi8 (const char* str, const char** endptr, int8_t* cint);
+WS_DLL_PUBLIC bool ws_strtoi (const char* str, const char** endptr, int* cint);
+
+WS_DLL_PUBLIC bool ws_strtou64(const char* str, const char** endptr, uint64_t* cint);
+WS_DLL_PUBLIC bool ws_strtou32(const char* str, const char** endptr, uint32_t* cint);
+WS_DLL_PUBLIC bool ws_strtou16(const char* str, const char** endptr, uint16_t* cint);
+WS_DLL_PUBLIC bool ws_strtou8 (const char* str, const char** endptr, uint8_t* cint);
+WS_DLL_PUBLIC bool ws_strtou (const char* str, const char** endptr, unsigned* cint);
+
+/*
+ * \brief Convert a hexadecimal string to an unsigned int, with error checks.
+ * \param str The string to convert
+ * \param endptr A pointer that will store a pointer to the first invalid
+ * character in str, allowing a number to be parsed even if there is trailing
+ * whitespace. If NULL, then the string is assumed to contain only valid
+ * characters (or it will error out).
+ * \param cint The converted integer
+ * \return true if the conversion succeeds, false otherwise.
+ * On error, errno is set to EINVAL for unrecognized input and ERANGE
+ * if the resulting number does not fit in the type.
+ */
+
+WS_DLL_PUBLIC bool ws_hexstrtou64(const char* str, const char** endptr, uint64_t* cint);
+WS_DLL_PUBLIC bool ws_hexstrtou32(const char* str, const char** endptr, uint32_t* cint);
+WS_DLL_PUBLIC bool ws_hexstrtou16(const char* str, const char** endptr, uint16_t* cint);
+WS_DLL_PUBLIC bool ws_hexstrtou8 (const char* str, const char** endptr, uint8_t* cint);
+WS_DLL_PUBLIC bool ws_hexstrtou (const char* str, const char** endptr, unsigned* cint);
+
+/*
+ * \brief Convert a string in the specified base to an unsigned int, with
+ * error checks.
+ * \param str The string to convert
+ * \param endptr A pointer that will store a pointer to the first invalid
+ * character in str, allowing a number to be parsed even if there is trailing
+ * whitespace. If NULL, then the string is assumed to contain only valid
+ * characters (or it will error out).
+ * \param cint The converted integer
+ * \param base The base for the integer; 0 means "if it begins with 0x,
+ * it's hex, otherwise if it begins with 0, it's octal, otherwise it's
+ * decimal".
+ * \return true if the conversion succeeds, false otherwise.
+ * On error, errno is set to EINVAL for unrecognized input and ERANGE
+ * if the resulting number does not fit in the type.
+ */
+
+WS_DLL_PUBLIC bool ws_basestrtou64(const char* str, const char** endptr, uint64_t* cint, int base);
+WS_DLL_PUBLIC bool ws_basestrtou32(const char* str, const char** endptr, uint32_t* cint, int base);
+WS_DLL_PUBLIC bool ws_basestrtou16(const char* str, const char** endptr, uint16_t* cint, int base);
+WS_DLL_PUBLIC bool ws_basestrtou8 (const char* str, const char** endptr, uint8_t* cint, int base);
+WS_DLL_PUBLIC bool ws_basestrtou (const char* str, const char** endptr, unsigned* cint, int base);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 noexpandtab:
+ * :indentSize=4:tabSize=8:noTabs=false:
+ */
diff --git a/wsutil/tempfile.c b/wsutil/tempfile.c
new file mode 100644
index 00000000..531ed914
--- /dev/null
+++ b/wsutil/tempfile.c
@@ -0,0 +1,153 @@
+/* tempfile.c
+ * Routines to create temporary files
+ *
+ * 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 "tempfile.h"
+
+#include <errno.h>
+
+#include "file_util.h"
+
+static char *
+sanitize_prefix(const char *prefix)
+{
+ if (!prefix) {
+ return NULL;
+ }
+
+ /* The characters in "delimiters" come from:
+ * https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#naming-conventions.
+ * Add to the list as necessary for other OS's.
+ */
+ const char *delimiters = "<>:\"/\\|?*"
+ "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a"
+ "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14"
+ "\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f";
+
+ /* Sanitize the prefix to resolve bug 7877 */
+ char *safe_prefx = g_strdup(prefix);
+ safe_prefx = g_strdelimit(safe_prefx, delimiters, '-');
+ return safe_prefx;
+}
+
+ /**
+ * Create a tempfile with the given prefix (e.g. "wireshark"). The path
+ * is created using g_file_open_tmp.
+ *
+ * @param tempdir [in] If not NULL, the directory in which to create the file.
+ * @param namebuf [in,out] If not NULL, receives the full path of the temp file.
+ * Must be freed.
+ * @param pfx [in] A prefix for the temporary file.
+ * @param sfx [in] A file extension for the temporary file. NULL can be passed
+ * if no file extension is needed
+ * @param err [out] Any error returned by g_file_open_tmp. May be NULL
+ * @return The file descriptor of the new tempfile, from mkstemps().
+ */
+int
+create_tempfile(const char *tempdir, char **namebuf, const char *pfx, const char *sfx, GError **err)
+{
+ int fd;
+ char *safe_pfx = sanitize_prefix(pfx);
+
+ if (tempdir == NULL || tempdir[0] == '\0') {
+ /* Use OS default tempdir behaviour */
+ char* filetmpl = ws_strdup_printf("%sXXXXXX%s", safe_pfx ? safe_pfx : "", sfx ? sfx : "");
+ g_free(safe_pfx);
+
+ fd = g_file_open_tmp(filetmpl, namebuf, err);
+ g_free(filetmpl);
+ }
+ else {
+ /* User-specified tempdir.
+ * We don't get libc's help generating a random name here.
+ */
+ const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
+ const int32_t a_len = 64;
+ char* filetmpl = NULL;
+
+ while(1) {
+ g_free(filetmpl);
+ filetmpl = ws_strdup_printf("%s%c%s%c%c%c%c%c%c%s",
+ tempdir,
+ G_DIR_SEPARATOR,
+ safe_pfx ? safe_pfx : "",
+ alphabet[g_random_int_range(0, a_len)],
+ alphabet[g_random_int_range(0, a_len)],
+ alphabet[g_random_int_range(0, a_len)],
+ alphabet[g_random_int_range(0, a_len)],
+ alphabet[g_random_int_range(0, a_len)],
+ alphabet[g_random_int_range(0, a_len)],
+ sfx ? sfx : "");
+
+ fd = ws_open(filetmpl, O_CREAT|O_EXCL|O_BINARY|O_WRONLY, 0600);
+ if (fd >= 0) {
+ break;
+ }
+ if (errno != EEXIST) {
+ g_set_error_literal(err, G_FILE_ERROR,
+ g_file_error_from_errno(errno), g_strerror(errno));
+ g_free(filetmpl);
+ filetmpl = NULL;
+ break;
+ }
+ /* Loop continues if error was EEXIST, meaning the file we tried
+ * to make already existed at the destination
+ */
+ }
+
+ if (namebuf == NULL) {
+ g_free(filetmpl);
+ }
+ else {
+ *namebuf = filetmpl;
+ }
+ g_free(safe_pfx);
+ }
+
+ return fd;
+}
+
+char *
+create_tempdir(const char *parent_dir, const char *tmpl, GError **err)
+{
+ if (parent_dir == NULL || parent_dir[0] == '\0') {
+ parent_dir = g_get_tmp_dir();
+ }
+
+ char *safe_pfx = sanitize_prefix(tmpl);
+ if (safe_pfx == NULL) {
+ safe_pfx = g_strdup("wireshark_XXXXXX");
+ }
+
+ char *temp_subdir = g_build_path(G_DIR_SEPARATOR_S, parent_dir, safe_pfx, NULL);
+ g_free(safe_pfx);
+ if (g_mkdtemp(temp_subdir) == NULL)
+ {
+ g_free(temp_subdir);
+ g_set_error_literal(err, G_FILE_ERROR,
+ g_file_error_from_errno(errno), g_strerror(errno));
+ return false;
+ }
+
+ return temp_subdir;
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local Variables:
+ * c-basic-offset: 2
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=2 tabstop=8 expandtab:
+ * :indentSize=2:tabSize=8:noTabs=true:
+ */
diff --git a/wsutil/tempfile.h b/wsutil/tempfile.h
new file mode 100644
index 00000000..328324f0
--- /dev/null
+++ b/wsutil/tempfile.h
@@ -0,0 +1,55 @@
+/* tempfile.h
+ * Declarations of routines to create temporary files
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __TEMPFILE_H__
+#define __TEMPFILE_H__
+
+#include <wireshark.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/** @file
+ * Convenience function for temporary file creation.
+ */
+
+/**
+ * Create a tempfile with the given prefix (e.g. "wireshark"). The path
+ * is created using g_file_open_tmp.
+ *
+ * @param tempdir [in] If not NULL, the directory in which to create the file.
+ * @param namebuf [in,out] If not NULL, receives the full path of the temp file.
+ * Must be g_freed.
+ * @param pfx [in] A prefix for the temporary file.
+ * @param sfx [in] A file extension for the temporary file. NULL can be passed
+ * if no file extension is needed
+ * @param err [out] Any error returned by g_file_open_tmp. May be NULL.
+ * @return The file descriptor of the new tempfile, from mkstemps().
+ */
+WS_DLL_PUBLIC int create_tempfile(const char *tempdir, char **namebuf, const char *pfx, const char *sfx, GError **err);
+
+/**
+ * Create a tempfile with the given parent directory (e.g. "/my/private/tmp"). The path
+ * is created using g_mkdtemp.
+ *
+ * @param parent_dir [in] If not NULL, the parent directory in which to create the subdirectory,
+ * otherwise the system temporary directory is used.
+ * @param tmpl [in] A template for the temporary directory.
+ * @param err [out] Any error returned by g_mkdtemp. May be NULL.
+ * @return The full path of the temporary directory or NULL on error. Must be g_freed.
+ */
+WS_DLL_PUBLIC char *create_tempdir(const char *parent_dir, const char *tmpl, GError **err);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __TEMPFILE_H__ */
diff --git a/wsutil/test_wsutil.c b/wsutil/test_wsutil.c
new file mode 100644
index 00000000..ff9e2f82
--- /dev/null
+++ b/wsutil/test_wsutil.c
@@ -0,0 +1,881 @@
+/*
+ * 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 <stdio.h>
+#include <glib.h>
+#include <wsutil/utf8_entities.h>
+#include <wsutil/time_util.h>
+
+#include "inet_addr.h"
+
+static void test_inet_pton4_test1(void)
+{
+ const char *str;
+ bool ok;
+ ws_in4_addr result, expect;
+
+ str = "198.51.100.200";
+ expect = g_htonl(3325256904);
+ ok = ws_inet_pton4(str, &result);
+ g_assert_true(ok);
+ g_assert_cmpint(result, ==, expect);
+}
+
+static void test_inet_ntop4_test1(void)
+{
+ char result[WS_INET_ADDRSTRLEN];
+ const char *expect, *ptr;
+ ws_in4_addr addr;
+
+ addr = g_htonl(3325256904);
+ expect = "198.51.100.200";
+ ptr = ws_inet_ntop4(&addr, result, sizeof(result));
+ g_assert_true(ptr == result);
+ g_assert_cmpstr(result, ==, expect);
+}
+
+struct in6_test {
+ char str[WS_INET6_ADDRSTRLEN];
+ ws_in6_addr addr;
+};
+
+static struct in6_test in6_test1 = {
+ .str = "2001:db8:ffaa:ddbb:1199:2288:3377:1",
+ .addr = { { 0x20, 0x01, 0x0d, 0xb8, 0xff, 0xaa, 0xdd, 0xbb,
+ 0x11, 0x99, 0x22, 0x88, 0x33, 0x77, 0x00, 0x01 } }
+};
+
+static void test_inet_pton6_test1(void)
+{
+ bool ok;
+ ws_in6_addr result;
+
+ ok = ws_inet_pton6(in6_test1.str, &result);
+ g_assert_true(ok);
+ g_assert_cmpmem(&result, sizeof(result), &in6_test1.addr, sizeof(in6_test1.addr));
+}
+
+static void test_inet_ntop6_test1(void)
+{
+ char result[WS_INET6_ADDRSTRLEN];
+ const char *ptr;
+
+ ptr = ws_inet_ntop6(&in6_test1.addr, result, sizeof(result));
+ g_assert_true(ptr == result);
+ g_assert_cmpstr(result, ==, in6_test1.str);
+}
+
+#include "str_util.h"
+
+static void test_format_size(void)
+{
+ char *str;
+
+ str = format_size(10000, FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_SI);
+ g_assert_cmpstr(str, ==, "10 kB");
+ g_free(str);
+
+ str = format_size(100000, FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_IEC);
+ g_assert_cmpstr(str, ==, "97 KiB");
+ g_free(str);
+
+ str = format_size(20971520, FORMAT_SIZE_UNIT_BITS, FORMAT_SIZE_PREFIX_IEC);
+ g_assert_cmpstr(str, ==, "20 Mib");
+ g_free(str);
+}
+
+static void test_escape_string(void)
+{
+ char *buf;
+
+ buf = ws_escape_string(NULL, "quoted \"\\\" backslash", true);
+ g_assert_cmpstr(buf, ==, "\"quoted \\\"\\\\\\\" backslash\"");
+ wmem_free(NULL, buf);
+
+ buf = ws_escape_string(NULL, "whitespace \t \n \r \f \v", true);
+ g_assert_cmpstr(buf, ==, "\"whitespace \\t \\n \\r \\f \\v""\"");
+ wmem_free(NULL, buf);
+
+ const char s1[] = { 'a', 'b', 'c', '\0', 'e', 'f', 'g'};
+ buf = ws_escape_null(NULL, s1, sizeof(s1), true);
+ g_assert_cmpstr(buf, ==, "\"abc\\0efg\"");
+ wmem_free(NULL, buf);
+}
+
+static void test_strconcat(void)
+{
+ wmem_allocator_t *allocator;
+ char *new_str;
+
+ allocator = wmem_allocator_new(WMEM_ALLOCATOR_BLOCK);
+
+ new_str = wmem_strconcat(allocator, "ABC", NULL);
+ g_assert_cmpstr(new_str, ==, "ABC");
+
+ new_str = wmem_strconcat(allocator, "ABC", "DEF", NULL);
+ g_assert_cmpstr(new_str, ==, "ABCDEF");
+
+ new_str = wmem_strconcat(allocator, "", "", "ABCDEF", "", "GH", NULL);
+ g_assert_cmpstr(new_str, ==, "ABCDEFGH");
+
+ wmem_destroy_allocator(allocator);
+}
+
+static void test_strsplit(void)
+{
+ wmem_allocator_t *allocator;
+ char **split_str;
+
+ allocator = wmem_allocator_new(WMEM_ALLOCATOR_BLOCK);
+
+ split_str = wmem_strsplit(allocator, "A-C", "-", 2);
+ g_assert_cmpstr(split_str[0], ==, "A");
+ g_assert_cmpstr(split_str[1], ==, "C");
+ g_assert_null(split_str[2]);
+
+ split_str = wmem_strsplit(allocator, "A-C", "-", 0);
+ g_assert_cmpstr(split_str[0], ==, "A");
+ g_assert_cmpstr(split_str[1], ==, "C");
+ g_assert_null(split_str[2]);
+
+ split_str = wmem_strsplit(allocator, "--aslkf-asio--asfj-as--", "-", 10);
+ g_assert_cmpstr(split_str[0], ==, "");
+ g_assert_cmpstr(split_str[1], ==, "");
+ g_assert_cmpstr(split_str[2], ==, "aslkf");
+ g_assert_cmpstr(split_str[3], ==, "asio");
+ g_assert_cmpstr(split_str[4], ==, "");
+ g_assert_cmpstr(split_str[5], ==, "asfj");
+ g_assert_cmpstr(split_str[6], ==, "as");
+ g_assert_cmpstr(split_str[7], ==, "");
+ g_assert_cmpstr(split_str[8], ==, "");
+ g_assert_null(split_str[9]);
+
+ split_str = wmem_strsplit(allocator, "--aslkf-asio--asfj-as--", "-", 5);
+ g_assert_cmpstr(split_str[0], ==, "");
+ g_assert_cmpstr(split_str[1], ==, "");
+ g_assert_cmpstr(split_str[2], ==, "aslkf");
+ g_assert_cmpstr(split_str[3], ==, "asio");
+ g_assert_cmpstr(split_str[4], ==, "-asfj-as--");
+ g_assert_null(split_str[5]);
+
+ split_str = wmem_strsplit(allocator, "", "-", -1);
+ g_assert_null(split_str[0]);
+
+ wmem_destroy_allocator(allocator);
+}
+
+static void test_str_ascii(void)
+{
+ wmem_allocator_t *allocator;
+ const char *orig_str;
+ char *new_str;
+
+ allocator = wmem_allocator_new(WMEM_ALLOCATOR_BLOCK);
+
+ orig_str = "TeStAsCiIsTrDoWn";
+ new_str = wmem_ascii_strdown(allocator, orig_str, -1);
+ g_assert_cmpstr(new_str, ==, "testasciistrdown");
+
+ wmem_destroy_allocator(allocator);
+}
+
+static void test_format_text(void)
+{
+ const char *have, *want;
+ char *res;
+
+ /* ASCII */
+ have = "abcdef";
+ want = "abcdef";
+ res = format_text_string(NULL, have);
+ g_assert_cmpstr(res, ==, want);
+ g_free(res);
+
+ /* ASCII with special escape characters. */
+ have = "abc\td\fe\nf";
+ want = "abc\\td\\fe\\nf";
+ res = format_text_string(NULL, have);
+ g_assert_cmpstr(res, ==, want);
+ g_free(res);
+
+ /* ASCII with non-printable characters. */
+ have = "abc \004 def";
+ want = "abc \\004 def";
+ res = format_text_string(NULL, have);
+ g_assert_cmpstr(res, ==, want);
+ g_free(res);
+
+ /* UTF-8 */
+ have = u8"Γαζέες καὶ μυρτιὲς δὲν θὰ βρῶ πιὰ στὸ χρυσαφὶ ξέφωτο";
+ want = u8"Γαζέες καὶ μυρτιὲς δὲν θὰ βρῶ πιὰ στὸ χρυσαφὶ ξέφωτο";
+ res = format_text_string(NULL, have);
+ g_assert_cmpstr(res, ==, want);
+ g_free(res);
+
+ /* UTF-8 with non-ASCII non-printable characters. */
+ have = u8"String with BOM \ufeff";
+ want = u8"String with BOM \\uFEFF";
+ res = format_text_string(NULL, have);
+ g_assert_cmpstr(res, ==, want);
+ g_free(res);
+
+}
+
+#define RESOURCE_USAGE_START get_resource_usage(&start_utime, &start_stime)
+
+#define RESOURCE_USAGE_END \
+ get_resource_usage(&end_utime, &end_stime); \
+ utime_ms = (end_utime - start_utime) * 1000.0; \
+ stime_ms = (end_stime - start_stime) * 1000.0
+
+static void test_format_text_perf(void)
+{
+#define LOOP_COUNT (1 * 1000 * 1000)
+ char *str;
+ int i;
+ double start_utime, start_stime, end_utime, end_stime, utime_ms, stime_ms;
+
+ const char *text = "The quick brown fox\tjumps over the lazy \001dog"UTF8_HORIZONTAL_ELLIPSIS"\n";
+
+ RESOURCE_USAGE_START;
+ for (i = 0; i < LOOP_COUNT; i++) {
+ str = format_text_string(NULL, text);
+ g_free(str);
+ }
+ RESOURCE_USAGE_END;
+ g_test_minimized_result(utime_ms + stime_ms,
+ "format_text_string(): u %.3f ms s %.3f ms", utime_ms, stime_ms);
+}
+
+#include "to_str.h"
+
+static void test_word_to_hex(void)
+{
+ static char buf[32];
+ char *str; /* String is not NULL terminated. */
+
+ str = guint8_to_hex(buf, 0x34);
+ g_assert_true(str == buf + 2);
+ g_assert_cmpint(str[-1], ==, '4');
+ g_assert_cmpint(str[-2], ==, '3');
+
+ str = word_to_hex(buf, 0x1234);
+ g_assert_true(str == buf + 4);
+ g_assert_cmpint(str[-1], ==, '4');
+ g_assert_cmpint(str[-2], ==, '3');
+ g_assert_cmpint(str[-3], ==, '2');
+ g_assert_cmpint(str[-4], ==, '1');
+
+ str = dword_to_hex(buf, 0x1234);
+ g_assert_true(str == buf + 8);
+ g_assert_cmpint(str[-1], ==, '4');
+ g_assert_cmpint(str[-2], ==, '3');
+ g_assert_cmpint(str[-3], ==, '2');
+ g_assert_cmpint(str[-4], ==, '1');
+ g_assert_cmpint(str[-5], ==, '0');
+ g_assert_cmpint(str[-6], ==, '0');
+ g_assert_cmpint(str[-7], ==, '0');
+ g_assert_cmpint(str[-8], ==, '0');
+
+ str = qword_to_hex(buf, G_GUINT64_CONSTANT(0xFEDCBA987654321));
+ g_assert_true(str == buf + 16);
+ g_assert_cmpint(str[-1], ==, '1');
+ g_assert_cmpint(str[-2], ==, '2');
+ g_assert_cmpint(str[-3], ==, '3');
+ g_assert_cmpint(str[-4], ==, '4');
+ g_assert_cmpint(str[-5], ==, '5');
+ g_assert_cmpint(str[-6], ==, '6');
+ g_assert_cmpint(str[-7], ==, '7');
+ g_assert_cmpint(str[-8], ==, '8');
+ g_assert_cmpint(str[-9], ==, '9');
+ g_assert_cmpint(str[-10], ==, 'a');
+ g_assert_cmpint(str[-11], ==, 'b');
+ g_assert_cmpint(str[-12], ==, 'c');
+ g_assert_cmpint(str[-13], ==, 'd');
+ g_assert_cmpint(str[-14], ==, 'e');
+ g_assert_cmpint(str[-15], ==, 'f');
+ g_assert_cmpint(str[-16], ==, '0');
+}
+
+static void test_bytes_to_str(void)
+{
+ char *str;
+
+ const uint8_t buf[] = { 1, 2, 3};
+
+ str = bytes_to_str(NULL, buf, sizeof(buf));
+ g_assert_cmpstr(str, ==, "010203");
+ g_free(str);
+}
+
+static void test_bytes_to_str_punct(void)
+{
+ char *str;
+
+ const uint8_t buf[] = { 1, 2, 3};
+
+ str = bytes_to_str_punct(NULL, buf, sizeof(buf), ':');
+ g_assert_cmpstr(str, ==, "01:02:03");
+ g_free(str);
+}
+
+static void test_bytes_to_str_punct_maxlen(void)
+{
+ char *str;
+
+ const uint8_t buf[] = { 1, 2, 3};
+
+ str = bytes_to_str_punct_maxlen(NULL, buf, sizeof(buf), ':', 4);
+ g_assert_cmpstr(str, ==, "01:02:03");
+ g_free(str);
+
+ str = bytes_to_str_punct_maxlen(NULL, buf, sizeof(buf), ':', 3);
+ g_assert_cmpstr(str, ==, "01:02:03");
+ g_free(str);
+
+ str = bytes_to_str_punct_maxlen(NULL, buf, sizeof(buf), ':', 2);
+ g_assert_cmpstr(str, ==, "01:02:" UTF8_HORIZONTAL_ELLIPSIS);
+ g_free(str);
+
+ str = bytes_to_str_punct_maxlen(NULL, buf, sizeof(buf), ':', 1);
+ g_assert_cmpstr(str, ==, "01:" UTF8_HORIZONTAL_ELLIPSIS);
+ g_free(str);
+
+ str = bytes_to_str_punct_maxlen(NULL, buf, sizeof(buf), ':', 0);
+ g_assert_cmpstr(str, ==, "01:02:03");
+ g_free(str);
+}
+
+static void test_bytes_to_str_maxlen(void)
+{
+ char *str;
+
+ const uint8_t buf[] = { 1, 2, 3};
+
+ str = bytes_to_str_maxlen(NULL, buf, sizeof(buf), 4);
+ g_assert_cmpstr(str, ==, "010203");
+ g_free(str);
+
+ str = bytes_to_str_maxlen(NULL, buf, sizeof(buf), 3);
+ g_assert_cmpstr(str, ==, "010203");
+ g_free(str);
+
+ str = bytes_to_str_maxlen(NULL, buf, sizeof(buf), 2);
+ g_assert_cmpstr(str, ==, "0102" UTF8_HORIZONTAL_ELLIPSIS);
+ g_free(str);
+
+ str = bytes_to_str_maxlen(NULL, buf, sizeof(buf), 1);
+ g_assert_cmpstr(str, ==, "01" UTF8_HORIZONTAL_ELLIPSIS);
+ g_free(str);
+
+ str = bytes_to_str_maxlen(NULL, buf, sizeof(buf), 0);
+ g_assert_cmpstr(str, ==, "010203");
+ g_free(str);
+}
+
+static void test_bytes_to_string_trunc1(void)
+{
+ char *str;
+
+ const uint8_t buf[] = {
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA
+ };
+ const char *expect =
+ "112233445566778899aa"
+ "112233445566778899aa"
+ "112233445566778899aa"
+ "112233445566" UTF8_HORIZONTAL_ELLIPSIS;
+
+ str = bytes_to_str(NULL, buf, sizeof(buf));
+ g_assert_cmpstr(str, ==, expect);
+ g_free(str);
+}
+
+static void test_bytes_to_string_punct_trunc1(void)
+{
+ char *str;
+
+ const uint8_t buf[] = {
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA
+ };
+ const char *expect =
+ "11:22:33:44:55:66:77:88:99:aa:"
+ "11:22:33:44:55:66:77:88:99:aa:"
+ "11:22:33:44:" UTF8_HORIZONTAL_ELLIPSIS;
+
+ str = bytes_to_str_punct(NULL, buf, sizeof(buf), ':');
+ g_assert_cmpstr(str, ==, expect);
+ g_free(str);
+}
+
+static char to_str_back_buf[32];
+#define BACK_PTR (&to_str_back_buf[31]) /* pointer to NUL string terminator */
+
+static void test_oct_to_str_back(void)
+{
+ char *str;
+
+ str = oct_to_str_back(BACK_PTR, 958769886);
+ g_assert_cmpstr(str, ==, "07111325336");
+
+ str = oct_to_str_back(BACK_PTR, 781499127);
+ g_assert_cmpstr(str, ==, "05645135367");
+
+ str = oct_to_str_back(BACK_PTR, 1177329882);
+ g_assert_cmpstr(str, ==, "010613120332");
+}
+
+static void test_oct64_to_str_back(void)
+{
+ char *str;
+
+ str = oct64_to_str_back(BACK_PTR, G_GUINT64_CONSTANT(13873797580070999420));
+ g_assert_cmpstr(str, ==, "01402115026217563452574");
+
+ str = oct64_to_str_back(BACK_PTR, G_GUINT64_CONSTANT(7072159458371400691));
+ g_assert_cmpstr(str, ==, "0610452670726711271763");
+
+ str = oct64_to_str_back(BACK_PTR, G_GUINT64_CONSTANT(12453513102400590374));
+ g_assert_cmpstr(str, ==, "01263236102754220511046");
+}
+
+static void test_hex_to_str_back_len(void)
+{
+ char *str;
+
+ str = hex_to_str_back_len(BACK_PTR, 2481, 8);
+ g_assert_cmpstr(str, ==, "0x000009b1");
+
+ str = hex_to_str_back_len(BACK_PTR, 2457, 8);
+ g_assert_cmpstr(str, ==, "0x00000999");
+
+ str = hex_to_str_back_len(BACK_PTR, 16230, 8);
+ g_assert_cmpstr(str, ==, "0x00003f66");
+}
+
+static void test_hex64_to_str_back_len(void)
+{
+ char *str;
+
+ str = hex64_to_str_back_len(BACK_PTR, G_GUINT64_CONSTANT(1), 16);
+ g_assert_cmpstr(str, ==, "0x0000000000000001");
+
+ str = hex64_to_str_back_len(BACK_PTR, G_GUINT64_CONSTANT(4294967295), 16);
+ g_assert_cmpstr(str, ==, "0x00000000ffffffff");
+
+ str = hex64_to_str_back_len(BACK_PTR, G_GUINT64_CONSTANT(18446744073709551615), 16);
+ g_assert_cmpstr(str, ==, "0xffffffffffffffff");
+}
+
+static void test_uint_to_str_back(void)
+{
+ char *str;
+
+ str = uint_to_str_back(BACK_PTR, 873735883);
+ g_assert_cmpstr(str, ==, "873735883");
+
+ str = uint_to_str_back(BACK_PTR, 1801148094);
+ g_assert_cmpstr(str, ==, "1801148094");
+
+ str = uint_to_str_back(BACK_PTR, 181787997);
+ g_assert_cmpstr(str, ==, "181787997");
+}
+
+static void test_uint64_to_str_back(void)
+{
+ char *str;
+
+ str = uint64_to_str_back(BACK_PTR, G_GUINT64_CONSTANT(585143757104211265));
+ g_assert_cmpstr(str, ==, "585143757104211265");
+
+ str = uint64_to_str_back(BACK_PTR, G_GUINT64_CONSTANT(7191580247919484847));
+ g_assert_cmpstr(str, ==, "7191580247919484847");
+
+ str = uint64_to_str_back(BACK_PTR, G_GUINT64_CONSTANT(95778573911934485));
+ g_assert_cmpstr(str, ==, "95778573911934485");
+}
+
+static void test_uint_to_str_back_len(void)
+{
+ char *str;
+
+ str = uint_to_str_back_len(BACK_PTR, 26630, 8);
+ g_assert_cmpstr(str, ==, "00026630");
+
+ str = uint_to_str_back_len(BACK_PTR, 25313, 8);
+ g_assert_cmpstr(str, ==, "00025313");
+
+ str = uint_to_str_back_len(BACK_PTR, 18750000, 8);
+ g_assert_cmpstr(str, ==, "18750000");
+}
+
+static void test_uint64_to_str_back_len(void)
+{
+ char *str;
+
+ str = uint64_to_str_back_len(BACK_PTR, G_GUINT64_CONSTANT(1), 16);
+ g_assert_cmpstr(str, ==, "0000000000000001");
+
+ str = uint64_to_str_back_len(BACK_PTR, G_GUINT64_CONSTANT(4294967295), 16);
+ g_assert_cmpstr(str, ==, "0000004294967295");
+
+ str = uint64_to_str_back_len(BACK_PTR, G_GUINT64_CONSTANT(18446744073709551615), 16);
+ g_assert_cmpstr(str, ==, "18446744073709551615");
+}
+
+static void test_int_to_str_back(void)
+{
+ char *str;
+
+ str = int_to_str_back(BACK_PTR, -763689611);
+ g_assert_cmpstr(str, ==, "-763689611");
+
+ str = int_to_str_back(BACK_PTR, -296015954);
+ g_assert_cmpstr(str, ==, "-296015954");
+
+ str = int_to_str_back(BACK_PTR, 898901469);
+ g_assert_cmpstr(str, ==, "898901469");
+}
+
+static void test_int64_to_str_back(void)
+{
+ char *str;
+
+ str = int64_to_str_back(BACK_PTR, G_GINT64_CONSTANT(-9223372036854775807));
+ g_assert_cmpstr(str, ==, "-9223372036854775807");
+
+ str = int64_to_str_back(BACK_PTR, G_GINT64_CONSTANT(1));
+ g_assert_cmpstr(str, ==, "1");
+
+ str = int64_to_str_back(BACK_PTR, G_GINT64_CONSTANT(9223372036854775807));
+ g_assert_cmpstr(str, ==, "9223372036854775807");
+}
+
+#include "nstime.h"
+#include "time_util.h"
+
+void test_nstime_from_iso8601(void)
+{
+ char *str;
+ const char *endp;
+ nstime_t result, expect;
+ struct tm tm1;
+
+ memset(&tm1, 0, sizeof(tm1));
+ tm1.tm_sec = 25;
+ tm1.tm_min = 45;
+ tm1.tm_hour = 23;
+ tm1.tm_mday = 30;
+ tm1.tm_mon = 4; /* starts at zero */
+ tm1.tm_year = 2013 - 1900;
+ tm1.tm_isdst = -1;
+
+ /* Date and time with local time. */
+ str = "2013-05-30T23:45:25.349124";
+ expect.secs = mktime(&tm1);
+ expect.nsecs = 349124 * 1000;
+ endp = iso8601_to_nstime(&result, str, ISO8601_DATETIME_AUTO);
+ g_assert_nonnull(endp);
+ g_assert(*endp == '\0');
+ g_assert_cmpint(result.secs, ==, expect.secs);
+ g_assert_cmpint(result.nsecs, ==, expect.nsecs);
+
+ /* Date and time with UTC timezone. */
+ str = "2013-05-30T23:45:25.349124Z";
+ expect.secs = mktime_utc(&tm1);
+ expect.nsecs = 349124 * 1000;
+ endp = iso8601_to_nstime(&result, str, ISO8601_DATETIME_AUTO);
+ g_assert_nonnull(endp);
+ g_assert(*endp == '\0');
+ g_assert_cmpint(result.secs, ==, expect.secs);
+ g_assert_cmpint(result.nsecs, ==, expect.nsecs);
+
+ /* Date and time with timezone offset with separator. */
+ str = "2013-05-30T23:45:25.349124+01:00";
+ expect.secs = mktime_utc(&tm1) - 1 * 60 * 60;
+ expect.nsecs = 349124 * 1000;
+ endp = iso8601_to_nstime(&result, str, ISO8601_DATETIME_AUTO);
+ g_assert_nonnull(endp);
+ g_assert(*endp == '\0');
+ g_assert_cmpint(result.secs, ==, expect.secs);
+ g_assert_cmpint(result.nsecs, ==, expect.nsecs);
+
+ /* Date and time with timezone offset without separator. */
+ str = "2013-05-30T23:45:25.349124+0100";
+ expect.secs = mktime_utc(&tm1) - 1 * 60 * 60;
+ expect.nsecs = 349124 * 1000;
+ endp = iso8601_to_nstime(&result, str, ISO8601_DATETIME_AUTO);
+ g_assert_nonnull(endp);
+ g_assert(*endp == '\0');
+ g_assert_cmpint(result.secs, ==, expect.secs);
+ g_assert_cmpint(result.nsecs, ==, expect.nsecs);
+
+ /* Date and time with timezone offset with hours only. */
+ str = "2013-05-30T23:45:25.349124+01";
+ expect.secs = mktime_utc(&tm1) - 1 * 60 * 60;
+ expect.nsecs = 349124 * 1000;
+ endp = iso8601_to_nstime(&result, str, ISO8601_DATETIME_AUTO);
+ g_assert_nonnull(endp);
+ g_assert(*endp == '\0');
+ g_assert_cmpint(result.secs, ==, expect.secs);
+ g_assert_cmpint(result.nsecs, ==, expect.nsecs);
+}
+
+#include "ws_getopt.h"
+
+#define ARGV_MAX 31
+
+static char **new_argv(int *argc_ptr, const char *args, ...)
+{
+ char **argv;
+ int argc = 0;
+ va_list ap;
+
+ argv = g_malloc((ARGV_MAX + 1) * sizeof(char *));
+
+ va_start(ap, args);
+ while (args != NULL) {
+ /* Increase ARGV_MAX or use a dynamic size if this assertion fails. */
+ g_assert_true(argc < ARGV_MAX);
+ argv[argc++] = g_strdup(args);
+ args = va_arg(ap, const char *);
+ }
+ argv[argc] = NULL;
+ va_end(ap);
+
+ *argc_ptr = argc;
+ return argv;
+}
+
+static void free_argv(char **argv)
+{
+ for (char **p = argv; *p != NULL; p++) {
+ g_free(*p);
+ }
+ g_free(argv);
+}
+
+static void test_getopt_long_basic1(void)
+{
+ char **argv;
+ int argc;
+
+ const char *optstring = "ab:c";
+ argv = new_argv(&argc, "/bin/ls", "-a", "-b", "arg1", "-c", "path", (char *)NULL);
+
+ ws_optind = 1;
+ int opt;
+
+ opt = ws_getopt_long(argc, argv, optstring, NULL, NULL);
+ g_assert_cmpint(opt, ==, 'a');
+ g_assert_null(ws_optarg);
+
+ opt = ws_getopt_long(argc, argv, optstring, NULL, NULL);
+ g_assert_cmpint(opt, ==, 'b');
+ g_assert_cmpstr(ws_optarg, ==, "arg1");
+
+ opt = ws_getopt_long(argc, argv, optstring, NULL, NULL);
+ g_assert_cmpint(opt, ==, 'c');
+ g_assert_null(ws_optarg);
+
+ opt = ws_getopt_long(argc, argv, optstring, NULL, NULL);
+ g_assert_cmpint(opt, ==, -1);
+
+ free_argv(argv);
+}
+
+static void test_getopt_long_basic2(void)
+{
+ char **argv;
+ int argc;
+
+ struct ws_option longopts[] = {
+ { "opt1", ws_no_argument, NULL, '1' },
+ { "opt2", ws_required_argument, NULL, '2' },
+ { "opt3", ws_required_argument, NULL, '3' },
+ { 0, 0, 0, 0 }
+ };
+ argv = new_argv(&argc, "/bin/ls", "--opt1", "--opt2", "arg1", "--opt3=arg2", "path", (char *)NULL);
+
+ ws_optind = 1;
+ int opt;
+
+ opt = ws_getopt_long(argc, argv, "", longopts, NULL);
+ g_assert_cmpint(opt, ==, '1');
+ g_assert_null(ws_optarg);
+
+ opt = ws_getopt_long(argc, argv, "", longopts, NULL);
+ g_assert_cmpint(opt, ==, '2');
+ g_assert_cmpstr(ws_optarg, ==, "arg1");
+
+ opt = ws_getopt_long(argc, argv, "", longopts, NULL);
+ g_assert_cmpint(opt, ==, '3');
+ g_assert_cmpstr(ws_optarg, ==, "arg2");
+
+ opt = ws_getopt_long(argc, argv, "", longopts, NULL);
+ g_assert_cmpint(opt, ==, -1);
+
+ free_argv(argv);
+}
+
+static void test_getopt_optional_argument1(void)
+{
+ char **argv;
+ int argc;
+ int opt;
+
+ struct ws_option longopts_optional[] = {
+ { "optional", ws_optional_argument, NULL, '1' },
+ { 0, 0, 0, 0 }
+ };
+
+ argv = new_argv(&argc, "/bin/ls", "--optional=arg1", (char *)NULL);
+
+ ws_optreset = 1;
+ opt = ws_getopt_long(argc, argv, "", longopts_optional, NULL);
+ g_assert_cmpint(opt, ==, '1');
+ g_assert_cmpstr(ws_optarg, ==, "arg1");
+
+ free_argv(argv);
+ argv = new_argv(&argc, "/bin/ls", "--optional", "arg1", (char *)NULL);
+
+ ws_optreset = 1;
+ opt = ws_getopt_long(argc, argv, "", longopts_optional, NULL);
+ g_assert_cmpint(opt, ==, '1');
+ /* Optional argument does not recognize the form "--arg param" (it's ambiguous). */
+ g_assert_null(ws_optarg);
+
+ free_argv(argv);
+ argv = new_argv(&argc, "/bin/ls", "--optional", (char *)NULL);
+
+ ws_optreset = 1;
+ opt = ws_getopt_long(argc, argv, "", longopts_optional, NULL);
+ g_assert_cmpint(opt, ==, '1');
+ g_assert_null(ws_optarg);
+
+ free_argv(argv);
+}
+
+static void test_getopt_opterr1(void)
+{
+ char **argv;
+ int argc;
+
+#ifdef _WIN32
+ g_test_skip("Not supported on Windows");
+ return;
+#endif
+
+ if (g_test_subprocess()) {
+ const char *optstring = "ab";
+ argv = new_argv(&argc, "/bin/ls", "-a", "-z", "path", (char *)NULL);
+
+ ws_optind = 0;
+ ws_opterr = 1;
+ int opt;
+
+ opt = ws_getopt_long(argc, argv, optstring, NULL, NULL);
+ g_assert_cmpint(opt, ==, 'a');
+
+ opt = ws_getopt_long(argc, argv, optstring, NULL, NULL);
+ g_assert_cmpint(opt, ==, '?');
+ g_assert_cmpint(ws_optopt, ==, 'z');
+
+ opt = ws_getopt_long(argc, argv, optstring, NULL, NULL);
+ g_assert_cmpint(opt, ==, -1);
+
+ free_argv(argv);
+
+ return;
+ }
+
+ g_test_trap_subprocess(NULL, 0, 0);
+ g_test_trap_assert_passed();
+ g_test_trap_assert_stderr("/bin/ls: unrecognized option: z\n");
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+
+ ws_log_init("test_wsutil", NULL);
+
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add_func("/inet_addr/inet_pton4", test_inet_pton4_test1);
+ g_test_add_func("/inet_addr/inet_ntop4", test_inet_ntop4_test1);
+ g_test_add_func("/inet_addr/inet_pton6", test_inet_pton6_test1);
+ g_test_add_func("/inet_addr/inet_ntop6", test_inet_ntop6_test1);
+
+ g_test_add_func("/str_util/format_size", test_format_size);
+ g_test_add_func("/str_util/escape_string", test_escape_string);
+ g_test_add_func("/str_util/strconcat", test_strconcat);
+ g_test_add_func("/str_util/strsplit", test_strsplit);
+ g_test_add_func("/str_util/str_ascii", test_str_ascii);
+ g_test_add_func("/str_util/format_text", test_format_text);
+
+ if (g_test_perf()) {
+ g_test_add_func("/str_util/format_text_perf", test_format_text_perf);
+ }
+
+ g_test_add_func("/to_str/word_to_hex", test_word_to_hex);
+ g_test_add_func("/to_str/bytes_to_str", test_bytes_to_str);
+ g_test_add_func("/to_str/bytes_to_str_punct", test_bytes_to_str_punct);
+ g_test_add_func("/to_str/bytes_to_str_maxlen", test_bytes_to_str_maxlen);
+ g_test_add_func("/to_str/bytes_to_str_punct_maxlen", test_bytes_to_str_punct_maxlen);
+ g_test_add_func("/to_str/bytes_to_str_trunc1", test_bytes_to_string_trunc1);
+ g_test_add_func("/to_str/bytes_to_str_punct_trunc1", test_bytes_to_string_punct_trunc1);
+ g_test_add_func("/to_str/oct_to_str_back", test_oct_to_str_back);
+ g_test_add_func("/to_str/oct64_to_str_back", test_oct64_to_str_back);
+ g_test_add_func("/to_str/hex_to_str_back_len", test_hex_to_str_back_len);
+ g_test_add_func("/to_str/hex64_to_str_back_len", test_hex64_to_str_back_len);
+ g_test_add_func("/to_str/uint_to_str_back", test_uint_to_str_back);
+ g_test_add_func("/to_str/uint64_to_str_back", test_uint64_to_str_back);
+ g_test_add_func("/to_str/uint_to_str_back_len", test_uint_to_str_back_len);
+ g_test_add_func("/to_str/uint64_to_str_back_len", test_uint64_to_str_back_len);
+ g_test_add_func("/to_str/int_to_str_back", test_int_to_str_back);
+ g_test_add_func("/to_str/int64_to_str_back", test_int64_to_str_back);
+
+ g_test_add_func("/nstime/from_iso8601", test_nstime_from_iso8601);
+
+ g_test_add_func("/ws_getopt/basic1", test_getopt_long_basic1);
+ g_test_add_func("/ws_getopt/basic2", test_getopt_long_basic2);
+ g_test_add_func("/ws_getopt/optional1", test_getopt_optional_argument1);
+ g_test_add_func("/ws_getopt/opterr1", test_getopt_opterr1);
+
+ ret = g_test_run();
+
+ 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:
+ */
diff --git a/wsutil/time_util.c b/wsutil/time_util.c
new file mode 100644
index 00000000..6d967953
--- /dev/null
+++ b/wsutil/time_util.c
@@ -0,0 +1,339 @@
+/* time_util.c
+ *
+ * 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"
+#define WS_LOG_DOMAIN LOG_DOMAIN_WSUTIL
+#include "time_util.h"
+
+#include <errno.h>
+
+#include <wsutil/epochs.h>
+
+#ifndef _WIN32
+#include <sys/time.h>
+#include <sys/resource.h>
+#else
+#include <windows.h>
+#endif
+
+/* Test if the given year is a leap year */
+#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
+
+/* converts a broken down date representation, relative to UTC,
+ * to a timestamp; it uses timegm() if it's available.
+ *
+ * Returns -1 and sets errno to EINVAL on error; returns the timestamp
+ * and sets errno to 0 on success.
+ */
+time_t
+mktime_utc(struct tm *tm)
+{
+ time_t retval;
+#ifndef HAVE_TIMEGM
+ /*
+ * We don't have timegm(), so use code copied from Glib source
+ * gtimer.c.
+ */
+ static const int days_before[] =
+ {
+ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
+ };
+
+ int yr;
+
+ if (tm->tm_mon < 0 || tm->tm_mon > 11) {
+ errno = EINVAL;
+ return (time_t) -1;
+ }
+
+ retval = (tm->tm_year - 70) * 365;
+
+ /* count number of leap years */
+ yr = tm->tm_year + 1900;
+ if (tm->tm_mon + 1 < 3 && isleap(yr))
+ yr--;
+ retval += (((yr / 4) - (yr / 100) + (yr / 400)) - 477); /* 477 = ((1970 / 4) - (1970 / 100) + (1970 / 400)) */
+
+ retval += days_before[tm->tm_mon] + tm->tm_mday - 1;
+
+ retval = ((((retval * 24) + tm->tm_hour) * 60) + tm->tm_min) * 60 + tm->tm_sec;
+
+ /*
+ * Just in case somebody asked for 1969-12-31 23:59:59 UTC,
+ * which is one second before the Unix epoch.
+ */
+ errno = 0;
+ return retval;
+#else
+ retval = timegm(tm);
+ /*
+ * If passed a struct tm for 2013-03-01 00:00:00, both
+ * macOS and FreeBSD timegm() return the epoch time
+ * value for 2013-03-01 00:00:00 UTC, but also set
+ * errno to EOVERFLOW. This may be true of other
+ * implementations based on the tzcode reference
+ * impelementation of timegm().
+ *
+ * The macOS and FreeBSD documentation for timegm() neither
+ * commit to leaving errno alone nor commit to setting it
+ * to a particular value.
+ *
+ * Force errno to 0, and check for an error and set it to
+ * EINVAL iff we got an error.
+ */
+ errno = 0;
+ if (retval == (time_t)-1) {
+ /*
+ * Did somebody ask for 1969-12-31 23:59:59 UTC,
+ * which is one second before the Unix epoch?
+ *
+ * If so, timegm() happened to return the correct
+ * timestamp (whether because it calculated it or
+ * because it failed in some fashion).
+ *
+ * If not, set errno to EINVAL.
+ */
+ if (tm->tm_year != (1969 - 1900) ||
+ tm->tm_mon != (12 - 1) ||
+ tm->tm_mday != 31 ||
+ tm->tm_hour != 23 ||
+ tm->tm_min != 59 ||
+ tm->tm_sec != 59)
+ errno = EINVAL;
+ }
+ return retval;
+#endif /* !HAVE_TIMEGM */
+}
+
+/* Validate the values in a time_t
+ * Currently checks tm_year, tm_mon, tm_mday, tm_hour, tm_min, and tm_sec;
+ * disregards tm_wday, tm_yday, and tm_isdst.
+ * Use this in situations where you wish to return an error rather than
+ * normalizing invalid dates; otherwise you could specify, for example,
+ * 2020-10-40 (to quote the macOS and probably *BSD manual
+ * page for ctime()/localtime()/mktime()/etc., "October 40
+ * is changed into November 9").
+ */
+bool
+tm_is_valid(struct tm *tm)
+{
+ static const int8_t days_in_month[12] = {
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ };
+
+ if (tm->tm_mon < 0 || tm->tm_mon > 11) {
+ return false;
+ }
+ if (tm->tm_mday < 0 || tm->tm_mday >
+ ((tm->tm_mon == 1 && isleap(tm->tm_year)) ? 29 : days_in_month[tm->tm_mon])) {
+ return false;
+ }
+ if (tm->tm_hour < 0 || tm->tm_hour > 23) {
+ return false;
+ }
+ /* XXX: ISO 8601 and others allow 24:00:00 for end of day, perhaps that
+ * one case should be allowed?
+ */
+ if (tm->tm_min < 0 || tm->tm_min > 59) {
+ return false;
+ }
+ if (tm->tm_sec < 0 || tm->tm_sec > 60) {
+ /* 60, not 59, to account for leap seconds */
+ return false;
+ }
+ return true;
+}
+
+void get_resource_usage(double *user_time, double *sys_time) {
+#ifndef _WIN32
+ struct rusage ru;
+
+ getrusage(RUSAGE_SELF, &ru);
+
+ *user_time = ru.ru_utime.tv_sec + (ru.ru_utime.tv_usec / 1000000.0);
+ *sys_time = ru.ru_stime.tv_sec + (ru.ru_stime.tv_usec / 1000000.0);
+#else /* _WIN32 */
+ HANDLE h_proc = GetCurrentProcess();
+ FILETIME cft, eft, kft, uft;
+ ULARGE_INTEGER uli_time;
+
+ GetProcessTimes(h_proc, &cft, &eft, &kft, &uft);
+
+ uli_time.LowPart = uft.dwLowDateTime;
+ uli_time.HighPart = uft.dwHighDateTime;
+ *user_time = uli_time.QuadPart / 10000000.0;
+ uli_time.LowPart = kft.dwLowDateTime;
+ uli_time.HighPart = kft.dwHighDateTime;
+ *sys_time = uli_time.QuadPart / 1000000000.0;
+#endif /* _WIN32 */
+}
+
+static double last_user_time = 0.0;
+static double last_sys_time = 0.0;
+
+void log_resource_usage(bool reset_delta, const char *format, ...) {
+ va_list ap;
+ GString *log_str = g_string_new("");
+ double user_time;
+ double sys_time;
+
+ get_resource_usage(&user_time, &sys_time);
+
+ if (reset_delta || last_user_time == 0.0) {
+ last_user_time = user_time;
+ last_sys_time = sys_time;
+ }
+
+ g_string_append_printf(log_str, "user %.3f +%.3f sys %.3f +%.3f ",
+ user_time, user_time - last_user_time,
+ sys_time, sys_time - last_sys_time);
+
+ va_start(ap, format);
+ g_string_append_vprintf(log_str, format, ap);
+ va_end(ap);
+
+ ws_warning("%s", log_str->str);
+ g_string_free(log_str, true);
+
+}
+
+/* Copied from pcapio.c pcapng_write_interface_statistics_block()*/
+uint64_t
+create_timestamp(void) {
+ uint64_t timestamp;
+#ifdef _WIN32
+ FILETIME now;
+#else
+ struct timeval now;
+#endif
+
+#ifdef _WIN32
+ /*
+ * Current time, represented as 100-nanosecond intervals since
+ * January 1, 1601, 00:00:00 UTC.
+ *
+ * I think DWORD might be signed, so cast both parts of "now"
+ * to uint32_t so that the sign bit doesn't get treated specially.
+ *
+ * Windows 8 provides GetSystemTimePreciseAsFileTime which we
+ * might want to use instead.
+ */
+ GetSystemTimeAsFileTime(&now);
+ timestamp = (((uint64_t)(uint32_t)now.dwHighDateTime) << 32) +
+ (uint32_t)now.dwLowDateTime;
+
+ /*
+ * Convert to same thing but as 1-microsecond, i.e. 1000-nanosecond,
+ * intervals.
+ */
+ timestamp /= 10;
+
+ /*
+ * Subtract difference, in microseconds, between January 1, 1601
+ * 00:00:00 UTC and January 1, 1970, 00:00:00 UTC.
+ */
+ timestamp -= EPOCH_DELTA_1601_01_01_00_00_00_UTC*1000000;
+#else
+ /*
+ * Current time, represented as seconds and microseconds since
+ * January 1, 1970, 00:00:00 UTC.
+ */
+ gettimeofday(&now, NULL);
+
+ /*
+ * Convert to delta in microseconds.
+ */
+ timestamp = (uint64_t)(now.tv_sec) * 1000000 + (uint64_t)(now.tv_usec);
+#endif
+ return timestamp;
+}
+
+struct timespec *
+ws_clock_get_realtime(struct timespec *ts)
+{
+#if defined(HAVE_CLOCK_GETTIME)
+ if (clock_gettime(CLOCK_REALTIME, ts) == 0)
+ return ts;
+#elif defined(HAVE_TIMESPEC_GET)
+ if (timespec_get(ts, TIME_UTC) == TIME_UTC)
+ return ts;
+#endif
+
+#ifndef _WIN32
+ /* Fall back on gettimeofday(). */
+ struct timeval usectimenow;
+ gettimeofday(&usectimenow, NULL);
+ ts->tv_sec = usectimenow.tv_sec;
+ ts->tv_nsec = usectimenow.tv_usec*1000;
+ return ts;
+#else
+ /* Fall back on time(). */
+ ts->tv_sec = time(NULL);
+ ts->tv_nsec = 0;
+ return ts;
+#endif
+}
+
+struct tm *
+ws_localtime_r(const time_t *timep, struct tm *result)
+{
+#if defined(HAVE_LOCALTIME_R)
+ return localtime_r(timep, result);
+#elif defined(_MSC_VER)
+ errno_t err = localtime_s(result, timep);
+ if (err == 0)
+ return result;
+ return NULL;
+#else
+ struct tm *aux = localtime(timep);
+ if (aux == NULL)
+ return NULL;
+ *result = *aux;
+ return result;
+#endif
+}
+
+void ws_tzset(void)
+{
+#ifdef HAVE_TZSET
+ tzset();
+#endif
+}
+
+struct tm *
+ws_gmtime_r(const time_t *timep, struct tm *result)
+{
+#if defined(HAVE_GMTIME_R)
+ return gmtime_r(timep, result);
+#elif defined(_MSC_VER)
+ errno_t err = gmtime_s(result, timep);
+ if (err == 0)
+ return result;
+ return NULL;
+#else
+ struct tm *aux = gmtime(timep);
+ if (aux == NULL)
+ return NULL;
+ *result = *aux;
+ return result;
+#endif
+}
+
+/*
+ * 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:
+ */
diff --git a/wsutil/time_util.h b/wsutil/time_util.h
new file mode 100644
index 00000000..6c2f52bf
--- /dev/null
+++ b/wsutil/time_util.h
@@ -0,0 +1,81 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __TIME_UTIL_H__
+#define __TIME_UTIL_H__
+
+#include <wireshark.h>
+#include <time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/** Converts a broken down date representation, relative to UTC,
+ * to a timestamp
+ */
+WS_DLL_PUBLIC
+time_t mktime_utc(struct tm *tm);
+
+/** Validate the values in a time_t.
+ * Currently checks tm_year, tm_mon, tm_mday, tm_hour, tm_min, and tm_sec;
+ * disregards tm_wday, tm_yday, and tm_isdst.
+ *
+ * @param tm The struct tm to validate.
+ */
+WS_DLL_PUBLIC
+bool tm_is_valid(struct tm *tm);
+
+/** Fetch the process CPU time.
+ *
+ * Fetch the current process user and system CPU times, convert them to
+ * seconds, and store them in the provided parameters.
+ *
+ * @param user_time Seconds spent in user mode.
+ * @param sys_time Seconds spent in system (kernel) mode.
+ */
+WS_DLL_PUBLIC
+void get_resource_usage(double *user_time, double *sys_time);
+
+/** Print the process CPU time followed by a log message.
+ *
+ * Print the current process user and system CPU times along with the times
+ * elapsed since the times were last reset.
+ *
+ * @param reset_delta Reset the delta times. This will typically be true when
+ * logging the first measurement and false thereafter.
+ * @param format Printf-style format string. Passed to g_string_vprintf.
+ * @param ... Parameters for the format string.
+ */
+WS_DLL_PUBLIC
+void log_resource_usage(bool reset_delta, const char *format, ...);
+
+/**
+ * Fetch the number of microseconds since midnight (0 hour), January 1, 1970.
+ */
+WS_DLL_PUBLIC
+uint64_t create_timestamp(void);
+
+WS_DLL_PUBLIC
+void ws_tzset(void);
+
+WS_DLL_PUBLIC
+struct timespec *ws_clock_get_realtime(struct timespec *ts);
+
+WS_DLL_PUBLIC
+struct tm *ws_localtime_r(const time_t *timep, struct tm *result);
+
+WS_DLL_PUBLIC
+struct tm *ws_gmtime_r(const time_t *timep, struct tm *result);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __TIME_UTIL_H__ */
diff --git a/wsutil/to_str.c b/wsutil/to_str.c
new file mode 100644
index 00000000..d15576d4
--- /dev/null
+++ b/wsutil/to_str.c
@@ -0,0 +1,989 @@
+/* wsutil/to_str.c
+ * Routines for utilities to convert various other types to strings.
+ *
+ * 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 "to_str.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include <wsutil/utf8_entities.h>
+#include <wsutil/wslog.h>
+#include <wsutil/inet_addr.h>
+#include <wsutil/pint.h>
+#include <wsutil/time_util.h>
+
+/*
+ * If a user _does_ pass in a too-small buffer, this is probably
+ * going to be too long to fit. However, even a partial string
+ * starting with "[Buf" should provide enough of a clue to be
+ * useful.
+ */
+#define _return_if_nospace(str_len, buf, buf_len) \
+ do { \
+ if ((str_len) > (buf_len)) { \
+ (void)g_strlcpy(buf, "[Buffer too small]", buf_len); \
+ return; \
+ } \
+ } while (0)
+
+static const char fast_strings[][4] = {
+ "0", "1", "2", "3", "4", "5", "6", "7",
+ "8", "9", "10", "11", "12", "13", "14", "15",
+ "16", "17", "18", "19", "20", "21", "22", "23",
+ "24", "25", "26", "27", "28", "29", "30", "31",
+ "32", "33", "34", "35", "36", "37", "38", "39",
+ "40", "41", "42", "43", "44", "45", "46", "47",
+ "48", "49", "50", "51", "52", "53", "54", "55",
+ "56", "57", "58", "59", "60", "61", "62", "63",
+ "64", "65", "66", "67", "68", "69", "70", "71",
+ "72", "73", "74", "75", "76", "77", "78", "79",
+ "80", "81", "82", "83", "84", "85", "86", "87",
+ "88", "89", "90", "91", "92", "93", "94", "95",
+ "96", "97", "98", "99", "100", "101", "102", "103",
+ "104", "105", "106", "107", "108", "109", "110", "111",
+ "112", "113", "114", "115", "116", "117", "118", "119",
+ "120", "121", "122", "123", "124", "125", "126", "127",
+ "128", "129", "130", "131", "132", "133", "134", "135",
+ "136", "137", "138", "139", "140", "141", "142", "143",
+ "144", "145", "146", "147", "148", "149", "150", "151",
+ "152", "153", "154", "155", "156", "157", "158", "159",
+ "160", "161", "162", "163", "164", "165", "166", "167",
+ "168", "169", "170", "171", "172", "173", "174", "175",
+ "176", "177", "178", "179", "180", "181", "182", "183",
+ "184", "185", "186", "187", "188", "189", "190", "191",
+ "192", "193", "194", "195", "196", "197", "198", "199",
+ "200", "201", "202", "203", "204", "205", "206", "207",
+ "208", "209", "210", "211", "212", "213", "214", "215",
+ "216", "217", "218", "219", "220", "221", "222", "223",
+ "224", "225", "226", "227", "228", "229", "230", "231",
+ "232", "233", "234", "235", "236", "237", "238", "239",
+ "240", "241", "242", "243", "244", "245", "246", "247",
+ "248", "249", "250", "251", "252", "253", "254", "255"
+};
+
+static inline char
+low_nibble_of_octet_to_hex(uint8_t oct)
+{
+ /* At least one version of Apple's C compiler/linker is buggy, causing
+ a complaint from the linker about the "literal C string section"
+ not ending with '\0' if we initialize a 16-element "char" array with
+ a 16-character string, the fact that initializing such an array with
+ such a string is perfectly legitimate ANSI C nonwithstanding, the 17th
+ '\0' byte in the string nonwithstanding. */
+ static const char hex_digits[16] =
+ { '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+ return hex_digits[oct & 0xF];
+}
+
+static inline char *
+byte_to_hex(char *out, uint32_t dword)
+{
+ *out++ = low_nibble_of_octet_to_hex(dword >> 4);
+ *out++ = low_nibble_of_octet_to_hex(dword);
+ return out;
+}
+
+char *
+guint8_to_hex(char *out, uint8_t val)
+{
+ return byte_to_hex(out, val);
+}
+
+char *
+word_to_hex(char *out, uint16_t word)
+{
+ out = byte_to_hex(out, word >> 8);
+ out = byte_to_hex(out, word);
+ return out;
+}
+
+char *
+word_to_hex_punct(char *out, uint16_t word, char punct)
+{
+ out = byte_to_hex(out, word >> 8);
+ *out++ = punct;
+ out = byte_to_hex(out, word);
+ return out;
+}
+
+char *
+word_to_hex_npad(char *out, uint16_t word)
+{
+ if (word >= 0x1000)
+ *out++ = low_nibble_of_octet_to_hex((uint8_t)(word >> 12));
+ if (word >= 0x0100)
+ *out++ = low_nibble_of_octet_to_hex((uint8_t)(word >> 8));
+ if (word >= 0x0010)
+ *out++ = low_nibble_of_octet_to_hex((uint8_t)(word >> 4));
+ *out++ = low_nibble_of_octet_to_hex((uint8_t)(word >> 0));
+ return out;
+}
+
+char *
+dword_to_hex(char *out, uint32_t dword)
+{
+ out = word_to_hex(out, dword >> 16);
+ out = word_to_hex(out, dword);
+ return out;
+}
+
+char *
+dword_to_hex_punct(char *out, uint32_t dword, char punct)
+{
+ out = word_to_hex_punct(out, dword >> 16, punct);
+ *out++ = punct;
+ out = word_to_hex_punct(out, dword, punct);
+ return out;
+}
+
+char *
+qword_to_hex(char *out, uint64_t qword)
+{
+ out = dword_to_hex(out, (uint32_t)(qword >> 32));
+ out = dword_to_hex(out, (uint32_t)(qword & 0xffffffff));
+ return out;
+}
+
+char *
+qword_to_hex_punct(char *out, uint64_t qword, char punct)
+{
+ out = dword_to_hex_punct(out, (uint32_t)(qword >> 32), punct);
+ *out++ = punct;
+ out = dword_to_hex_punct(out, (uint32_t)(qword & 0xffffffff), punct);
+ return out;
+}
+
+/*
+ * This does *not* null-terminate the string. It returns a pointer
+ * to the position in the string following the last character it
+ * puts there, so that the caller can either put the null terminator
+ * in or can append more stuff to the buffer.
+ *
+ * There needs to be at least len * 2 bytes left in the buffer.
+ */
+char *
+bytes_to_hexstr(char *out, const uint8_t *ad, size_t len)
+{
+ size_t i;
+
+ ws_return_val_if(!ad, NULL);
+
+ for (i = 0; i < len; i++)
+ out = byte_to_hex(out, ad[i]);
+ return out;
+}
+
+/*
+ * This does *not* null-terminate the string. It returns a pointer
+ * to the position in the string following the last character it
+ * puts there, so that the caller can either put the null terminator
+ * in or can append more stuff to the buffer.
+ *
+ * There needs to be at least len * 3 - 1 bytes left in the buffer.
+ */
+char *
+bytes_to_hexstr_punct(char *out, const uint8_t *ad, size_t len, char punct)
+{
+ size_t i;
+
+ ws_return_val_if(!ad, NULL);
+
+ out = byte_to_hex(out, ad[0]);
+ for (i = 1; i < len; i++) {
+ *out++ = punct;
+ out = byte_to_hex(out, ad[i]);
+ }
+ return out;
+}
+
+/* Routine to convert a sequence of bytes to a hex string, one byte/two hex
+ * digits at a time, with a specified punctuation character between
+ * the bytes.
+ *
+ * If punct is '\0', no punctuation is applied (and thus
+ * the resulting string is (len-1) bytes shorter)
+ */
+char *
+bytes_to_str_punct_maxlen(wmem_allocator_t *scope,
+ const uint8_t *src, size_t src_size,
+ char punct, size_t max_bytes_len)
+{
+ char *buf;
+ size_t max_char_size;
+ char *buf_ptr;
+ int truncated = 0;
+
+ ws_return_str_if(!src, scope);
+ ws_return_str_if(!src_size, scope);
+
+ if (!punct)
+ return bytes_to_str_maxlen(scope, src, src_size, max_bytes_len);
+
+ if (max_bytes_len == 0 || max_bytes_len > src_size) {
+ max_bytes_len = src_size;
+ }
+ else if (max_bytes_len < src_size) {
+ truncated = 1;
+ }
+
+ /* Include space for ellipsis and '\0'. Optional extra punct
+ * at the end is already accounted for. */
+ max_char_size = max_bytes_len * 3 + strlen(UTF8_HORIZONTAL_ELLIPSIS) + 1;
+
+ buf = wmem_alloc(scope, max_char_size);
+ buf_ptr = bytes_to_hexstr_punct(buf, src, max_bytes_len, punct);
+
+ if (truncated) {
+ *buf_ptr++ = punct;
+ buf_ptr = g_stpcpy(buf_ptr, UTF8_HORIZONTAL_ELLIPSIS);
+ }
+
+ *buf_ptr = '\0';
+ return buf;
+}
+
+char *
+bytes_to_str_maxlen(wmem_allocator_t *scope,
+ const uint8_t *src, size_t src_size,
+ size_t max_bytes_len)
+{
+ char *buf;
+ size_t max_char_size;
+ char *buf_ptr;
+ int truncated = 0;
+
+ ws_return_str_if(!src, scope);
+ ws_return_str_if(!src_size, scope);
+
+ if (max_bytes_len == 0 || max_bytes_len > src_size) {
+ max_bytes_len = src_size;
+ }
+ else if (max_bytes_len < src_size) {
+ truncated = 1;
+ }
+
+ max_char_size = max_bytes_len * 2 + strlen(UTF8_HORIZONTAL_ELLIPSIS) + 1;
+
+ buf = wmem_alloc(scope, max_char_size);
+ buf_ptr = bytes_to_hexstr(buf, src, max_bytes_len);
+
+ if (truncated)
+ buf_ptr = g_stpcpy(buf_ptr, UTF8_HORIZONTAL_ELLIPSIS);
+
+ *buf_ptr = '\0';
+ return buf;
+}
+
+/*
+ * The *_to_str_back() functions measured approx. a x7.5 speed-up versus
+ * snprintf() on my Linux system with GNU libc.
+ */
+
+char *
+oct_to_str_back(char *ptr, uint32_t value)
+{
+ while (value) {
+ *(--ptr) = '0' + (value & 0x7);
+ value >>= 3;
+ }
+
+ *(--ptr) = '0';
+ return ptr;
+}
+
+char *
+oct64_to_str_back(char *ptr, uint64_t value)
+{
+ while (value) {
+ *(--ptr) = '0' + (value & 0x7);
+ value >>= 3;
+ }
+
+ *(--ptr) = '0';
+ return ptr;
+}
+
+char *
+hex_to_str_back_len(char *ptr, uint32_t value, int len)
+{
+ do {
+ *(--ptr) = low_nibble_of_octet_to_hex(value);
+ value >>= 4;
+ len--;
+ } while (value);
+
+ /* pad */
+ while (len > 0) {
+ *(--ptr) = '0';
+ len--;
+ }
+
+ *(--ptr) = 'x';
+ *(--ptr) = '0';
+
+ return ptr;
+}
+
+char *
+hex64_to_str_back_len(char *ptr, uint64_t value, int len)
+{
+ do {
+ *(--ptr) = low_nibble_of_octet_to_hex(value & 0xF);
+ value >>= 4;
+ len--;
+ } while (value);
+
+ /* pad */
+ while (len > 0) {
+ *(--ptr) = '0';
+ len--;
+ }
+
+ *(--ptr) = 'x';
+ *(--ptr) = '0';
+
+ return ptr;
+}
+
+char *
+uint_to_str_back(char *ptr, uint32_t value)
+{
+ char const *p;
+
+ /* special case */
+ if (value == 0)
+ *(--ptr) = '0';
+
+ while (value >= 10) {
+ p = fast_strings[100 + (value % 100)];
+
+ value /= 100;
+
+ *(--ptr) = p[2];
+ *(--ptr) = p[1];
+ }
+
+ if (value)
+ *(--ptr) = (value) | '0';
+
+ return ptr;
+}
+
+char *
+uint64_to_str_back(char *ptr, uint64_t value)
+{
+ char const *p;
+
+ /* special case */
+ if (value == 0)
+ *(--ptr) = '0';
+
+ while (value >= 10) {
+ p = fast_strings[100 + (value % 100)];
+
+ value /= 100;
+
+ *(--ptr) = p[2];
+ *(--ptr) = p[1];
+ }
+
+ /* value will be 0..9, so using '& 0xF' is safe, and faster than '% 10' */
+ if (value)
+ *(--ptr) = (value & 0xF) | '0';
+
+ return ptr;
+}
+
+char *
+uint_to_str_back_len(char *ptr, uint32_t value, int len)
+{
+ char *new_ptr;
+
+ new_ptr = uint_to_str_back(ptr, value);
+
+ /* substract from len number of generated characters */
+ len -= (int)(ptr - new_ptr);
+
+ /* pad remaining with '0' */
+ while (len > 0)
+ {
+ *(--new_ptr) = '0';
+ len--;
+ }
+
+ return new_ptr;
+}
+
+char *
+uint64_to_str_back_len(char *ptr, uint64_t value, int len)
+{
+ char *new_ptr;
+
+ new_ptr = uint64_to_str_back(ptr, value);
+
+ /* substract from len number of generated characters */
+ len -= (int)(ptr - new_ptr);
+
+ /* pad remaining with '0' */
+ while (len > 0)
+ {
+ *(--new_ptr) = '0';
+ len--;
+ }
+
+ return new_ptr;
+}
+
+char *
+int_to_str_back(char *ptr, int32_t value)
+{
+ if (value < 0) {
+ ptr = uint_to_str_back(ptr, -value);
+ *(--ptr) = '-';
+ } else
+ ptr = uint_to_str_back(ptr, value);
+
+ return ptr;
+}
+
+char *
+int64_to_str_back(char *ptr, int64_t value)
+{
+ if (value < 0) {
+ ptr = uint64_to_str_back(ptr, -value);
+ *(--ptr) = '-';
+ } else
+ ptr = uint64_to_str_back(ptr, value);
+
+ return ptr;
+}
+
+static size_t
+guint32_to_str_buf_len(const uint32_t u)
+{
+ /* ((2^32)-1) == 2147483647 */
+ if (u >= 1000000000)return 10;
+ if (u >= 100000000) return 9;
+ if (u >= 10000000) return 8;
+ if (u >= 1000000) return 7;
+ if (u >= 100000) return 6;
+ if (u >= 10000) return 5;
+ if (u >= 1000) return 4;
+ if (u >= 100) return 3;
+ if (u >= 10) return 2;
+
+ return 1;
+}
+
+void
+guint32_to_str_buf(uint32_t u, char *buf, size_t buf_len)
+{
+ size_t str_len = guint32_to_str_buf_len(u)+1;
+
+ char *bp = &buf[str_len];
+
+ _return_if_nospace(str_len, buf, buf_len);
+
+ *--bp = '\0';
+
+ uint_to_str_back(bp, u);
+}
+
+static size_t
+guint64_to_str_buf_len(const uint64_t u)
+{
+ /* ((2^64)-1) == 18446744073709551615 */
+
+ if (u >= G_GUINT64_CONSTANT(10000000000000000000)) return 20;
+ if (u >= G_GUINT64_CONSTANT(1000000000000000000)) return 19;
+ if (u >= G_GUINT64_CONSTANT(100000000000000000)) return 18;
+ if (u >= G_GUINT64_CONSTANT(10000000000000000)) return 17;
+ if (u >= G_GUINT64_CONSTANT(1000000000000000)) return 16;
+ if (u >= G_GUINT64_CONSTANT(100000000000000)) return 15;
+ if (u >= G_GUINT64_CONSTANT(10000000000000)) return 14;
+ if (u >= G_GUINT64_CONSTANT(1000000000000)) return 13;
+ if (u >= G_GUINT64_CONSTANT(100000000000)) return 12;
+ if (u >= G_GUINT64_CONSTANT(10000000000)) return 11;
+ if (u >= G_GUINT64_CONSTANT(1000000000)) return 10;
+ if (u >= G_GUINT64_CONSTANT(100000000)) return 9;
+ if (u >= G_GUINT64_CONSTANT(10000000)) return 8;
+ if (u >= G_GUINT64_CONSTANT(1000000)) return 7;
+ if (u >= G_GUINT64_CONSTANT(100000)) return 6;
+ if (u >= G_GUINT64_CONSTANT(10000)) return 5;
+ if (u >= G_GUINT64_CONSTANT(1000)) return 4;
+ if (u >= G_GUINT64_CONSTANT(100)) return 3;
+ if (u >= G_GUINT64_CONSTANT(10)) return 2;
+
+ return 1;
+}
+
+void
+guint64_to_str_buf(uint64_t u, char *buf, size_t buf_len)
+{
+ size_t str_len = guint64_to_str_buf_len(u)+1;
+
+ char *bp = &buf[str_len];
+
+ _return_if_nospace(str_len, buf, buf_len);
+
+ *--bp = '\0';
+
+ uint64_to_str_back(bp, u);
+}
+
+/*
+ This function is very fast and this function is called a lot.
+ XXX update the address_to_str stuff to use this function.
+ */
+void
+ip_to_str_buf(const uint8_t *ad, char *buf, const int buf_len)
+{
+ register char const *p;
+ register char *b=buf;
+
+ _return_if_nospace(WS_INET_ADDRSTRLEN, buf, buf_len);
+
+ p=fast_strings[*ad++];
+ do {
+ *b++=*p;
+ p++;
+ } while(*p);
+ *b++='.';
+
+ p=fast_strings[*ad++];
+ do {
+ *b++=*p;
+ p++;
+ } while(*p);
+ *b++='.';
+
+ p=fast_strings[*ad++];
+ do {
+ *b++=*p;
+ p++;
+ } while(*p);
+ *b++='.';
+
+ p=fast_strings[*ad];
+ do {
+ *b++=*p;
+ p++;
+ } while(*p);
+ *b=0;
+}
+
+char *ip_to_str(wmem_allocator_t *scope, const uint8_t *ad)
+{
+ char *buf = wmem_alloc(scope, WS_INET_ADDRSTRLEN * sizeof(char));
+
+ ip_to_str_buf(ad, buf, WS_INET_ADDRSTRLEN);
+
+ return buf;
+}
+
+void
+ip6_to_str_buf(const ws_in6_addr *addr, char *buf, size_t buf_size)
+{
+ /*
+ * If there is not enough space then ws_inet_ntop6() will leave
+ * an error message in the buffer, we don't need
+ * to use _return_if_nospace().
+ */
+ ws_inet_ntop6(addr, buf, (unsigned)buf_size);
+}
+
+char *ip6_to_str(wmem_allocator_t *scope, const ws_in6_addr *ad)
+{
+ char *buf = wmem_alloc(scope, WS_INET6_ADDRSTRLEN * sizeof(char));
+
+ ws_inet_ntop6(ad, buf, WS_INET6_ADDRSTRLEN);
+
+ return buf;
+}
+
+char *
+ipxnet_to_str_punct(wmem_allocator_t *allocator, const uint32_t ad, const char punct)
+{
+ char *buf = (char *)wmem_alloc(allocator, 12);
+
+ *dword_to_hex_punct(buf, ad, punct) = '\0';
+ return buf;
+}
+
+#define WS_EUI64_STRLEN 24
+
+char *
+eui64_to_str(wmem_allocator_t *scope, const uint64_t ad) {
+ char *buf, *tmp;
+ uint8_t *p_eui64;
+
+ p_eui64=(uint8_t *)wmem_alloc(NULL, 8);
+ buf=(char *)wmem_alloc(scope, WS_EUI64_STRLEN);
+
+ /* Copy and convert the address to network byte order. */
+ *(uint64_t *)(void *)(p_eui64) = pntoh64(&(ad));
+
+ tmp = bytes_to_hexstr_punct(buf, p_eui64, 8, ':');
+ *tmp = '\0'; /* NULL terminate */
+ wmem_free(NULL, p_eui64);
+ return buf;
+}
+
+/*
+ * Number of characters required by a 64-bit signed number.
+ */
+#define CHARS_64_BIT_SIGNED 20 /* sign plus 19 digits */
+
+/*
+ * Number of characters required by a fractional part, in nanoseconds,
+ * not counting the decimal point.
+ */
+#define CHARS_NANOSECONDS 9 /* 000000001 */
+
+/*
+ * Format the fractional part of a time, with the specified precision.
+ * Returns the number of bytes formatted.
+ */
+int
+format_fractional_part_nsecs(char *buf, size_t buflen, uint32_t nsecs, const char *decimal_point, int precision)
+{
+ char *ptr;
+ size_t remaining;
+ int num_bytes;
+ size_t decimal_point_len;
+ uint32_t frac_part;
+ int8_t num_buf[CHARS_NANOSECONDS];
+ int8_t *num_end = &num_buf[CHARS_NANOSECONDS];
+ int8_t *num_ptr;
+ size_t num_len;
+
+ ws_assert(precision != 0);
+
+ if (buflen == 0) {
+ /*
+ * No room in the buffer for anything, including
+ * a terminating '\0'.
+ */
+ return 0;
+ }
+
+ /*
+ * If the fractional part is >= 1, don't show it as a
+ * fractional part.
+ */
+ if (nsecs >= 1000000000U) {
+ num_bytes = snprintf(buf, buflen, "%s(%u nanoseconds)",
+ decimal_point, nsecs);
+ if ((unsigned int)num_bytes >= buflen) {
+ /*
+ * That filled up or would have overflowed
+ * the buffer. Nothing more to do; return
+ * the remaining space in the buffer, minus
+ * one byte for the terminating '\0',* as
+ * that's the number of bytes we copied.
+ */
+ return (int)(buflen - 1);
+ }
+ return num_bytes;
+ }
+
+ ptr = buf;
+ remaining = buflen;
+ num_bytes = 0;
+
+ /*
+ * Copy the decimal point.
+ * (We assume here that the locale's decimal point does
+ * not contain so many characters that its size doesn't
+ * fit in an int. :-))
+ */
+ decimal_point_len = g_strlcpy(buf, decimal_point, buflen);
+ if (decimal_point_len >= buflen) {
+ /*
+ * The decimal point didn't fit in the buffer
+ * and was truncated. Nothing more to do;
+ * return the remaining space in the buffer,
+ * minus one byte for the terminating '\0',
+ * as that's the number of bytes we copied.
+ */
+ return (int)(buflen - 1);
+ }
+ ptr += decimal_point_len;
+ remaining -= decimal_point_len;
+ num_bytes += (int)decimal_point_len;
+
+ /*
+ * Fill in num_buf with the nanoseconds value, padded with
+ * leading zeroes, to the specified precision.
+ *
+ * We scale the fractional part in advance, as that just
+ * takes one division by a constant (which may be
+ * optimized to a faster multiplication by a constant)
+ * and gets rid of some divisions and remainders by 100
+ * done to generate the digits.
+ *
+ * We pass preciions as the last argument to
+ * uint_to_str_back_len(), as that might mean that
+ * all of the cases end up using common code to
+ * do part of the call to uint_to_str_back_len().
+ */
+ switch (precision) {
+
+ case 1:
+ /*
+ * Scale down to units of 1/10 second.
+ */
+ frac_part = nsecs / 100000000U;
+ break;
+
+ case 2:
+ /*
+ * Scale down to units of 1/100 second.
+ */
+ frac_part = nsecs / 10000000U;
+ break;
+
+ case 3:
+ /*
+ * Scale down to units of 1/1000 second.
+ */
+ frac_part = nsecs / 1000000U;
+ break;
+
+ case 4:
+ /*
+ * Scale down to units of 1/10000 second.
+ */
+ frac_part = nsecs / 100000U;
+ break;
+
+ case 5:
+ /*
+ * Scale down to units of 1/100000 second.
+ */
+ frac_part = nsecs / 10000U;
+ break;
+
+ case 6:
+ /*
+ * Scale down to units of 1/1000000 second.
+ */
+ frac_part = nsecs / 1000U;
+ break;
+
+ case 7:
+ /*
+ * Scale down to units of 1/10000000 second.
+ */
+ frac_part = nsecs / 100U;
+ break;
+
+ case 8:
+ /*
+ * Scale down to units of 1/100000000 second.
+ */
+ frac_part = nsecs / 10U;
+ break;
+
+ case 9:
+ /*
+ * We're already in units of 1/1000000000 second.
+ */
+ frac_part = nsecs;
+ break;
+
+ default:
+ ws_assert_not_reached();
+ break;
+ }
+
+ num_ptr = uint_to_str_back_len(num_end, frac_part, precision);
+
+ /*
+ * The length of the string that we want to copy to the buffer
+ * is the minimum of:
+ *
+ * the length of the digit string;
+ * the remaining space in the buffer, minus 1 for the
+ * terminating '\0'.
+ */
+ num_len = MIN((size_t)(num_end - num_ptr), remaining - 1);
+ if (num_len == 0) {
+ /*
+ * Not enough room to copy anything.
+ * Return the number of bytes we've generated.
+ */
+ return num_bytes;
+ }
+
+ /*
+ * Copy over the fractional part.
+ * (We assume here that the fractional part does not contain
+ * so many characters that its size doesn't fit in an int. :-))
+ */
+ memcpy(ptr, num_ptr, num_len);
+ ptr += num_len;
+ num_bytes += (int)num_len;
+
+ /*
+ * '\0'-terminate it.
+ */
+ *ptr = '\0';
+ return num_bytes;
+}
+
+void
+display_epoch_time(char *buf, size_t buflen, const nstime_t *ns, int precision)
+{
+ display_signed_time(buf, buflen, ns, precision);
+}
+
+void
+display_signed_time(char *buf, size_t buflen, const nstime_t *ns, int precision)
+{
+ int nsecs;
+ /* this buffer is not NUL terminated */
+ int8_t num_buf[CHARS_64_BIT_SIGNED];
+ int8_t *num_end = &num_buf[CHARS_64_BIT_SIGNED];
+ int8_t *num_ptr;
+ size_t num_len;
+
+ if (buflen < 1)
+ return;
+
+ /* If the fractional part of the time stamp is negative,
+ print its absolute value and, if the seconds part isn't
+ (the seconds part should be zero in that case), stick
+ a "-" in front of the entire time stamp. */
+ nsecs = ns->nsecs;
+ if (nsecs < 0) {
+ nsecs = -nsecs;
+ if (ns->secs >= 0) {
+ buf[0] = '-';
+ buf++;
+ buflen--;
+ }
+ }
+
+ /*
+ * Fill in num_buf with the seconds value.
+ */
+ num_ptr = int64_to_str_back(num_end, ns->secs);
+
+ /*
+ * The length of the string that we want to copy to the buffer
+ * is the minimum of:
+ *
+ * the length of the digit string;
+ * the size of the buffer, minus 1 for the terminating
+ * '\0'.
+ */
+ num_len = MIN((size_t)(num_end - num_ptr), buflen - 1);
+ if (num_len == 0) {
+ /*
+ * Not enough room to copy anything.
+ */
+ return;
+ }
+
+ /*
+ * Copy over the seconds value.
+ */
+ memcpy(buf, num_ptr, num_len);
+ buf += num_len;
+ buflen -= num_len;
+
+ if (precision == 0) {
+ /*
+ * Seconds precision, so no nanosecond.
+ * Nothing more to do other than to
+ * '\0'-terminate the string.
+ */
+ *buf = '\0';
+ return;
+ }
+
+ /*
+ * Append the fractional part.
+ */
+ format_fractional_part_nsecs(buf, buflen, (uint32_t)nsecs, ".", precision);
+}
+
+void
+format_nstime_as_iso8601(char *buf, size_t buflen, const nstime_t *ns,
+ char *decimal_point, bool local, int precision)
+{
+ struct tm tm, *tmp;
+ char *ptr;
+ size_t remaining;
+ int num_bytes;
+
+ if (local)
+ tmp = ws_localtime_r(&ns->secs, &tm);
+ else
+ tmp = ws_gmtime_r(&ns->secs, &tm);
+ if (tmp == NULL) {
+ snprintf(buf, buflen, "Not representable");
+ return;
+ }
+ ptr = buf;
+ remaining = buflen;
+ num_bytes = snprintf(ptr, remaining,
+ "%04d-%02d-%02d %02d:%02d:%02d",
+ tmp->tm_year + 1900,
+ tmp->tm_mon + 1,
+ tmp->tm_mday,
+ tmp->tm_hour,
+ tmp->tm_min,
+ tmp->tm_sec);
+ if (num_bytes < 0) {
+ /*
+ * That got an error.
+ * Not much else we can do.
+ */
+ snprintf(buf, buflen, "snprintf() failed");
+ return;
+ }
+ if ((unsigned int)num_bytes >= remaining) {
+ /*
+ * That filled up or would have overflowed the buffer.
+ * Nothing more we can do.
+ */
+ return;
+ }
+ ptr += num_bytes;
+ remaining -= num_bytes;
+
+ if (precision != 0) {
+ /*
+ * Append the fractional part.
+ * Get the nsecs as a 32-bit unsigned value, as it should
+ * never be negative, so we treat it as unsigned.
+ */
+ format_fractional_part_nsecs(ptr, remaining, (uint32_t)ns->nsecs, decimal_point, precision);
+ }
+}
+
+/*
+ * 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:
+ */
diff --git a/wsutil/to_str.h b/wsutil/to_str.h
new file mode 100644
index 00000000..08bd536b
--- /dev/null
+++ b/wsutil/to_str.h
@@ -0,0 +1,314 @@
+/** @file
+ *
+ * Definitions for utilities to convert various other types to strings.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WSUTIL_TO_STR_H__
+#define __WSUTIL_TO_STR_H__
+
+#include <wireshark.h>
+
+#include <wsutil/wmem/wmem.h>
+#include <wsutil/inet_ipv6.h>
+#include <wsutil/nstime.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * guint8_to_hex()
+ *
+ * Output uint8_t hex representation to 'out', and return pointer after last character (out + 2).
+ * It will always output full representation (padded with 0).
+ *
+ * String is not NUL terminated by this routine.
+ * There needs to be at least 2 bytes in the buffer.
+ */
+WS_DLL_PUBLIC char *guint8_to_hex(char *out, uint8_t val);
+
+/**
+ * word_to_hex()
+ *
+ * Output uint16_t hex representation to 'out', and return pointer after last character (out + 4).
+ * It will always output full representation (padded with 0).
+ *
+ * String is not NUL terminated by this routine.
+ * There needs to be at least 4 bytes in the buffer.
+ */
+WS_DLL_PUBLIC char *word_to_hex(char *out, uint16_t word);
+
+/**
+ * word_to_hex_punct()
+ *
+ * Output uint16_t hex representation to 'out', and return pointer after last character.
+ * Each byte will be separated with punct character (cannot be NUL).
+ * It will always output full representation (padded with 0).
+ *
+ * String is not NUL terminated by this routine.
+ * There needs to be at least 5 bytes in the buffer.
+ */
+WS_DLL_PUBLIC char *word_to_hex_punct(char *out, uint16_t word, char punct);
+
+/**
+ * word_to_hex_npad()
+ *
+ * Output uint16_t hex representation to 'out', and return pointer after last character.
+ * Value is not padded.
+ *
+ * String is not NUL terminated by this routine.
+ * There needs to be at least 4 bytes in the buffer.
+ */
+WS_DLL_PUBLIC char *word_to_hex_npad(char *out, uint16_t word);
+
+/**
+ * dword_to_hex()
+ *
+ * Output uint32_t hex representation to 'out', and return pointer after last character.
+ * It will always output full representation (padded with 0).
+ *
+ * String is not NUL terminated by this routine.
+ * There needs to be at least 8 bytes in the buffer.
+ */
+WS_DLL_PUBLIC char *dword_to_hex(char *out, uint32_t dword);
+
+/**
+ * dword_to_hex_punct()
+ *
+ * Output uint32_t hex representation to 'out', and return pointer after last character.
+ * Each byte will be separated with punct character (cannot be NUL).
+ * It will always output full representation (padded with 0).
+ *
+ * String is not NUL terminated by this routine.
+ * There needs to be at least 11 bytes in the buffer.
+ */
+WS_DLL_PUBLIC char *dword_to_hex_punct(char *out, uint32_t dword, char punct);
+
+/**
+ * qword_to_hex()
+ *
+ * Output uint64_t hex representation to 'out', and return pointer after last character.
+ * It will always output full representation (padded with 0).
+ *
+ * String is not NUL terminated by this routine.
+ * There needs to be at least 16 bytes in the buffer.
+ */
+WS_DLL_PUBLIC char *qword_to_hex(char *out, uint64_t qword);
+
+/**
+ * qword_to_hex_punct()
+ *
+ * Output uint64_t hex representation to 'out', and return pointer after last character.
+ * Each byte will be separated with punct character (cannot be NUL).
+ * It will always output full representation (padded with 0).
+ *
+ * String is not NUL terminated by this routine.
+ * There needs to be at least 22 bytes in the buffer.
+ */
+WS_DLL_PUBLIC char *qword_to_hex_punct(char *out, uint64_t qword, char punct);
+
+/**
+ * bytes_to_hexstr()
+ *
+ * Output hex representation of uint8_t array, and return pointer after last character.
+ * It will always output full representation (padded with 0).
+ *
+ * String is not NUL terminated by this routine.
+ * There needs to be at least len * 2 bytes in the buffer.
+ */
+WS_DLL_PUBLIC char *bytes_to_hexstr(char *out, const uint8_t *ad, size_t len);
+
+/**
+ * bytes_to_hexstr_punct()
+ *
+ * Output hex representation of uint8_t array, and return pointer after last character.
+ * Each byte will be separated with punct character (cannot be NUL).
+ * It will always output full representation (padded with 0).
+ *
+ * String is not NUL terminated by this routine.
+ * There needs to be at least len * 3 - 1 bytes in the buffer.
+ */
+WS_DLL_PUBLIC char *bytes_to_hexstr_punct(char *out, const uint8_t *ad, size_t len, char punct);
+
+/** Turn an array of bytes into a string showing the bytes in hex,
+ * separated by a punctuation character.
+ *
+ * @param scope memory allocation scheme used
+ * @param buf A pointer to the byte array
+ * @param buf_size The length of the byte array
+ * @param punct The punctuation character
+ * @param max_bytes_len Maximum number of bytes to represent, zero for no limit.
+ * @return A pointer to the formatted string
+ */
+WS_DLL_PUBLIC char *bytes_to_str_punct_maxlen(wmem_allocator_t *scope,
+ const uint8_t *buf, size_t buf_size,
+ char punct, size_t max_bytes_len);
+
+#define bytes_to_str_punct(scope, buf, buf_size, punct) \
+ bytes_to_str_punct_maxlen(scope, buf, buf_size, punct, 24)
+
+/** Turn an array of bytes into a string showing the bytes in hex.
+ *
+ * @param scope memory allocation scheme used
+ * @param buf A pointer to the byte array
+ * @param buf_size The length of the byte array
+ * @param max_bytes_len Maximum number of bytes to represent, zero for no limit.
+ * @return A pointer to the formatted string
+ */
+WS_DLL_PUBLIC char *bytes_to_str_maxlen(wmem_allocator_t *scope,
+ const uint8_t *buf, size_t buf_size,
+ size_t max_bytes_len);
+
+#define bytes_to_str(scope, buf, buf_size) \
+ bytes_to_str_maxlen(scope, buf, buf_size, 36)
+
+/**
+ * oct_to_str_back()
+ *
+ * Output uint32_t octal representation backward (last character will be written on ptr - 1),
+ * and return pointer to first character.
+ *
+ * String is not NUL terminated by this routine.
+ * There needs to be at least 12 bytes in the buffer.
+ */
+WS_DLL_PUBLIC char *oct_to_str_back(char *ptr, uint32_t value);
+
+/**
+ * oct64_to_str_back()
+ *
+ * Output uint64_t octal representation backward (last character will be written on ptr - 1),
+ * and return pointer to first character.
+ *
+ * String is not NUL terminated by this routine.
+ * There needs to be at least 12 bytes in the buffer.
+ */
+WS_DLL_PUBLIC char *oct64_to_str_back(char *ptr, uint64_t value);
+
+/**
+ * hex_to_str_back()
+ *
+ * Output uint32_t hex representation backward (last character will be written on ptr - 1),
+ * and return pointer to first character.
+ * This routine will output for sure (can output more) 'len' decimal characters (number padded with '0').
+ *
+ * String is not NUL terminated by this routine.
+ * There needs to be at least 2 + MAX(8, len) bytes in the buffer.
+ */
+WS_DLL_PUBLIC char *hex_to_str_back_len(char *ptr, uint32_t value, int len);
+
+/**
+ * hex64_to_str_back()
+ *
+ * Output uint64_t hex representation backward (last character will be written on ptr - 1),
+ * and return pointer to first character.
+ * This routine will output for sure (can output more) 'len' decimal characters (number padded with '0').
+ *
+ * String is not NUL terminated by this routine.
+ * There needs to be at least 2 + MAX(16, len) bytes in the buffer.
+ */
+WS_DLL_PUBLIC char *hex64_to_str_back_len(char *ptr, uint64_t value, int len);
+
+/**
+ * uint_to_str_back()
+ *
+ * Output uint32_t decimal representation backward (last character will be written on ptr - 1),
+ * and return pointer to first character.
+ *
+ * String is not NUL terminated by this routine.
+ * There needs to be at least 10 bytes in the buffer.
+ */
+WS_DLL_PUBLIC char *uint_to_str_back(char *ptr, uint32_t value);
+
+/**
+ * uint64_str_back()
+ *
+ * Output uint64_t decimal representation backward (last character will be written on ptr - 1),
+ * and return pointer to first character.
+ *
+ * String is not NUL terminated by this routine.
+ * There needs to be at least 20 bytes in the buffer.
+ */
+WS_DLL_PUBLIC char *uint64_to_str_back(char *ptr, uint64_t value);
+
+/**
+ * uint_to_str_back_len()
+ *
+ * Output uint32_t decimal representation backward (last character will be written on ptr - 1),
+ * and return pointer to first character.
+ * This routine will output for sure (can output more) 'len' decimal characters (number padded with '0').
+ *
+ * String is not NUL terminated by this routine.
+ * There needs to be at least MAX(10, len) bytes in the buffer.
+ */
+WS_DLL_PUBLIC char *uint_to_str_back_len(char *ptr, uint32_t value, int len);
+
+/**
+ * uint64_to_str_back_len()
+ *
+ * Output uint64_t decimal representation backward (last character will be written on ptr - 1),
+ * and return pointer to first character.
+ * This routine will output for sure (can output more) 'len' decimal characters (number padded with '0').
+ *
+ * String is not NUL terminated by this routine.
+ * There needs to be at least MAX(20, len) bytes in the buffer.
+ */
+WS_DLL_PUBLIC char *uint64_to_str_back_len(char *ptr, uint64_t value, int len);
+
+/**
+ * int_to_str_back()
+ *
+ * Output int32_t decimal representation backward (last character will be written on ptr - 1),
+ * and return pointer to first character.
+ *
+ * String is not NUL terminated by this routine.
+ * There needs to be at least 11 bytes in the buffer.
+ */
+WS_DLL_PUBLIC char *int_to_str_back(char *ptr, int32_t value);
+
+/**
+ * int64_to_str_back()
+ *
+ * Output int64_t decimal representation backward (last character will be written on ptr - 1),
+ * and return pointer to first character.
+ *
+ * String is not NUL terminated by this routine.
+ * There needs to be at least 21 bytes in the buffer.
+ */
+WS_DLL_PUBLIC char *int64_to_str_back(char *ptr, int64_t value);
+
+WS_DLL_PUBLIC void guint32_to_str_buf(uint32_t u, char *buf, size_t buf_len);
+
+WS_DLL_PUBLIC void guint64_to_str_buf(uint64_t u, char *buf, size_t buf_len);
+
+WS_DLL_PUBLIC void ip_to_str_buf(const uint8_t *ad, char *buf, const int buf_len);
+
+WS_DLL_PUBLIC char *ip_to_str(wmem_allocator_t *scope, const uint8_t *ad);
+
+/* Returns length of the result. */
+WS_DLL_PUBLIC void ip6_to_str_buf(const ws_in6_addr *ad, char *buf, size_t buf_size);
+
+WS_DLL_PUBLIC char *ip6_to_str(wmem_allocator_t *scope, const ws_in6_addr *ad);
+
+WS_DLL_PUBLIC char *ipxnet_to_str_punct(wmem_allocator_t *scope, const uint32_t ad, const char punct);
+
+WS_DLL_PUBLIC char *eui64_to_str(wmem_allocator_t *scope, const uint64_t ad);
+
+WS_DLL_PUBLIC int format_fractional_part_nsecs(char *, size_t, uint32_t, const char *, int);
+
+WS_DLL_PUBLIC void display_epoch_time(char *, size_t, const nstime_t *, int);
+
+WS_DLL_PUBLIC void display_signed_time(char *, size_t, const nstime_t *, int);
+
+WS_DLL_PUBLIC void format_nstime_as_iso8601(char *, size_t, const nstime_t *, char *, bool, int);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __TO_STR_H__ */
diff --git a/wsutil/type_util.c b/wsutil/type_util.c
new file mode 100644
index 00000000..f98a1b1d
--- /dev/null
+++ b/wsutil/type_util.c
@@ -0,0 +1,68 @@
+/* type_util.c
+ * Types utility routines
+ *
+ * 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 <glib.h>
+
+#include "type_util.h"
+
+/*
+ * uint64_t to double conversions taken from gstutils.c of GStreamer project
+ *
+ * GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ * 2002 Thomas Vander Stichele <thomas@apestaart.org>
+ *
+ * gstutils.h: Header for various utility functions
+ *
+ * GNU GPL v2
+ *
+ */
+
+/* work around error C2520: conversion from unsigned __int64 to double
+ * not implemented, use signed __int64
+ *
+ * These are implemented as functions because on some platforms a 64bit int to
+ * double conversion is not defined/implemented.
+ */
+
+double
+type_util_guint64_to_gdouble(uint64_t value)
+{
+ if (value & G_GUINT64_CONSTANT (0x8000000000000000))
+ return (double) ((int64_t) value) + (double) 18446744073709551616.;
+ else
+ return (double) ((int64_t) value);
+}
+
+uint64_t
+type_util_gdouble_to_guint64(double value)
+{
+ if (value < (double) 9223372036854775808.) /* 1 << 63 */
+ return ((uint64_t) ((int64_t) value));
+
+ value -= (double) 18446744073709551616.;
+ return ((uint64_t) ((int64_t) value));
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local Variables:
+ * c-basic-offset: 2
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=2 tabstop=8 expandtab:
+ * :indentSize=2:tabSize=8:noTabs=true:
+ */
diff --git a/wsutil/type_util.h b/wsutil/type_util.h
new file mode 100644
index 00000000..eab9ff9b
--- /dev/null
+++ b/wsutil/type_util.h
@@ -0,0 +1,44 @@
+/** @file
+ * Types utility definitions
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __TYPE_UTIL_H__
+#define __TYPE_UTIL_H__
+
+#include <inttypes.h>
+#include "ws_symbol_export.h"
+
+/*
+ * uint64_t to double conversions taken from gstutils.h of GStreamer project
+ *
+ * GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ * 2002 Thomas Vander Stichele <thomas@apestaart.org>
+ *
+ * gstutils.h: Header for various utility functions
+ *
+ * GNU GPL v2
+ *
+ */
+
+WS_DLL_PUBLIC
+uint64_t type_util_gdouble_to_guint64(double value);
+WS_DLL_PUBLIC
+double type_util_guint64_to_gdouble(uint64_t value);
+
+#ifdef _WIN32
+#define gdouble_to_guint64(value) type_util_gdouble_to_guint64(value)
+#define guint64_to_gdouble(value) type_util_guint64_to_gdouble(value)
+#else
+#define gdouble_to_guint64(value) ((uint64_t)(value))
+#define guint64_to_gdouble(value) ((double)(value))
+#endif
+
+#endif /* __TYPE_UTIL_H__ */
diff --git a/wsutil/unicode-utils.c b/wsutil/unicode-utils.c
new file mode 100644
index 00000000..4ed4b326
--- /dev/null
+++ b/wsutil/unicode-utils.c
@@ -0,0 +1,368 @@
+/* unicode-utils.c
+ * Unicode utility routines
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2006 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include "unicode-utils.h"
+
+int ws_utf8_seqlen[256] = {
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 0x00...0x0f */
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 0x10...0x1f */
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 0x20...0x2f */
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 0x30...0x3f */
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 0x40...0x4f */
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 0x50...0x5f */
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 0x60...0x6f */
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 0x70...0x7f */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x80...0x8f */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x90...0x9f */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xa0...0xaf */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xb0...0xbf */
+ 0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2, /* 0xc0...0xcf */
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, /* 0xd0...0xdf */
+ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, /* 0xe0...0xef */
+ 4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0, /* 0xf0...0xff */
+};
+
+/* Given a pointer and a length, validates a string of bytes as UTF-8.
+ * Returns the number of valid bytes, and a pointer immediately past
+ * the checked region.
+ *
+ * Differs from Glib's g_utf8_validate_len in that null bytes are
+ * considered valid UTF-8, and that maximal subparts are replaced as
+ * a unit. (I.e., given a sequence of 2 or 3 bytes which are a
+ * truncated version of a 3 or 4 byte UTF-8 character, but the next
+ * byte does not continue the character, the set of 2 or 3 bytes
+ * are replaced with one REPLACMENT CHARACTER.)
+ */
+static inline size_t
+utf_8_validate(const uint8_t *start, ssize_t length, const uint8_t **end)
+{
+ const uint8_t *ptr = start;
+ uint8_t ch;
+ size_t unichar_len, valid_bytes = 0;
+
+ while (length > 0) {
+
+ ch = *ptr;
+
+ if (ch < 0x80) {
+ valid_bytes++;
+ ptr++;
+ length--;
+ continue;
+ }
+
+ ch = *ptr;
+
+ if (ch < 0xc2 || ch > 0xf4) {
+ ptr++;
+ length--;
+ *end = ptr;
+ return valid_bytes;
+ }
+
+ if (ch < 0xe0) { /* 110xxxxx, 2 byte char */
+ unichar_len = 2;
+ } else if (ch < 0xf0) { /* 1110xxxx, 3 byte char */
+ unichar_len = 3;
+ ptr++;
+ length--;
+ if (length < 1) {
+ *end = ptr;
+ return valid_bytes;
+ }
+ switch (ch) {
+ case 0xe0:
+ if (*ptr < 0xa0 || *ptr > 0xbf) {
+ *end = ptr;
+ return valid_bytes;
+ }
+ break;
+ case 0xed:
+ if (*ptr < 0x80 || *ptr > 0x9f) {
+ *end = ptr;
+ return valid_bytes;
+ }
+ break;
+ default:
+ if (*ptr < 0x80 || *ptr > 0xbf) {
+ *end = ptr;
+ return valid_bytes;
+ }
+ }
+ } else { /* 11110xxx, 4 byte char - > 0xf4 excluded above */
+ unichar_len = 4;
+ ptr++;
+ length--;
+ if (length < 1) {
+ *end = ptr;
+ return valid_bytes;
+ }
+ switch (ch) {
+ case 0xf0:
+ if (*ptr < 0x90 || *ptr > 0xbf) {
+ *end = ptr;
+ return valid_bytes;
+ }
+ break;
+ case 0xf4:
+ if (*ptr < 0x80 || *ptr > 0x8f) {
+ *end = ptr;
+ return valid_bytes;
+ }
+ break;
+ default:
+ if (*ptr < 0x80 || *ptr > 0xbf) {
+ *end = ptr;
+ return valid_bytes;
+ }
+ }
+ ptr++;
+ length--;
+ if (length < 1) {
+ *end = ptr;
+ return valid_bytes;
+ }
+ if (*ptr < 0x80 || *ptr > 0xbf) {
+ *end = ptr;
+ return valid_bytes;
+ }
+ }
+
+ ptr++;
+ length--;
+ if (length < 1) {
+ *end = ptr;
+ return valid_bytes;
+ }
+ if (*ptr < 0x80 || *ptr > 0xbf) {
+ *end = ptr;
+ return valid_bytes;
+ } else {
+ ptr++;
+ length--;
+ valid_bytes += unichar_len;
+ }
+
+ }
+ *end = ptr;
+ return valid_bytes;
+}
+
+/*
+ * Given a wmem scope, a pointer, and a length, treat the string of bytes
+ * referred to by the pointer and length as a UTF-8 string, and return a
+ * pointer to a UTF-8 string, allocated using the wmem scope, with all
+ * ill-formed sequences replaced with the Unicode REPLACEMENT CHARACTER
+ * according to the recommended "best practices" given in the Unicode
+ * Standard and specified by W3C/WHATWG.
+ *
+ * Note that in conformance with the Unicode Standard, this treats three
+ * byte sequences corresponding to UTF-16 surrogate halves (paired or unpaired)
+ * and two byte overlong encodings of 7-bit ASCII characters as invalid and
+ * substitutes REPLACEMENT CHARACTER for them. Explicit support for nonstandard
+ * derivative encoding formats (e.g. CESU-8, Java Modified UTF-8, WTF-8) could
+ * be added later.
+ *
+ * Compared with g_utf8_make_valid(), this function does not consider
+ * internal NUL bytes as invalid and replace them with replacment characters.
+ * It also replaces maximal subparts as a unit; i.e., a sequence of 2 or 3
+ * bytes which are a truncated version of a valid 3 or 4 byte character (but
+ * the next byte does not continue the character) are replaced with a single
+ * REPLACEMENT CHARACTER, whereas the Glib function replaces each byte of the
+ * sequence with its own (3 octet) REPLACEMENT CHARACTER.
+ *
+ * XXX: length should probably be a size_t instead of a int in all
+ * these encoding functions
+ * XXX: the buffer returned can be of different length than the input,
+ * and can have internal NULs as well (so that strlen doesn't give its
+ * length). As with the other encoding functions, we should return the
+ * length of the output buffer (or a wmem_strbuf_t directly) and an
+ * indication of whether there was an invalid character (i.e.
+ * REPLACEMENT CHARACTER was used.)
+ */
+wmem_strbuf_t *
+ws_utf8_make_valid_strbuf(wmem_allocator_t *scope, const uint8_t *ptr, ssize_t length)
+{
+ wmem_strbuf_t *str;
+
+ str = wmem_strbuf_new_sized(scope, length+1);
+
+ /* See the Unicode Standard conformance chapter at
+ * https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf especially
+ * Table 3-7 "Well-Formed UTF-8 Byte Sequences" and
+ * U+FFFD Substitution of Maximal Subparts. */
+
+ while (length > 0) {
+ const uint8_t *prev = ptr;
+ size_t valid_bytes = utf_8_validate(prev, length, &ptr);
+
+ if (valid_bytes) {
+ wmem_strbuf_append_len(str, prev, valid_bytes);
+ }
+ length -= ptr - prev;
+ prev += valid_bytes;
+ if (ptr - prev) {
+ wmem_strbuf_append_unichar_repl(str);
+ }
+ }
+
+ return str;
+}
+
+uint8_t *
+ws_utf8_make_valid(wmem_allocator_t *scope, const uint8_t *ptr, ssize_t length)
+{
+ wmem_strbuf_t *str = ws_utf8_make_valid_strbuf(scope, ptr, length);
+ return wmem_strbuf_finalize(str);
+}
+
+#ifdef _WIN32
+
+#include <strsafe.h>
+
+/** @file
+ * Unicode utilities (internal interface)
+ *
+ * We define UNICODE and _UNICODE under Windows. This means that
+ * Windows SDK routines expect UTF-16 strings, in contrast to newer
+ * versions of Glib and GTK+ which expect UTF-8. This module provides
+ * convenience routines for converting between UTF-8 and UTF-16.
+ */
+
+#define INITIAL_UTFBUF_SIZE 128
+
+/*
+ * XXX - Should we use g_utf8_to_utf16() and g_utf16_to_utf8()
+ * instead? The goal of the functions below was to provide simple
+ * wrappers for UTF-8 <-> UTF-16 conversion without making the
+ * caller worry about freeing up memory afterward.
+ */
+
+/* Convert from UTF-8 to UTF-16. */
+const wchar_t *
+utf_8to16(const char *utf8str)
+{
+ static wchar_t *utf16buf[3];
+ static int utf16buf_len[3];
+ static int idx;
+
+ if (utf8str == NULL)
+ return NULL;
+
+ idx = (idx + 1) % 3;
+
+ /*
+ * Allocate the buffer if it's not already allocated.
+ */
+ if (utf16buf[idx] == NULL) {
+ utf16buf_len[idx] = INITIAL_UTFBUF_SIZE;
+ utf16buf[idx] = g_malloc(utf16buf_len[idx] * sizeof(wchar_t));
+ }
+
+ while (MultiByteToWideChar(CP_UTF8, 0, utf8str, -1, NULL, 0) >= utf16buf_len[idx]) {
+ /*
+ * Double the buffer's size if it's not big enough.
+ * The size of the buffer starts at 128, so doubling its size
+ * adds at least another 128 bytes, which is more than enough
+ * for one more character plus a terminating '\0'.
+ */
+ utf16buf_len[idx] *= 2;
+ utf16buf[idx] = g_realloc(utf16buf[idx], utf16buf_len[idx] * sizeof(wchar_t));
+ }
+
+ if (MultiByteToWideChar(CP_UTF8, 0, utf8str, -1, utf16buf[idx], utf16buf_len[idx]) == 0)
+ return NULL;
+
+ return utf16buf[idx];
+}
+
+void
+utf_8to16_snprintf(TCHAR *utf16buf, int utf16buf_len, const char* fmt, ...)
+{
+ va_list ap;
+ char* dst;
+
+ va_start(ap,fmt);
+ dst = ws_strdup_vprintf(fmt, ap);
+ va_end(ap);
+
+ StringCchPrintf(utf16buf, utf16buf_len, _T("%s"), utf_8to16(dst));
+
+ g_free(dst);
+}
+
+/* Convert from UTF-16 to UTF-8. */
+char *
+utf_16to8(const wchar_t *utf16str)
+{
+ static char *utf8buf[3];
+ static int utf8buf_len[3];
+ static int idx;
+
+ if (utf16str == NULL)
+ return NULL;
+
+ idx = (idx + 1) % 3;
+
+ /*
+ * Allocate the buffer if it's not already allocated.
+ */
+ if (utf8buf[idx] == NULL) {
+ utf8buf_len[idx] = INITIAL_UTFBUF_SIZE;
+ utf8buf[idx] = g_malloc(utf8buf_len[idx]);
+ }
+
+ while (WideCharToMultiByte(CP_UTF8, 0, utf16str, -1, NULL, 0, NULL, NULL) >= utf8buf_len[idx]) {
+ /*
+ * Double the buffer's size if it's not big enough.
+ * The size of the buffer starts at 128, so doubling its size
+ * adds at least another 128 bytes, which is more than enough
+ * for one more character plus a terminating '\0'.
+ */
+ utf8buf_len[idx] *= 2;
+ utf8buf[idx] = g_realloc(utf8buf[idx], utf8buf_len[idx]);
+ }
+
+ if (WideCharToMultiByte(CP_UTF8, 0, utf16str, -1, utf8buf[idx], utf8buf_len[idx], NULL, NULL) == 0)
+ return NULL;
+
+ return utf8buf[idx];
+}
+
+/* Convert our argument list from UTF-16 to UTF-8. */
+char **
+arg_list_utf_16to8(int argc, wchar_t *wc_argv[]) {
+ char **argv;
+ int i;
+
+ argv = (char **)g_malloc((argc + 1) * sizeof(char *));
+ for (i = 0; i < argc; i++) {
+ argv[i] = g_utf16_to_utf8(wc_argv[i], -1, NULL, NULL, NULL);
+ }
+ argv[argc] = NULL;
+ return argv;
+}
+
+#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/wsutil/unicode-utils.h b/wsutil/unicode-utils.h
new file mode 100644
index 00000000..21c50ec0
--- /dev/null
+++ b/wsutil/unicode-utils.h
@@ -0,0 +1,138 @@
+/* unicode-utils.h
+ * Unicode utility definitions
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2006 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __UNICODEUTIL_H__
+#define __UNICODEUTIL_H__
+
+#include <wireshark.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#include <tchar.h>
+#include <wchar.h>
+#endif
+
+/**
+ * @file
+ * Unicode convenience routines.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef WS_DEBUG_UTF_8
+#define DEBUG_UTF_8_ENABLED true
+#else
+#define DEBUG_UTF_8_ENABLED false
+#endif
+
+#define _CHECK_UTF_8(level, str, len) \
+ do { \
+ const char *__uni_endptr; \
+ if (DEBUG_UTF_8_ENABLED && (str) != NULL && \
+ !g_utf8_validate(str, len, &__uni_endptr)) { \
+ ws_log_utf8(str, len, __uni_endptr); \
+ } \
+ } while (0)
+
+#define WS_UTF_8_CHECK(str, len) \
+ _CHECK_UTF_8(LOG_LEVEL_DEBUG, str, len)
+
+#define WS_UTF_8_DEBUG_HERE(str, len) \
+ _CHECK_UTF_8(LOG_LEVEL_ECHO, str, len)
+
+WSUTIL_EXPORT
+int ws_utf8_seqlen[256];
+
+/** Given the first byte in an UTF-8 encoded code point,
+ * return the length of the multibyte sequence, or *ZERO*
+ * if the byte is invalid as the first byte in a multibyte
+ * sequence.
+ */
+#define ws_utf8_char_len(ch) (ws_utf8_seqlen[(ch)])
+
+/*
+ * Given a wmem scope, a pointer, and a length, treat the string of bytes
+ * referred to by the pointer and length as a UTF-8 string, and return a
+ * pointer to a UTF-8 string, allocated using the wmem scope, with all
+ * ill-formed sequences replaced with the Unicode REPLACEMENT CHARACTER
+ * according to the recommended "best practices" given in the Unicode
+ * Standard and specified by W3C/WHATWG.
+ */
+WS_DLL_PUBLIC uint8_t *
+ws_utf8_make_valid(wmem_allocator_t *scope, const uint8_t *ptr, ssize_t length);
+
+/*
+ * Same as ws_utf8_make_valid() but returns a wmem_strbuf_t.
+ */
+WS_DLL_PUBLIC wmem_strbuf_t *
+ws_utf8_make_valid_strbuf(wmem_allocator_t *scope, const uint8_t *ptr, ssize_t length);
+
+#ifdef _WIN32
+
+/** Given a UTF-8 string, convert it to UTF-16. This is meant to be used
+ * to convert between GTK+ 2.x (UTF-8) to Windows (UTF-16).
+ *
+ * @param utf8str The string to convert. May be NULL.
+ * @return The string converted to UTF-16. If utf8str is NULL, returns
+ * NULL. The return value should NOT be freed by the caller.
+ */
+WS_DLL_PUBLIC
+const wchar_t * utf_8to16(const char *utf8str);
+
+/** Create a UTF-16 string (in place) according to the format string.
+ *
+ * @param utf16buf The buffer to return the UTF-16 string in.
+ * @param utf16buf_len The size of the 'utf16buf' parameter
+ * @param fmt A standard printf() format string
+ */
+WS_DLL_PUBLIC
+void utf_8to16_snprintf(TCHAR *utf16buf, int utf16buf_len, const char* fmt, ...)
+G_GNUC_PRINTF(3, 4);
+
+/** Given a UTF-16 string, convert it to UTF-8. This is meant to be used
+ * to convert between GTK+ 2.x (UTF-8) to Windows (UTF-16).
+ *
+ * @param utf16str The string to convert. May be NULL.
+ * @return The string converted to UTF-8. If utf16str is NULL, returns
+ * NULL. The return value should NOT be freed by the caller.
+ */
+WS_DLL_PUBLIC
+char * utf_16to8(const wchar_t *utf16str);
+
+/** Convert the supplied program argument list from UTF-16 to UTF-8
+ * return a pointer to the array of UTF-8 arguments. This is intended
+ * to be used to normalize command line arguments at program startup.
+ *
+ * @param argc The number of arguments.
+ * @param argv The argument values (vector).
+ */
+WS_DLL_PUBLIC
+char **arg_list_utf_16to8(int argc, wchar_t *wc_argv[]);
+
+#endif /* _WIN32 */
+
+/*
+ * defines for helping with UTF-16 surrogate pairs
+ */
+
+#define IS_LEAD_SURROGATE(uchar2) \
+ ((uchar2) >= 0xd800 && (uchar2) < 0xdc00)
+#define IS_TRAIL_SURROGATE(uchar2) \
+ ((uchar2) >= 0xdc00 && (uchar2) < 0xe000)
+#define SURROGATE_VALUE(lead, trail) \
+ (((((lead) - 0xd800) << 10) | ((trail) - 0xdc00)) + 0x10000)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __UNICODEUTIL_H__ */
diff --git a/wsutil/utf8_entities.h b/wsutil/utf8_entities.h
new file mode 100644
index 00000000..3d2f2533
--- /dev/null
+++ b/wsutil/utf8_entities.h
@@ -0,0 +1,75 @@
+/** @file
+ *
+ * Byte sequences for various UTF-8 entities
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+
+#ifndef __UTF8_ENTITIES_H__
+#define __UTF8_ENTITIES_H__
+
+/*
+ * Sequences can be found at
+ * http://www.fileformat.info/info/unicode/
+ * http://www.utf8-chartable.de/
+ * and other places
+ *
+ * Please be conservative when adding code points below. While many modern
+ * systems default to UTF-8 and handle it well, some do not. The Windows
+ * console is a notable example. As a general rule you probably shouldn't
+ * stray too far from code page 437 or WGL4:
+ * https://en.wikipedia.org/wiki/Code_page_437
+ * https://en.wikipedia.org/wiki/Windows_Glyph_List_4
+ *
+ * Hopefully we can dispense with the sequences below and simply encode our
+ * files as UTF 8 at some point. For example gcc has supported UTF 8 since
+ * at least 3.4. Visual C++ on the other hand is much more problematic.
+ * 2015 and later support /source-charset:utf-8, but prior versions appear
+ * to require a UTF 8 BOM.
+ */
+
+#define UTF8_DEGREE_SIGN "\xc2\xb0" /* 176 / 0xb0 */
+#define UTF8_SUPERSCRIPT_TWO "\xc2\xb2" /* 178 / 0xb2 */
+#define UTF8_MICRO_SIGN "\xc2\xb5" /* 181 / 0xb5 */
+#define UTF8_MIDDLE_DOT "\xc2\xb7" /* 183 / 0xb7 */
+#define UTF8_RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK "\xc2\xbb" /* 187 / 0xbb */
+
+#define UTF8_BULLET "\xe2\x80\xa2" /* 8226 / 0x2024 */
+#define UTF8_EM_DASH "\xe2\x80\x94" /* 8212 / 0x2014 */
+#define UTF8_HORIZONTAL_ELLIPSIS "\xe2\x80\xa6" /* 8230 / 0x2026 */
+
+#define UTF8_LEFTWARDS_ARROW "\xe2\x86\x90" /* 8592 / 0x2190 */
+#define UTF8_RIGHTWARDS_ARROW "\xe2\x86\x92" /* 8594 / 0x2192 */
+#define UTF8_LEFT_RIGHT_ARROW "\xe2\x86\x94" /* 8596 / 0x2194 */
+
+/* macOS command key */
+#define UTF8_PLACE_OF_INTEREST_SIGN "\xe2\x8c\x98" /* 8984 / 0x2318 */
+
+#define UTF8_SYMBOL_FOR_NULL "\xe2\x90\x80" /* 9216 / 0x2400 */
+
+#define UTF8_CHECK_MARK "\xe2\x9c\x93" /* 10003 / 0x2713 */
+#define UTF8_BALLOT_X "\xe2\x9c\x97" /* 10007 / 0x2717 */
+#define UTF8_LONG_RIGHTWARDS_ARROW "\xe2\x9f\xb6" /* 10230 / 0x27f6 */
+
+#define UTF8_ZERO_WIDTH_NO_BREAK_SPACE "\xef\xbb\xbf" /* 65279 / 0xffef */
+#define UTF8_BOM UTF8_ZERO_WIDTH_NO_BREAK_SPACE
+
+#endif /* __UTF8_ENTITIES_H__ */
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wsutil/version_info.c b/wsutil/version_info.c
new file mode 100644
index 00000000..ab450686
--- /dev/null
+++ b/wsutil/version_info.c
@@ -0,0 +1,612 @@
+/* version_info.c
+ * Routines to report version information for Wireshark programs
+ *
+ * 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 "version_info.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#elif __APPLE__
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#elif __linux__
+#include <sys/sysinfo.h>
+#endif
+
+#include <glib.h>
+#include <pcre2.h>
+
+#ifdef HAVE_ZLIB
+#include <zlib.h>
+#endif
+
+#include "vcs_version.h"
+
+#include <wsutil/cpu_info.h>
+#include <wsutil/os_version_info.h>
+#include <wsutil/crash_info.h>
+#include <wsutil/plugins.h>
+
+static char *appname_with_version;
+static char *copyright_info;
+static char *license_info;
+static char *comp_info;
+static char *runtime_info;
+
+static void end_string(GString *str);
+static void get_compiler_info(GString *str);
+static void get_mem_info(GString *str);
+
+void
+ws_init_version_info(const char *appname,
+ gather_feature_func gather_compile,
+ gather_feature_func gather_runtime)
+{
+ GString *comp_info_str, *runtime_info_str;
+ GString *copyright_info_str;
+ GString *license_info_str;
+
+ copyright_info_str = g_string_new(get_copyright_info());
+ end_string(copyright_info_str);
+ copyright_info = g_string_free(copyright_info_str, false);
+
+ license_info_str = g_string_new(get_license_info_short());
+ end_string(license_info_str);
+ license_info = g_string_free(license_info_str, false);
+
+ /*
+ * Combine the supplied application name string with the
+ * version - including the VCS version, for a build from
+ * a checkout.
+ */
+ if (strstr(appname, "Wireshark") != NULL) {
+ appname_with_version = ws_strdup_printf("%s %s",
+ appname, get_ws_vcs_version_info());
+ }
+ else {
+ appname_with_version = ws_strdup_printf("%s (Wireshark) %s",
+ appname, get_ws_vcs_version_info());
+ }
+
+ /* Get the compile-time version information string */
+ comp_info_str = get_compiled_version_info(gather_compile);
+
+ /* Get the run-time version information string */
+ runtime_info_str = get_runtime_version_info(gather_runtime);
+
+ comp_info = g_string_free(comp_info_str, false);
+ runtime_info = g_string_free(runtime_info_str, false);
+
+ /* Add this information to the information to be reported on a crash. */
+ ws_add_crash_info("%s\n"
+ "\n"
+ "%s\n"
+ "%s",
+ appname_with_version, comp_info, runtime_info);
+}
+
+/*
+ * Take the gathered list of present/absent features (dependencies)
+ * and add them to the given string.
+ * Callback function for g_list_foreach() used in
+ * get_compiled_version_info() and get_runtime_version_info().
+ */
+static void
+feature_to_gstring(void * data, void * user_data)
+{
+ char *feature = (char *)data;
+ GString *str = (GString *)user_data;
+ if (str->len > 0) {
+ g_string_append(str, ", ");
+ }
+ g_string_append_printf(str, "%s %s",
+ (*feature == '+' ? "with" : "without"), feature + 1);
+}
+
+/*
+ * If the string doesn't end with a newline, append one.
+ * Then word-wrap it to 80 columns.
+ */
+static void
+end_string(GString *str)
+{
+ size_t point;
+ char *p, *q;
+
+ point = str->len;
+ if (point == 0 || str->str[point - 1] != '\n')
+ g_string_append(str, "\n");
+ p = str->str;
+ while (*p != '\0') {
+ q = strchr(p, '\n');
+ if (q - p > 80) {
+ /*
+ * Break at or before this point.
+ */
+ q = p + 80;
+ while (q > p && *q != ' ')
+ q--;
+ if (q != p)
+ *q = '\n';
+ }
+ p = q + 1;
+ }
+}
+
+const char *
+get_appname_and_version(void)
+{
+ return appname_with_version;
+}
+
+void
+gather_pcre2_compile_info(feature_list l)
+{
+ with_feature(l, "PCRE2");
+}
+
+void
+gather_zlib_compile_info(feature_list l)
+{
+#ifdef HAVE_ZLIB
+#ifdef ZLIB_VERSION
+ with_feature(l, "zlib "ZLIB_VERSION);
+#else
+ with_feature(l, "zlib (version unknown)");
+#endif /* ZLIB_VERSION */
+#else
+ without_feature(l, "zlib");
+#endif /* HAVE_ZLIB */
+}
+
+/*
+ * Get various library compile-time versions, put them in a GString,
+ * and return the GString.
+ */
+GString *
+get_compiled_version_info(gather_feature_func gather_compile)
+{
+ GString *str;
+ GList *l = NULL;
+
+ str = g_string_new("Compiled ");
+ g_string_append_printf(str, "(%d-bit) ", (int)sizeof(str) * 8);
+
+ /* Compiler info */
+ g_string_append(str, "using ");
+ get_compiler_info(str);
+
+#ifdef GLIB_MAJOR_VERSION
+ with_feature(&l,
+ "GLib %d.%d.%d", GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION,
+ GLIB_MICRO_VERSION);
+#else
+ with_feature(&l,
+ "GLib (version unknown)");
+#endif
+
+ if (gather_compile != NULL) {
+ gather_compile(&l);
+ }
+
+ l = g_list_reverse(l);
+ g_list_foreach(l, feature_to_gstring, str);
+
+#ifdef HAVE_PLUGINS
+ g_string_append(str, ", with binary plugins");
+#else
+ g_string_append(str, ", without binary plugins");
+#endif
+
+#ifdef WS_DEBUG
+ g_string_append(str, ", debug build");
+#endif
+
+ g_string_append(str, ".");
+ end_string(str);
+ free_features(&l);
+
+ return str;
+}
+
+static void
+get_mem_info(GString *str)
+{
+ int64_t memsize = 0;
+
+#ifdef _WIN32
+ MEMORYSTATUSEX statex;
+
+ statex.dwLength = sizeof (statex);
+
+ if (GlobalMemoryStatusEx(&statex))
+ memsize = statex.ullTotalPhys;
+#elif __APPLE__
+ size_t len = sizeof(memsize);
+ sysctlbyname("hw.memsize", &memsize, &len, NULL, 0);
+#elif __linux__
+ struct sysinfo info;
+ if (sysinfo(&info) == 0)
+ memsize = info.totalram * info.mem_unit;
+#endif
+
+ if (memsize > 0)
+ g_string_append_printf(str, ", with %" PRId64 " MB of physical memory", memsize/(1024*1024));
+}
+
+/*
+ * Get compiler information, and append it to the GString.
+ */
+static void
+get_compiler_info(GString *str)
+{
+ /*
+ * See https://sourceforge.net/apps/mediawiki/predef/index.php?title=Compilers
+ * information on various defined strings.
+ *
+ * GCC's __VERSION__ is a nice text string for humans to
+ * read. The page at sourceforge.net largely describes
+ * numeric #defines that encode the version; if the compiler
+ * doesn't also offer a nice printable string, we try prettifying
+ * the number somehow.
+ */
+ #if defined(_MSC_FULL_VER)
+ /*
+ * We check for this first, as Microsoft have a version of their
+ * compiler that has Clang as the front end and their code generator
+ * as the back end.
+ *
+ * My head asplode.
+ */
+
+ /* As of Wireshark 3.0, we support only Visual Studio 2015 (14.x)
+ * or later.
+ *
+ * https://dev.to/yumetodo/list-of-mscver-and-mscfullver-8nd
+ * has a *large* table of Microsoft product names, VC++ versions,
+ * _MSC_VER values, and _MSC_FULL_VER values. All the versions
+ * we support define _MSC_FULL_VER. We don't bother trying to
+ * get the SP/update/version number from the build number, as
+ * we'd have to keep updating that with every update; there's no
+ * way to get that information directly from a predefine, and in
+ * some cases multiple updates/versions have the *same* build
+ * number (because they didn't update the toolchain).
+ *
+ * https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=vs-2017
+ * defines the format of _MSC_VER and _MSC_FULL_VER. _MSC_FULL_VER
+ * is a decimal number of the form MMmmBBBBB, where MM is the compiler/
+ * toolchain major version, mm is the minor version, and BBBBB is the
+ * build. We break it down based on that.
+ */
+ #define COMPILER_MAJOR_VERSION (_MSC_FULL_VER / 10000000)
+ #define COMPILER_MINOR_VERSION ((_MSC_FULL_VER % 10000000) / 100000)
+ #define COMPILER_BUILD_NUMBER (_MSC_FULL_VER % 100000)
+
+ /*
+ * From https://web.archive.org/web/20190125151548/https://blogs.msdn.microsoft.com/vcblog/2014/11/17/c111417-features-in-vs-2015-preview/
+ * Bakersfield: DevDiv's upper management determines the scheduling
+ * of new major versions. They also decided to increment the product
+ * version from 12 (for VS 2013) to 14 (for VS 2015). However, the
+ * C++ compiler's version incremented normally, from 18 to 19.
+ * (It's larger because the C++ compiler predates the "Visual" in
+ * Visual C++.)
+ *
+ * So the product version number is 5 less than the compiler version
+ * number.
+ */
+ #define VCPP_MAJOR_VERSION (COMPILER_MAJOR_VERSION - 5)
+
+ #if VCPP_MAJOR_VERSION == 14
+ /*
+ * From https://devblogs.microsoft.com/cppblog/side-by-side-minor-version-msvc-toolsets-in-visual-studio-2017/
+ *
+ * We've been delivering improvements to Visual Studio 2017 more
+ * frequently than ever before. Since its first release in March
+ * we've released four major updates to VS2017 and are currently
+ * previewing the fifth update, VS2017 version 15.5.
+ *
+ * The MSVC toolset in VS2017 is built as a minor version update to
+ * the VS2015 compiler toolset. This minor version bump indicates
+ * that the VS2017 MSVC toolset is binary compatible with the VS2015
+ * MSVC toolset, enabling an easier upgrade for VS2015 users. Even
+ * though the MSVC compiler toolset in VS2017 delivers many new
+ * features and conformance improvements it is a minor version,
+ * compatible update from 14.00 in VS2015 to 14.10 in VS2017.
+ */
+ #if COMPILER_MINOR_VERSION < 10
+ #define VS_VERSION "2015"
+ #elif COMPILER_MINOR_VERSION < 20
+ #define VS_VERSION "2017"
+ #elif COMPILER_MINOR_VERSION < 30
+ #define VS_VERSION "2019"
+ #else
+ #define VS_VERSION "2022"
+ #endif
+ #else
+ /*
+ * Add additional checks here, before the #else.
+ */
+ #define VS_VERSION "(unknown)"
+ #endif /* VCPP_MAJOR_VERSION */
+
+ /*
+ * XXX - should we show the raw compiler version number, as is
+ * shown by "cl /?", which would be %d.%d.%d.%d with
+ * COMPILER_MAJOR_VERSION, COMPILER_MINOR_VERSION,
+ * COMPILER_BUILD_NUMBER, and _MSC_BUILD, the last of which is
+ * "the revision number element of the compiler's version number",
+ * which I guess is not to be confused with the build number,
+ * the _BUILD in the name nonwithstanding.
+ */
+ g_string_append_printf(str, "Microsoft Visual Studio " VS_VERSION " (VC++ %d.%d, build %d)",
+ VCPP_MAJOR_VERSION, COMPILER_MINOR_VERSION, COMPILER_BUILD_NUMBER);
+ #if defined(__clang__)
+ /*
+ * See above.
+ */
+ g_string_append_printf(str, " clang/C2 %s and -fno-ms-compatibility",
+ __VERSION__);
+ #endif
+ #elif defined(__GNUC__) && defined(__VERSION__)
+ /*
+ * Clang and llvm-gcc also define __GNUC__ and __VERSION__;
+ * distinguish between them.
+ */
+ #if defined(__clang__)
+ /* clang */
+ char *version; /* clang's version string has a trailing space. */
+ #if defined(__clang_version__)
+ version = g_strdup(__clang_version__);
+ g_string_append_printf(str, "Clang %s", g_strstrip(version));
+ #else
+ version = g_strdup(__VERSION__);
+ g_string_append_printf(str, "%s", g_strstrip(version));
+ #endif /* __clang_version__ */
+ g_free(version);
+ #elif defined(__llvm__)
+ /* llvm-gcc */
+ g_string_append_printf(str, "llvm-gcc %s", __VERSION__);
+ #else
+ /* boring old GCC */
+ g_string_append_printf(str, "GCC %s", __VERSION__);
+ #endif
+ #elif defined(__HP_aCC)
+ g_string_append_printf(str, "HP aCC %d", __HP_aCC);
+ #elif defined(__xlC__)
+ g_string_append_printf(str, "IBM XL C %d.%d",
+ (__xlC__ >> 8) & 0xFF, __xlC__ & 0xFF);
+ #ifdef __IBMC__
+ if ((__IBMC__ % 10) != 0)
+ g_string_append_printf(str, " patch %d", __IBMC__ % 10);
+ #endif /* __IBMC__ */
+ #elif defined(__INTEL_COMPILER)
+ g_string_append_printf(str, "Intel C %d.%d",
+ __INTEL_COMPILER / 100, (__INTEL_COMPILER / 10) % 10);
+ if ((__INTEL_COMPILER % 10) != 0)
+ g_string_append_printf(str, " patch %d", __INTEL_COMPILER % 10);
+ #ifdef __INTEL_COMPILER_BUILD_DATE
+ g_string_sprinta(str, ", compiler built %04d-%02d-%02d",
+ __INTEL_COMPILER_BUILD_DATE / 10000,
+ (__INTEL_COMPILER_BUILD_DATE / 100) % 100,
+ __INTEL_COMPILER_BUILD_DATE % 100);
+ #endif /* __INTEL_COMPILER_BUILD_DATE */
+ #elif defined(__SUNPRO_C)
+ g_string_append_printf(str, "Sun C %d.%d",
+ (__SUNPRO_C >> 8) & 0xF, (__SUNPRO_C >> 4) & 0xF);
+ if ((__SUNPRO_C & 0xF) != 0)
+ g_string_append_printf(str, " patch %d", __SUNPRO_C & 0xF);
+ #else
+ g_string_append(str, "unknown compiler");
+ #endif
+}
+
+void
+gather_pcre2_runtime_info(feature_list l)
+{
+ /* From pcre2_api(3):
+ * The where argument should point to a buffer that is at least 24 code
+ * units long. (The exact length required can be found by calling
+ * pcre2_config() with where set to NULL.)
+ *
+ * The API should accept a buffer size as additional input. We could opt for a
+ * stack buffer size greater than 24 but let's just go with the weirdness...
+ */
+ int size;
+ char *buf_pcre2;
+
+ size = pcre2_config(PCRE2_CONFIG_VERSION, NULL);
+ if (size < 0 || size > 255) {
+ without_feature(l, "PCRE2 (error querying)");
+ return;
+ }
+ buf_pcre2 = g_malloc(size + 1);
+ pcre2_config(PCRE2_CONFIG_VERSION, buf_pcre2);
+ buf_pcre2[size] = '\0';
+ with_feature(l, "PCRE2 %s", buf_pcre2);
+ g_free(buf_pcre2);
+}
+
+void
+gather_zlib_runtime_info(feature_list l)
+{
+ (void)l;
+#if defined(HAVE_ZLIB) && !defined(_WIN32)
+ with_feature(l, "zlib %s", zlibVersion());
+#endif
+}
+
+/*
+ * Get various library run-time versions, and the OS version, and append
+ * them to the specified GString.
+ *
+ * "additional_info" is called at the end to append any additional
+ * information; this is required in order to, for example, put the
+ * libcap information at the end of the string, as we currently
+ * don't use libcap in TShark.
+ */
+GString *
+get_runtime_version_info(gather_feature_func gather_runtime)
+{
+ GString *str;
+ char *lc;
+ GList *l = NULL;
+
+ str = g_string_new("Running on ");
+
+ get_os_version_info(str);
+
+ /* CPU Info */
+ get_cpu_info(str);
+
+ /* Get info about installed memory */
+ get_mem_info(str);
+
+ with_feature(&l, "GLib %u.%u.%u",
+ glib_major_version, glib_minor_version, glib_micro_version);
+
+ if (gather_runtime != NULL) {
+ gather_runtime(&l);
+ }
+
+ l = g_list_reverse(l);
+ g_list_foreach(l, feature_to_gstring, str);
+
+ /*
+ * Display LC_CTYPE as a relevant, portable and sort of representative
+ * locale configuration without being exceedingly verbose and including
+ * the whole shebang of categories using LC_ALL.
+ */
+ if ((lc = setlocale(LC_CTYPE, NULL)) != NULL) {
+ g_string_append_printf(str, ", with LC_TYPE=%s", lc);
+ }
+
+#ifdef HAVE_PLUGINS
+ if (plugins_supported()) {
+ g_string_append(str, ", binary plugins supported");
+ }
+#endif
+
+ g_string_append_c(str, '.');
+ end_string(str);
+ free_features(&l);
+
+ return str;
+}
+
+/*
+ * Return a version number string for Wireshark, including, for builds
+ * from a tree checked out from Wireshark's version control system,
+ * something identifying what version was checked out.
+ */
+const char *
+get_ws_vcs_version_info(void)
+{
+#ifdef VCSVERSION
+ return VERSION " (" VCSVERSION ")";
+#else
+ return VERSION;
+#endif
+}
+
+const char *
+get_ws_vcs_version_info_short(void)
+{
+#ifdef VCSVERSION
+ return VCSVERSION;
+#else
+ return VERSION;
+#endif
+}
+
+void
+get_ws_version_number(int *major, int *minor, int *micro)
+{
+ if (major)
+ *major = VERSION_MAJOR;
+ if (minor)
+ *minor = VERSION_MINOR;
+ if (micro)
+ *micro = VERSION_MICRO;
+}
+
+void
+show_version(void)
+{
+ printf("%s.\n\n"
+ "%s"
+ "%s\n"
+ "%s\n"
+ "%s",
+ appname_with_version,
+ copyright_info,
+ license_info,
+ comp_info,
+ runtime_info);
+}
+
+void
+show_help_header(const char *description)
+{
+ printf("%s\n", appname_with_version);
+ if (description) {
+ printf("%s\n", description);
+ printf("See https://www.wireshark.org for more information.\n");
+ }
+}
+
+/*
+ * Get copyright information.
+ */
+const char *
+get_copyright_info(void)
+{
+ return
+ "Copyright 1998-2024 Gerald Combs <gerald@wireshark.org> and contributors.";
+}
+
+const char *
+get_license_info_short(void)
+{
+ return
+ "Licensed under the terms of the GNU General Public License (version 2 or later). "
+ "This is free software; see the file named COPYING in the distribution. "
+ "There is NO WARRANTY; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.";
+}
+
+const char *
+get_license_info(void)
+{
+ return
+ "This program is free software: you can redistribute it and/or modify "
+ "it under the terms of the GNU General Public License as published by "
+ "the Free Software Foundation, either version 2 of the License, or "
+ "(at your option) any later version. This program is distributed in the "
+ "hope that it will be useful, but WITHOUT ANY WARRANTY; without even "
+ "the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. "
+ "See the GNU General Public License for more details.";
+}
+
+/*
+ * 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:
+ */
diff --git a/wsutil/version_info.h b/wsutil/version_info.h
new file mode 100644
index 00000000..1285cd2c
--- /dev/null
+++ b/wsutil/version_info.h
@@ -0,0 +1,141 @@
+/** @file
+ *
+ * Declarations of routines to report version information for Wireshark
+ * programs
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WS_VERSION_INFO_H__
+#define __WS_VERSION_INFO_H__
+
+#include <glib.h>
+#include <wsutil/feature_list.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * Initialize information about the program for various purposes, including
+ * reporting the version and build information for the program, putting
+ * that information into crash dumps if possible, and giving the program
+ * name and version information into capture files written by the program
+ * if possible.
+ *
+ * "appname" is a string that appears at the beginning of the information;
+ * it should be the application name. "(Wireshark)" will be added if
+ * the program isn't Wireshark.
+ *
+ * "gather_compile" is called (if non-null) to add any additional build-time
+ * information.
+ *
+ * "gather_runtime" is called (if non-null) to add any additional
+ * run-time information; this is required in order to, for example,
+ * put the libcap information into the string, as we currently
+ * don't use libcap in TShark.
+ */
+WS_DLL_PUBLIC
+void ws_init_version_info(const char *appname,
+ gather_feature_func gather_compile,
+ gather_feature_func gather_runtime);
+
+/*
+ * Get a string giving the application name, as provided to
+ * ws_init_version_info(), followed by a string giving the
+ * application version.
+ */
+WS_DLL_PUBLIC
+const char *get_appname_and_version(void);
+
+WS_DLL_PUBLIC
+void
+gather_pcre2_compile_info(feature_list l);
+
+WS_DLL_PUBLIC
+void
+gather_zlib_compile_info(feature_list l);
+
+/*
+ * Get various library compile-time versions, put them in a GString,
+ * and return the GString.
+ *
+ * "gather_compile" is called (if non-null) to add any additional build-time
+ * information.
+ */
+WS_DLL_PUBLIC
+GString *get_compiled_version_info(gather_feature_func gather_compile);
+
+WS_DLL_PUBLIC
+void
+gather_pcre2_runtime_info(feature_list l);
+
+WS_DLL_PUBLIC
+void
+gather_zlib_runtime_info(feature_list l);
+
+/*
+ * Get various library run-time versions, and the OS version, put them in
+ * a GString, and return the GString.
+ *
+ * "gather_runtime" is called (if non-null) to add any additional
+ * run-time information; this is required in order to, for example,
+ * put the libcap information into the string, as we currently
+ * don't use libcap in TShark.
+ */
+WS_DLL_PUBLIC
+GString *get_runtime_version_info(gather_feature_func gather_runtime);
+
+/*
+ * Return a version number string for Wireshark, including, for builds
+ * from a tree checked out from Wireshark's version control system,
+ * something identifying what version was checked out.
+ */
+WS_DLL_PUBLIC
+const char *get_ws_vcs_version_info(void);
+
+/*
+ * Shorter version of get_ws_vcs_version_info().
+ */
+WS_DLL_PUBLIC
+const char *get_ws_vcs_version_info_short(void);
+
+/*
+ * Return version number as integers.
+ */
+WS_DLL_PUBLIC
+void get_ws_version_number(int *major, int *minor, int *micro);
+
+/*
+ * Show the program name and version number information on the standard
+ * output; this is used for command-line "show the version" options.
+ */
+WS_DLL_PUBLIC
+void show_version(void);
+
+/*
+ * Show the program name and version number information, a supplied
+ * description string, and a "See {URL} for more information" message.
+ * This is used for command-line "help" options.
+ */
+WS_DLL_PUBLIC
+void show_help_header(const char *description);
+
+WS_DLL_PUBLIC
+const char *get_copyright_info(void);
+
+WS_DLL_PUBLIC
+const char *get_license_info(void);
+
+WS_DLL_PUBLIC
+const char *get_license_info_short(void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WS_VERSION_INFO_H__ */
diff --git a/wsutil/win32-utils.c b/wsutil/win32-utils.c
new file mode 100644
index 00000000..b1cef5cd
--- /dev/null
+++ b/wsutil/win32-utils.c
@@ -0,0 +1,313 @@
+/* win32-utils.c
+ * Win32 utility routines
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2006 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <config.h>
+
+#include "win32-utils.h"
+
+#include <tchar.h>
+#include <versionhelpers.h>
+
+/* Quote the argument element if necessary, so that it will get
+ * reconstructed correctly in the C runtime startup code. Note that
+ * the unquoting algorithm in the C runtime is really weird, and
+ * rather different than what Unix shells do. See stdargv.c in the C
+ * runtime sources (in the Platform SDK, in src/crt).
+ *
+ * Stolen from GLib's protect_argv(), an internal routine that quotes
+ * string in an argument list so that they arguments will be handled
+ * correctly in the command-line string passed to CreateProcess()
+ * if that string is constructed by gluing those strings together.
+ */
+char *
+protect_arg (const char *argv)
+{
+ char *new_arg;
+ const char *p = argv;
+ char *q;
+ int len = 0;
+ bool need_dblquotes = false;
+
+ while (*p) {
+ if (*p == ' ' || *p == '\t')
+ need_dblquotes = true;
+ else if (*p == '"')
+ len++;
+ else if (*p == '\\') {
+ const char *pp = p;
+
+ while (*pp && *pp == '\\')
+ pp++;
+ if (*pp == '"')
+ len++;
+ }
+ len++;
+ p++;
+ }
+
+ q = new_arg = g_malloc (len + need_dblquotes*2 + 1);
+ p = argv;
+
+ if (need_dblquotes)
+ *q++ = '"';
+
+ while (*p) {
+ if (*p == '"')
+ *q++ = '\\';
+ else if (*p == '\\') {
+ const char *pp = p;
+
+ while (*pp && *pp == '\\')
+ pp++;
+ if (*pp == '"')
+ *q++ = '\\';
+ }
+ *q++ = *p;
+ p++;
+ }
+
+ if (need_dblquotes)
+ *q++ = '"';
+ *q++ = '\0';
+
+ return new_arg;
+}
+
+/*
+ * Generate a UTF-8 string for a Windows error.
+ */
+
+/*
+ * We make the buffer at least this big, under the assumption that doing
+ * so will reduce the number of reallocations to do. (Otherwise, why
+ * did Microsoft bother supporting a minimum buffer size?)
+ */
+#define ERRBUF_SIZE 128
+const char *
+win32strerror(DWORD error)
+{
+ DWORD retval;
+ WCHAR *utf16_message;
+ char *utf8_message;
+ char *tempmsg;
+ const char *msg;
+
+ /*
+ * XXX - what language ID to use?
+ *
+ * For UN*Xes, g_strerror() may or may not return localized strings.
+ *
+ * We currently don't have localized strings, except for GUI items,
+ * but we might want to do so. On the other hand, if most of these
+ * messages are going to be read by Wireshark developers, English
+ * might be a better choice, so the developer doesn't have to get
+ * the message translated if it's in a language they don't happen
+ * to understand. Then again, we're including the error number,
+ * so the developer can just look that up.
+ */
+ retval = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK,
+ NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR)&utf16_message, ERRBUF_SIZE, NULL);
+ if (retval == 0) {
+ /* Failed. */
+ tempmsg = ws_strdup_printf("Couldn't get error message for error (%lu) (because %lu)",
+ error, GetLastError());
+ msg = g_intern_string(tempmsg);
+ g_free(tempmsg);
+ return msg;
+ }
+
+ utf8_message = g_utf16_to_utf8(utf16_message, -1, NULL, NULL, NULL);
+ LocalFree(utf16_message);
+ if (utf8_message == NULL) {
+ /* Conversion failed. */
+ tempmsg = ws_strdup_printf("Couldn't convert error message for error to UTF-8 (%lu) (because %lu)",
+ error, GetLastError());
+ msg = g_intern_string(tempmsg);
+ g_free(tempmsg);
+ return msg;
+ }
+ tempmsg = ws_strdup_printf("%s (%lu)", utf8_message, error);
+ g_free(utf8_message);
+ msg = g_intern_string(tempmsg);
+ g_free(tempmsg);
+ return msg;
+}
+
+/*
+ * Generate a string for a Win32 exception code.
+ */
+const char *
+win32strexception(DWORD exception)
+{
+ static char errbuf[ERRBUF_SIZE+1];
+ static const struct exception_msg {
+ DWORD code;
+ char *msg;
+ } exceptions[] = {
+ { EXCEPTION_ACCESS_VIOLATION, "Access violation" },
+ { EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "Array bounds exceeded" },
+ { EXCEPTION_BREAKPOINT, "Breakpoint" },
+ { EXCEPTION_DATATYPE_MISALIGNMENT, "Data type misalignment" },
+ { EXCEPTION_FLT_DENORMAL_OPERAND, "Denormal floating-point operand" },
+ { EXCEPTION_FLT_DIVIDE_BY_ZERO, "Floating-point divide by zero" },
+ { EXCEPTION_FLT_INEXACT_RESULT, "Floating-point inexact result" },
+ { EXCEPTION_FLT_INVALID_OPERATION, "Invalid floating-point operation" },
+ { EXCEPTION_FLT_OVERFLOW, "Floating-point overflow" },
+ { EXCEPTION_FLT_STACK_CHECK, "Floating-point stack check" },
+ { EXCEPTION_FLT_UNDERFLOW, "Floating-point underflow" },
+ { EXCEPTION_GUARD_PAGE, "Guard page violation" },
+ { EXCEPTION_ILLEGAL_INSTRUCTION, "Illegal instruction" },
+ { EXCEPTION_IN_PAGE_ERROR, "Page-in error" },
+ { EXCEPTION_INT_DIVIDE_BY_ZERO, "Integer divide by zero" },
+ { EXCEPTION_INT_OVERFLOW, "Integer overflow" },
+ { EXCEPTION_INVALID_DISPOSITION, "Invalid disposition" },
+ { EXCEPTION_INVALID_HANDLE, "Invalid handle" },
+ { EXCEPTION_NONCONTINUABLE_EXCEPTION, "Non-continuable exception" },
+ { EXCEPTION_PRIV_INSTRUCTION, "Privileged instruction" },
+ { EXCEPTION_SINGLE_STEP, "Single-step complete" },
+ { EXCEPTION_STACK_OVERFLOW, "Stack overflow" },
+ { 0, NULL }
+ };
+#define N_EXCEPTIONS (sizeof exceptions / sizeof exceptions[0])
+
+ for (size_t i = 0; i < N_EXCEPTIONS; i++) {
+ if (exceptions[i].code == exception)
+ return exceptions[i].msg;
+ }
+ snprintf(errbuf, sizeof errbuf, "Exception 0x%08lx", exception);
+ return errbuf;
+}
+
+// This appears to be the closest equivalent to SIGPIPE on Windows.
+// https://devblogs.microsoft.com/oldnewthing/?p=2433
+// https://stackoverflow.com/a/53214/82195
+
+static void win32_kill_child_on_exit(HANDLE child_handle) {
+ static HANDLE cjo_handle = NULL;
+ if (!cjo_handle) {
+ cjo_handle = CreateJobObject(NULL, NULL);
+
+ if (!cjo_handle) {
+ ws_log(LOG_DOMAIN_CAPTURE, LOG_LEVEL_DEBUG, "Could not create child cleanup job object: %s",
+ win32strerror(GetLastError()));
+ return;
+ }
+
+ JOBOBJECT_EXTENDED_LIMIT_INFORMATION cjo_jel_info = { 0 };
+ cjo_jel_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+ BOOL sijo_ret = SetInformationJobObject(cjo_handle, JobObjectExtendedLimitInformation,
+ &cjo_jel_info, sizeof(cjo_jel_info));
+ if (!sijo_ret) {
+ ws_log(LOG_DOMAIN_CAPTURE, LOG_LEVEL_DEBUG, "Could not set child cleanup limits: %s",
+ win32strerror(GetLastError()));
+ }
+ }
+
+ BOOL aptjo_ret = AssignProcessToJobObject(cjo_handle, child_handle);
+ if (!aptjo_ret) {
+ ws_log(LOG_DOMAIN_CAPTURE, LOG_LEVEL_DEBUG, "Could not assign child cleanup process: %s",
+ win32strerror(GetLastError()));
+ }
+}
+
+BOOL win32_create_process(const char *application_name, const char *command_line, LPSECURITY_ATTRIBUTES process_attributes, LPSECURITY_ATTRIBUTES thread_attributes, size_t n_inherit_handles, HANDLE *inherit_handles, DWORD creation_flags, LPVOID environment, const char *current_directory, LPSTARTUPINFO startup_info, LPPROCESS_INFORMATION process_information)
+{
+ gunichar2 *wappname = NULL, *wcurrentdirectory = NULL;
+ gunichar2 *wcommandline = g_utf8_to_utf16(command_line, -1, NULL, NULL, NULL);
+ LPPROC_THREAD_ATTRIBUTE_LIST attribute_list = NULL;
+ STARTUPINFOEX startup_infoex;
+ size_t i;
+ // CREATE_SUSPENDED: Suspend the child so that we can cleanly call
+ // AssignProcessToJobObject.
+ DWORD wcreationflags = creation_flags|CREATE_SUSPENDED;
+ // CREATE_BREAKAWAY_FROM_JOB: The main application might be associated with a job,
+ // e.g. if we're running under "Run As", ConEmu, or Visual Studio. On Windows
+ // <= 7 our child process needs to break away from it so that we can cleanly
+ // call AssignProcessToJobObject on *our* job.
+ // Windows >= 8 supports nested jobs so this isn't necessary there.
+ // https://blogs.msdn.microsoft.com/winsdk/2014/09/22/job-object-insanity/
+ //
+ if (! IsWindowsVersionOrGreater(6, 2, 0)) { // Windows 8
+ wcreationflags |= CREATE_BREAKAWAY_FROM_JOB;
+ }
+
+ if (application_name) {
+ wappname = g_utf8_to_utf16(application_name, -1, NULL, NULL, NULL);
+ }
+ if (current_directory) {
+ wcurrentdirectory = g_utf8_to_utf16(current_directory, -1, NULL, NULL, NULL);
+ }
+ if (n_inherit_handles > 0) {
+ size_t attr_size = 0;
+ BOOL success;
+ success = InitializeProcThreadAttributeList(NULL, 1, 0, &attr_size);
+ if (success || (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
+ attribute_list = g_malloc(attr_size);
+ success = InitializeProcThreadAttributeList(attribute_list, 1, 0, &attr_size);
+ }
+ if (success && (attribute_list != NULL)) {
+ success = UpdateProcThreadAttribute(attribute_list, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
+ inherit_handles, n_inherit_handles * sizeof(HANDLE), NULL, NULL);
+ }
+ if (!success && (attribute_list != NULL)) {
+ DeleteProcThreadAttributeList(attribute_list);
+ g_free(attribute_list);
+ attribute_list = NULL;
+ }
+ }
+ memset(&startup_infoex, 0, sizeof(startup_infoex));
+ startup_infoex.StartupInfo = *startup_info;
+ startup_infoex.StartupInfo.cb = sizeof(startup_infoex);
+ startup_infoex.lpAttributeList = attribute_list;
+ wcreationflags |= EXTENDED_STARTUPINFO_PRESENT;
+ for (i = 0; i < n_inherit_handles; i++) {
+ SetHandleInformation(inherit_handles[i], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
+ }
+ BOOL cp_res = CreateProcess(wappname, wcommandline, process_attributes, thread_attributes,
+ (n_inherit_handles > 0) ? true : false, wcreationflags, environment, wcurrentdirectory,
+ &startup_infoex.StartupInfo, process_information);
+ /* While this function makes the created process inherit only the explicitly
+ * listed handles, there can be other functions (in 3rd party libraries)
+ * that create processes inheriting all inheritable handles. To minimize
+ * number of unwanted handle duplicates (handle duplicate can extend object
+ * lifetime, e.g. pipe write end) created that way clear the inherit flag.
+ */
+ for (i = 0; i < n_inherit_handles; i++) {
+ SetHandleInformation(inherit_handles[i], HANDLE_FLAG_INHERIT, 0);
+ }
+ if (cp_res) {
+ win32_kill_child_on_exit(process_information->hProcess);
+ ResumeThread(process_information->hThread);
+ }
+ // XXX Else try again if CREATE_BREAKAWAY_FROM_JOB and GetLastError() == ERROR_ACCESS_DENIED?
+
+ if (attribute_list) {
+ DeleteProcThreadAttributeList(attribute_list);
+ g_free(attribute_list);
+ }
+ g_free(wappname);
+ g_free(wcommandline);
+ g_free(wcurrentdirectory);
+ return cp_res;
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wsutil/win32-utils.h b/wsutil/win32-utils.h
new file mode 100644
index 00000000..44471c59
--- /dev/null
+++ b/wsutil/win32-utils.h
@@ -0,0 +1,95 @@
+/* win32-utils.h
+ * Windows utility definitions
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2006 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WIN32UTIL_H__
+#define __WIN32UTIL_H__
+
+#include <wireshark.h>
+
+#include <windows.h>
+
+/**
+ * @file
+ * Unicode convenience routines.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Quote the argument element if necessary, so that it will get
+ * reconstructed correctly in the C runtime startup code. Note that
+ * the unquoting algorithm in the C runtime is really weird, and
+ * rather different than what Unix shells do. See stdargv.c in the C
+ * runtime sources (in the Platform SDK, in src/crt).
+ *
+ * Stolen from GLib's protect_argv(), an internal routine that quotes
+ * string in an argument list so that they arguments will be handled
+ * correctly in the command-line string passed to CreateProcess()
+ * if that string is constructed by gluing those strings together.
+ *
+ * @param argv The string to be quoted. May be NULL.
+ * @return The string quoted to be used by CreateProcess
+ */
+WS_DLL_PUBLIC
+char * protect_arg (const char *argv);
+
+/** Generate a string for a Windows error.
+ *
+ * @param error The Windows error code
+ * @return a localized UTF-8 string containing the corresponding error message
+ */
+WS_DLL_PUBLIC
+const char * win32strerror(DWORD error);
+
+/** Generate a string for a Win32 exception code.
+ *
+ * @param exception The exception code
+ * @return a non-localized string containing the error message
+ */
+WS_DLL_PUBLIC
+const char * win32strexception(DWORD exception);
+
+/**
+ * @brief ws_pipe_create_process Create a process and assign it to the main application
+ * job object so that it will be killed when the main application exits.
+ *
+ * In order to limit unwanted handle duplicates in subprocesses all handles should be
+ * created as not inheritable and passed in the inherit_handles array. This function
+ * marks the handles as inheritable for as short time as possible. Note that handles
+ * passed to this function will have the inheritable flag cleared on exit. Processes
+ * created with this function inherit only the provided handles.
+ *
+ * @param application_name Application name. Will be converted to its UTF-16 equivalent or NULL.
+ * @param command_line Command line. Will be converted to its UTF-16 equivalent.
+ * @param process_attributes Same as CreateProcess.
+ * @param thread_attributes Same as CreateProcess.
+ * @param n_inherit_handles Number of handles the child process will inherit.
+ * @param inherit_handles Handles the child process will inherit.
+ * @param creation_flags Will be ORed with CREATE_SUSPENDED|CREATE_BREAKAWAY_FROM_JOB.
+ * @param environment Same as CreateProcess.
+ * @param current_directory Current directory. Will be converted to its UTF-16 equivalent or NULL.
+ * @param startup_info Same as CreateProcess.
+ * @param process_information Same as CreateProcess.
+ * @return
+ */
+WS_DLL_PUBLIC
+BOOL win32_create_process(const char *application_name, const char *command_line,
+ LPSECURITY_ATTRIBUTES process_attributes, LPSECURITY_ATTRIBUTES thread_attributes,
+ size_t n_inherit_handles, HANDLE *inherit_handles,
+ DWORD creation_flags, LPVOID environment,
+ const char *current_directory, LPSTARTUPINFO startup_info, LPPROCESS_INFORMATION process_information
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __WIN32UTIL_H__ */
diff --git a/wsutil/wmem/wmem-int.h b/wsutil/wmem/wmem-int.h
new file mode 100644
index 00000000..6c4e4985
--- /dev/null
+++ b/wsutil/wmem/wmem-int.h
@@ -0,0 +1,32 @@
+/** @file
+ *
+ * Internal definitions for the Wireshark Memory Manager
+ * Copyright 2012, Evan Huus <eapache@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WMEM_INT_H__
+#define __WMEM_INT_H__
+
+#include <glib.h>
+#include <wsutil/ws_assert.h>
+
+#endif /* __WMEM_INT_H__ */
+
+/*
+ * 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/wsutil/wmem/wmem.h b/wsutil/wmem/wmem.h
new file mode 100644
index 00000000..81429151
--- /dev/null
+++ b/wsutil/wmem/wmem.h
@@ -0,0 +1,43 @@
+/** @file
+ *
+ * Definitions for the Wireshark Memory Manager
+ * Copyright 2012, Evan Huus <eapache@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WMEM_H__
+#define __WMEM_H__
+
+#include "wmem_array.h"
+#include "wmem_core.h"
+#include "wmem_list.h"
+#include "wmem_map.h"
+#include "wmem_miscutl.h"
+#include "wmem_multimap.h"
+#include "wmem_queue.h"
+#include "wmem_stack.h"
+#include "wmem_strbuf.h"
+#include "wmem_strutl.h"
+#include "wmem_tree.h"
+#include "wmem_interval_tree.h"
+#include "wmem_user_cb.h"
+
+#endif /* __WMEM_H__ */
+
+/*
+ * 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/wsutil/wmem/wmem_allocator.h b/wsutil/wmem/wmem_allocator.h
new file mode 100644
index 00000000..8890054a
--- /dev/null
+++ b/wsutil/wmem/wmem_allocator.h
@@ -0,0 +1,64 @@
+/** @file
+ *
+ * Definitions for the Wireshark Memory Manager Allocator
+ * Copyright 2012, Evan Huus <eapache@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WMEM_ALLOCATOR_H__
+#define __WMEM_ALLOCATOR_H__
+
+#include <glib.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+struct _wmem_user_cb_container_t;
+
+/* See section "4. Internal Design" of doc/README.wmem for details
+ * on this structure */
+struct _wmem_allocator_t {
+ /* Consumer functions */
+ void *(*walloc)(void *private_data, const size_t size);
+ void (*wfree)(void *private_data, void *ptr);
+ void *(*wrealloc)(void *private_data, void *ptr, const size_t size);
+
+ /* Producer/Manager functions */
+ void (*free_all)(void *private_data);
+ void (*gc)(void *private_data);
+ void (*cleanup)(void *private_data);
+
+ /* Callback List */
+ struct _wmem_user_cb_container_t *callbacks;
+
+ /* Implementation details */
+ void *private_data;
+ enum _wmem_allocator_type_t type;
+ bool in_scope;
+};
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WMEM_ALLOCATOR_H__ */
+
+/*
+ * 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/wsutil/wmem/wmem_allocator_block.c b/wsutil/wmem/wmem_allocator_block.c
new file mode 100644
index 00000000..b8a0f598
--- /dev/null
+++ b/wsutil/wmem/wmem_allocator_block.c
@@ -0,0 +1,1125 @@
+/* wmem_allocator_block.c
+ * Wireshark Memory Manager Large-Block Allocator (version 3)
+ * Copyright 2013, Evan Huus <eapache@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include "wmem_core.h"
+#include "wmem_allocator.h"
+#include "wmem_allocator_block.h"
+
+/* This has turned into a very interesting excercise in algorithms and data
+ * structures.
+ *
+ * HISTORY
+ *
+ * Version 1 of this allocator was embedded in the original emem framework. It
+ * didn't have to handle realloc or free, so it was very simple: it just grabbed
+ * a block from the OS and served allocations sequentially out of that until it
+ * ran out, then allocated a new block. The old block was never revisited, so
+ * it generally had a bit of wasted space at the end, but the waste was
+ * small enough that it was simply ignored. This allocator provided very fast
+ * constant-time allocation for any request that didn't require a new block from
+ * the OS, and that cost could be amortized away.
+ *
+ * Version 2 of this allocator was prompted by the need to support realloc and
+ * free in wmem. The original version simply didn't save enough metadata to do
+ * this, so I added a layer on top to make it possible. The primary principle
+ * was the same (allocate sequentially out of big blocks) with a bit of extra
+ * magic. Allocations were still fast constant-time, and frees were as well.
+ * Large parts of that design are still present in this one, but for more
+ * details see older versions of this file from git or svn.
+ *
+ * Version 3 of this allocator was written to address some issues that
+ * eventually showed up with version 2 under real-world usage. Specifically,
+ * version 2 dealt very poorly with memory fragmentation, almost never reusing
+ * freed blocks and choosing to just keep allocating from the master block
+ * instead. This led to particularly poor behaviour under the tick-tock loads
+ * (alloc/free/alloc/free or alloc/alloc/free/alloc/alloc/free/ or ...) that
+ * showed up in a couple of different protocol dissectors (TCP, Kafka).
+ *
+ * BLOCKS AND CHUNKS
+ *
+ * As in previous versions, allocations typically happen sequentially out of
+ * large OS-level blocks. Each block has a short embedded header used to
+ * maintain a doubly-linked list of all blocks (used or not) currently owned by
+ * the allocator. Each block is divided into chunks, which represent allocations
+ * and free sections (a block is initialized with one large, free, chunk). Each
+ * chunk is prefixed with a wmem_block_chunk_t structure, which is a short
+ * metadata header (8 bytes, regardless of 32 or 64-bit architecture unless
+ * alignment requires it to be padded) that contains the length of the chunk,
+ * the length of the previous chunk, a flag marking the chunk as free or used,
+ * and a flag marking the last chunk in a block. This serves to implement an
+ * inline sequential doubly-linked list of all the chunks in each block. A block
+ * with three chunks might look something like this:
+ *
+ * 0 _________________________
+ * ^ ___________ / ______________ \ __________
+ * ||---||--|-----/-----------||--------/--------------||--\-----/----------||
+ * ||hdr|| prv | len | body || prv | len | body || prv | len | body ||
+ * ||---||--------------------||--/--------------------||-------------------||
+ * \______________________/
+ *
+ *
+ * When allocating, a free chunk is found (more on that later) and split into
+ * two chunks: the first of the requested size and the second containing any
+ * remaining free. The first is marked used and returned to the caller.
+ *
+ * When freeing, the chunk in question is marked as free. Its neighbouring
+ * chunks are then checked; if either of them are free, the consecutive free
+ * chunks are merged into a single larger free chunk. Induction can show that
+ * applying this operation consistently prevents us ever having consecutive
+ * free chunks.
+ *
+ * Free chunks (because they are not being used for anything else) each store an
+ * additional pair of pointers (see the wmem_block_free_t structure) that form
+ * the backbone of the data structures used to track free chunks.
+ *
+ * MASTER AND RECYCLER
+ *
+ * The extra pair of pointers in free chunks are used to build two doubly-linked
+ * lists: the master and the recycler. The recycler is circular, the master is
+ * a stack.
+ *
+ * The master stack is only populated by chunks from new OS-level blocks,
+ * so every chunk in this list is guaranteed to be able to serve any allocation
+ * request (the allocator will not serve requests larger than its block size).
+ * The chunk at the head of the master list shrinks as it serves requests. When
+ * it is too small to serve the current request, it is popped and inserted into
+ * the recycler. If the master list is empty, a new OS-level block is allocated,
+ * and its chunk is pushed onto the master stack.
+ *
+ * The recycler is populated by 'leftovers' from the master, as well as any
+ * chunks that were returned to the allocator via a call to free(). Although the
+ * recycler is circular, we will refer to the element referenced from the
+ * allocator as the 'head' of the list for convenience. The primary operation on
+ * the recycler is called cycling it. In this operation, the head is compared
+ * with its clockwise neighbour. If the neighbour is as large or larger, it
+ * becomes the head (the list rotates counter-clockwise). If the neighbour is
+ * smaller, then it is removed from its location and inserted as the counter-
+ * clockwise neighbour of the head (the list still rotates counter-clockwise,
+ * but the head element is held fixed while the rest of the list spins). This
+ * operation has the following properties:
+ * - fast constant time
+ * - once the largest chunk is at the head, it remains at the head
+ * - more iterations increases the probability that the largest chunk will be
+ * the head (for a list with n items, n iterations guarantees that the
+ * largest chunk will be the head).
+ *
+ * ALLOCATING
+ *
+ * When an allocation request is received, the allocator first attempts to
+ * satisfy it with the chunk at the head of the recycler. If that does not
+ * succeed, the request is satisfied by the master list instead. Regardless of
+ * which chunk satisfied the request, the recycler is always cycled.
+ */
+
+/* https://mail.gnome.org/archives/gtk-devel-list/2004-December/msg00091.html
+ * The 2*sizeof(size_t) alignment here is borrowed from GNU libc, so it should
+ * be good most everywhere. It is more conservative than is needed on some
+ * 64-bit platforms, but ia64 does require a 16-byte alignment. The SIMD
+ * extensions for x86 and ppc32 would want a larger alignment than this, but
+ * we don't need to do better than malloc.
+ */
+#define WMEM_ALIGN_AMOUNT (2 * sizeof (size_t))
+#define WMEM_ALIGN_SIZE(SIZE) ((~(WMEM_ALIGN_AMOUNT-1)) & \
+ ((SIZE) + (WMEM_ALIGN_AMOUNT-1)))
+
+/* When required, allocate more memory from the OS in chunks of this size.
+ * 8MB is a pretty arbitrary value - it's big enough that it should last a while
+ * and small enough that a mostly-unused one doesn't waste *too* much. It's
+ * also a nice power of two, of course. */
+#define WMEM_BLOCK_SIZE (8 * 1024 * 1024)
+
+/* The header for an entire OS-level 'block' of memory */
+typedef struct _wmem_block_hdr_t {
+ struct _wmem_block_hdr_t *prev, *next;
+} wmem_block_hdr_t;
+
+/* The header for a single 'chunk' of memory as returned from alloc/realloc.
+ * The 'jumbo' flag indicates an allocation larger than a normal-sized block
+ * would be capable of serving. If this is set, it is the only chunk in the
+ * block and the other chunk header fields are irrelevant.
+ */
+typedef struct _wmem_block_chunk_t {
+ uint32_t prev;
+
+ /* flags */
+ uint32_t last:1;
+ uint32_t used:1;
+ uint32_t jumbo:1;
+
+ uint32_t len:29;
+} wmem_block_chunk_t;
+
+/* Handy macros for navigating the chunks in a block as if they were a
+ * doubly-linked list. */
+#define WMEM_CHUNK_PREV(CHUNK) ((CHUNK)->prev \
+ ? ((wmem_block_chunk_t*)(((uint8_t*)(CHUNK)) - (CHUNK)->prev)) \
+ : NULL)
+
+#define WMEM_CHUNK_NEXT(CHUNK) ((CHUNK)->last \
+ ? NULL \
+ : ((wmem_block_chunk_t*)(((uint8_t*)(CHUNK)) + (CHUNK)->len)))
+
+#define WMEM_CHUNK_HEADER_SIZE WMEM_ALIGN_SIZE(sizeof(wmem_block_chunk_t))
+
+#define WMEM_BLOCK_MAX_ALLOC_SIZE (WMEM_BLOCK_SIZE - \
+ (WMEM_BLOCK_HEADER_SIZE + WMEM_CHUNK_HEADER_SIZE))
+
+/* other handy chunk macros */
+#define WMEM_CHUNK_TO_DATA(CHUNK) ((void*)((uint8_t*)(CHUNK) + WMEM_CHUNK_HEADER_SIZE))
+#define WMEM_DATA_TO_CHUNK(DATA) ((wmem_block_chunk_t*)((uint8_t*)(DATA) - WMEM_CHUNK_HEADER_SIZE))
+#define WMEM_CHUNK_DATA_LEN(CHUNK) ((CHUNK)->len - WMEM_CHUNK_HEADER_SIZE)
+
+/* some handy block macros */
+#define WMEM_BLOCK_HEADER_SIZE WMEM_ALIGN_SIZE(sizeof(wmem_block_hdr_t))
+#define WMEM_BLOCK_TO_CHUNK(BLOCK) ((wmem_block_chunk_t*)((uint8_t*)(BLOCK) + WMEM_BLOCK_HEADER_SIZE))
+#define WMEM_CHUNK_TO_BLOCK(CHUNK) ((wmem_block_hdr_t*)((uint8_t*)(CHUNK) - WMEM_BLOCK_HEADER_SIZE))
+
+/* This is what the 'data' section of a chunk contains if it is free. */
+typedef struct _wmem_block_free_t {
+ wmem_block_chunk_t *prev, *next;
+} wmem_block_free_t;
+
+/* Handy macro for accessing the free-header of a chunk */
+#define WMEM_GET_FREE(CHUNK) ((wmem_block_free_t*)WMEM_CHUNK_TO_DATA(CHUNK))
+
+typedef struct _wmem_block_allocator_t {
+ wmem_block_hdr_t *block_list;
+ wmem_block_chunk_t *master_head;
+ wmem_block_chunk_t *recycler_head;
+} wmem_block_allocator_t;
+
+/* DEBUG AND TEST */
+static int
+wmem_block_verify_block(wmem_block_hdr_t *block)
+{
+ int total_free_space = 0;
+ uint32_t total_len;
+ wmem_block_chunk_t *chunk;
+
+ chunk = WMEM_BLOCK_TO_CHUNK(block);
+ total_len = WMEM_BLOCK_HEADER_SIZE;
+
+ if (chunk->jumbo) {
+ /* We can tell nothing else about jumbo chunks except that they are
+ * always used. */
+ return 0;
+ }
+
+ g_assert_true(chunk->prev == 0);
+
+ do {
+ total_len += chunk->len;
+
+ g_assert_true(chunk->len >= WMEM_CHUNK_HEADER_SIZE);
+ g_assert_true(!chunk->jumbo);
+
+ if (WMEM_CHUNK_NEXT(chunk)) {
+ g_assert_true(chunk->len == WMEM_CHUNK_NEXT(chunk)->prev);
+ }
+
+ if (!chunk->used &&
+ WMEM_CHUNK_DATA_LEN(chunk) >= sizeof(wmem_block_free_t)) {
+
+ total_free_space += chunk->len;
+
+ if (!chunk->last) {
+ g_assert_true(WMEM_GET_FREE(chunk)->next);
+ g_assert_true(WMEM_GET_FREE(chunk)->prev);
+ }
+ }
+
+ chunk = WMEM_CHUNK_NEXT(chunk);
+ } while (chunk);
+
+ g_assert_true(total_len == WMEM_BLOCK_SIZE);
+
+ return total_free_space;
+}
+
+static int
+wmem_block_verify_master_list(wmem_block_allocator_t *allocator)
+{
+ wmem_block_chunk_t *cur;
+ wmem_block_free_t *cur_free;
+ int free_space = 0;
+
+ cur = allocator->master_head;
+ if (!cur) {
+ return 0;
+ }
+
+ g_assert_true(WMEM_GET_FREE(cur)->prev == NULL);
+
+ while (cur) {
+ free_space += cur->len;
+
+ cur_free = WMEM_GET_FREE(cur);
+
+ g_assert_true(! cur->used);
+
+ if (cur_free->next) {
+ g_assert_true(WMEM_GET_FREE(cur_free->next)->prev == cur);
+ }
+
+ if (cur != allocator->master_head) {
+ g_assert_true(cur->len == WMEM_BLOCK_SIZE);
+ }
+
+ cur = cur_free->next;
+ }
+
+ return free_space;
+}
+
+static int
+wmem_block_verify_recycler(wmem_block_allocator_t *allocator)
+{
+ wmem_block_chunk_t *cur;
+ wmem_block_free_t *cur_free;
+ int free_space = 0;
+
+ cur = allocator->recycler_head;
+ if (!cur) {
+ return 0;
+ }
+
+ do {
+ free_space += cur->len;
+
+ cur_free = WMEM_GET_FREE(cur);
+
+ g_assert_true(! cur->used);
+
+ g_assert_true(cur_free->prev);
+ g_assert_true(cur_free->next);
+
+ g_assert_true(WMEM_GET_FREE(cur_free->prev)->next == cur);
+ g_assert_true(WMEM_GET_FREE(cur_free->next)->prev == cur);
+
+ cur = cur_free->next;
+ } while (cur != allocator->recycler_head);
+
+ return free_space;
+}
+
+void
+wmem_block_verify(wmem_allocator_t *allocator)
+{
+ wmem_block_hdr_t *cur;
+ wmem_block_allocator_t *private_allocator;
+ int master_free, recycler_free, chunk_free = 0;
+
+ /* Normally it would be bad for an allocator helper function to depend
+ * on receiving the right type of allocator, but this is for testing only
+ * and is not part of any real API. */
+ g_assert_true(allocator->type == WMEM_ALLOCATOR_BLOCK);
+
+ private_allocator = (wmem_block_allocator_t*) allocator->private_data;
+
+ if (private_allocator->block_list == NULL) {
+ g_assert_true(! private_allocator->master_head);
+ g_assert_true(! private_allocator->recycler_head);
+ return;
+ }
+
+ master_free = wmem_block_verify_master_list(private_allocator);
+ recycler_free = wmem_block_verify_recycler(private_allocator);
+
+ cur = private_allocator->block_list;
+ g_assert_true(cur->prev == NULL);
+ while (cur) {
+ if (cur->next) {
+ g_assert_true(cur->next->prev == cur);
+ }
+ chunk_free += wmem_block_verify_block(cur);
+ cur = cur->next;
+ }
+
+ g_assert_true(chunk_free == master_free + recycler_free);
+}
+
+/* MASTER/RECYCLER HELPERS */
+
+/* Cycles the recycler. See the design notes at the top of this file for more
+ * details. */
+static void
+wmem_block_cycle_recycler(wmem_block_allocator_t *allocator)
+{
+ wmem_block_chunk_t *chunk;
+ wmem_block_free_t *free_chunk;
+
+ chunk = allocator->recycler_head;
+
+ if (chunk == NULL) {
+ return;
+ }
+
+ free_chunk = WMEM_GET_FREE(chunk);
+
+ if (free_chunk->next->len < chunk->len) {
+ /* Hold the current head fixed during rotation. */
+ WMEM_GET_FREE(free_chunk->next)->prev = free_chunk->prev;
+ WMEM_GET_FREE(free_chunk->prev)->next = free_chunk->next;
+
+ free_chunk->prev = free_chunk->next;
+ free_chunk->next = WMEM_GET_FREE(free_chunk->next)->next;
+
+ WMEM_GET_FREE(free_chunk->next)->prev = chunk;
+ WMEM_GET_FREE(free_chunk->prev)->next = chunk;
+ }
+ else {
+ /* Just rotate everything. */
+ allocator->recycler_head = free_chunk->next;
+ }
+}
+
+/* Adds a chunk from the recycler. */
+static void
+wmem_block_add_to_recycler(wmem_block_allocator_t *allocator,
+ wmem_block_chunk_t *chunk)
+{
+ wmem_block_free_t *free_chunk;
+
+ if (WMEM_CHUNK_DATA_LEN(chunk) < sizeof(wmem_block_free_t)) {
+ return;
+ }
+
+ free_chunk = WMEM_GET_FREE(chunk);
+
+ if (! allocator->recycler_head) {
+ /* First one */
+ free_chunk->next = chunk;
+ free_chunk->prev = chunk;
+ allocator->recycler_head = chunk;
+ }
+ else {
+ free_chunk->next = allocator->recycler_head;
+ free_chunk->prev = WMEM_GET_FREE(allocator->recycler_head)->prev;
+
+ WMEM_GET_FREE(free_chunk->next)->prev = chunk;
+ WMEM_GET_FREE(free_chunk->prev)->next = chunk;
+
+ if (chunk->len > allocator->recycler_head->len) {
+ allocator->recycler_head = chunk;
+ }
+ }
+}
+
+/* Removes a chunk from the recycler. */
+static void
+wmem_block_remove_from_recycler(wmem_block_allocator_t *allocator,
+ wmem_block_chunk_t *chunk)
+{
+ wmem_block_free_t *free_chunk;
+
+ free_chunk = WMEM_GET_FREE(chunk);
+
+ if (free_chunk->prev == chunk && free_chunk->next == chunk) {
+ /* Only one item in recycler, just empty it. */
+ allocator->recycler_head = NULL;
+ }
+ else {
+ /* Two or more items, usual doubly-linked-list removal. It's circular
+ * so we don't need to worry about null-checking anything, which is
+ * nice. */
+ WMEM_GET_FREE(free_chunk->prev)->next = free_chunk->next;
+ WMEM_GET_FREE(free_chunk->next)->prev = free_chunk->prev;
+ if (allocator->recycler_head == chunk) {
+ allocator->recycler_head = free_chunk->next;
+ }
+ }
+}
+
+/* Pushes a chunk onto the master stack. */
+static void
+wmem_block_push_master(wmem_block_allocator_t *allocator,
+ wmem_block_chunk_t *chunk)
+{
+ wmem_block_free_t *free_chunk;
+
+ free_chunk = WMEM_GET_FREE(chunk);
+ free_chunk->prev = NULL;
+ free_chunk->next = allocator->master_head;
+ if (free_chunk->next) {
+ WMEM_GET_FREE(free_chunk->next)->prev = chunk;
+ }
+ allocator->master_head = chunk;
+}
+
+/* Removes the top chunk from the master stack. */
+static void
+wmem_block_pop_master(wmem_block_allocator_t *allocator)
+{
+ wmem_block_chunk_t *chunk;
+ wmem_block_free_t *free_chunk;
+
+ chunk = allocator->master_head;
+
+ free_chunk = WMEM_GET_FREE(chunk);
+
+ allocator->master_head = free_chunk->next;
+ if (free_chunk->next) {
+ WMEM_GET_FREE(free_chunk->next)->prev = NULL;
+ }
+}
+
+/* CHUNK HELPERS */
+
+/* Takes a free chunk and checks the chunks to its immediate right and left in
+ * the block. If they are also free, the contigous free chunks are merged into
+ * a single free chunk. The resulting chunk ends up in either the master list or
+ * the recycler, depending on where the merged chunks were originally.
+ */
+static void
+wmem_block_merge_free(wmem_block_allocator_t *allocator,
+ wmem_block_chunk_t *chunk)
+{
+ wmem_block_chunk_t *tmp;
+ wmem_block_chunk_t *left_free = NULL;
+ wmem_block_chunk_t *right_free = NULL;
+
+ /* Check the chunk to our right. If it is free, merge it into our current
+ * chunk. If it is big enough to hold a free-header, save it for later (we
+ * need to know about the left chunk before we decide what goes where). */
+ tmp = WMEM_CHUNK_NEXT(chunk);
+ if (tmp && !tmp->used) {
+ if (WMEM_CHUNK_DATA_LEN(tmp) >= sizeof(wmem_block_free_t)) {
+ right_free = tmp;
+ }
+ chunk->len += tmp->len;
+ chunk->last = tmp->last;
+ }
+
+ /* Check the chunk to our left. If it is free, merge our current chunk into
+ * it (thus chunk = tmp). As before, save it if it has enough space to
+ * hold a free-header. */
+ tmp = WMEM_CHUNK_PREV(chunk);
+ if (tmp && !tmp->used) {
+ if (WMEM_CHUNK_DATA_LEN(tmp) >= sizeof(wmem_block_free_t)) {
+ left_free = tmp;
+ }
+ tmp->len += chunk->len;
+ tmp->last = chunk->last;
+ chunk = tmp;
+ }
+
+ /* The length of our chunk may have changed. If we have a chunk following,
+ * update its 'prev' count. */
+ if (!chunk->last) {
+ WMEM_CHUNK_NEXT(chunk)->prev = chunk->len;
+ }
+
+ /* Now that the chunk headers are merged and consistent, we need to figure
+ * out what goes where in which free list. */
+ if (right_free && right_free == allocator->master_head) {
+ /* If we merged right, and that chunk was the head of the master list,
+ * then we leave the resulting chunk at the head of the master list. */
+ wmem_block_free_t *moved;
+ if (left_free) {
+ wmem_block_remove_from_recycler(allocator, left_free);
+ }
+ moved = WMEM_GET_FREE(chunk);
+ moved->prev = NULL;
+ moved->next = WMEM_GET_FREE(right_free)->next;
+ allocator->master_head = chunk;
+ if (moved->next) {
+ WMEM_GET_FREE(moved->next)->prev = chunk;
+ }
+ }
+ else {
+ /* Otherwise, we remove the right-merged chunk (if there was one) from
+ * the recycler. Then, if we merged left we have nothing to do, since
+ * that recycler entry is still valid. If not, we add the chunk. */
+ if (right_free) {
+ wmem_block_remove_from_recycler(allocator, right_free);
+ }
+ if (!left_free) {
+ wmem_block_add_to_recycler(allocator, chunk);
+ }
+ }
+}
+
+/* Takes an unused chunk and a size, and splits it into two chunks if possible.
+ * The first chunk (at the same address as the input chunk) is guaranteed to
+ * hold at least `size` bytes of data, and to not be in either the master or
+ * recycler lists.
+ *
+ * The second chunk gets whatever data is left over. It is marked unused and
+ * replaces the input chunk in whichever list it originally inhabited. */
+static void
+wmem_block_split_free_chunk(wmem_block_allocator_t *allocator,
+ wmem_block_chunk_t *chunk,
+ const size_t size)
+{
+ wmem_block_chunk_t *extra;
+ wmem_block_free_t *old_blk, *new_blk;
+ size_t aligned_size, available;
+ bool last;
+
+ aligned_size = WMEM_ALIGN_SIZE(size) + WMEM_CHUNK_HEADER_SIZE;
+
+ if (WMEM_CHUNK_DATA_LEN(chunk) < aligned_size + sizeof(wmem_block_free_t)) {
+ /* If the available space is not enought to store all of
+ * (hdr + requested size + alignment padding + hdr + free-header) then
+ * just remove the current chunk from the free list and return, since we
+ * can't usefully split it. */
+ if (chunk == allocator->master_head) {
+ wmem_block_pop_master(allocator);
+ }
+ else if (WMEM_CHUNK_DATA_LEN(chunk) >= sizeof(wmem_block_free_t)) {
+ wmem_block_remove_from_recycler(allocator, chunk);
+ }
+ return;
+ }
+
+ /* preserve a few values from chunk that we'll need to manipulate */
+ last = chunk->last;
+ available = chunk->len - aligned_size;
+
+ /* set new values for chunk */
+ chunk->len = (uint32_t) aligned_size;
+ chunk->last = false;
+
+ /* with chunk's values set, we can use the standard macro to calculate
+ * the location and size of the new free chunk */
+ extra = WMEM_CHUNK_NEXT(chunk);
+
+ /* Now we move the free chunk's address without changing its location
+ * in whichever list it is in.
+ *
+ * Note that the new chunk header 'extra' may overlap the old free header,
+ * so we have to copy the free header before we write anything to extra.
+ */
+ old_blk = WMEM_GET_FREE(chunk);
+ new_blk = WMEM_GET_FREE(extra);
+
+ if (allocator->master_head == chunk) {
+ new_blk->prev = old_blk->prev;
+ new_blk->next = old_blk->next;
+
+ if (old_blk->next) {
+ WMEM_GET_FREE(old_blk->next)->prev = extra;
+ }
+
+ allocator->master_head = extra;
+ }
+ else {
+ if (old_blk->prev == chunk) {
+ new_blk->prev = extra;
+ new_blk->next = extra;
+ }
+ else {
+ new_blk->prev = old_blk->prev;
+ new_blk->next = old_blk->next;
+
+ WMEM_GET_FREE(old_blk->prev)->next = extra;
+ WMEM_GET_FREE(old_blk->next)->prev = extra;
+ }
+
+ if (allocator->recycler_head == chunk) {
+ allocator->recycler_head = extra;
+ }
+ }
+
+ /* Now that we've copied over the free-list stuff (which may have overlapped
+ * with our new chunk header) we can safely write our new chunk header. */
+ extra->len = (uint32_t) available;
+ extra->last = last;
+ extra->prev = chunk->len;
+ extra->used = false;
+ extra->jumbo = false;
+
+ /* Correctly update the following chunk's back-pointer */
+ if (!last) {
+ WMEM_CHUNK_NEXT(extra)->prev = extra->len;
+ }
+}
+
+/* Takes a used chunk and a size, and splits it into two chunks if possible.
+ * The first chunk can hold at least `size` bytes of data, while the second gets
+ * whatever's left over. The second is marked as unused and is added to the
+ * recycler. */
+static void
+wmem_block_split_used_chunk(wmem_block_allocator_t *allocator,
+ wmem_block_chunk_t *chunk,
+ const size_t size)
+{
+ wmem_block_chunk_t *extra;
+ size_t aligned_size, available;
+ bool last;
+
+ aligned_size = WMEM_ALIGN_SIZE(size) + WMEM_CHUNK_HEADER_SIZE;
+
+ if (aligned_size > WMEM_CHUNK_DATA_LEN(chunk)) {
+ /* in this case we don't have enough space to really split it, so
+ * it's basically a no-op */
+ return;
+ }
+ /* otherwise, we have room to split it, though the remaining free chunk
+ * may still not be usefully large */
+
+ /* preserve a few values from chunk that we'll need to manipulate */
+ last = chunk->last;
+ available = chunk->len - aligned_size;
+
+ /* set new values for chunk */
+ chunk->len = (uint32_t) aligned_size;
+ chunk->last = false;
+
+ /* with chunk's values set, we can use the standard macro to calculate
+ * the location and size of the new free chunk */
+ extra = WMEM_CHUNK_NEXT(chunk);
+
+ /* set the new values for the chunk */
+ extra->len = (uint32_t) available;
+ extra->last = last;
+ extra->prev = chunk->len;
+ extra->used = false;
+ extra->jumbo = false;
+
+ /* Correctly update the following chunk's back-pointer */
+ if (!last) {
+ WMEM_CHUNK_NEXT(extra)->prev = extra->len;
+ }
+
+ /* Merge it to its right if possible (it can't be merged left, obviously).
+ * This also adds it to the recycler. */
+ wmem_block_merge_free(allocator, extra);
+}
+
+/* BLOCK HELPERS */
+
+/* Add a block to the allocator's embedded doubly-linked list of OS-level blocks
+ * that it owns. */
+static void
+wmem_block_add_to_block_list(wmem_block_allocator_t *allocator,
+ wmem_block_hdr_t *block)
+{
+ block->prev = NULL;
+ block->next = allocator->block_list;
+ if (block->next) {
+ block->next->prev = block;
+ }
+ allocator->block_list = block;
+}
+
+/* Remove a block from the allocator's embedded doubly-linked list of OS-level
+ * blocks that it owns. */
+static void
+wmem_block_remove_from_block_list(wmem_block_allocator_t *allocator,
+ wmem_block_hdr_t *block)
+{
+ if (block->prev) {
+ block->prev->next = block->next;
+ }
+ else {
+ allocator->block_list = block->next;
+ }
+
+ if (block->next) {
+ block->next->prev = block->prev;
+ }
+}
+
+/* Initializes a single unused chunk at the beginning of the block, and
+ * adds that chunk to the free list. */
+static void
+wmem_block_init_block(wmem_block_allocator_t *allocator,
+ wmem_block_hdr_t *block)
+{
+ wmem_block_chunk_t *chunk;
+
+ /* a new block contains one chunk, right at the beginning */
+ chunk = WMEM_BLOCK_TO_CHUNK(block);
+
+ chunk->used = false;
+ chunk->jumbo = false;
+ chunk->last = true;
+ chunk->prev = 0;
+ chunk->len = WMEM_BLOCK_SIZE - WMEM_BLOCK_HEADER_SIZE;
+
+ /* now push that chunk onto the master list */
+ wmem_block_push_master(allocator, chunk);
+}
+
+/* Creates a new block, and initializes it. */
+static void
+wmem_block_new_block(wmem_block_allocator_t *allocator)
+{
+ wmem_block_hdr_t *block;
+
+ /* allocate the new block and add it to the block list */
+ block = (wmem_block_hdr_t *)wmem_alloc(NULL, WMEM_BLOCK_SIZE);
+ wmem_block_add_to_block_list(allocator, block);
+
+ /* initialize it */
+ wmem_block_init_block(allocator, block);
+}
+
+/* JUMBO ALLOCATIONS */
+
+/* Allocates special 'jumbo' blocks for sizes that won't fit normally. */
+static void *
+wmem_block_alloc_jumbo(wmem_block_allocator_t *allocator, const size_t size)
+{
+ wmem_block_hdr_t *block;
+ wmem_block_chunk_t *chunk;
+
+ /* allocate a new block of exactly the right size */
+ block = (wmem_block_hdr_t *) wmem_alloc(NULL, size
+ + WMEM_BLOCK_HEADER_SIZE
+ + WMEM_CHUNK_HEADER_SIZE);
+
+ /* add it to the block list */
+ wmem_block_add_to_block_list(allocator, block);
+
+ /* the new block contains a single jumbo chunk */
+ chunk = WMEM_BLOCK_TO_CHUNK(block);
+ chunk->last = true;
+ chunk->used = true;
+ chunk->jumbo = true;
+ chunk->len = 0;
+ chunk->prev = 0;
+
+ /* and return the data pointer */
+ return WMEM_CHUNK_TO_DATA(chunk);
+}
+
+/* Frees special 'jumbo' blocks of sizes that won't fit normally. */
+static void
+wmem_block_free_jumbo(wmem_block_allocator_t *allocator,
+ wmem_block_chunk_t *chunk)
+{
+ wmem_block_hdr_t *block;
+
+ block = WMEM_CHUNK_TO_BLOCK(chunk);
+
+ wmem_block_remove_from_block_list(allocator, block);
+
+ wmem_free(NULL, block);
+}
+
+/* Reallocs special 'jumbo' blocks of sizes that won't fit normally. */
+static void *
+wmem_block_realloc_jumbo(wmem_block_allocator_t *allocator,
+ wmem_block_chunk_t *chunk,
+ const size_t size)
+{
+ wmem_block_hdr_t *block;
+
+ block = WMEM_CHUNK_TO_BLOCK(chunk);
+
+ block = (wmem_block_hdr_t *) wmem_realloc(NULL, block, size
+ + WMEM_BLOCK_HEADER_SIZE
+ + WMEM_CHUNK_HEADER_SIZE);
+
+ if (block->next) {
+ block->next->prev = block;
+ }
+
+ if (block->prev) {
+ block->prev->next = block;
+ }
+ else {
+ allocator->block_list = block;
+ }
+
+ return WMEM_CHUNK_TO_DATA(WMEM_BLOCK_TO_CHUNK(block));
+}
+
+/* API */
+
+static void *
+wmem_block_alloc(void *private_data, const size_t size)
+{
+ wmem_block_allocator_t *allocator = (wmem_block_allocator_t*) private_data;
+ wmem_block_chunk_t *chunk;
+
+ if (size > WMEM_BLOCK_MAX_ALLOC_SIZE) {
+ return wmem_block_alloc_jumbo(allocator, size);
+ }
+
+ if (allocator->recycler_head &&
+ WMEM_CHUNK_DATA_LEN(allocator->recycler_head) >= size) {
+
+ /* If we can serve it from the recycler, do so. */
+ chunk = allocator->recycler_head;
+ }
+ else {
+ if (allocator->master_head &&
+ WMEM_CHUNK_DATA_LEN(allocator->master_head) < size) {
+
+ /* Recycle the head of the master list if necessary. */
+ chunk = allocator->master_head;
+ wmem_block_pop_master(allocator);
+ wmem_block_add_to_recycler(allocator, chunk);
+ }
+
+ if (!allocator->master_head) {
+ /* Allocate a new block if necessary. */
+ wmem_block_new_block(allocator);
+ }
+
+ chunk = allocator->master_head;
+ }
+
+ /* Split our chunk into two to preserve any trailing free space */
+ wmem_block_split_free_chunk(allocator, chunk, size);
+
+ /* Now cycle the recycler */
+ wmem_block_cycle_recycler(allocator);
+
+ /* mark it as used */
+ chunk->used = true;
+
+ /* and return the user's pointer */
+ return WMEM_CHUNK_TO_DATA(chunk);
+}
+
+static void
+wmem_block_free(void *private_data, void *ptr)
+{
+ wmem_block_allocator_t *allocator = (wmem_block_allocator_t*) private_data;
+ wmem_block_chunk_t *chunk;
+
+ chunk = WMEM_DATA_TO_CHUNK(ptr);
+
+ if (chunk->jumbo) {
+ wmem_block_free_jumbo(allocator, chunk);
+ return;
+ }
+
+ /* mark it as unused */
+ chunk->used = false;
+
+ /* merge it with any other free chunks adjacent to it, so that contiguous
+ * free space doesn't get fragmented */
+ wmem_block_merge_free(allocator, chunk);
+
+ /* Now cycle the recycler */
+ wmem_block_cycle_recycler(allocator);
+}
+
+static void *
+wmem_block_realloc(void *private_data, void *ptr, const size_t size)
+{
+ wmem_block_allocator_t *allocator = (wmem_block_allocator_t*) private_data;
+ wmem_block_chunk_t *chunk;
+
+ chunk = WMEM_DATA_TO_CHUNK(ptr);
+
+ if (chunk->jumbo) {
+ return wmem_block_realloc_jumbo(allocator, chunk, size);
+ }
+
+ if (size > WMEM_CHUNK_DATA_LEN(chunk)) {
+ /* grow */
+ wmem_block_chunk_t *tmp;
+
+ tmp = WMEM_CHUNK_NEXT(chunk);
+
+ if (tmp && (!tmp->used) &&
+ (size < WMEM_CHUNK_DATA_LEN(chunk) + tmp->len)) {
+ /* the next chunk is free and has enough extra, so just grab
+ * from that */
+ size_t split_size;
+
+ /* we ask for the next chunk to be split, but we don't end up
+ * using the split chunk header (it just gets merged into this one),
+ * so we want the split to be of (size - curdatalen - header_size).
+ * However, this can underflow by header_size, so we do a quick
+ * check here and floor the value to 0. */
+ split_size = size - WMEM_CHUNK_DATA_LEN(chunk);
+
+ if (split_size < WMEM_CHUNK_HEADER_SIZE) {
+ split_size = 0;
+ }
+ else {
+ split_size -= WMEM_CHUNK_HEADER_SIZE;
+ }
+
+ wmem_block_split_free_chunk(allocator, tmp, split_size);
+
+ /* Now do a 'quickie' merge between the current block and the left-
+ * hand side of the split. Simply calling wmem_block_merge_free
+ * might confuse things, since we may temporarily have two blocks
+ * to our right that are both free (and it isn't guaranteed to
+ * handle that case). Update our 'next' count and last flag, and
+ * our (new) successor's 'prev' count */
+ chunk->len += tmp->len;
+ chunk->last = tmp->last;
+ tmp = WMEM_CHUNK_NEXT(chunk);
+ if (tmp) {
+ tmp->prev = chunk->len;
+ }
+
+ /* Now cycle the recycler */
+ wmem_block_cycle_recycler(allocator);
+
+ /* And return the same old pointer */
+ return ptr;
+ }
+ else {
+ /* no room to grow, need to alloc, copy, free */
+ void *newptr;
+
+ newptr = wmem_block_alloc(private_data, size);
+ memcpy(newptr, ptr, WMEM_CHUNK_DATA_LEN(chunk));
+ wmem_block_free(private_data, ptr);
+
+ /* No need to cycle the recycler, alloc and free both did that
+ * already */
+
+ return newptr;
+ }
+ }
+ else if (size < WMEM_CHUNK_DATA_LEN(chunk)) {
+ /* shrink */
+ wmem_block_split_used_chunk(allocator, chunk, size);
+
+ /* Now cycle the recycler */
+ wmem_block_cycle_recycler(allocator);
+
+ return ptr;
+ }
+
+ /* no-op */
+ return ptr;
+}
+
+static void
+wmem_block_free_all(void *private_data)
+{
+ wmem_block_allocator_t *allocator = (wmem_block_allocator_t*) private_data;
+ wmem_block_hdr_t *cur;
+ wmem_block_chunk_t *chunk;
+
+ /* the existing free lists are entirely irrelevant */
+ allocator->master_head = NULL;
+ allocator->recycler_head = NULL;
+
+ /* iterate through the blocks, reinitializing each one */
+ cur = allocator->block_list;
+
+ while (cur) {
+ chunk = WMEM_BLOCK_TO_CHUNK(cur);
+ if (chunk->jumbo) {
+ wmem_block_remove_from_block_list(allocator, cur);
+ cur = cur->next;
+ wmem_free(NULL, WMEM_CHUNK_TO_BLOCK(chunk));
+ }
+ else {
+ wmem_block_init_block(allocator, cur);
+ cur = cur->next;
+ }
+ }
+}
+
+static void
+wmem_block_gc(void *private_data)
+{
+ wmem_block_allocator_t *allocator = (wmem_block_allocator_t*) private_data;
+ wmem_block_hdr_t *cur, *next;
+ wmem_block_chunk_t *chunk;
+ wmem_block_free_t *free_chunk;
+
+ /* Walk through the blocks, adding used blocks to the new list and
+ * completely destroying unused blocks. */
+ cur = allocator->block_list;
+ allocator->block_list = NULL;
+
+ while (cur) {
+ chunk = WMEM_BLOCK_TO_CHUNK(cur);
+ next = cur->next;
+
+ if (!chunk->jumbo && !chunk->used && chunk->last) {
+ /* If the first chunk is also the last, and is unused, then
+ * the block as a whole is entirely unused, so return it to
+ * the OS and remove it from whatever lists it is in. */
+ free_chunk = WMEM_GET_FREE(chunk);
+ if (free_chunk->next) {
+ WMEM_GET_FREE(free_chunk->next)->prev = free_chunk->prev;
+ }
+ if (free_chunk->prev) {
+ WMEM_GET_FREE(free_chunk->prev)->next = free_chunk->next;
+ }
+ if (allocator->recycler_head == chunk) {
+ if (free_chunk->next == chunk) {
+ allocator->recycler_head = NULL;
+ }
+ else {
+ allocator->recycler_head = free_chunk->next;
+ }
+ }
+ else if (allocator->master_head == chunk) {
+ allocator->master_head = free_chunk->next;
+ }
+ wmem_free(NULL, cur);
+ }
+ else {
+ /* part of this block is used, so add it to the new block list */
+ wmem_block_add_to_block_list(allocator, cur);
+ }
+
+ cur = next;
+ }
+}
+
+static void
+wmem_block_allocator_cleanup(void *private_data)
+{
+ /* wmem guarantees that free_all() is called directly before this, so
+ * calling gc will return all our blocks to the OS automatically */
+ wmem_block_gc(private_data);
+
+ /* then just free the allocator structs */
+ wmem_free(NULL, private_data);
+}
+
+void
+wmem_block_allocator_init(wmem_allocator_t *allocator)
+{
+ wmem_block_allocator_t *block_allocator;
+
+ block_allocator = wmem_new(NULL, wmem_block_allocator_t);
+
+ allocator->walloc = &wmem_block_alloc;
+ allocator->wrealloc = &wmem_block_realloc;
+ allocator->wfree = &wmem_block_free;
+
+ allocator->free_all = &wmem_block_free_all;
+ allocator->gc = &wmem_block_gc;
+ allocator->cleanup = &wmem_block_allocator_cleanup;
+
+ allocator->private_data = (void*) block_allocator;
+
+ block_allocator->block_list = NULL;
+ block_allocator->master_head = NULL;
+ block_allocator->recycler_head = NULL;
+}
+
+/*
+ * 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/wsutil/wmem/wmem_allocator_block.h b/wsutil/wmem/wmem_allocator_block.h
new file mode 100644
index 00000000..e7018dfb
--- /dev/null
+++ b/wsutil/wmem/wmem_allocator_block.h
@@ -0,0 +1,46 @@
+/** @file
+ *
+ * Definitions for the Wireshark Memory Manager Large-Block Allocator
+ * Copyright 2012, Evan Huus <eapache@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WMEM_ALLOCATOR_BLOCK_H__
+#define __WMEM_ALLOCATOR_BLOCK_H__
+
+#include "wmem_core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void
+wmem_block_allocator_init(wmem_allocator_t *allocator);
+
+/* Exposed only for testing purposes */
+void
+wmem_block_verify(wmem_allocator_t *allocator);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WMEM_ALLOCATOR_BLOCK_H__ */
+
+/*
+ * 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/wsutil/wmem/wmem_allocator_block_fast.c b/wsutil/wmem/wmem_allocator_block_fast.c
new file mode 100644
index 00000000..538f5187
--- /dev/null
+++ b/wsutil/wmem/wmem_allocator_block_fast.c
@@ -0,0 +1,261 @@
+/* wmem_allocator_block.c
+ * Wireshark Memory Manager Fast Large-Block Allocator
+ *
+ * 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 <stdio.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include "wmem_core.h"
+#include "wmem_allocator.h"
+#include "wmem_allocator_block_fast.h"
+
+/* https://mail.gnome.org/archives/gtk-devel-list/2004-December/msg00091.html
+ * The 2*sizeof(size_t) alignment here is borrowed from GNU libc, so it should
+ * be good most everywhere. It is more conservative than is needed on some
+ * 64-bit platforms, but ia64 does require a 16-byte alignment. The SIMD
+ * extensions for x86 and ppc32 would want a larger alignment than this, but
+ * we don't need to do better than malloc.
+ */
+#define WMEM_ALIGN_AMOUNT (2 * sizeof (size_t))
+#define WMEM_ALIGN_SIZE(SIZE) ((~(WMEM_ALIGN_AMOUNT-1)) & \
+ ((SIZE) + (WMEM_ALIGN_AMOUNT-1)))
+
+#define WMEM_CHUNK_TO_DATA(CHUNK) ((void*)((uint8_t*)(CHUNK) + WMEM_CHUNK_HEADER_SIZE))
+#define WMEM_DATA_TO_CHUNK(DATA) ((wmem_block_fast_chunk_t*)((uint8_t*)(DATA) - WMEM_CHUNK_HEADER_SIZE))
+
+#define WMEM_BLOCK_MAX_ALLOC_SIZE (WMEM_BLOCK_SIZE - (WMEM_BLOCK_HEADER_SIZE + WMEM_CHUNK_HEADER_SIZE))
+
+/* When required, allocate more memory from the OS in chunks of this size.
+ * 2MB is a pretty arbitrary value - it's big enough that it should last a while
+ * and small enough that a mostly-unused one doesn't waste *too* much. It's
+ * also a nice power of two, of course. */
+#define WMEM_BLOCK_SIZE (2 * 1024 * 1024)
+
+/* The header for an entire OS-level 'block' of memory */
+typedef struct _wmem_block_fast_hdr {
+ struct _wmem_block_fast_hdr *next;
+
+ int32_t pos;
+} wmem_block_fast_hdr_t;
+#define WMEM_BLOCK_HEADER_SIZE WMEM_ALIGN_SIZE(sizeof(wmem_block_fast_hdr_t))
+
+typedef struct {
+ uint32_t len;
+} wmem_block_fast_chunk_t;
+#define WMEM_CHUNK_HEADER_SIZE WMEM_ALIGN_SIZE(sizeof(wmem_block_fast_chunk_t))
+
+#define JUMBO_MAGIC 0xFFFFFFFF
+typedef struct _wmem_block_fast_jumbo {
+ struct _wmem_block_fast_jumbo *prev, *next;
+} wmem_block_fast_jumbo_t;
+#define WMEM_JUMBO_HEADER_SIZE WMEM_ALIGN_SIZE(sizeof(wmem_block_fast_jumbo_t))
+
+typedef struct {
+ wmem_block_fast_hdr_t *block_list;
+ wmem_block_fast_jumbo_t *jumbo_list;
+} wmem_block_fast_allocator_t;
+
+/* Creates a new block, and initializes it. */
+static inline void
+wmem_block_fast_new_block(wmem_block_fast_allocator_t *allocator)
+{
+ wmem_block_fast_hdr_t *block;
+
+ /* allocate/initialize the new block and add it to the block list */
+ block = (wmem_block_fast_hdr_t *)wmem_alloc(NULL, WMEM_BLOCK_SIZE);
+
+ block->pos = WMEM_BLOCK_HEADER_SIZE;
+ block->next = allocator->block_list;
+
+ allocator->block_list = block;
+}
+
+/* API */
+
+static void *
+wmem_block_fast_alloc(void *private_data, const size_t size)
+{
+ wmem_block_fast_allocator_t *allocator = (wmem_block_fast_allocator_t*) private_data;
+ wmem_block_fast_chunk_t *chunk;
+ int32_t real_size;
+
+ if (size > WMEM_BLOCK_MAX_ALLOC_SIZE) {
+ wmem_block_fast_jumbo_t *block;
+
+ /* allocate/initialize a new block of the necessary size */
+ block = (wmem_block_fast_jumbo_t *)wmem_alloc(NULL,
+ size + WMEM_JUMBO_HEADER_SIZE + WMEM_CHUNK_HEADER_SIZE);
+
+ block->next = allocator->jumbo_list;
+ if (block->next) {
+ block->next->prev = block;
+ }
+ block->prev = NULL;
+ allocator->jumbo_list = block;
+
+ chunk = ((wmem_block_fast_chunk_t*)((uint8_t*)(block) + WMEM_JUMBO_HEADER_SIZE));
+ chunk->len = JUMBO_MAGIC;
+
+ return WMEM_CHUNK_TO_DATA(chunk);
+ }
+
+ real_size = (int32_t)(WMEM_ALIGN_SIZE(size) + WMEM_CHUNK_HEADER_SIZE);
+
+ /* Allocate a new block if necessary. */
+ if (!allocator->block_list ||
+ (WMEM_BLOCK_SIZE - allocator->block_list->pos) < real_size) {
+ wmem_block_fast_new_block(allocator);
+ }
+
+ chunk = (wmem_block_fast_chunk_t *) ((uint8_t *) allocator->block_list + allocator->block_list->pos);
+ /* safe to cast, size smaller than WMEM_BLOCK_MAX_ALLOC_SIZE */
+ chunk->len = (uint32_t) size;
+
+ allocator->block_list->pos += real_size;
+
+ /* and return the user's pointer */
+ return WMEM_CHUNK_TO_DATA(chunk);
+}
+
+static void
+wmem_block_fast_free(void *private_data _U_, void *ptr _U_)
+{
+ /* free is NOP */
+}
+
+static void *
+wmem_block_fast_realloc(void *private_data, void *ptr, const size_t size)
+{
+ wmem_block_fast_chunk_t *chunk;
+
+ chunk = WMEM_DATA_TO_CHUNK(ptr);
+
+ if (chunk->len == JUMBO_MAGIC) {
+ wmem_block_fast_jumbo_t *block;
+
+ block = ((wmem_block_fast_jumbo_t*)((uint8_t*)(chunk) - WMEM_JUMBO_HEADER_SIZE));
+ block = (wmem_block_fast_jumbo_t*)wmem_realloc(NULL, block,
+ size + WMEM_JUMBO_HEADER_SIZE + WMEM_CHUNK_HEADER_SIZE);
+ if (block->prev) {
+ block->prev->next = block;
+ }
+ else {
+ wmem_block_fast_allocator_t *allocator = (wmem_block_fast_allocator_t*) private_data;
+ allocator->jumbo_list = block;
+ }
+ if (block->next) {
+ block->next->prev = block;
+ }
+ return ((void*)((uint8_t*)(block) + WMEM_JUMBO_HEADER_SIZE + WMEM_CHUNK_HEADER_SIZE));
+ }
+ else if (chunk->len < size) {
+ /* grow */
+ void *newptr;
+
+ /* need to alloc and copy; free is no-op, so don't call it */
+ newptr = wmem_block_fast_alloc(private_data, size);
+ memcpy(newptr, ptr, chunk->len);
+
+ return newptr;
+ }
+
+ /* shrink or same space - great we can do nothing */
+ return ptr;
+}
+
+static void
+wmem_block_fast_free_all(void *private_data)
+{
+ wmem_block_fast_allocator_t *allocator = (wmem_block_fast_allocator_t*) private_data;
+ wmem_block_fast_hdr_t *cur, *nxt;
+ wmem_block_fast_jumbo_t *cur_jum, *nxt_jum;
+
+ /* iterate through the blocks, freeing all but the first and reinitializing
+ * that one */
+ cur = allocator->block_list;
+
+ if (cur) {
+ cur->pos = WMEM_BLOCK_HEADER_SIZE;
+ nxt = cur->next;
+ cur->next = NULL;
+ cur = nxt;
+ }
+
+ while (cur) {
+ nxt = cur->next;
+ wmem_free(NULL, cur);
+ cur = nxt;
+ }
+
+ /* now do the jumbo blocks, freeing all of them */
+ cur_jum = allocator->jumbo_list;
+ while (cur_jum) {
+ nxt_jum = cur_jum->next;
+ wmem_free(NULL, cur_jum);
+ cur_jum = nxt_jum;
+ }
+ allocator->jumbo_list = NULL;
+}
+
+static void
+wmem_block_fast_gc(void *private_data _U_)
+{
+ /* No-op */
+}
+
+static void
+wmem_block_fast_allocator_cleanup(void *private_data)
+{
+ wmem_block_fast_allocator_t *allocator = (wmem_block_fast_allocator_t*) private_data;
+
+ /* wmem guarantees that free_all() is called directly before this, so
+ * simply free the first block */
+ wmem_free(NULL, allocator->block_list);
+
+ /* then just free the allocator structs */
+ wmem_free(NULL, private_data);
+}
+
+void
+wmem_block_fast_allocator_init(wmem_allocator_t *allocator)
+{
+ wmem_block_fast_allocator_t *block_allocator;
+
+ block_allocator = wmem_new(NULL, wmem_block_fast_allocator_t);
+
+ allocator->walloc = &wmem_block_fast_alloc;
+ allocator->wrealloc = &wmem_block_fast_realloc;
+ allocator->wfree = &wmem_block_fast_free;
+
+ allocator->free_all = &wmem_block_fast_free_all;
+ allocator->gc = &wmem_block_fast_gc;
+ allocator->cleanup = &wmem_block_fast_allocator_cleanup;
+
+ allocator->private_data = (void*) block_allocator;
+
+ block_allocator->block_list = NULL;
+ block_allocator->jumbo_list = NULL;
+}
+
+/*
+ * 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/wsutil/wmem/wmem_allocator_block_fast.h b/wsutil/wmem/wmem_allocator_block_fast.h
new file mode 100644
index 00000000..ff5f90cd
--- /dev/null
+++ b/wsutil/wmem/wmem_allocator_block_fast.h
@@ -0,0 +1,41 @@
+/** @file
+ *
+ * Definitions for the Wireshark Memory Manager Fast Large-Block Allocator
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WMEM_ALLOCATOR_BLOCK_FAST_H__
+#define __WMEM_ALLOCATOR_BLOCK_FAST_H__
+
+#include "wmem_core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void
+wmem_block_fast_allocator_init(wmem_allocator_t *allocator);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WMEM_ALLOCATOR_BLOCK_FAST_H__ */
+
+/*
+ * 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/wsutil/wmem/wmem_allocator_simple.c b/wsutil/wmem/wmem_allocator_simple.c
new file mode 100644
index 00000000..0c7d9b0b
--- /dev/null
+++ b/wsutil/wmem/wmem_allocator_simple.c
@@ -0,0 +1,152 @@
+/* wmem_allocator_simple.c
+ * Wireshark Memory Manager Simple Allocator
+ * Copyright 2012, Evan Huus <eapache@gmail.com>
+ *
+ * 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 <string.h>
+
+#include <glib.h>
+
+#include "wmem_core.h"
+#include "wmem_allocator.h"
+#include "wmem_allocator_simple.h"
+
+#define DEFAULT_ALLOCS 8192
+
+typedef struct _wmem_simple_allocator_t {
+ int size;
+ int count;
+ void **ptrs;
+} wmem_simple_allocator_t;
+
+static void *
+wmem_simple_alloc(void *private_data, const size_t size)
+{
+ wmem_simple_allocator_t *allocator;
+
+ allocator = (wmem_simple_allocator_t*) private_data;
+
+ if (G_UNLIKELY(allocator->count == allocator->size)) {
+ allocator->size *= 2;
+ allocator->ptrs = (void**)wmem_realloc(NULL, allocator->ptrs,
+ sizeof(void*) * allocator->size);
+ }
+
+ return allocator->ptrs[allocator->count++] = wmem_alloc(NULL, size);
+}
+
+static void
+wmem_simple_free(void *private_data, void *ptr)
+{
+ int i;
+ wmem_simple_allocator_t *allocator;
+
+ allocator = (wmem_simple_allocator_t*) private_data;
+
+ wmem_free(NULL, ptr);
+ allocator->count--;
+
+ for (i=allocator->count; i>=0; i--) {
+ if (ptr == allocator->ptrs[i]) {
+ if (i < allocator->count) {
+ allocator->ptrs[i] = allocator->ptrs[allocator->count];
+ }
+ return;
+ }
+ }
+
+ g_assert_not_reached();
+}
+
+static void *
+wmem_simple_realloc(void *private_data, void *ptr, const size_t size)
+{
+ int i;
+ wmem_simple_allocator_t *allocator;
+
+ allocator = (wmem_simple_allocator_t*) private_data;
+
+ for (i=allocator->count-1; i>=0; i--) {
+ if (ptr == allocator->ptrs[i]) {
+ return allocator->ptrs[i] = wmem_realloc(NULL, allocator->ptrs[i], size);
+ }
+ }
+
+ g_assert_not_reached();
+ /* not reached */
+ return NULL;
+}
+
+static void
+wmem_simple_free_all(void *private_data)
+{
+ wmem_simple_allocator_t *allocator;
+ int i;
+
+ allocator = (wmem_simple_allocator_t*) private_data;
+
+ for (i = 0; i<allocator->count; i++) {
+ wmem_free(NULL, allocator->ptrs[i]);
+ }
+ allocator->count = 0;
+}
+
+static void
+wmem_simple_gc(void *private_data _U_)
+{
+ /* In this simple allocator, there is nothing to garbage-collect */
+}
+
+static void
+wmem_simple_allocator_cleanup(void *private_data)
+{
+ wmem_simple_allocator_t *allocator;
+
+ allocator = (wmem_simple_allocator_t*) private_data;
+
+ wmem_free(NULL, allocator->ptrs);
+ wmem_free(NULL, allocator);
+}
+
+void
+wmem_simple_allocator_init(wmem_allocator_t *allocator)
+{
+ wmem_simple_allocator_t *simple_allocator;
+
+ simple_allocator = wmem_new(NULL, wmem_simple_allocator_t);
+
+ allocator->walloc = &wmem_simple_alloc;
+ allocator->wrealloc = &wmem_simple_realloc;
+ allocator->wfree = &wmem_simple_free;
+
+ allocator->free_all = &wmem_simple_free_all;
+ allocator->gc = &wmem_simple_gc;
+ allocator->cleanup = &wmem_simple_allocator_cleanup;
+
+ allocator->private_data = (void*) simple_allocator;
+
+ simple_allocator->count = 0;
+ simple_allocator->size = DEFAULT_ALLOCS;
+ simple_allocator->ptrs = wmem_alloc_array(NULL, void*, DEFAULT_ALLOCS);
+}
+
+/*
+ * 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/wsutil/wmem/wmem_allocator_simple.h b/wsutil/wmem/wmem_allocator_simple.h
new file mode 100644
index 00000000..a843525e
--- /dev/null
+++ b/wsutil/wmem/wmem_allocator_simple.h
@@ -0,0 +1,42 @@
+/** @file
+ *
+ * Definitions for the Wireshark Memory Manager Simple Allocator
+ * Copyright 2012, Evan Huus <eapache@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WMEM_ALLOCATOR_SIMPLE_H__
+#define __WMEM_ALLOCATOR_SIMPLE_H__
+
+#include "wmem_core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void
+wmem_simple_allocator_init(wmem_allocator_t *allocator);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WMEM_ALLOCATOR_SIMPLE_H__ */
+
+/*
+ * 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/wsutil/wmem/wmem_allocator_strict.c b/wsutil/wmem/wmem_allocator_strict.c
new file mode 100644
index 00000000..87ef01e2
--- /dev/null
+++ b/wsutil/wmem/wmem_allocator_strict.c
@@ -0,0 +1,230 @@
+/* wmem_allocator_strict.c
+ * Wireshark Memory Manager Strict Allocator
+ * Copyright 2012, Evan Huus <eapache@gmail.com>
+ *
+ * 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 <string.h>
+
+#include <glib.h>
+
+#include "wmem_core.h"
+#include "wmem_allocator.h"
+#include "wmem_allocator_strict.h"
+
+/* In this allocator, we do everything we can to catch invalid memory accesses.
+ * This includes using canaries (what Valgrind calls redzones) and
+ * filling allocated and freed memory with garbage. Valgrind is still the
+ * better tool on the platforms where it is available - use it instead if
+ * possible.
+ */
+
+#define WMEM_CANARY_SIZE 8 /* in bytes */
+#define WMEM_CANARY_VALUE 0x9E
+
+#define WMEM_PREFILL 0xA1
+#define WMEM_POSTFILL 0x1A
+
+typedef struct _wmem_strict_allocator_block_t {
+ struct _wmem_strict_allocator_block_t *prev, *next;
+
+ /* Just the length of real_data, not counting the canaries */
+ size_t data_len;
+} wmem_strict_allocator_block_t;
+
+#define WMEM_DATA_TO_BLOCK(DATA) ((wmem_strict_allocator_block_t*)((uint8_t*)(DATA) - WMEM_CANARY_SIZE - sizeof(wmem_strict_allocator_block_t)))
+#define WMEM_BLOCK_TO_DATA(BLOCK) ((void*)((uint8_t*)(BLOCK) + WMEM_CANARY_SIZE + sizeof(wmem_strict_allocator_block_t)))
+#define WMEM_BLOCK_TO_PRE_CANARY(BLOCK) ((uint8_t*)(BLOCK) + sizeof(wmem_strict_allocator_block_t))
+#define WMEM_BLOCK_TO_POST_CANARY(BLOCK) ((uint8_t*)(BLOCK) + WMEM_CANARY_SIZE + sizeof(wmem_strict_allocator_block_t) + (BLOCK)->data_len)
+#define WMEM_FULL_SIZE(SIZE) ((SIZE) + sizeof(wmem_strict_allocator_block_t) + (2*WMEM_CANARY_SIZE))
+
+typedef struct _wmem_strict_allocator_t {
+ wmem_strict_allocator_block_t *blocks;
+} wmem_strict_allocator_t;
+
+/*
+ * some internal helper functions
+ */
+static inline void
+wmem_strict_block_check_canaries(wmem_strict_allocator_block_t *block)
+{
+ unsigned i;
+ uint8_t *canary;
+
+ canary = WMEM_BLOCK_TO_PRE_CANARY(block);
+ for (i=0; i<WMEM_CANARY_SIZE; i++) g_assert_true(canary[i] == WMEM_CANARY_VALUE);
+
+ canary = WMEM_BLOCK_TO_POST_CANARY(block);
+ for (i=0; i<WMEM_CANARY_SIZE; i++) g_assert_true(canary[i] == WMEM_CANARY_VALUE);
+}
+
+/*
+ * public API functions
+ */
+static void *
+wmem_strict_alloc(void *private_data, const size_t size)
+{
+ wmem_strict_allocator_t *allocator;
+ wmem_strict_allocator_block_t *block;
+ unsigned i;
+ uint8_t *canary;
+
+ allocator = (wmem_strict_allocator_t*) private_data;
+
+ block = (wmem_strict_allocator_block_t *)wmem_alloc(NULL, WMEM_FULL_SIZE(size));
+ block->data_len = size;
+
+ memset(WMEM_BLOCK_TO_DATA(block), WMEM_PREFILL, block->data_len);
+
+ canary = WMEM_BLOCK_TO_PRE_CANARY(block);
+ for (i=0; i<WMEM_CANARY_SIZE; i++) canary[i] = WMEM_CANARY_VALUE;
+
+ canary = WMEM_BLOCK_TO_POST_CANARY(block);
+ for (i=0; i<WMEM_CANARY_SIZE; i++) canary[i] = WMEM_CANARY_VALUE;
+
+ if (allocator->blocks) {
+ allocator->blocks->prev = block;
+ }
+ block->next = allocator->blocks;
+ block->prev = NULL;
+ allocator->blocks = block;
+
+ return WMEM_BLOCK_TO_DATA(block);
+}
+
+static void
+wmem_strict_free(void *private_data, void *ptr)
+{
+ wmem_strict_allocator_t *allocator;
+ wmem_strict_allocator_block_t *block;
+
+ allocator = (wmem_strict_allocator_t*) private_data;
+
+ block = WMEM_DATA_TO_BLOCK(ptr);
+
+ wmem_strict_block_check_canaries(block);
+
+ if (block->next) {
+ block->next->prev = block->prev;
+ }
+
+ if (block->prev) {
+ block->prev->next = block->next;
+ }
+ else {
+ allocator->blocks = block->next;
+ }
+
+ memset(block, WMEM_POSTFILL, WMEM_FULL_SIZE(block->data_len));
+
+ wmem_free(NULL, block);
+}
+
+static void *
+wmem_strict_realloc(void *private_data, void *ptr, const size_t size)
+{
+ wmem_strict_allocator_block_t *block;
+ void *new_ptr;
+
+ block = WMEM_DATA_TO_BLOCK(ptr);
+
+ /* create a new block */
+ new_ptr = wmem_strict_alloc(private_data, size);
+
+ /* copy from the old block to the new */
+ if (block->data_len > size) {
+ memcpy(new_ptr, ptr, size);
+ }
+ else {
+ memcpy(new_ptr, ptr, block->data_len);
+ }
+
+ /* free the old block */
+ wmem_strict_free(private_data, ptr);
+
+ return new_ptr;
+}
+
+void
+wmem_strict_check_canaries(wmem_allocator_t *allocator)
+{
+ wmem_strict_allocator_t *private_allocator;
+ wmem_strict_allocator_block_t *block;
+
+ if (allocator->type != WMEM_ALLOCATOR_STRICT) {
+ return;
+ }
+
+ private_allocator = (wmem_strict_allocator_t*) allocator->private_data;
+
+ block = private_allocator->blocks;
+ while (block) {
+ wmem_strict_block_check_canaries(block);
+ block = block->next;
+ }
+}
+
+static void
+wmem_strict_free_all(void *private_data)
+{
+ wmem_strict_allocator_t *allocator;
+
+ allocator = (wmem_strict_allocator_t*) private_data;
+
+ while (allocator->blocks) {
+ wmem_strict_free(private_data, WMEM_BLOCK_TO_DATA(allocator->blocks));
+ }
+}
+
+static void
+wmem_strict_gc(void *private_data _U_)
+{
+ /* We don't really have anything to garbage-collect, but it might be worth
+ * checking our canaries at this point? */
+}
+
+static void
+wmem_strict_allocator_cleanup(void *private_data)
+{
+ wmem_free(NULL, private_data);
+}
+
+void
+wmem_strict_allocator_init(wmem_allocator_t *allocator)
+{
+ wmem_strict_allocator_t *strict_allocator;
+
+ strict_allocator = wmem_new(NULL, wmem_strict_allocator_t);
+
+ allocator->walloc = &wmem_strict_alloc;
+ allocator->wrealloc = &wmem_strict_realloc;
+ allocator->wfree = &wmem_strict_free;
+
+ allocator->free_all = &wmem_strict_free_all;
+ allocator->gc = &wmem_strict_gc;
+ allocator->cleanup = &wmem_strict_allocator_cleanup;
+
+ allocator->private_data = (void*) strict_allocator;
+
+ strict_allocator->blocks = NULL;
+}
+
+/*
+ * 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/wsutil/wmem/wmem_allocator_strict.h b/wsutil/wmem/wmem_allocator_strict.h
new file mode 100644
index 00000000..014f76fb
--- /dev/null
+++ b/wsutil/wmem/wmem_allocator_strict.h
@@ -0,0 +1,45 @@
+/** @file
+ *
+ * Definitions for the Wireshark Memory Manager Strict Allocator
+ * Copyright 2012, Evan Huus <eapache@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WMEM_ALLOCATOR_STRICT_H__
+#define __WMEM_ALLOCATOR_STRICT_H__
+
+#include "wmem_core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void
+wmem_strict_allocator_init(wmem_allocator_t *allocator);
+
+void
+wmem_strict_check_canaries(wmem_allocator_t *allocator);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WMEM_ALLOCATOR_STRICT_H__ */
+
+/*
+ * 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/wsutil/wmem/wmem_array.c b/wsutil/wmem/wmem_array.c
new file mode 100644
index 00000000..47f6e8e0
--- /dev/null
+++ b/wsutil/wmem/wmem_array.c
@@ -0,0 +1,196 @@
+/* wmem_array.c
+ * Wireshark Memory Manager Array
+ * Copyright 2013, Evan Huus <eapache@gmail.com>
+ *
+ * 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 <string.h>
+#include <stdlib.h>
+#include <glib.h>
+
+#include "wmem_core.h"
+#include "wmem_array.h"
+
+/* Holds a wmem-allocated array.
+ * elem_len is the size of each element
+ * elem_count is the number of used elements
+ * alloc_count is the length (in elems) of the raw buffer pointed to by buf,
+ * regardless of how many elems are used (the contents)
+ */
+struct _wmem_array_t {
+ wmem_allocator_t *allocator;
+
+ uint8_t *buf;
+
+ size_t elem_size;
+
+ unsigned elem_count;
+ unsigned alloc_count;
+
+ bool null_terminated;
+};
+
+wmem_array_t *
+wmem_array_sized_new(wmem_allocator_t *allocator, size_t elem_size,
+ unsigned alloc_count)
+{
+ wmem_array_t *array;
+
+ array = wmem_new(allocator, wmem_array_t);
+
+ array->allocator = allocator;
+ array->elem_size = elem_size;
+ array->elem_count = 0;
+ array->alloc_count = alloc_count ? alloc_count : 1;
+ array->null_terminated = false;
+
+ array->buf = (uint8_t *)wmem_alloc(array->allocator,
+ array->elem_size * array->alloc_count);
+
+ return array;
+}
+
+wmem_array_t *
+wmem_array_new(wmem_allocator_t *allocator, const size_t elem_size)
+{
+ wmem_array_t *array;
+
+ array = wmem_array_sized_new(allocator, elem_size, 1);
+
+ return array;
+}
+
+void
+wmem_array_grow(wmem_array_t *array, const unsigned to_add)
+{
+ unsigned new_alloc_count, new_count;
+
+ new_alloc_count = array->alloc_count;
+ new_count = array->elem_count + to_add;
+
+ while (new_alloc_count < new_count) {
+ new_alloc_count *= 2;
+ }
+
+ if (new_alloc_count == array->alloc_count) {
+ return;
+ }
+
+ array->buf = (uint8_t *)wmem_realloc(array->allocator, array->buf,
+ new_alloc_count * array->elem_size);
+
+ array->alloc_count = new_alloc_count;
+}
+
+static void
+wmem_array_write_null_terminator(wmem_array_t *array)
+{
+ if (array->null_terminated) {
+ wmem_array_grow(array, 1);
+ memset(&array->buf[array->elem_count * array->elem_size], 0x0, array->elem_size);
+ }
+}
+
+void
+wmem_array_set_null_terminator(wmem_array_t *array)
+{
+ array->null_terminated = true;
+ wmem_array_write_null_terminator(array);
+}
+
+void
+wmem_array_bzero(wmem_array_t *array)
+{
+ memset(array->buf, 0x0, array->elem_size * array->elem_count);
+}
+
+void
+wmem_array_append(wmem_array_t *array, const void *in, unsigned count)
+{
+ wmem_array_grow(array, count);
+
+ memcpy(&array->buf[array->elem_count * array->elem_size], in,
+ count * array->elem_size);
+
+ array->elem_count += count;
+
+ wmem_array_write_null_terminator(array);
+}
+
+void *
+wmem_array_index(wmem_array_t *array, unsigned array_index)
+{
+ g_assert(array_index < array->elem_count);
+ return &array->buf[array_index * array->elem_size];
+}
+
+int
+wmem_array_try_index(wmem_array_t *array, unsigned array_index, void *val)
+{
+ if (array_index >= array->elem_count)
+ return -1;
+ memcpy(val, &array->buf[array_index * array->elem_size], array->elem_size);
+ return 0;
+}
+
+void
+wmem_array_sort(wmem_array_t *array, int (*compar)(const void*,const void*))
+{
+ qsort(array->buf, array->elem_count, array->elem_size, compar);
+}
+
+void *
+wmem_array_get_raw(wmem_array_t *array)
+{
+ return array->buf;
+}
+
+unsigned
+wmem_array_get_count(wmem_array_t *array)
+{
+ if (array == NULL)
+ return 0;
+
+ return array->elem_count;
+}
+
+void *
+wmem_array_finalize(wmem_array_t *array)
+{
+ if (array == NULL)
+ return NULL;
+
+ size_t used_size = array->null_terminated ? (array->elem_count + 1) * array->elem_size : array->elem_count * array->elem_size;
+ void *ret = wmem_realloc(array->allocator, array->buf, used_size);
+
+ wmem_free(array->allocator, array);
+
+ return ret;
+}
+
+void
+wmem_destroy_array(wmem_array_t *array)
+{
+ wmem_free(array->allocator, array->buf);
+ wmem_free(array->allocator, array);
+}
+
+/*
+ * 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/wsutil/wmem/wmem_array.h b/wsutil/wmem/wmem_array.h
new file mode 100644
index 00000000..e813232d
--- /dev/null
+++ b/wsutil/wmem/wmem_array.h
@@ -0,0 +1,121 @@
+/** @file
+ * Definitions for the Wireshark Memory Manager Array
+ * Copyright 2013, Evan Huus <eapache@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WMEM_ARRAY_H__
+#define __WMEM_ARRAY_H__
+
+#include <string.h>
+#include <glib.h>
+
+#include "wmem_core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/** @addtogroup wmem
+ * @{
+ * @defgroup wmem-array Array
+ *
+ * A resizable array implementation on top of wmem.
+ *
+ * @{
+ */
+
+struct _wmem_array_t;
+
+typedef struct _wmem_array_t wmem_array_t;
+
+WS_DLL_PUBLIC
+wmem_array_t *
+wmem_array_sized_new(wmem_allocator_t *allocator, size_t elem_size,
+ unsigned alloc_count)
+G_GNUC_MALLOC;
+
+WS_DLL_PUBLIC
+wmem_array_t *
+wmem_array_new(wmem_allocator_t *allocator, const size_t elem_size)
+G_GNUC_MALLOC;
+
+WS_DLL_PUBLIC
+void
+wmem_array_grow(wmem_array_t *array, const unsigned to_add);
+
+WS_DLL_PUBLIC
+void
+wmem_array_set_null_terminator(wmem_array_t *array);
+
+WS_DLL_PUBLIC
+void
+wmem_array_bzero(wmem_array_t *array);
+
+WS_DLL_PUBLIC
+void
+wmem_array_append(wmem_array_t *array, const void *in, unsigned count);
+
+#define wmem_array_append_one(ARRAY, VAL) \
+ wmem_array_append((ARRAY), &(VAL), 1)
+
+WS_DLL_PUBLIC
+void *
+wmem_array_index(wmem_array_t *array, unsigned array_index);
+
+WS_DLL_PUBLIC
+int
+wmem_array_try_index(wmem_array_t *array, unsigned array_index, void *val);
+
+WS_DLL_PUBLIC
+void
+wmem_array_sort(wmem_array_t *array, int (*compar)(const void*,const void*));
+
+WS_DLL_PUBLIC
+void *
+wmem_array_get_raw(wmem_array_t *array);
+
+WS_DLL_PUBLIC
+unsigned
+wmem_array_get_count(wmem_array_t *array);
+
+/* Truncates the underlying array to the elements contained within
+ * (including null terminator if set), frees the wmem_array_t
+ * structure, and returns a pointer to the raw array. The wmem_array_t
+ * struct cannot be used after this is called. This is for when you are
+ * done adding elements to the array but still need the underlying array.
+ */
+WS_DLL_PUBLIC
+void *
+wmem_array_finalize(wmem_array_t *array);
+
+WS_DLL_PUBLIC
+void
+wmem_destroy_array(wmem_array_t *array);
+
+/** @}
+ * @} */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WMEM_ARRAY_H__ */
+
+/*
+ * 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/wsutil/wmem/wmem_core.c b/wsutil/wmem/wmem_core.c
new file mode 100644
index 00000000..a166bf84
--- /dev/null
+++ b/wsutil/wmem/wmem_core.c
@@ -0,0 +1,240 @@
+/* wmem_core.c
+ * Wireshark Memory Manager Core
+ * Copyright 2012, Evan Huus <eapache@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+
+#include "wmem-int.h"
+#include "wmem_core.h"
+#include "wmem_map_int.h"
+#include "wmem_user_cb_int.h"
+#include "wmem_allocator.h"
+#include "wmem_allocator_simple.h"
+#include "wmem_allocator_block.h"
+#include "wmem_allocator_block_fast.h"
+#include "wmem_allocator_strict.h"
+
+/* Set according to the WIRESHARK_DEBUG_WMEM_OVERRIDE environment variable in
+ * wmem_init. Should not be set again. */
+static bool do_override = false;
+static wmem_allocator_type_t override_type;
+
+void *
+wmem_alloc(wmem_allocator_t *allocator, const size_t size)
+{
+ if (allocator == NULL) {
+ return g_malloc(size);
+ }
+
+ ws_assert(allocator->in_scope);
+
+ if (size == 0) {
+ return NULL;
+ }
+
+ return allocator->walloc(allocator->private_data, size);
+}
+
+void *
+wmem_alloc0(wmem_allocator_t *allocator, const size_t size)
+{
+ void *buf;
+
+ buf = wmem_alloc(allocator, size);
+
+ if (buf) {
+ memset(buf, 0, size);
+ }
+
+ return buf;
+}
+
+void
+wmem_free(wmem_allocator_t *allocator, void *ptr)
+{
+ if (allocator == NULL) {
+ g_free(ptr);
+ return;
+ }
+
+ ws_assert(allocator->in_scope);
+
+ if (ptr == NULL) {
+ return;
+ }
+
+ allocator->wfree(allocator->private_data, ptr);
+}
+
+void *
+wmem_realloc(wmem_allocator_t *allocator, void *ptr, const size_t size)
+{
+ if (allocator == NULL) {
+ return g_realloc(ptr, size);
+ }
+
+ if (ptr == NULL) {
+ return wmem_alloc(allocator, size);
+ }
+
+ if (size == 0) {
+ wmem_free(allocator, ptr);
+ return NULL;
+ }
+
+ ws_assert(allocator->in_scope);
+
+ return allocator->wrealloc(allocator->private_data, ptr, size);
+}
+
+static void
+wmem_free_all_real(wmem_allocator_t *allocator, bool final)
+{
+ wmem_call_callbacks(allocator,
+ final ? WMEM_CB_DESTROY_EVENT : WMEM_CB_FREE_EVENT);
+ allocator->free_all(allocator->private_data);
+}
+
+void
+wmem_free_all(wmem_allocator_t *allocator)
+{
+ wmem_free_all_real(allocator, false);
+}
+
+void
+wmem_gc(wmem_allocator_t *allocator)
+{
+ allocator->gc(allocator->private_data);
+}
+
+void
+wmem_destroy_allocator(wmem_allocator_t *allocator)
+{
+
+ wmem_free_all_real(allocator, true);
+ allocator->cleanup(allocator->private_data);
+ wmem_free(NULL, allocator);
+}
+
+wmem_allocator_t *
+wmem_allocator_new(const wmem_allocator_type_t type)
+{
+ wmem_allocator_t *allocator;
+ wmem_allocator_type_t real_type;
+
+ if (do_override) {
+ real_type = override_type;
+ }
+ else {
+ real_type = type;
+ }
+
+ allocator = wmem_new(NULL, wmem_allocator_t);
+ allocator->type = real_type;
+ allocator->callbacks = NULL;
+ allocator->in_scope = true;
+
+ switch (real_type) {
+ case WMEM_ALLOCATOR_SIMPLE:
+ wmem_simple_allocator_init(allocator);
+ break;
+ case WMEM_ALLOCATOR_BLOCK:
+ wmem_block_allocator_init(allocator);
+ break;
+ case WMEM_ALLOCATOR_BLOCK_FAST:
+ wmem_block_fast_allocator_init(allocator);
+ break;
+ case WMEM_ALLOCATOR_STRICT:
+ wmem_strict_allocator_init(allocator);
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ };
+
+ return allocator;
+}
+
+void
+wmem_init(void)
+{
+ const char *override_env;
+
+ /* Our valgrind script uses this environment variable to override the
+ * usual allocator choice so that everything goes through system-level
+ * allocations that it understands and can track. Otherwise it will get
+ * confused by the block allocator etc. */
+ override_env = getenv("WIRESHARK_DEBUG_WMEM_OVERRIDE");
+
+ if (override_env == NULL) {
+ do_override = false;
+ }
+ else {
+ do_override = true;
+ if (strncmp(override_env, "simple", strlen("simple")) == 0) {
+ override_type = WMEM_ALLOCATOR_SIMPLE;
+ }
+ else if (strncmp(override_env, "block", strlen("block")) == 0) {
+ override_type = WMEM_ALLOCATOR_BLOCK;
+ }
+ else if (strncmp(override_env, "strict", strlen("strict")) == 0) {
+ override_type = WMEM_ALLOCATOR_STRICT;
+ }
+ else if (strncmp(override_env, "block_fast", strlen("block_fast")) == 0) {
+ override_type = WMEM_ALLOCATOR_BLOCK_FAST;
+ }
+ else {
+ g_warning("Unrecognized wmem override");
+ do_override = false;
+ }
+ }
+
+ wmem_init_hashing();
+}
+
+void
+wmem_cleanup(void)
+{
+}
+
+void
+wmem_enter_scope(wmem_allocator_t *allocator)
+{
+ allocator->in_scope = true;
+}
+
+void
+wmem_leave_scope(wmem_allocator_t *allocator)
+{
+ wmem_free_all(allocator);
+ allocator->in_scope = false;
+}
+
+bool
+wmem_in_scope(wmem_allocator_t *allocator)
+{
+ return allocator->in_scope;
+}
+
+
+/*
+ * 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/wsutil/wmem/wmem_core.h b/wsutil/wmem/wmem_core.h
new file mode 100644
index 00000000..2f423133
--- /dev/null
+++ b/wsutil/wmem/wmem_core.h
@@ -0,0 +1,252 @@
+/** @file
+ * Definitions for the Wireshark Memory Manager Core
+ * Copyright 2012, Evan Huus <eapache@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WMEM_CORE_H__
+#define __WMEM_CORE_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <glib.h>
+#include <ws_symbol_export.h>
+#include <ws_attributes.h>
+#include <ws_posix_compat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/** @defgroup wmem Wireshark Memory Manager
+ *
+ * Wmem is a memory management framework for Wireshark that makes it simple to
+ * write dissectors (and other 'user-space' code) that doesn't leak memory. The
+ * core module provides basic functions like malloc, realloc and free, but
+ * many other functions are available (see the "Modules" list at the top of
+ * the generated doxygen HTML).
+ *
+ * Any wmem functions which allocate memory are guaranteed to either succeed or
+ * abort the program. However, they *can* still legally return NULL when the
+ * amount of requested memory is zero.
+ *
+ * @{
+ */
+
+struct _wmem_allocator_t;
+/** A public opaque type representing one wmem allocation pool. */
+typedef struct _wmem_allocator_t wmem_allocator_t;
+
+/** An enumeration of the different types of available allocators. */
+typedef enum _wmem_allocator_type_t {
+ WMEM_ALLOCATOR_SIMPLE, /**< A trivial allocator that mallocs requested
+ memory and tracks allocations via a hash table. As simple as
+ possible, intended more as a demo than for practical usage. Also
+ has the benefit of being friendly to tools like valgrind. */
+ WMEM_ALLOCATOR_BLOCK, /**< A block allocator that grabs large chunks of
+ memory at a time (8 MB currently) and serves allocations out of
+ those chunks. Designed for efficiency, especially in the
+ free_all operation. */
+ WMEM_ALLOCATOR_STRICT, /**< An allocator that does its best to find invalid
+ memory usage via things like canaries and scrubbing freed
+ memory. Valgrind is the better choice on platforms that support
+ it. */
+ WMEM_ALLOCATOR_BLOCK_FAST /**< A block allocator like WMEM_ALLOCATOR_BLOCK
+ but even faster by tracking absolutely minimal metadata and
+ making 'free' a no-op. Useful only for very short-lived scopes
+ where there's no reason to free individual allocations because
+ the next free_all is always just around the corner. */
+} wmem_allocator_type_t;
+
+/** Allocate the requested amount of memory in the given pool.
+ *
+ * @param allocator The allocator object to use to allocate the memory.
+ * @param size The amount of memory to allocate.
+ * @return A void pointer to the newly allocated memory.
+ */
+WS_DLL_PUBLIC
+void *
+wmem_alloc(wmem_allocator_t *allocator, const size_t size)
+G_GNUC_MALLOC;
+
+/** Allocate memory sufficient to hold one object of the given type.
+ *
+ * @param allocator The allocator object to use to allocate the memory.
+ * @param type The type that the newly allocated memory will hold.
+ * @return A void pointer to the newly allocated memory.
+ */
+#define wmem_new(allocator, type) \
+ ((type*)wmem_alloc((allocator), sizeof(type)))
+
+/*
+ * Overflow-safe multiplication of the size of a type by a number of
+ * items of that type, returning 0 if the result would overflow (or
+ * if the number of elements is negative), and the product otherwise.
+ */
+#define wmem_safe_mult_type_size(type, num) \
+ ((((num) <= 0) || ((size_t)sizeof(type) > (G_MAXSSIZE / (size_t)(num)))) ? 0 : (sizeof(type) * (num)))
+
+/** Allocate memory sufficient to hold n objects of the given type.
+ *
+ * @param allocator The allocator object to use to allocate the memory.
+ * @param type The type that the newly allocated memory will hold.
+ * @param num The number of objects that the newly allocated memory will hold.
+ * @return A void pointer to the newly allocated memory.
+ */
+#define wmem_alloc_array(allocator, type, num) \
+ ((type*)wmem_alloc((allocator), wmem_safe_mult_type_size(type, (num))))
+
+/** Allocate the requested amount of memory in the given pool. Initializes the
+ * allocated memory with zeroes.
+ *
+ * @param allocator The allocator object to use to allocate the memory.
+ * @param size The amount of memory to allocate.
+ * @return A void pointer to the newly allocated and zeroed memory.
+ */
+WS_DLL_PUBLIC
+void *
+wmem_alloc0(wmem_allocator_t *allocator, const size_t size)
+G_GNUC_MALLOC;
+
+/** Allocate memory sufficient to hold one object of the given type.
+ * Initializes the allocated memory with zeroes.
+ *
+ * @param allocator The allocator object to use to allocate the memory.
+ * @param type The type that the newly allocated memory will hold.
+ * @return A void pointer to the newly allocated and zeroed memory.
+ */
+#define wmem_new0(allocator, type) \
+ ((type*)wmem_alloc0((allocator), sizeof(type)))
+
+/** Allocate memory sufficient to hold n objects of the given type.
+ * Initializes the allocated memory with zeroes.
+ *
+ * @param allocator The allocator object to use to allocate the memory.
+ * @param type The type that the newly allocated memory will hold.
+ * @param num The number of objects that the newly allocated memory will hold.
+ * @return A void pointer to the newly allocated and zeroed memory.
+ */
+#define wmem_alloc0_array(allocator, type, num) \
+ ((type*)wmem_alloc0((allocator), wmem_safe_mult_type_size(type, (num))))
+
+/** Returns the allocated memory to the allocator. This function should only
+ * be called directly by allocators when the allocated block is sufficiently
+ * large that the reduced memory usage is worth the cost of the extra function
+ * call. It's usually easier to just let it get cleaned up when wmem_free_all()
+ * is called.
+ *
+ * @param allocator The allocator object used to originally allocate the memory.
+ * @param ptr The pointer to the memory block to free. After this function
+ * returns it no longer points to valid memory.
+ */
+WS_DLL_PUBLIC
+void
+wmem_free(wmem_allocator_t *allocator, void *ptr);
+
+/** Resizes a block of memory, potentially moving it if resizing it in place
+ * is not possible.
+ *
+ * @param allocator The allocator object used to originally allocate the memory.
+ * @param ptr The pointer to the memory block to resize.
+ * @param size The new size for the memory block.
+ * @return The new location of the memory block. If this is different from ptr
+ * then ptr no longer points to valid memory.
+ */
+WS_DLL_PUBLIC
+void *
+wmem_realloc(wmem_allocator_t *allocator, void *ptr, const size_t size)
+G_GNUC_MALLOC;
+
+/** Frees all the memory allocated in a pool. Depending on the allocator
+ * implementation used this can be significantly cheaper than calling
+ * wmem_free() on all the individual blocks. It also doesn't require you to have
+ * external pointers to those blocks.
+ *
+ * @param allocator The allocator to free the memory from.
+ */
+WS_DLL_PUBLIC
+void
+wmem_free_all(wmem_allocator_t *allocator);
+
+/** Triggers a garbage-collection in the allocator. This does not free any
+ * memory, but it can return unused blocks to the operating system or perform
+ * other optimizations.
+ *
+ * @param allocator The allocator in which to trigger the garbage collection.
+ */
+WS_DLL_PUBLIC
+void
+wmem_gc(wmem_allocator_t *allocator);
+
+/** Destroy the given allocator, freeing all memory allocated in it. Once this
+ * function has been called, no memory allocated with the allocator is valid.
+ *
+ * @param allocator The allocator to destroy.
+ */
+WS_DLL_PUBLIC
+void
+wmem_destroy_allocator(wmem_allocator_t *allocator);
+
+/** Create a new allocator of the given type. The type may be overridden by the
+ * WIRESHARK_DEBUG_WMEM_OVERRIDE environment variable.
+ *
+ * @param type The type of allocator to create.
+ * @return The new allocator.
+ */
+WS_DLL_PUBLIC
+wmem_allocator_t *
+wmem_allocator_new(const wmem_allocator_type_t type);
+
+/** Initialize the wmem subsystem. This must be called before any other wmem
+ * function, usually at the very beginning of your program.
+ */
+WS_DLL_PUBLIC
+void
+wmem_init(void);
+
+/** Teardown the wmem subsystem. This must be called after all other wmem
+ * functions, usually at the very end of your program. This function will not
+ * destroy outstanding allocators, you must do that yourself.
+ */
+WS_DLL_PUBLIC
+void
+wmem_cleanup(void);
+
+WS_DLL_PUBLIC
+void
+wmem_enter_scope(wmem_allocator_t *allocator);
+
+WS_DLL_PUBLIC
+void
+wmem_leave_scope(wmem_allocator_t *allocator);
+
+WS_DLL_PUBLIC
+bool
+wmem_in_scope(wmem_allocator_t *allocator);
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WMEM_CORE_H__ */
+
+/*
+ * 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/wsutil/wmem/wmem_interval_tree.c b/wsutil/wmem/wmem_interval_tree.c
new file mode 100644
index 00000000..8b28549a
--- /dev/null
+++ b/wsutil/wmem/wmem_interval_tree.c
@@ -0,0 +1,190 @@
+/* wmem_interval_tree.c
+ * Implements an augmented interval tree
+ * Based on the red-black tree implementation in epan/wmem.*
+ * Copyright 2015, Matthieu coudron <matthieu.coudron@lip6.fr>
+ *
+ * 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 <inttypes.h>
+#include <string.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <glib.h>
+
+#include "wmem-int.h"
+#include "wmem_core.h"
+#include "wmem_tree-int.h"
+#include "wmem_strutl.h"
+#include "wmem_interval_tree.h"
+#include "wmem_user_cb.h"
+
+
+static void
+print_range(const void *value)
+{
+ const wmem_range_t *range = (const wmem_range_t *)value;
+ if(!value) {
+ return;
+ }
+ printf("Range: low=%" PRIu64 " high=%" PRIu64 " max_edge=%" PRIu64 "\n", range->low, range->high, range->max_edge);
+}
+
+/**
+ * In an augmented interval tree, each node saves the maximum edge of its child subtrees
+ * This function compares the children max_edge with the current max_edge
+ * and propagates any change to the parent nodes.
+ */
+static void
+update_max_edge(wmem_tree_node_t *node)
+{
+ wmem_range_t *range;
+ const wmem_range_t *range_l;
+ const wmem_range_t *range_r;
+ uint64_t maxEdge = 0;
+
+ if(!node) {
+ return ;
+ }
+
+ range = (wmem_range_t *)node->key;
+
+ range_l = (node->left) ? (const wmem_range_t *) (node->left->key) : NULL;
+ range_r = (node->right) ? (const wmem_range_t *) (node->right->key) : NULL;
+
+ maxEdge = range->high;
+
+ if(range_r) {
+ maxEdge = MAX(maxEdge, range_r->max_edge) ;
+ }
+ if(range_l) {
+ maxEdge = MAX(maxEdge, range_l->max_edge) ;
+ }
+
+ /* update the parent nodes only if a change happened (optimization) */
+ if(range->max_edge != maxEdge) {
+ range->max_edge = maxEdge;
+ update_max_edge(node->parent);
+ }
+}
+
+bool
+wmem_itree_range_overlap(const wmem_range_t *r1, const wmem_range_t *r2)
+{
+ return (r1->low <= r2->high && r2->low <= r1->high);
+}
+
+
+/* after a rotation, some of the children nodes might (dis)appear, thus we need
+ * to refresh children max_edge. Changes will propagate to parents */
+static void update_edges_after_rotation(wmem_tree_node_t *node) {
+ if(node->left) update_max_edge(node->left);
+ if(node->right) update_max_edge(node->right);
+}
+
+wmem_itree_t *
+wmem_itree_new(wmem_allocator_t *allocator)
+{
+ wmem_itree_t *tree = wmem_tree_new(allocator);
+ tree->post_rotation_cb = &update_edges_after_rotation;
+ return tree;
+}
+
+bool
+wmem_itree_is_empty(wmem_itree_t *tree)
+{
+ return wmem_tree_is_empty(tree);
+}
+
+static int
+wmem_tree_compare_ranges(const wmem_range_t *ra, const wmem_range_t *rb)
+{
+ if( ra->low == rb->low) {
+ return 0;
+ }
+ else if(ra->low < rb->low) {
+ return -1;
+ }
+ else {
+ return 1;
+ }
+}
+
+
+void
+wmem_itree_insert(wmem_itree_t *tree, const uint64_t low, const uint64_t high, void *data)
+{
+ wmem_tree_node_t *node;
+ wmem_range_t *range = (wmem_range_t *)wmem_new(tree->data_allocator, wmem_range_t);
+
+ ws_assert(low <= high);
+ range->low = low;
+ range->high = high;
+ range->max_edge = 0;
+
+ node = wmem_tree_insert(tree, range, data, (compare_func)wmem_tree_compare_ranges);
+
+ /* in absence of rotation, we still need to update max_edge */
+ update_max_edge(node);
+}
+
+
+static void
+wmem_itree_find_intervals_in_subtree(wmem_tree_node_t *node, wmem_range_t requested, wmem_list_t *results)
+{
+ const wmem_range_t* current;
+
+ if(!node) {
+ return;
+ }
+ current = (wmem_range_t*)node->key;
+
+ /* there is no child that can possibly match */
+ if(requested.low > current->max_edge) {
+ return;
+ }
+
+ if(wmem_itree_range_overlap(current, &requested)) {
+ wmem_list_prepend(results, node->data);
+ }
+
+ wmem_itree_find_intervals_in_subtree(node->left, requested, results);
+ wmem_itree_find_intervals_in_subtree(node->right, requested, results);
+}
+
+wmem_list_t *
+wmem_itree_find_intervals(wmem_itree_t *tree, wmem_allocator_t *allocator, uint64_t low, uint64_t high)
+{
+ wmem_list_t *results = NULL;
+ wmem_range_t requested = { low, high, 0 };
+ results = wmem_list_new(allocator);
+
+ wmem_itree_find_intervals_in_subtree(tree->root, requested, results);
+ return results;
+}
+
+
+void
+wmem_print_itree(wmem_tree_t *tree)
+{
+ wmem_print_tree(tree, &print_range, NULL);
+}
+
+/*
+ * 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/wsutil/wmem/wmem_interval_tree.h b/wsutil/wmem/wmem_interval_tree.h
new file mode 100644
index 00000000..1d7bc4df
--- /dev/null
+++ b/wsutil/wmem/wmem_interval_tree.h
@@ -0,0 +1,101 @@
+/** @file
+ * Definitions for the Wireshark Memory Manager Red-Black Tree
+ * Based on the red-black tree implementation in epan/emem.*
+ * Copyright 2015, Matthieu coudron <matthieu.coudron@lip6.fr>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef __WMEM_INTERVAL_TREE_H__
+#define __WMEM_INTERVAL_TREE_H__
+
+#include "wmem_core.h"
+#include "wmem_tree.h"
+#include "wmem_list.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/** @addtogroup wmem
+ * @{
+ * @defgroup wmem-interval-tree Interval Tree
+ *
+ * http://www.geeksforgeeks.org/interval-tree/
+ * The idea is to augment a self-balancing Binary Search Tree (BST) like Red Black Tree, AVL Tree, etc ...
+ * to maintain a set of intervals so that all operations can be done in O(Logn) time.
+ * @{
+ * Following wikipedia's convention this is an augmented tree rather then an interval tree
+ * http://www.wikiwand.com/en/Interval_tree
+ */
+
+struct _wmem_tree_t;
+typedef struct _wmem_tree_t wmem_itree_t;
+
+struct _wmem_range_t {
+ uint64_t low; /* low is used as the key in the binary tree */
+ uint64_t high; /* Max value of the range */
+ uint64_t max_edge; /* max value among subtrees */
+};
+
+WS_DLL_PUBLIC
+wmem_itree_t *
+wmem_itree_new(wmem_allocator_t *allocator)
+G_GNUC_MALLOC;
+
+
+/** Returns true if the tree is empty (has no nodes). */
+WS_DLL_PUBLIC
+bool
+wmem_itree_is_empty(wmem_itree_t *tree);
+
+
+/** Inserts a range low-high indexed by "low" in O(log(n)).
+ * As in wmem_tree, if a key "low" already exists, it will be overwritten with the new data
+ *
+ */
+WS_DLL_PUBLIC
+void
+wmem_itree_insert(wmem_itree_t *tree, const uint64_t low, const uint64_t high, void *data);
+
+
+/*
+ * Save results in a wmem_list with the scope passed as a parameter.
+ * wmem_list_t is always allocated even if there is no result
+ */
+WS_DLL_PUBLIC
+wmem_list_t *
+wmem_itree_find_intervals(wmem_itree_t *tree, wmem_allocator_t *allocator, uint64_t low, uint64_t high);
+
+
+/**
+ * Print ranges along the tree
+ */
+void
+wmem_print_itree(wmem_itree_t *tree);
+
+/** @}
+ * @} */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WMEM_INTERVAL_TREE_H__ */
+
+/*
+ * 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/wsutil/wmem/wmem_list.c b/wsutil/wmem/wmem_list.c
new file mode 100644
index 00000000..c03ffe31
--- /dev/null
+++ b/wsutil/wmem/wmem_list.c
@@ -0,0 +1,276 @@
+/* wmem_list.c
+ * Wireshark Memory Manager Doubly-Linked List
+ * Copyright 2012, Evan Huus <eapache@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <string.h>
+#include <glib.h>
+
+#include "wmem_core.h"
+#include "wmem_list.h"
+
+struct _wmem_list_frame_t {
+ struct _wmem_list_frame_t *next, *prev;
+ void *data;
+};
+
+struct _wmem_list_t {
+ unsigned count;
+ wmem_list_frame_t *head, *tail;
+ wmem_allocator_t *allocator;
+};
+
+unsigned
+wmem_list_count(const wmem_list_t *list)
+{
+ return list->count;
+}
+
+wmem_list_frame_t *
+wmem_list_head(const wmem_list_t *list)
+{
+ return list->head;
+}
+
+wmem_list_frame_t *
+wmem_list_tail(const wmem_list_t *list)
+{
+ return list->tail;
+}
+
+wmem_list_frame_t *
+wmem_list_frame_next(const wmem_list_frame_t *frame)
+{
+ return frame->next;
+}
+
+wmem_list_frame_t *
+wmem_list_frame_prev(const wmem_list_frame_t *frame)
+{
+ return frame->prev;
+}
+
+void *
+wmem_list_frame_data(const wmem_list_frame_t *frame)
+{
+ return frame->data;
+}
+
+void
+wmem_list_remove(wmem_list_t *list, void *data)
+{
+ wmem_list_frame_t *frame;
+
+ frame = list->head;
+
+ while (frame && frame->data != data) {
+ frame = frame->next;
+ }
+
+ if (frame == NULL) {
+ return;
+ }
+
+ wmem_list_remove_frame(list, frame);
+}
+
+void
+wmem_list_remove_frame(wmem_list_t *list, wmem_list_frame_t *frame)
+{
+ if (frame->prev) {
+ frame->prev->next = frame->next;
+ }
+ else {
+ list->head = frame->next;
+ }
+
+ if (frame->next) {
+ frame->next->prev = frame->prev;
+ }
+ else {
+ list->tail = frame->prev;
+ }
+
+ list->count--;
+ wmem_free(list->allocator, frame);
+}
+
+wmem_list_frame_t *
+wmem_list_find(wmem_list_t *list, const void *data)
+{
+ wmem_list_frame_t *cur;
+
+ for (cur = list->head; cur; cur = cur->next) {
+ if(cur->data == data)
+ return cur;
+ }
+
+ return NULL;
+}
+
+wmem_list_frame_t *
+wmem_list_find_custom(wmem_list_t *list, const void *data, GCompareFunc compare_func)
+{
+ wmem_list_frame_t *cur;
+
+ for (cur = list->head; cur != NULL; cur = cur->next) {
+ if (compare_func(cur->data, data) == 0) {
+ return cur;
+ }
+ }
+
+ return NULL;
+}
+
+void
+wmem_list_prepend(wmem_list_t *list, void *data)
+{
+ wmem_list_frame_t *new_frame;
+
+ new_frame = wmem_new(list->allocator, wmem_list_frame_t);
+
+ new_frame->data = data;
+ new_frame->next = list->head;
+ new_frame->prev = NULL;
+
+ if (list->head) {
+ list->head->prev = new_frame;
+ }
+ else {
+ list->tail = new_frame;
+ }
+
+ list->head = new_frame;
+ list->count++;
+}
+
+void
+wmem_list_append(wmem_list_t *list, void *data)
+{
+ wmem_list_frame_t *new_frame;
+
+ new_frame = wmem_new(list->allocator, wmem_list_frame_t);
+ new_frame->data = data;
+ new_frame->next = NULL;
+ new_frame->prev = list->tail;
+
+ if (list->tail) {
+ list->tail->next = new_frame;
+ }
+ else {
+ list->head = new_frame;
+ }
+
+ list->tail = new_frame;
+ list->count++;
+}
+
+void
+wmem_list_insert_sorted(wmem_list_t *list, void* data, GCompareFunc func)
+{
+ wmem_list_frame_t *new_frame;
+ wmem_list_frame_t *cur;
+ wmem_list_frame_t *prev;
+
+ new_frame = wmem_new(list->allocator, wmem_list_frame_t);
+ new_frame->data = data;
+ new_frame->next = NULL;
+ new_frame->prev = NULL;
+
+ list->count++;
+
+ if (!list->head) {
+ list->head = new_frame;
+ list->tail = new_frame;
+ return;
+ }
+
+ cur = list->head;
+
+ if (func(cur->data, data) >= 0) {
+ cur->prev = new_frame;
+ new_frame->next = cur;
+ list->head = new_frame;
+ return;
+ }
+
+ do {
+ prev = cur;
+ cur = cur->next;
+ } while (cur && func(cur->data, data) <= 0);
+
+ if (!cur) {
+ prev->next = new_frame;
+ new_frame->prev = prev;
+ list->tail = new_frame;
+ return;
+ }
+
+ new_frame->prev = prev;
+ new_frame->next = cur;
+ new_frame->prev->next = new_frame;
+ new_frame->next->prev = new_frame;
+}
+
+wmem_list_t *
+wmem_list_new(wmem_allocator_t *allocator)
+{
+ wmem_list_t *list;
+
+ list = wmem_new(allocator, wmem_list_t);
+
+ list->count = 0;
+ list->head = NULL;
+ list->tail = NULL;
+ list->allocator = allocator;
+
+ return list;
+}
+
+void
+wmem_destroy_list(wmem_list_t *list)
+{
+ wmem_list_frame_t *cur, *next;
+
+ cur = list->head;
+
+ while (cur) {
+ next = cur->next;
+ wmem_free(list->allocator, cur);
+ cur = next;
+ }
+
+ wmem_free(list->allocator, list);
+}
+
+void
+wmem_list_foreach(wmem_list_t *list, GFunc foreach_func, void * user_data)
+{
+ wmem_list_frame_t *cur;
+
+ cur = list->head;
+
+ while (cur) {
+ foreach_func(cur->data, user_data);
+ cur = cur->next;
+ }
+}
+
+/*
+ * 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/wsutil/wmem/wmem_list.h b/wsutil/wmem/wmem_list.h
new file mode 100644
index 00000000..fc0e5229
--- /dev/null
+++ b/wsutil/wmem/wmem_list.h
@@ -0,0 +1,128 @@
+/** @file
+ * Definitions for the Wireshark Memory Manager Doubly-Linked List
+ * Copyright 2012, Evan Huus <eapache@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WMEM_LIST_H__
+#define __WMEM_LIST_H__
+
+#include <string.h>
+#include <glib.h>
+
+#include "wmem_core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/** @addtogroup wmem
+ * @{
+ * @defgroup wmem-list Doubly-Linked List
+ *
+ * A doubly-linked list implementation on top of wmem.
+ *
+ * @{
+ */
+
+struct _wmem_list_t;
+struct _wmem_list_frame_t;
+
+typedef struct _wmem_list_t wmem_list_t;
+typedef struct _wmem_list_frame_t wmem_list_frame_t;
+
+WS_DLL_PUBLIC
+unsigned
+wmem_list_count(const wmem_list_t *list);
+
+WS_DLL_PUBLIC
+wmem_list_frame_t *
+wmem_list_head(const wmem_list_t *list);
+
+WS_DLL_PUBLIC
+wmem_list_frame_t *
+wmem_list_tail(const wmem_list_t *list);
+
+WS_DLL_PUBLIC
+wmem_list_frame_t *
+wmem_list_frame_next(const wmem_list_frame_t *frame);
+
+WS_DLL_PUBLIC
+wmem_list_frame_t *
+wmem_list_frame_prev(const wmem_list_frame_t *frame);
+
+WS_DLL_PUBLIC
+void *
+wmem_list_frame_data(const wmem_list_frame_t *frame);
+
+WS_DLL_PUBLIC
+void
+wmem_list_remove(wmem_list_t *list, void *data);
+
+WS_DLL_PUBLIC
+void
+wmem_list_remove_frame(wmem_list_t *list, wmem_list_frame_t *frame);
+
+/*
+ * Linear search, search is O(n)
+ */
+WS_DLL_PUBLIC
+wmem_list_frame_t *
+wmem_list_find(wmem_list_t *list, const void *data);
+
+WS_DLL_PUBLIC
+wmem_list_frame_t *
+wmem_list_find_custom(wmem_list_t *list, const void *data, GCompareFunc func);
+
+WS_DLL_PUBLIC
+void
+wmem_list_prepend(wmem_list_t *list, void *data);
+
+WS_DLL_PUBLIC
+void
+wmem_list_append(wmem_list_t *list, void *data);
+
+WS_DLL_PUBLIC
+void
+wmem_list_insert_sorted(wmem_list_t *list, void* data, GCompareFunc func);
+
+
+WS_DLL_PUBLIC
+wmem_list_t *
+wmem_list_new(wmem_allocator_t *allocator)
+G_GNUC_MALLOC;
+
+WS_DLL_PUBLIC
+void
+wmem_list_foreach(wmem_list_t *list, GFunc foreach_func, void * user_data);
+
+WS_DLL_PUBLIC
+void
+wmem_destroy_list(wmem_list_t *list);
+
+/** @}
+ * @} */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WMEM_LIST_H__ */
+
+/*
+ * 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/wsutil/wmem/wmem_map.c b/wsutil/wmem/wmem_map.c
new file mode 100644
index 00000000..ea76f455
--- /dev/null
+++ b/wsutil/wmem/wmem_map.c
@@ -0,0 +1,515 @@
+/* wmem_map.c
+ * Wireshark Memory Manager Hash Map
+ * Copyright 2014, Evan Huus <eapache@gmail.com>
+ *
+ * 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 <glib.h>
+
+#include "wmem_core.h"
+#include "wmem_list.h"
+#include "wmem_map.h"
+#include "wmem_map_int.h"
+#include "wmem_user_cb.h"
+
+static uint32_t x; /* Used for universal integer hashing (see the HASH macro) */
+
+/* Used for the wmem_strong_hash() function */
+static uint32_t preseed;
+static uint32_t postseed;
+
+void
+wmem_init_hashing(void)
+{
+ x = g_random_int();
+ if (G_UNLIKELY(x == 0))
+ x = 1;
+
+ preseed = g_random_int();
+ postseed = g_random_int();
+}
+
+typedef struct _wmem_map_item_t {
+ const void *key;
+ void *value;
+ struct _wmem_map_item_t *next;
+} wmem_map_item_t;
+
+struct _wmem_map_t {
+ unsigned count; /* number of items stored */
+
+ /* The base-2 logarithm of the actual size of the table. We store this
+ * value for efficiency in hashing, since finding the actual capacity
+ * becomes just a left-shift (see the CAPACITY macro) whereas taking
+ * logarithms is expensive. */
+ size_t capacity;
+
+ wmem_map_item_t **table;
+
+ GHashFunc hash_func;
+ GEqualFunc eql_func;
+
+ unsigned metadata_scope_cb_id;
+ unsigned data_scope_cb_id;
+
+ wmem_allocator_t *metadata_allocator;
+ wmem_allocator_t *data_allocator;
+};
+
+/* As per the comment on the 'capacity' member of the wmem_map_t struct, this is
+ * the base-2 logarithm, meaning the actual default capacity is 2^5 = 32 */
+#define WMEM_MAP_DEFAULT_CAPACITY 5
+
+/* Macro for calculating the real capacity of the map by using a left-shift to
+ * do the 2^x operation. */
+#define CAPACITY(MAP) (((size_t)1) << (MAP)->capacity)
+
+/* Efficient universal integer hashing:
+ * https://en.wikipedia.org/wiki/Universal_hashing#Avoiding_modular_arithmetic
+ */
+#define HASH(MAP, KEY) \
+ ((uint32_t)(((MAP)->hash_func(KEY) * x) >> (32 - (MAP)->capacity)))
+
+static void
+wmem_map_init_table(wmem_map_t *map)
+{
+ map->count = 0;
+ map->capacity = WMEM_MAP_DEFAULT_CAPACITY;
+ map->table = wmem_alloc0_array(map->data_allocator, wmem_map_item_t*, CAPACITY(map));
+}
+
+wmem_map_t *
+wmem_map_new(wmem_allocator_t *allocator,
+ GHashFunc hash_func, GEqualFunc eql_func)
+{
+ wmem_map_t *map;
+
+ map = wmem_new(allocator, wmem_map_t);
+
+ map->hash_func = hash_func;
+ map->eql_func = eql_func;
+ map->metadata_allocator = allocator;
+ map->data_allocator = allocator;
+ map->count = 0;
+ map->table = NULL;
+
+ return map;
+}
+
+static bool
+wmem_map_reset_cb(wmem_allocator_t *allocator _U_, wmem_cb_event_t event,
+ void *user_data)
+{
+ wmem_map_t *map = (wmem_map_t*)user_data;
+
+ map->count = 0;
+ map->table = NULL;
+
+ if (event == WMEM_CB_DESTROY_EVENT) {
+ wmem_unregister_callback(map->metadata_allocator, map->metadata_scope_cb_id);
+ wmem_free(map->metadata_allocator, map);
+ }
+
+ return true;
+}
+
+static bool
+wmem_map_destroy_cb(wmem_allocator_t *allocator _U_, wmem_cb_event_t event _U_,
+ void *user_data)
+{
+ wmem_map_t *map = (wmem_map_t*)user_data;
+
+ wmem_unregister_callback(map->data_allocator, map->data_scope_cb_id);
+
+ return false;
+}
+
+wmem_map_t *
+wmem_map_new_autoreset(wmem_allocator_t *metadata_scope, wmem_allocator_t *data_scope,
+ GHashFunc hash_func, GEqualFunc eql_func)
+{
+ wmem_map_t *map;
+
+ map = wmem_new(metadata_scope, wmem_map_t);
+
+ map->hash_func = hash_func;
+ map->eql_func = eql_func;
+ map->metadata_allocator = metadata_scope;
+ map->data_allocator = data_scope;
+ map->count = 0;
+ map->table = NULL;
+
+ map->metadata_scope_cb_id = wmem_register_callback(metadata_scope, wmem_map_destroy_cb, map);
+ map->data_scope_cb_id = wmem_register_callback(data_scope, wmem_map_reset_cb, map);
+
+ return map;
+}
+
+static inline void
+wmem_map_grow(wmem_map_t *map)
+{
+ wmem_map_item_t **old_table, *cur, *nxt;
+ size_t old_cap, i;
+ unsigned slot;
+
+ /* store the old table and capacity */
+ old_table = map->table;
+ old_cap = CAPACITY(map);
+
+ /* double the size (capacity is base-2 logarithm, so this just means
+ * increment it) and allocate new table */
+ map->capacity++;
+ map->table = wmem_alloc0_array(map->data_allocator, wmem_map_item_t*, CAPACITY(map));
+
+ /* copy all the elements over from the old table */
+ for (i=0; i<old_cap; i++) {
+ cur = old_table[i];
+ while (cur) {
+ nxt = cur->next;
+ slot = HASH(map, cur->key);
+ cur->next = map->table[slot];
+ map->table[slot] = cur;
+ cur = nxt;
+ }
+ }
+
+ /* free the old table */
+ wmem_free(map->data_allocator, old_table);
+}
+
+void *
+wmem_map_insert(wmem_map_t *map, const void *key, void *value)
+{
+ wmem_map_item_t **item;
+ void *old_val;
+
+ /* Make sure we have a table */
+ if (map->table == NULL) {
+ wmem_map_init_table(map);
+ }
+
+ /* get a pointer to the slot */
+ item = &(map->table[HASH(map, key)]);
+
+ /* check existing items in that slot */
+ while (*item) {
+ if (map->eql_func(key, (*item)->key)) {
+ /* replace and return old value for this key */
+ old_val = (*item)->value;
+ (*item)->value = value;
+ return old_val;
+ }
+ item = &((*item)->next);
+ }
+
+ /* insert new item */
+ (*item) = wmem_new(map->data_allocator, wmem_map_item_t);
+
+ (*item)->key = key;
+ (*item)->value = value;
+ (*item)->next = NULL;
+
+ map->count++;
+
+ /* increase size if we are over-full */
+ if (map->count >= CAPACITY(map)) {
+ wmem_map_grow(map);
+ }
+
+ /* no previous entry, return NULL */
+ return NULL;
+}
+
+bool
+wmem_map_contains(wmem_map_t *map, const void *key)
+{
+ wmem_map_item_t *item;
+
+ /* Make sure we have a table */
+ if (map->table == NULL) {
+ return false;
+ }
+
+ /* find correct slot */
+ item = map->table[HASH(map, key)];
+
+ /* scan list of items in this slot for the correct value */
+ while (item) {
+ if (map->eql_func(key, item->key)) {
+ return true;
+ }
+ item = item->next;
+ }
+
+ return false;
+}
+
+void *
+wmem_map_lookup(wmem_map_t *map, const void *key)
+{
+ wmem_map_item_t *item;
+
+ /* Make sure we have a table */
+ if (map->table == NULL) {
+ return NULL;
+ }
+
+ /* find correct slot */
+ item = map->table[HASH(map, key)];
+
+ /* scan list of items in this slot for the correct value */
+ while (item) {
+ if (map->eql_func(key, item->key)) {
+ return item->value;
+ }
+ item = item->next;
+ }
+
+ return NULL;
+}
+
+bool
+wmem_map_lookup_extended(wmem_map_t *map, const void *key, const void **orig_key, void **value)
+{
+ wmem_map_item_t *item;
+
+ /* Make sure we have a table */
+ if (map->table == NULL) {
+ return false;
+ }
+
+ /* find correct slot */
+ item = map->table[HASH(map, key)];
+
+ /* scan list of items in this slot for the correct value */
+ while (item) {
+ if (map->eql_func(key, item->key)) {
+ if (orig_key) {
+ *orig_key = item->key;
+ }
+ if (value) {
+ *value = item->value;
+ }
+ return true;
+ }
+ item = item->next;
+ }
+
+ return false;
+}
+
+void *
+wmem_map_remove(wmem_map_t *map, const void *key)
+{
+ wmem_map_item_t **item, *tmp;
+ void *value;
+
+ /* Make sure we have a table */
+ if (map->table == NULL) {
+ return NULL;
+ }
+
+ /* get a pointer to the slot */
+ item = &(map->table[HASH(map, key)]);
+
+ /* check the items in that slot */
+ while (*item) {
+ if (map->eql_func(key, (*item)->key)) {
+ /* found it */
+ tmp = (*item);
+ value = tmp->value;
+ (*item) = tmp->next;
+ wmem_free(map->data_allocator, tmp);
+ map->count--;
+ return value;
+ }
+ item = &((*item)->next);
+ }
+
+ /* didn't find it */
+ return NULL;
+}
+
+bool
+wmem_map_steal(wmem_map_t *map, const void *key)
+{
+ wmem_map_item_t **item, *tmp;
+
+ /* Make sure we have a table */
+ if (map->table == NULL) {
+ return false;
+ }
+
+ /* get a pointer to the slot */
+ item = &(map->table[HASH(map, key)]);
+
+ /* check the items in that slot */
+ while (*item) {
+ if (map->eql_func(key, (*item)->key)) {
+ /* found it */
+ tmp = (*item);
+ (*item) = tmp->next;
+ map->count--;
+ return true;
+ }
+ item = &((*item)->next);
+ }
+
+ /* didn't find it */
+ return false;
+}
+
+wmem_list_t*
+wmem_map_get_keys(wmem_allocator_t *list_allocator, wmem_map_t *map)
+{
+ size_t capacity, i;
+ wmem_map_item_t *cur;
+ wmem_list_t* list = wmem_list_new(list_allocator);
+
+ if (map->table != NULL) {
+ capacity = CAPACITY(map);
+
+ /* copy all the elements into the list over from table */
+ for (i=0; i<capacity; i++) {
+ cur = map->table[i];
+ while (cur) {
+ wmem_list_prepend(list, (void*)cur->key);
+ cur = cur->next;
+ }
+ }
+ }
+
+ return list;
+}
+
+void
+wmem_map_foreach(wmem_map_t *map, GHFunc foreach_func, void * user_data)
+{
+ wmem_map_item_t *cur;
+ unsigned i;
+
+ /* Make sure we have a table */
+ if (map->table == NULL) {
+ return;
+ }
+
+ for (i = 0; i < CAPACITY(map); i++) {
+ cur = map->table[i];
+ while (cur) {
+ foreach_func((void *)cur->key, (void *)cur->value, user_data);
+ cur = cur->next;
+ }
+ }
+}
+
+unsigned
+wmem_map_foreach_remove(wmem_map_t *map, GHRFunc foreach_func, void * user_data)
+{
+ wmem_map_item_t **item, *tmp;
+ unsigned i, deleted = 0;
+
+ /* Make sure we have a table */
+ if (map->table == NULL) {
+ return 0;
+ }
+
+ for (i = 0; i < CAPACITY(map); i++) {
+ item = &(map->table[i]);
+ while (*item) {
+ if (foreach_func((void *)(*item)->key, (void *)(*item)->value, user_data)) {
+ tmp = *item;
+ *item = tmp->next;
+ wmem_free(map->data_allocator, tmp);
+ map->count--;
+ deleted++;
+ } else {
+ item = &((*item)->next);
+ }
+ }
+ }
+ return deleted;
+}
+
+unsigned
+wmem_map_size(wmem_map_t *map)
+{
+ return map->count;
+}
+
+/* Borrowed from Perl 5.18. This is based on Bob Jenkin's one-at-a-time
+ * algorithm with some additional randomness seeded in. It is believed to be
+ * generally secure against collision attacks. See
+ * http://blog.booking.com/hardening-perls-hash-function.html
+ */
+uint32_t
+wmem_strong_hash(const uint8_t *buf, const size_t len)
+{
+ const uint8_t * const end = (const uint8_t *)buf + len;
+ uint32_t hash = preseed + (uint32_t)len;
+
+ while (buf < end) {
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ hash += *buf++;
+ }
+
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ hash += ((uint8_t*)&postseed)[0];
+
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ hash += ((uint8_t*)&postseed)[1];
+
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ hash += ((uint8_t*)&postseed)[2];
+
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ hash += ((uint8_t*)&postseed)[3];
+
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ return (hash + (hash << 15));
+}
+
+unsigned
+wmem_str_hash(gconstpointer key)
+{
+ return wmem_strong_hash((const uint8_t *)key, strlen((const char *)key));
+}
+
+unsigned
+wmem_int64_hash(gconstpointer key)
+{
+ return wmem_strong_hash((const uint8_t *)key, sizeof(uint64_t));
+}
+
+unsigned
+wmem_double_hash(gconstpointer key)
+{
+ return wmem_strong_hash((const uint8_t *)key, sizeof(double));
+}
+
+/*
+ * 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/wsutil/wmem/wmem_map.h b/wsutil/wmem/wmem_map.h
new file mode 100644
index 00000000..a6886dd4
--- /dev/null
+++ b/wsutil/wmem/wmem_map.h
@@ -0,0 +1,246 @@
+/** @file
+ * Definitions for the Wireshark Memory Manager Hash Map
+ * Copyright 2014, Evan Huus <eapache@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WMEM_MAP_H__
+#define __WMEM_MAP_H__
+
+#include <glib.h>
+
+#include "wmem_core.h"
+#include "wmem_list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/** @addtogroup wmem
+ * @{
+ * @defgroup wmem-map Hash Map
+ *
+ * A hash map implementation on top of wmem. Provides insertion, deletion and
+ * lookup in expected amortized constant time. Uses universal hashing to map
+ * keys into buckets, and provides a generic strong hash function that makes
+ * it secure against algorithmic complexity attacks, and suitable for use
+ * even with untrusted data.
+ *
+ * @{
+ */
+
+struct _wmem_map_t;
+typedef struct _wmem_map_t wmem_map_t;
+
+/** Creates a map with the given allocator scope. When the scope is emptied,
+ * the map is fully destroyed. Items stored in it will not be freed unless they
+ * were allocated from the same scope. For details on the GHashFunc and
+ * GEqualFunc parameters, see the glib documentation at:
+ * https://developer-old.gnome.org/glib/stable/glib-Hash-Tables.html
+ *
+ * If the keys are coming from untrusted data, do *not* use glib's default hash
+ * functions for strings, int64s or doubles. Wmem provides stronger equivalents
+ * below. Feel free to use the g_direct_hash, g_int_hash, and any of the
+ * g_*_equal functions though, as they should be safe.
+ *
+ * @param allocator The allocator scope with which to create the map.
+ * @param hash_func The hash function used to place inserted keys.
+ * @param eql_func The equality function used to compare inserted keys.
+ * @return The newly-allocated map.
+ */
+WS_DLL_PUBLIC
+wmem_map_t *
+wmem_map_new(wmem_allocator_t *allocator,
+ GHashFunc hash_func, GEqualFunc eql_func)
+G_GNUC_MALLOC;
+
+/** Creates a map with two allocator scopes. The base structure lives in the
+ * metadata scope, and the map data lives in the data scope. Every time free_all
+ * occurs in the data scope the map is transparently emptied without affecting
+ * the location of the base / metadata structure.
+ *
+ * WARNING: None of the map (even the part in the metadata scope) can be used
+ * after the data scope has been *destroyed*.
+ *
+ * The primary use for this function is to create maps that reset for each new
+ * capture file that is loaded. This can be done by specifying wmem_epan_scope()
+ * as the metadata scope and wmem_file_scope() as the data scope.
+ */
+WS_DLL_PUBLIC
+wmem_map_t *
+wmem_map_new_autoreset(wmem_allocator_t *metadata_scope, wmem_allocator_t *data_scope,
+ GHashFunc hash_func, GEqualFunc eql_func)
+G_GNUC_MALLOC;
+
+/** Inserts a value into the map.
+ *
+ * @param map The map to insert into.
+ * @param key The key to insert by.
+ * @param value The value to insert.
+ * @return The previous value stored at this key if any, or NULL.
+ */
+WS_DLL_PUBLIC
+void *
+wmem_map_insert(wmem_map_t *map, const void *key, void *value);
+
+/** Check if a value is in the map.
+ *
+ * @param map The map to search in.
+ * @param key The key to lookup.
+ * @return true if the key is in the map, otherwise false.
+ */
+WS_DLL_PUBLIC
+bool
+wmem_map_contains(wmem_map_t *map, const void *key);
+
+/** Lookup a value in the map.
+ *
+ * @param map The map to search in.
+ * @param key The key to lookup.
+ * @return The value stored at the key if any, or NULL.
+ */
+WS_DLL_PUBLIC
+void *
+wmem_map_lookup(wmem_map_t *map, const void *key);
+
+/** Lookup a value in the map, returning the key, value, and a boolean which
+ * is true if the key is found.
+ *
+ * @param map The map to search in.
+ * @param key The key to lookup.
+ * @param orig_key (optional) The key that was determined to be a match, if any.
+ * @param value (optional) The value stored at the key, if any.
+ * @return true if the key is in the map, otherwise false.
+ */
+WS_DLL_PUBLIC
+bool
+wmem_map_lookup_extended(wmem_map_t *map, const void *key, const void **orig_key, void **value);
+
+/** Remove a value from the map. If no value is stored at that key, nothing
+ * happens.
+ *
+ * @param map The map to remove from.
+ * @param key The key of the value to remove.
+ * @return The (removed) value stored at the key if any, or NULL.
+ */
+WS_DLL_PUBLIC
+void *
+wmem_map_remove(wmem_map_t *map, const void *key);
+
+/** Remove a key and value from the map but does not destroy (free) them. If no
+ * value is stored at that key, nothing happens.
+ *
+ * @param map The map to remove from.
+ * @param key The key of the value to remove.
+ * @return true if key is found false if not.
+ */
+WS_DLL_PUBLIC
+bool
+wmem_map_steal(wmem_map_t *map, const void *key);
+
+/** Retrieves a list of keys inside the map
+ *
+ * @param list_allocator The allocator scope for the returned list.
+ * @param map The map to extract keys from
+ * @return list of keys in the map
+ */
+WS_DLL_PUBLIC
+wmem_list_t*
+wmem_map_get_keys(wmem_allocator_t *list_allocator, wmem_map_t *map);
+
+/** Run a function against all key/value pairs in the map. The order
+ * of the calls is unpredictable, since it is based on the internal
+ * storage of data.
+ *
+ * @param map The map to use
+ * @param foreach_func the function to call for each key/value pair
+ * @param user_data user data to pass to the function
+ */
+WS_DLL_PUBLIC
+void
+wmem_map_foreach(wmem_map_t *map, GHFunc foreach_func, void * user_data);
+
+/** Run a function against all key/value pairs in the map. If the
+ * function returns true, then the key/value pair is removed from
+ * the map. The order of the calls is unpredictable, since it is
+ * based on the internal storage of data.
+ *
+ * @param map The map to use
+ * @param foreach_func the function to call for each key/value pair
+ * @param user_data user data to pass to the function
+ * @return The number of items removed
+ */
+WS_DLL_PUBLIC
+unsigned
+wmem_map_foreach_remove(wmem_map_t *map, GHRFunc foreach_func, void * user_data);
+
+/** Return the number of elements of the map.
+ *
+ * @param map The map to use
+ * @return the number of elements
+*/
+WS_DLL_PUBLIC
+unsigned
+wmem_map_size(wmem_map_t *map);
+
+/** Compute a strong hash value for an arbitrary sequence of bytes. Use of this
+ * hash value should be secure against algorithmic complexity attacks, even for
+ * short keys. The computation uses a random seed which is generated on wmem
+ * initialization, so the same key will hash to different values on different
+ * runs of the application.
+ *
+ * @param buf The buffer of bytes (does not have to be aligned).
+ * @param len The length of buf to use for the hash computation.
+ * @return The hash value.
+ */
+WS_DLL_PUBLIC
+uint32_t
+wmem_strong_hash(const uint8_t *buf, const size_t len);
+
+/** An implementation of GHashFunc using wmem_strong_hash. Prefer this over
+ * g_str_hash when the data comes from an untrusted source.
+ */
+WS_DLL_PUBLIC
+unsigned
+wmem_str_hash(gconstpointer key);
+
+/** An implementation of GHashFunc using wmem_strong_hash. Prefer this over
+ * g_int64_hash when the data comes from an untrusted source.
+ */
+WS_DLL_PUBLIC
+unsigned
+wmem_int64_hash(gconstpointer key);
+
+/** An implementation of GHashFunc using wmem_strong_hash. Prefer this over
+ * g_double_hash when the data comes from an untrusted source.
+ */
+WS_DLL_PUBLIC
+unsigned
+wmem_double_hash(gconstpointer key);
+
+/** @}
+ * @} */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WMEM_MAP_H__ */
+
+/*
+ * 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/wsutil/wmem/wmem_map_int.h b/wsutil/wmem/wmem_map_int.h
new file mode 100644
index 00000000..2798983a
--- /dev/null
+++ b/wsutil/wmem/wmem_map_int.h
@@ -0,0 +1,41 @@
+/** @file
+ *
+ * Definitions for the Wireshark Memory Manager Hash Map Internals
+ * Copyright 2014, Evan Huus <eapache@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WMEM_MAP_INT_H__
+#define __WMEM_MAP_INT_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+WS_DLL_LOCAL
+void
+wmem_init_hashing(void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WMEM_MAP_INT_H__ */
+
+/*
+ * 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/wsutil/wmem/wmem_miscutl.c b/wsutil/wmem/wmem_miscutl.c
new file mode 100644
index 00000000..14426447
--- /dev/null
+++ b/wsutil/wmem/wmem_miscutl.c
@@ -0,0 +1,55 @@
+/* wmem_miscutl.c
+ * Wireshark Memory Manager Misc Utilities
+ * Copyright 2013, Evan Huus <eapache@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <string.h>
+#include <glib.h>
+
+#include "wmem_core.h"
+#include "wmem_miscutl.h"
+
+void *
+wmem_memdup(wmem_allocator_t *allocator, const void *source, const size_t size)
+{
+ void *dest;
+
+ if (!size)
+ return NULL;
+
+ dest = wmem_alloc(allocator, size);
+ memcpy(dest, source, size);
+
+ return dest;
+}
+
+int
+wmem_compare_int(gconstpointer a, gconstpointer b)
+{
+ return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
+}
+
+int
+wmem_compare_uint(gconstpointer a, gconstpointer b)
+{
+ return GPOINTER_TO_UINT(a) > GPOINTER_TO_UINT(b) ? 1 : (GPOINTER_TO_UINT(a) < GPOINTER_TO_UINT(b) ? -1 : 0);
+}
+
+/*
+ * 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/wsutil/wmem/wmem_miscutl.h b/wsutil/wmem/wmem_miscutl.h
new file mode 100644
index 00000000..714ee5cc
--- /dev/null
+++ b/wsutil/wmem/wmem_miscutl.h
@@ -0,0 +1,73 @@
+/** @file
+ * Definitions for the Wireshark Memory Manager Misc Utilities
+ * Copyright 2013, Evan Huus <eapache@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WMEM_MISCUTL_H__
+#define __WMEM_MISCUTL_H__
+
+#include <string.h>
+#include <glib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/** @addtogroup wmem
+ * @{
+ * @defgroup wmem-strutl String Utilities
+ *
+ * A collection of misc. utility functions for wmem.
+ *
+ * @{
+ */
+
+/** Copies a block of memory.
+ *
+ * @param allocator The allocator object to use to allocate memory to copy into.
+ * @param source The pointer to the memory block to copy.
+ * @param size The amount of memory to copy.
+ * @return The location of the memory copy or NULL if size is 0.
+ */
+WS_DLL_PUBLIC
+void *
+wmem_memdup(wmem_allocator_t *allocator, const void *source, const size_t size)
+G_GNUC_MALLOC;
+
+/** Generic GCompareFunc implementations to compare signed/unsigned integer
+ */
+WS_DLL_PUBLIC
+int
+wmem_compare_int(gconstpointer a, gconstpointer b);
+
+WS_DLL_PUBLIC
+int
+wmem_compare_uint(gconstpointer a, gconstpointer b);
+
+/** @}
+ * @} */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WMEM_MISCUTL_H__ */
+
+/*
+ * 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/wsutil/wmem/wmem_multimap.c b/wsutil/wmem/wmem_multimap.c
new file mode 100644
index 00000000..b36e5ced
--- /dev/null
+++ b/wsutil/wmem/wmem_multimap.c
@@ -0,0 +1,185 @@
+/* wmem_multimap.c
+ * Wireshark Memory Manager Hash Multimap
+ * Copyright 2021, John Thacker <johnthacker@gmail.com>
+ * Copyright 2014, Evan Huus <eapache@gmail.com>
+ *
+ * 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 <glib.h>
+
+#include "wmem_core.h"
+#include "wmem_list.h"
+#include "wmem_map.h"
+#include "wmem_multimap.h"
+#include "wmem_tree.h"
+#include "wmem_user_cb.h"
+
+struct _wmem_multimap_t {
+
+ wmem_map_t *map;
+
+ unsigned metadata_scope_cb_id;
+ unsigned data_scope_cb_id;
+
+ wmem_allocator_t *metadata_allocator;
+ wmem_allocator_t *data_allocator;
+};
+
+wmem_multimap_t *
+wmem_multimap_new(wmem_allocator_t *allocator,
+ GHashFunc hash_func, GEqualFunc eql_func)
+{
+ wmem_multimap_t *multimap;
+
+ multimap = wmem_new(allocator, wmem_multimap_t);
+
+ multimap->map = wmem_map_new(allocator, hash_func, eql_func);
+ multimap->metadata_allocator = allocator;
+ multimap->data_allocator = allocator;
+
+ return multimap;
+}
+
+static bool
+wmem_multimap_reset_cb(wmem_allocator_t *allocator _U_, wmem_cb_event_t event,
+ void *user_data)
+{
+ wmem_multimap_t *multimap = (wmem_multimap_t*)user_data;
+
+ if (event == WMEM_CB_DESTROY_EVENT) {
+ wmem_unregister_callback(multimap->metadata_allocator, multimap->metadata_scope_cb_id);
+ wmem_free(multimap->metadata_allocator, multimap);
+ }
+
+ return true;
+}
+
+static bool
+wmem_multimap_destroy_cb(wmem_allocator_t *allocator _U_, wmem_cb_event_t event _U_,
+ void *user_data)
+{
+ wmem_multimap_t *multimap = (wmem_multimap_t*)user_data;
+
+ wmem_unregister_callback(multimap->data_allocator, multimap->data_scope_cb_id);
+
+ return false;
+}
+
+wmem_multimap_t *
+wmem_multimap_new_autoreset(wmem_allocator_t *metadata_scope, wmem_allocator_t *data_scope,
+ GHashFunc hash_func, GEqualFunc eql_func)
+{
+ wmem_multimap_t *multimap;
+
+ multimap = wmem_new(metadata_scope, wmem_multimap_t);
+
+ multimap->map = wmem_map_new_autoreset(metadata_scope, data_scope, hash_func, eql_func);
+ multimap->metadata_allocator = metadata_scope;
+ multimap->data_allocator = data_scope;
+
+ multimap->metadata_scope_cb_id = wmem_register_callback(metadata_scope, wmem_multimap_destroy_cb, multimap);
+ multimap->data_scope_cb_id = wmem_register_callback(data_scope, wmem_multimap_reset_cb, multimap);
+
+ return multimap;
+}
+
+wmem_list_t*
+wmem_multimap_get_keys(wmem_allocator_t *list_allocator, wmem_multimap_t *map)
+{
+ return wmem_map_get_keys(list_allocator, map->map);
+}
+
+static void
+count_nodes(void * key _U_, void * value, void * user_data)
+{
+ unsigned* count = (unsigned*)user_data;
+ (*count) += wmem_tree_count(value);
+}
+
+unsigned
+wmem_multimap_size(wmem_multimap_t *map)
+{
+ unsigned count = 0;
+
+ wmem_map_foreach(map->map, count_nodes, &count);
+ return count;
+}
+
+unsigned
+wmem_multimap_count(wmem_multimap_t *map, const void *key)
+{
+ wmem_tree_t *tree;
+
+ if ((tree = wmem_map_lookup(map->map, key)) == NULL) {
+ return 0;
+ }
+ return wmem_tree_count(tree);
+}
+
+bool
+wmem_multimap_insert32(wmem_multimap_t *map, const void *key, uint32_t frame_num, void *value)
+{
+ wmem_tree_t *tree;
+ bool ret = true;
+
+ if ((tree = wmem_map_lookup(map->map, key)) == NULL) {
+ tree = wmem_tree_new(map->data_allocator);
+ wmem_map_insert(map->map, key, tree);
+ ret = false;
+ }
+ wmem_tree_insert32(tree, frame_num, value);
+
+ return ret;
+}
+
+void *
+wmem_multimap_lookup32(wmem_multimap_t *map, const void *key, uint32_t frame_num)
+{
+ wmem_tree_t *tree;
+
+ if ((tree = wmem_map_lookup(map->map, key)) == NULL) {
+ return NULL;
+ }
+ return wmem_tree_lookup32(tree, frame_num);
+}
+
+void *
+wmem_multimap_lookup32_le(wmem_multimap_t *map, const void *key, uint32_t frame_num)
+{
+ wmem_tree_t *tree;
+
+ if ((tree = wmem_map_lookup(map->map, key)) == NULL) {
+ return NULL;
+ }
+ return wmem_tree_lookup32_le(tree, frame_num);
+}
+
+void *
+wmem_multimap_remove32(wmem_multimap_t *map, const void *key, const uint32_t frame_num)
+{
+ wmem_tree_t *tree;
+
+ if ((tree = wmem_map_lookup(map->map, key)) == NULL) {
+ return NULL;
+ }
+ return wmem_tree_remove32(tree, frame_num);
+}
+
+/*
+ * 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/wsutil/wmem/wmem_multimap.h b/wsutil/wmem/wmem_multimap.h
new file mode 100644
index 00000000..57f0fefb
--- /dev/null
+++ b/wsutil/wmem/wmem_multimap.h
@@ -0,0 +1,195 @@
+/** @file
+ *
+ * Definitions for the Wireshark Memory Manager Hash Multimap
+ * Copyright 2021, John Thacker <johnthacker@gmail.com>
+ * Copyright 2014, Evan Huus <eapache@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WMEM_MULTIMAP_H__
+#define __WMEM_MULTIMAP_H__
+
+#include <glib.h>
+
+#include "wmem_core.h"
+#include "wmem_list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/** @addtogroup wmem
+ * @{
+ * @defgroup wmem-multimap Hash Multimap
+ *
+ * A hash multimap implementation on top of wmem_map and wmem_tree, storing
+ * multiple values at each hash key in a tree indexed by a 32 bit integer.
+ *
+ * The primary use case is a protocol with an ID used as the hash lookup
+ * key that can be reused in a capture, and the frame number used as the
+ * tree key. We often want to find the most recent frame that had a certain
+ * ID, e.g. for request/response matching, and wmem_multimap_lookup32_le()
+ * serves that purpose.
+ *
+ * Since the tree implementation is a self-balancing red-black tree, lookup
+ * time is still O(log(n)) even though elements with equivalent hash keys
+ * are usually added in increasing order of frame number.
+ *
+ * NOTE: The multimap does not yet support inserting items without
+ * specifying the tree key, because the total capacity of individual trees
+ * (including deleted nodes) is not tracked.
+ *
+ * @{
+ */
+
+typedef struct _wmem_multimap_t wmem_multimap_t;
+
+/** Creates a multimap with the given allocator scope. When the scope is emptied,
+ * the map is fully destroyed. Items stored in it will not be freed unless they
+ * were allocated from the same scope.
+ *
+ * @param allocator The allocator scope with which to create the map.
+ * @param hash_func The hash function used to place inserted keys.
+ * @param eql_func The equality function used to compare inserted keys.
+ * @return The newly-allocated map.
+ */
+WS_DLL_PUBLIC
+wmem_multimap_t *
+wmem_multimap_new(wmem_allocator_t *allocator,
+ GHashFunc hash_func, GEqualFunc eql_func)
+G_GNUC_MALLOC;
+
+/** Creates a multimap with two allocator scopes. The base structure lives in the
+ * metadata scope, and the map data lives in the data scope. Every time free_all
+ * occurs in the data scope the map is transparently emptied without affecting
+ * the location of the base / metadata structure.
+ *
+ * WARNING: None of the map (even the part in the metadata scope) can be used
+ * after the data scope has been *destroyed*.
+ *
+ * The primary use for this function is to create maps that reset for each new
+ * capture file that is loaded. This can be done by specifying wmem_epan_scope()
+ * as the metadata scope and wmem_file_scope() as the data scope.
+ */
+WS_DLL_PUBLIC
+wmem_multimap_t *
+wmem_multimap_new_autoreset(wmem_allocator_t *metadata_scope, wmem_allocator_t *data_scope,
+ GHashFunc hash_func, GEqualFunc eql_func)
+G_GNUC_MALLOC;
+
+/** Retrieves a list of the keys inside the multimap
+ *
+ * @param list_allocator The allocator scope for the returned list.
+ * @param map The multimap to extract keys from
+ * @return list of keys in the multimap
+ */
+WS_DLL_PUBLIC
+wmem_list_t*
+wmem_multimap_get_keys(wmem_allocator_t *list_allocator, wmem_multimap_t *map);
+
+/** Return the total number of elements in the multimap.
+ *
+ * @param map The multimap to use
+ * @return the number of elements
+*/
+WS_DLL_PUBLIC
+unsigned
+wmem_multimap_size(wmem_multimap_t *map);
+
+/** Returns the number of values in the multimap with a certain hash key.
+ * (Note: This is the number of current elements, so this can only be used to
+ * safely generate unique tree keys prior to insertion if no values have been
+ * removed, due to how the tree implementation works.)
+ *
+ * @param map The multimap to search in.
+ * @param key The primary key to lookup in the map.
+ * @return The number of values in the tree stored at map key, or zero if no
+ * tree exists at that key.
+ */
+WS_DLL_PUBLIC
+unsigned
+wmem_multimap_count(wmem_multimap_t *map, const void *key);
+
+/** Insert a value in the multimap.
+ *
+ * @param map The multimap to insert into.
+ * @param key The key to insert by in the map.
+ * @param frame_num The key to insert by in the tree.
+ * @param value The value to insert.
+ * @return true if there was already a tree mapped at key, in which case the
+ * caller may safely free key. (This is not necessary if key is allocated with
+ * a wmem pool.)
+ *
+ * Note: as with wmem_tree, if there is already a node with the same pair
+ * of keys, then the existing value will simply be overwritten. This is not
+ * a problem if the value is wmem allocated, but if it is manually managed,
+ * then you must ensure that the pair is unique or do a lookup before inserting.
+ */
+WS_DLL_PUBLIC
+bool
+wmem_multimap_insert32(wmem_multimap_t *map, const void *key, uint32_t frame_num, void *value);
+
+/** Lookup a value in the multimap combination with an exact match.
+ *
+ * @param map The multimap to search in.
+ * @param key The primary key to lookup in the map.
+ * @param frame_num The secondary key to lookup in the tree.
+ * @return The value stored at the keys if any, or NULL.
+ */
+WS_DLL_PUBLIC
+void *
+wmem_multimap_lookup32(wmem_multimap_t *map, const void *key, const uint32_t frame_num);
+
+/** Lookup a value in the multimap with an exact match for the map key
+ * and the largest value less than or equal to the tree key. This is
+ * useful for request/response matching where IDs can be reused.
+ *
+ * @param map The multimap to search in.
+ * @param key The primary key to lookup in the map.
+ * @param frame_num The secondary key to lookup in the tree.
+ * @return The value stored at the primary key in the map and with the largest
+ * key in the tree that is less than or equal to the second key if any, or NULL.
+ */
+WS_DLL_PUBLIC
+void *
+wmem_multimap_lookup32_le(wmem_multimap_t *map, const void *key, const uint32_t frame_num);
+
+/** Remove a value from the multimap. If no value is stored at that key pair,
+ * nothing happens. As with wmem_tree, this is not really a remove, but the
+ * value is set to NULL so that wmem_multimap_lookup32 not will find it.
+ *
+ * @param map The multimap to remove from.
+ * @param key The map key of the value to remove.
+ * @param frame_num The tree key of the value to remove.
+ * @return The (removed) value stored at the key if any, or NULL.
+ */
+WS_DLL_PUBLIC
+void *
+wmem_multimap_remove32(wmem_multimap_t *map, const void *key, const uint32_t frame_num);
+
+/** @}
+ * @} */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WMEM_MULTIMAP_H__ */
+
+/*
+ * 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/wsutil/wmem/wmem_queue.h b/wsutil/wmem/wmem_queue.h
new file mode 100644
index 00000000..49cd3e39
--- /dev/null
+++ b/wsutil/wmem/wmem_queue.h
@@ -0,0 +1,70 @@
+/** @file
+ *
+ * Definitions for the Wireshark Memory Manager Queue
+ * Copyright 2013, Evan Huus <eapache@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WMEM_QUEUE_H__
+#define __WMEM_QUEUE_H__
+
+#include <string.h>
+#include <glib.h>
+
+#include "wmem_core.h"
+#include "wmem_list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/** @addtogroup wmem
+ * @{
+ * @defgroup wmem-queue Queue
+ *
+ * A queue implementation on top of wmem.
+ *
+ * @{
+ */
+
+/* Wmem queue is implemented as a dumb wrapper over Wmem list and stack */
+typedef wmem_list_t wmem_queue_t;
+
+#define wmem_queue_count(X) wmem_list_count(X)
+
+#define wmem_queue_peek(QUEUE) wmem_stack_peek(QUEUE)
+
+#define wmem_queue_pop(QUEUE) wmem_stack_pop(QUEUE)
+
+#define wmem_queue_push(QUEUE, DATA) wmem_list_append((QUEUE), (DATA))
+
+#define wmem_queue_new(ALLOCATOR) wmem_list_new(ALLOCATOR)
+
+#define wmem_destroy_queue(QUEUE) wmem_destroy_list(QUEUE)
+
+/** @}
+ * @} */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WMEM_QUEUE_H__ */
+
+/*
+ * 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/wsutil/wmem/wmem_stack.c b/wsutil/wmem/wmem_stack.c
new file mode 100644
index 00000000..5123aad1
--- /dev/null
+++ b/wsutil/wmem/wmem_stack.c
@@ -0,0 +1,57 @@
+/* wmem_stack.c
+ * Wireshark Memory Manager Stack
+ * Copyright 2012, Evan Huus <eapache@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <string.h>
+#include <glib.h>
+
+#include "wmem-int.h"
+#include "wmem_core.h"
+#include "wmem_stack.h"
+#include "wmem_list.h"
+
+/* Wmem stack is implemented as a simple wrapper over Wmem list */
+
+void *
+wmem_stack_peek(const wmem_stack_t *stack)
+{
+ wmem_list_frame_t *frame;
+
+ frame = wmem_list_head(stack);
+
+ ws_assert(frame);
+
+ return wmem_list_frame_data(frame);
+}
+
+void *
+wmem_stack_pop(wmem_stack_t *stack)
+{
+ void *data;
+
+ data = wmem_stack_peek(stack);
+
+ wmem_list_remove(stack, data);
+
+ return data;
+}
+
+/*
+ * 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/wsutil/wmem/wmem_stack.h b/wsutil/wmem/wmem_stack.h
new file mode 100644
index 00000000..b4fd2d56
--- /dev/null
+++ b/wsutil/wmem/wmem_stack.h
@@ -0,0 +1,73 @@
+/** @file
+ * Definitions for the Wireshark Memory Manager Stack
+ * Copyright 2012, Evan Huus <eapache@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WMEM_STACK_H__
+#define __WMEM_STACK_H__
+
+#include <string.h>
+#include <glib.h>
+
+#include "wmem_core.h"
+#include "wmem_list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/** @addtogroup wmem
+ * @{
+ * @defgroup wmem-stack Stack
+ *
+ * A stack implementation on top of wmem.
+ *
+ * @{
+ */
+
+/* Wmem stack is implemented as a simple wrapper over Wmem list */
+typedef wmem_list_t wmem_stack_t;
+
+#define wmem_stack_count(X) wmem_list_count(X)
+
+WS_DLL_PUBLIC
+void *
+wmem_stack_peek(const wmem_stack_t *stack);
+
+WS_DLL_PUBLIC
+void *
+wmem_stack_pop(wmem_stack_t *stack);
+
+#define wmem_stack_push(STACK, DATA) wmem_list_prepend((STACK), (DATA))
+
+#define wmem_stack_new(ALLOCATOR) wmem_list_new(ALLOCATOR)
+
+#define wmem_destroy_stack(STACK) wmem_destroy_list(STACK)
+
+/** @}
+ * @} */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WMEM_STACK_H__ */
+
+/*
+ * 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/wsutil/wmem/wmem_strbuf.c b/wsutil/wmem/wmem_strbuf.c
new file mode 100644
index 00000000..ff49f6b8
--- /dev/null
+++ b/wsutil/wmem/wmem_strbuf.c
@@ -0,0 +1,470 @@
+/* wmem_strbuf.c
+ * Wireshark Memory Manager String Buffer
+ * Copyright 2012, Evan Huus <eapache@gmail.com>
+ *
+ * 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 "wmem_strbuf.h"
+
+#include <stdio.h>
+#include <errno.h>
+
+#include "wmem-int.h"
+#include "wmem_strutl.h"
+
+#include <wsutil/unicode-utils.h>
+
+#define DEFAULT_MINIMUM_SIZE 16
+
+/* _ROOM accounts for the null-terminator, _RAW_ROOM does not.
+ * Some functions need one, some functions need the other. */
+#define WMEM_STRBUF_ROOM(S) ((S)->alloc_size - (S)->len - 1)
+#define WMEM_STRBUF_RAW_ROOM(S) ((S)->alloc_size - (S)->len)
+
+wmem_strbuf_t *
+wmem_strbuf_new_sized(wmem_allocator_t *allocator,
+ size_t alloc_size)
+{
+ wmem_strbuf_t *strbuf;
+
+ strbuf = wmem_new(allocator, wmem_strbuf_t);
+
+ strbuf->allocator = allocator;
+ strbuf->len = 0;
+ strbuf->alloc_size = alloc_size ? alloc_size : DEFAULT_MINIMUM_SIZE;
+
+ strbuf->str = (char *)wmem_alloc(strbuf->allocator, strbuf->alloc_size);
+ strbuf->str[0] = '\0';
+
+ return strbuf;
+}
+
+wmem_strbuf_t *
+wmem_strbuf_new_len(wmem_allocator_t *allocator, const char *str, size_t len)
+{
+ wmem_strbuf_t *strbuf;
+ size_t alloc_size;
+
+ alloc_size = DEFAULT_MINIMUM_SIZE;
+
+ /* +1 for the null-terminator */
+ while (alloc_size < (len + 1)) {
+ alloc_size *= 2;
+ }
+
+ strbuf = wmem_strbuf_new_sized(allocator, alloc_size);
+
+ if (str && len > 0) {
+ ws_assert(strbuf->alloc_size >= len + 1);
+ memcpy(strbuf->str, str, len);
+ strbuf->str[len] = '\0';
+ strbuf->len = len;
+ }
+
+ return strbuf;
+}
+
+wmem_strbuf_t *
+wmem_strbuf_new(wmem_allocator_t *allocator, const char *str)
+{
+ return wmem_strbuf_new_len(allocator, str, str ? strlen(str) : 0);
+}
+
+wmem_strbuf_t *
+wmem_strbuf_dup(wmem_allocator_t *allocator, const wmem_strbuf_t *src)
+{
+ wmem_strbuf_t *new;
+
+ new = wmem_strbuf_new_sized(allocator, src->alloc_size);
+ new->len = src->len;
+ memcpy(new->str, src->str, new->len);
+ new->str[new->len] = '\0';
+ return new;
+}
+
+/* grows the allocated size of the wmem_strbuf_t. If max_size is set, then
+ * not guaranteed to grow by the full amount to_add */
+static inline void
+wmem_strbuf_grow(wmem_strbuf_t *strbuf, const size_t to_add)
+{
+ size_t new_alloc_len, new_len;
+
+ /* short-circuit for efficiency if we have room already; greatly speeds up
+ * repeated calls to wmem_strbuf_append_c and others which grow a little bit
+ * at a time.
+ */
+ if (WMEM_STRBUF_ROOM(strbuf) >= to_add) {
+ return;
+ }
+
+ new_alloc_len = strbuf->alloc_size;
+ new_len = strbuf->len + to_add;
+
+ /* +1 for the null-terminator */
+ while (new_alloc_len < (new_len + 1)) {
+ new_alloc_len *= 2;
+ }
+
+ if (new_alloc_len == strbuf->alloc_size) {
+ return;
+ }
+
+ strbuf->str = (char *)wmem_realloc(strbuf->allocator, strbuf->str, new_alloc_len);
+
+ strbuf->alloc_size = new_alloc_len;
+}
+
+void
+wmem_strbuf_append(wmem_strbuf_t *strbuf, const char *str)
+{
+ size_t append_len;
+
+ if (!str || str[0] == '\0') {
+ return;
+ }
+
+ append_len = strlen(str);
+ wmem_strbuf_grow(strbuf, append_len);
+
+ ws_assert(WMEM_STRBUF_RAW_ROOM(strbuf) >= append_len + 1);
+ memcpy(&strbuf->str[strbuf->len], str, append_len);
+ strbuf->len += append_len;
+ strbuf->str[strbuf->len] = '\0';
+}
+
+void
+wmem_strbuf_append_len(wmem_strbuf_t *strbuf, const char *str, size_t append_len)
+{
+
+ if (!append_len || !str) {
+ return;
+ }
+
+ wmem_strbuf_grow(strbuf, append_len);
+
+ memcpy(&strbuf->str[strbuf->len], str, append_len);
+ strbuf->len += append_len;
+ strbuf->str[strbuf->len] = '\0';
+}
+
+static inline
+int _strbuf_vsnprintf(wmem_strbuf_t *strbuf, const char *format, va_list ap)
+{
+ int want_len;
+ char *buffer = &strbuf->str[strbuf->len];
+ size_t buffer_size = WMEM_STRBUF_RAW_ROOM(strbuf);
+
+ want_len = vsnprintf(buffer, buffer_size, format, ap);
+ if (want_len < 0) {
+ /* Error. */
+ g_warning("%s: vsnprintf: (%d) %s", G_STRFUNC, want_len, g_strerror(errno));
+ return -1;
+ }
+ if ((size_t)want_len < buffer_size) {
+ /* Success. */
+ strbuf->len += want_len;
+ return 0;
+ }
+
+ /* Not enough space in buffer, output was truncated. */
+ strbuf->str[strbuf->len] = '\0'; /* Reset. */
+
+ return want_len; /* Length (not including terminating null) that would be written
+ if there was enough space in buffer. */
+}
+
+void
+wmem_strbuf_append_vprintf(wmem_strbuf_t *strbuf, const char *fmt, va_list ap)
+{
+ int want_len;
+ va_list ap2;
+
+ va_copy(ap2, ap);
+ /* Try to write buffer, check if output fits. */
+ want_len = _strbuf_vsnprintf(strbuf, fmt, ap2);
+ va_end(ap2);
+ if (want_len <= 0)
+ return;
+
+ /* Resize buffer and try again. */
+ wmem_strbuf_grow(strbuf, want_len);
+ want_len = _strbuf_vsnprintf(strbuf, fmt, ap);
+ /* Second time must succeed or error out. */
+ ws_assert(want_len <= 0);
+}
+
+void
+wmem_strbuf_append_printf(wmem_strbuf_t *strbuf, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ wmem_strbuf_append_vprintf(strbuf, format, ap);
+ va_end(ap);
+}
+
+void
+wmem_strbuf_append_c(wmem_strbuf_t *strbuf, const char c)
+{
+ wmem_strbuf_grow(strbuf, 1);
+
+ strbuf->str[strbuf->len] = c;
+ strbuf->len++;
+ strbuf->str[strbuf->len] = '\0';
+}
+
+void
+wmem_strbuf_append_c_count(wmem_strbuf_t *strbuf, const char c, size_t count)
+{
+ wmem_strbuf_grow(strbuf, count);
+
+ while (count-- > 0) {
+ strbuf->str[strbuf->len++] = c;
+ }
+ strbuf->str[strbuf->len] = '\0';
+}
+
+void
+wmem_strbuf_append_unichar(wmem_strbuf_t *strbuf, const gunichar c)
+{
+ char buf[6];
+ size_t charlen;
+
+ charlen = g_unichar_to_utf8(c, buf);
+
+ wmem_strbuf_grow(strbuf, charlen);
+
+ memcpy(&strbuf->str[strbuf->len], buf, charlen);
+ strbuf->len += charlen;
+ strbuf->str[strbuf->len] = '\0';
+}
+
+void
+wmem_strbuf_append_unichar_validated(wmem_strbuf_t *strbuf, const gunichar c)
+{
+ if (g_unichar_validate(c)) {
+ wmem_strbuf_append_unichar(strbuf, c);
+ } else {
+ wmem_strbuf_append_unichar(strbuf, UNICODE_REPLACEMENT_CHARACTER);
+ }
+}
+
+static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+#define HEX_CODELEN 4
+
+void
+wmem_strbuf_append_hex(wmem_strbuf_t *strbuf, uint8_t ch)
+{
+ wmem_strbuf_grow(strbuf, HEX_CODELEN * 1);
+
+ strbuf->str[strbuf->len++] = '\\';
+ strbuf->str[strbuf->len++] = 'x';
+ strbuf->str[strbuf->len++] = hex[(ch >> 4) & 0xF];
+ strbuf->str[strbuf->len++] = hex[(ch >> 0) & 0xF];
+ strbuf->str[strbuf->len] = '\0';
+}
+
+#define BMP_CODELEN 6
+
+static inline
+void append_hex_bmp(wmem_strbuf_t *strbuf, gunichar ch)
+{
+ wmem_strbuf_grow(strbuf, BMP_CODELEN * 1);
+
+ strbuf->str[strbuf->len++] = '\\';
+ strbuf->str[strbuf->len++] = 'u';
+ strbuf->str[strbuf->len++] = hex[(ch >> 12) & 0xF];
+ strbuf->str[strbuf->len++] = hex[(ch >> 8) & 0xF];
+ strbuf->str[strbuf->len++] = hex[(ch >> 4) & 0xF];
+ strbuf->str[strbuf->len++] = hex[(ch >> 0) & 0xF];
+ strbuf->str[strbuf->len] = '\0';
+}
+
+#define ANY_CODELEN 10
+
+static inline
+void append_hex_any(wmem_strbuf_t *strbuf, gunichar ch)
+{
+ wmem_strbuf_grow(strbuf, ANY_CODELEN * 1);
+
+ strbuf->str[strbuf->len++] = '\\';
+ strbuf->str[strbuf->len++] = 'U';
+ strbuf->str[strbuf->len++] = hex[(ch >> 28) & 0xF];
+ strbuf->str[strbuf->len++] = hex[(ch >> 24) & 0xF];
+ strbuf->str[strbuf->len++] = hex[(ch >> 20) & 0xF];
+ strbuf->str[strbuf->len++] = hex[(ch >> 16) & 0xF];
+ strbuf->str[strbuf->len++] = hex[(ch >> 12) & 0xF];
+ strbuf->str[strbuf->len++] = hex[(ch >> 8) & 0xF];
+ strbuf->str[strbuf->len++] = hex[(ch >> 4) & 0xF];
+ strbuf->str[strbuf->len++] = hex[(ch >> 0) & 0xF];
+ strbuf->str[strbuf->len] = '\0';
+}
+
+size_t
+wmem_strbuf_append_hex_unichar(wmem_strbuf_t *strbuf, gunichar ch)
+{
+ if (ch <= 0x7f) {
+ wmem_strbuf_append_hex(strbuf, (uint8_t)ch);
+ return HEX_CODELEN;
+ }
+ if (ch <= 0xffff) {
+ append_hex_bmp(strbuf, ch);
+ return BMP_CODELEN;
+ }
+ append_hex_any(strbuf, ch);
+ return ANY_CODELEN;
+}
+
+void
+wmem_strbuf_truncate(wmem_strbuf_t *strbuf, const size_t len)
+{
+ if (len >= strbuf->len) {
+ return;
+ }
+
+ strbuf->str[len] = '\0';
+ strbuf->len = len;
+}
+
+const char *
+wmem_strbuf_get_str(const wmem_strbuf_t *strbuf)
+{
+ return strbuf->str;
+}
+
+size_t
+wmem_strbuf_get_len(const wmem_strbuf_t *strbuf)
+{
+ return strbuf->len;
+}
+
+static inline int
+_memcmp_len(const void *s1, size_t s1_len, const void *s2, size_t s2_len)
+{
+ size_t len;
+ int cmp;
+
+ len = MIN(s1_len, s2_len);
+ if ((cmp = memcmp(s1, s2, len)) != 0)
+ return cmp;
+ if (s1_len < s2_len)
+ return -1;
+ if (s1_len > s2_len)
+ return 1;
+ return 0;
+}
+
+WS_DLL_PUBLIC
+int
+wmem_strbuf_strcmp(const wmem_strbuf_t *sb1, const wmem_strbuf_t *sb2)
+{
+ return _memcmp_len(sb1->str, sb1->len, sb2->str, sb2->len);
+}
+
+const char *
+wmem_strbuf_strstr(const wmem_strbuf_t *haystack, const wmem_strbuf_t *needle)
+{
+ return ws_memmem(haystack->str, haystack->len, needle->str, needle->len);
+}
+
+/* Truncates the allocated memory down to the minimal amount, frees the header
+ * structure, and returns a non-const pointer to the raw string. The
+ * wmem_strbuf_t structure cannot be used after this is called.
+ */
+char *
+wmem_strbuf_finalize(wmem_strbuf_t *strbuf)
+{
+ if (strbuf == NULL)
+ return NULL;
+
+ char *ret = (char *)wmem_realloc(strbuf->allocator, strbuf->str, strbuf->len+1);
+
+ wmem_free(strbuf->allocator, strbuf);
+
+ return ret;
+}
+
+void
+wmem_strbuf_destroy(wmem_strbuf_t *strbuf)
+{
+ if (strbuf == NULL)
+ return;
+
+ wmem_free(strbuf->allocator, strbuf->str);
+ wmem_free(strbuf->allocator, strbuf);
+}
+
+static bool
+string_utf8_validate(const char *str, ssize_t max_len, const char **endpptr)
+{
+ bool valid;
+ const char *endp;
+
+ if (max_len <= 0) {
+ if (endpptr) {
+ *endpptr = str;
+ }
+ return true;
+ }
+
+ valid = g_utf8_validate(str, max_len, &endp);
+
+ if (valid || *endp != '\0') {
+ if (endpptr) {
+ *endpptr = endp;
+ }
+ return valid;
+ }
+
+ /* Invalid because of a nul byte. Skip nuls and continue. */
+ max_len -= endp - str;
+ str = endp;
+ while (max_len > 0 && *str == '\0') {
+ str++;
+ max_len--;
+ }
+ return string_utf8_validate(str, max_len, endpptr);
+}
+
+/* g_utf8_validate() returns false in the string contains embedded NUL
+ * bytes. We accept \x00 as valid and work around that to validate the
+ * entire len bytes. */
+bool
+wmem_strbuf_utf8_validate(wmem_strbuf_t *strbuf, const char **endpptr)
+{
+ return string_utf8_validate(strbuf->str, strbuf->len, endpptr);
+}
+
+void
+wmem_strbuf_utf8_make_valid(wmem_strbuf_t *strbuf)
+{
+ wmem_strbuf_t *tmp = ws_utf8_make_valid_strbuf(strbuf->allocator, strbuf->str, strbuf->len);
+
+ wmem_free(strbuf->allocator, strbuf->str);
+ strbuf->str = tmp->str;
+ strbuf->len = tmp->len;
+ strbuf->alloc_size = tmp->alloc_size;
+
+ wmem_free(strbuf->allocator, tmp);
+}
+
+/*
+ * 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/wsutil/wmem/wmem_strbuf.h b/wsutil/wmem/wmem_strbuf.h
new file mode 100644
index 00000000..9c00b6e4
--- /dev/null
+++ b/wsutil/wmem/wmem_strbuf.h
@@ -0,0 +1,194 @@
+/** @file
+ * Definitions for the Wireshark Memory Manager String Buffer
+ * Copyright 2012, Evan Huus <eapache@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WMEM_STRBUF_H__
+#define __WMEM_STRBUF_H__
+
+#include <ws_codepoints.h>
+
+#include "wmem_core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/** @addtogroup wmem
+ * @{
+ * @defgroup wmem-strbuf String Buffer
+ *
+ * A string object implementation on top of wmem.
+ *
+ * @{
+ */
+
+/* Holds a wmem-allocated string-buffer.
+ * len is the length of the string (not counting the null-terminator) and
+ * should be the same as strlen(str) unless the string contains embedded
+ * nulls.
+ * alloc_size is the size of the raw buffer pointed to by str, regardless of
+ * what string is actually being stored (i.e. the buffer contents)
+ * max_size is the maximum permitted alloc_size (NOT the maximum permitted len,
+ * which must be one shorter than alloc_size to permit null-termination).
+ * When max_size is 0 (the default), no maximum is enforced.
+ */
+struct _wmem_strbuf_t {
+ /* read-only fields */
+ wmem_allocator_t *allocator;
+ char *str;
+ size_t len;
+
+ /* private fields */
+ size_t alloc_size;
+};
+
+typedef struct _wmem_strbuf_t wmem_strbuf_t;
+
+WS_DLL_PUBLIC
+wmem_strbuf_t *
+wmem_strbuf_new_sized(wmem_allocator_t *allocator, size_t alloc_size)
+G_GNUC_MALLOC;
+
+WS_DLL_PUBLIC
+wmem_strbuf_t *
+wmem_strbuf_new(wmem_allocator_t *allocator, const char *str)
+G_GNUC_MALLOC;
+
+#define wmem_strbuf_create(allocator) \
+ wmem_strbuf_new(allocator, "")
+
+WS_DLL_PUBLIC
+wmem_strbuf_t *
+wmem_strbuf_new_len(wmem_allocator_t *allocator, const char *str, size_t len)
+G_GNUC_MALLOC;
+
+WS_DLL_PUBLIC
+wmem_strbuf_t *
+wmem_strbuf_dup(wmem_allocator_t *allocator, const wmem_strbuf_t *strbuf)
+G_GNUC_MALLOC;
+
+WS_DLL_PUBLIC
+void
+wmem_strbuf_append(wmem_strbuf_t *strbuf, const char *str);
+
+/* Appends up to append_len bytes (as allowed by strbuf->max_size) from
+ * str. Ensures that strbuf is null terminated afterwards but will copy
+ * embedded nulls. */
+WS_DLL_PUBLIC
+void
+wmem_strbuf_append_len(wmem_strbuf_t *strbuf, const char *str, size_t append_len);
+
+WS_DLL_PUBLIC
+void
+wmem_strbuf_append_printf(wmem_strbuf_t *strbuf, const char *format, ...)
+G_GNUC_PRINTF(2, 3);
+
+WS_DLL_PUBLIC
+void
+wmem_strbuf_append_vprintf(wmem_strbuf_t *strbuf, const char *fmt, va_list ap);
+
+WS_DLL_PUBLIC
+void
+wmem_strbuf_append_c(wmem_strbuf_t *strbuf, const char c);
+
+WS_DLL_PUBLIC
+void
+wmem_strbuf_append_c_count(wmem_strbuf_t *strbuf, const char c, size_t count);
+
+WS_DLL_PUBLIC
+void
+wmem_strbuf_append_unichar(wmem_strbuf_t *strbuf, const gunichar c);
+
+#define wmem_strbuf_append_unichar_repl(buf) \
+ wmem_strbuf_append_unichar(buf, UNICODE_REPLACEMENT_CHARACTER)
+
+/* As wmem_strbuf_append_unichar but appends a REPLACEMENT CHARACTER
+ * instead for any invalid Unicode codepoints.
+ */
+WS_DLL_PUBLIC
+void
+wmem_strbuf_append_unichar_validated(wmem_strbuf_t *strbuf, const gunichar c);
+
+WS_DLL_PUBLIC
+void
+wmem_strbuf_append_hex(wmem_strbuf_t *strbuf, uint8_t);
+
+/* Returns the number of characters written (4, 6 or 10). */
+WS_DLL_PUBLIC
+size_t
+wmem_strbuf_append_hex_unichar(wmem_strbuf_t *strbuf, gunichar);
+
+WS_DLL_PUBLIC
+void
+wmem_strbuf_truncate(wmem_strbuf_t *strbuf, const size_t len);
+
+WS_DLL_PUBLIC
+const char *
+wmem_strbuf_get_str(const wmem_strbuf_t *strbuf);
+
+WS_DLL_PUBLIC
+size_t
+wmem_strbuf_get_len(const wmem_strbuf_t *strbuf);
+
+WS_DLL_PUBLIC
+int
+wmem_strbuf_strcmp(const wmem_strbuf_t *sb1, const wmem_strbuf_t *sb2);
+
+WS_DLL_PUBLIC
+const char *
+wmem_strbuf_strstr(const wmem_strbuf_t *haystack, const wmem_strbuf_t *needle);
+
+/** Truncates the allocated memory down to the minimal amount, frees the header
+ * structure, and returns a non-const pointer to the raw string. The
+ * wmem_strbuf_t structure cannot be used after this is called. Basically a
+ * destructor for when you still need the underlying C-string.
+ */
+WS_DLL_PUBLIC
+char *
+wmem_strbuf_finalize(wmem_strbuf_t *strbuf);
+
+WS_DLL_PUBLIC
+void
+wmem_strbuf_destroy(wmem_strbuf_t *strbuf);
+
+/* Validates the string buffer as UTF-8.
+ * Unlike g_utf8_validate(), accepts embedded NUL bytes as valid UTF-8.
+ * If endpptr is non-NULL, then the end of the valid range is stored there
+ * (i.e. the first invalid character, or the end of the buffer otherwise).
+ */
+WS_DLL_PUBLIC
+bool
+wmem_strbuf_utf8_validate(wmem_strbuf_t *strbuf, const char **endptr);
+
+WS_DLL_PUBLIC
+void
+wmem_strbuf_utf8_make_valid(wmem_strbuf_t *strbuf);
+
+/** @}
+ * @} */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WMEM_STRBUF_H__ */
+
+/*
+ * 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/wsutil/wmem/wmem_strutl.c b/wsutil/wmem/wmem_strutl.c
new file mode 100644
index 00000000..99f1f8c6
--- /dev/null
+++ b/wsutil/wmem/wmem_strutl.c
@@ -0,0 +1,159 @@
+/* wmem_strutl.c
+ * Wireshark Memory Manager String Utilities
+ * Copyright 2012, Evan Huus <eapache@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#define _GNU_SOURCE
+#include "config.h"
+#include "wmem_strutl.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+char *
+wmem_strdup(wmem_allocator_t *allocator, const char *src)
+{
+ size_t len;
+
+ /* If the string is NULL, just return the string "<NULL>" so that the
+ * callers don't have to bother checking it. */
+ if (!src) {
+ src = "<NULL>";
+ }
+
+ len = strlen(src) + 1; /* +1 for the null-terminator */
+
+ return (char *)memcpy(wmem_alloc(allocator, len), src, len);
+}
+
+char *
+wmem_strndup(wmem_allocator_t *allocator, const char *src, const size_t len)
+{
+ char *dst;
+ unsigned i;
+
+ dst = (char *)wmem_alloc(allocator, len+1);
+
+ for (i=0; (i < len) && src[i]; i++) {
+ dst[i] = src[i];
+ }
+
+ dst[i] = '\0';
+
+ return dst;
+}
+
+char *
+wmem_strdup_printf(wmem_allocator_t *allocator, const char *fmt, ...)
+{
+ va_list ap;
+ char *dst;
+
+ va_start(ap, fmt);
+ dst = wmem_strdup_vprintf(allocator, fmt, ap);
+ va_end(ap);
+
+ return dst;
+}
+
+#ifdef HAVE_VASPRINTF
+static char *
+_strdup_vasprintf(const char *fmt, va_list ap)
+{
+ char *str = NULL;
+ int ret;
+
+ ret = vasprintf(&str, fmt, ap);
+ if (ret == -1 && errno == ENOMEM) {
+ /* Out of memory. We have to mimic GLib here and abort. */
+ g_error("%s: failed to allocate memory", G_STRLOC);
+ }
+ return str;
+}
+#endif /* HAVE_VASPRINTF */
+
+#define WMEM_STRDUP_VPRINTF_DEFAULT_BUFFER 256
+char *
+wmem_strdup_vprintf(wmem_allocator_t *allocator, const char *fmt, va_list ap)
+{
+ va_list ap2;
+ char buf[WMEM_STRDUP_VPRINTF_DEFAULT_BUFFER];
+ int needed_len;
+ char *new_buf;
+ size_t new_buf_size;
+
+#ifdef HAVE_VASPRINTF
+ if (allocator == NULL) {
+ return _strdup_vasprintf(fmt, ap);
+ }
+#endif
+
+ va_copy(ap2, ap);
+ needed_len = vsnprintf(buf, sizeof(buf), fmt, ap2);
+ va_end(ap2);
+
+ new_buf_size = needed_len + 1;
+ new_buf = wmem_alloc(allocator, new_buf_size);
+
+ if (new_buf_size <= WMEM_STRDUP_VPRINTF_DEFAULT_BUFFER) {
+ memcpy(new_buf, buf, new_buf_size);
+ return new_buf;
+ }
+ vsnprintf(new_buf, new_buf_size, fmt, ap);
+ return new_buf;
+}
+
+/* Return the first occurrence of needle in haystack.
+ * If not found, return NULL.
+ * If either haystack or needle has 0 length, return NULL.*/
+const uint8_t *
+ws_memmem(const void *_haystack, size_t haystack_len,
+ const void *_needle, size_t needle_len)
+{
+#ifdef HAVE_MEMMEM
+ return memmem(_haystack, haystack_len, _needle, needle_len);
+#else
+ /* Algorithm copied from GNU's glibc 2.3.2 memmem() under LGPL 2.1+ */
+ const uint8_t *haystack = _haystack;
+ const uint8_t *needle = _needle;
+ const uint8_t *begin;
+ const uint8_t *const last_possible = haystack + haystack_len - needle_len;
+
+ if (needle_len == 0) {
+ return NULL;
+ }
+
+ if (needle_len > haystack_len) {
+ return NULL;
+ }
+
+ for (begin = haystack ; begin <= last_possible; ++begin) {
+ if (begin[0] == needle[0] &&
+ !memcmp(&begin[1], needle + 1,
+ needle_len - 1)) {
+ return begin;
+ }
+ }
+
+ return NULL;
+#endif /* HAVE_MEMMEM */
+}
+
+/*
+ * 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/wsutil/wmem/wmem_strutl.h b/wsutil/wmem/wmem_strutl.h
new file mode 100644
index 00000000..ee3aed65
--- /dev/null
+++ b/wsutil/wmem/wmem_strutl.h
@@ -0,0 +1,95 @@
+/** @file
+ * Definitions for the Wireshark Memory Manager String Utilities
+ * Copyright 2012, Evan Huus <eapache@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WMEM_STRUTL_H__
+#define __WMEM_STRUTL_H__
+
+#include <stdarg.h>
+
+#include "wmem_core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/** @addtogroup wmem
+ * @{
+ * @defgroup wmem-strutl String Utilities
+ *
+ * A collection of utility function for operating on C strings with wmem.
+ *
+ * @{
+ */
+
+WS_DLL_PUBLIC
+char *
+wmem_strdup(wmem_allocator_t *allocator, const char *src)
+G_GNUC_MALLOC;
+
+#define ws_strdup(src) wmem_strdup(NULL, src)
+
+WS_DLL_PUBLIC
+char *
+wmem_strndup(wmem_allocator_t *allocator, const char *src, const size_t len)
+G_GNUC_MALLOC;
+
+#define ws_strndup(src, len) wmem_strndup(NULL, src, len)
+
+WS_DLL_PUBLIC
+char *
+wmem_strdup_printf(wmem_allocator_t *allocator, const char *fmt, ...)
+G_GNUC_MALLOC G_GNUC_PRINTF(2, 3);
+
+#define ws_strdup_printf(...) wmem_strdup_printf(NULL, __VA_ARGS__)
+
+WS_DLL_PUBLIC
+char *
+wmem_strdup_vprintf(wmem_allocator_t *allocator, const char *fmt, va_list ap)
+G_GNUC_MALLOC;
+
+#define ws_strdup_vprintf(fmt, ap) wmem_strdup_vprintf(NULL, fmt, ap)
+
+/**
+ * Return the first occurrence of needle in haystack.
+ *
+ * @param haystack The data to search
+ * @param haystack_len The length of the search data
+ * @param needle The string to look for
+ * @param needle_len The length of the search string
+ * @return A pointer to the first occurrence of "needle" in
+ * "haystack". If "needle" isn't found or is NULL, or if
+ * "needle_len" is 0, NULL is returned.
+ */
+WS_DLL_PUBLIC
+const uint8_t *ws_memmem(const void *haystack, size_t haystack_len,
+ const void *needle, size_t needle_len);
+
+/** @}
+ * @} */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WMEM_STRUTL_H__ */
+
+/*
+ * 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/wsutil/wmem/wmem_test.c b/wsutil/wmem/wmem_test.c
new file mode 100644
index 00000000..0dc908b7
--- /dev/null
+++ b/wsutil/wmem/wmem_test.c
@@ -0,0 +1,1496 @@
+/* wmem_test.c
+ * Wireshark Memory Manager Tests
+ * Copyright 2012, Evan Huus <eapache@gmail.com>
+ *
+ * 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 <stdio.h>
+#include <glib.h>
+
+#include "wmem.h"
+#include "wmem_tree-int.h"
+#include "wmem_allocator.h"
+#include "wmem_allocator_block.h"
+#include "wmem_allocator_block_fast.h"
+#include "wmem_allocator_simple.h"
+#include "wmem_allocator_strict.h"
+
+#include <wsutil/time_util.h>
+
+#define STRING_80 "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
+#define MAX_ALLOC_SIZE (1024*64)
+#define MAX_SIMULTANEOUS_ALLOCS 1024
+#define CONTAINER_ITERS 10000
+
+typedef void (*wmem_verify_func)(wmem_allocator_t *allocator);
+
+/* A local copy of wmem_allocator_new that ignores the
+ * WIRESHARK_DEBUG_WMEM_OVERRIDE variable so that test functions are
+ * guaranteed to actually get the allocator type they asked for */
+static wmem_allocator_t *
+wmem_allocator_force_new(const wmem_allocator_type_t type)
+{
+ wmem_allocator_t *allocator;
+
+ allocator = wmem_new(NULL, wmem_allocator_t);
+ allocator->type = type;
+ allocator->callbacks = NULL;
+ allocator->in_scope = true;
+
+ switch (type) {
+ case WMEM_ALLOCATOR_SIMPLE:
+ wmem_simple_allocator_init(allocator);
+ break;
+ case WMEM_ALLOCATOR_BLOCK:
+ wmem_block_allocator_init(allocator);
+ break;
+ case WMEM_ALLOCATOR_BLOCK_FAST:
+ wmem_block_fast_allocator_init(allocator);
+ break;
+ case WMEM_ALLOCATOR_STRICT:
+ wmem_strict_allocator_init(allocator);
+ break;
+ default:
+ g_assert_not_reached();
+ /* This is necessary to squelch MSVC errors; is there
+ any way to tell it that g_assert_not_reached()
+ never returns? */
+ return NULL;
+ };
+
+ return allocator;
+}
+
+/* A helper for generating pseudo-random strings. Just uses glib's random number
+ * functions to generate 'numbers' in the printable character range. */
+static char *
+wmem_test_rand_string(wmem_allocator_t *allocator, int minlen, int maxlen)
+{
+ char *str;
+ int len, i;
+
+ len = g_random_int_range(minlen, maxlen);
+
+ /* +1 for null-terminator */
+ str = (char*)wmem_alloc(allocator, len + 1);
+ str[len] = '\0';
+
+ for (i=0; i<len; i++) {
+ /* ASCII normal printable range is 32 (space) to 126 (tilde) */
+ str[i] = (char) g_random_int_range(32, 126);
+ }
+
+ return str;
+}
+
+static int
+wmem_test_compare_guint32(const void *a, const void *b)
+{
+ uint32_t l, r;
+
+ l = *(const uint32_t*)a;
+ r = *(const uint32_t*)b;
+
+ return l - r;
+}
+
+/* Some helpers for properly testing callback functionality */
+wmem_allocator_t *expected_allocator;
+void *expected_user_data;
+wmem_cb_event_t expected_event;
+int cb_called_count;
+int cb_continue_count;
+bool value_seen[CONTAINER_ITERS];
+
+static bool
+wmem_test_cb(wmem_allocator_t *allocator, wmem_cb_event_t event,
+ void *user_data)
+{
+ g_assert_true(allocator == expected_allocator);
+ g_assert_true(event == expected_event);
+
+ cb_called_count++;
+
+ return *(bool*)user_data;
+}
+
+static bool
+wmem_test_foreach_cb(const void *key _U_, void *value, void *user_data)
+{
+ g_assert_true(user_data == expected_user_data);
+
+ g_assert_true(! value_seen[GPOINTER_TO_INT(value)]);
+ value_seen[GPOINTER_TO_INT(value)] = true;
+
+ cb_called_count++;
+ cb_continue_count--;
+
+ return (cb_continue_count == 0);
+}
+
+/* ALLOCATOR TESTING FUNCTIONS (/wmem/allocator/) */
+
+static void
+wmem_test_allocator_callbacks(void)
+{
+ wmem_allocator_t *allocator;
+ bool t = true;
+ bool f = false;
+ unsigned cb_id;
+
+ allocator = wmem_allocator_new(WMEM_ALLOCATOR_STRICT);
+
+ expected_allocator = allocator;
+
+ wmem_register_callback(expected_allocator, &wmem_test_cb, &f);
+ wmem_register_callback(expected_allocator, &wmem_test_cb, &f);
+ cb_id = wmem_register_callback(expected_allocator, &wmem_test_cb, &t);
+ wmem_register_callback(expected_allocator, &wmem_test_cb, &t);
+ wmem_register_callback(expected_allocator, &wmem_test_cb, &f);
+
+ expected_event = WMEM_CB_FREE_EVENT;
+
+ cb_called_count = 0;
+ wmem_free_all(allocator);
+ g_assert_true(cb_called_count == 5);
+
+ cb_called_count = 0;
+ wmem_free_all(allocator);
+ g_assert_true(cb_called_count == 2);
+
+ cb_called_count = 0;
+ wmem_free_all(allocator);
+ g_assert_true(cb_called_count == 2);
+
+ wmem_unregister_callback(allocator, cb_id);
+ cb_called_count = 0;
+ wmem_free_all(allocator);
+ g_assert_true(cb_called_count == 1);
+
+ cb_id = wmem_register_callback(expected_allocator, &wmem_test_cb, &f);
+ wmem_register_callback(expected_allocator, &wmem_test_cb, &t);
+
+ cb_called_count = 0;
+ wmem_free_all(allocator);
+ g_assert_true(cb_called_count == 3);
+
+ wmem_unregister_callback(allocator, cb_id);
+ cb_called_count = 0;
+ wmem_free_all(allocator);
+ g_assert_true(cb_called_count == 2);
+
+ wmem_register_callback(expected_allocator, &wmem_test_cb, &t);
+
+ expected_event = WMEM_CB_DESTROY_EVENT;
+ cb_called_count = 0;
+ wmem_destroy_allocator(allocator);
+ g_assert_true(cb_called_count == 3);
+}
+
+static void
+wmem_test_allocator_det(wmem_allocator_t *allocator, wmem_verify_func verify,
+ unsigned len)
+{
+ int i;
+ char *ptrs[MAX_SIMULTANEOUS_ALLOCS];
+
+ /* we use wmem_alloc0 in part because it tests slightly more code, but
+ * primarily so that if the allocator doesn't give us enough memory or
+ * gives us memory that includes its own metadata, we write to it and
+ * things go wrong, causing the tests to fail */
+ for (i=0; i<MAX_SIMULTANEOUS_ALLOCS; i++) {
+ ptrs[i] = (char *)wmem_alloc0(allocator, len);
+ }
+ for (i=MAX_SIMULTANEOUS_ALLOCS-1; i>=0; i--) {
+ /* no wmem_realloc0 so just use memset manually */
+ ptrs[i] = (char *)wmem_realloc(allocator, ptrs[i], 4*len);
+ memset(ptrs[i], 0, 4*len);
+ }
+ for (i=0; i<MAX_SIMULTANEOUS_ALLOCS; i++) {
+ wmem_free(allocator, ptrs[i]);
+ }
+
+ if (verify) (*verify)(allocator);
+ wmem_free_all(allocator);
+ wmem_gc(allocator);
+ if (verify) (*verify)(allocator);
+}
+
+static void
+wmem_test_allocator_jumbo(wmem_allocator_type_t type, wmem_verify_func verify)
+{
+ wmem_allocator_t *allocator;
+ char *ptr, *ptr1;
+
+ allocator = wmem_allocator_force_new(type);
+
+ ptr = (char*)wmem_alloc0(allocator, 4*1024*1024);
+ wmem_free(allocator, ptr);
+ wmem_gc(allocator);
+ ptr = (char*)wmem_alloc0(allocator, 4*1024*1024);
+
+ if (verify) (*verify)(allocator);
+ wmem_free(allocator, ptr);
+ wmem_gc(allocator);
+ if (verify) (*verify)(allocator);
+
+ ptr = (char *)wmem_alloc0(allocator, 10*1024*1024);
+ ptr1 = (char *)wmem_alloc0(allocator, 13*1024*1024);
+ ptr1 = (char *)wmem_realloc(allocator, ptr1, 10*1024*1024);
+ memset(ptr1, 0, 10*1024*1024);
+ ptr = (char *)wmem_realloc(allocator, ptr, 13*1024*1024);
+ memset(ptr, 0, 13*1024*1024);
+ if (verify) (*verify)(allocator);
+ wmem_gc(allocator);
+ if (verify) (*verify)(allocator);
+ wmem_free(allocator, ptr1);
+ if (verify) (*verify)(allocator);
+ wmem_free_all(allocator);
+ wmem_gc(allocator);
+ if (verify) (*verify)(allocator);
+
+ wmem_destroy_allocator(allocator);
+}
+
+static void
+wmem_test_allocator(wmem_allocator_type_t type, wmem_verify_func verify,
+ int iterations)
+{
+ int i;
+ char *ptrs[MAX_SIMULTANEOUS_ALLOCS];
+ wmem_allocator_t *allocator;
+
+ allocator = wmem_allocator_force_new(type);
+
+ if (verify) (*verify)(allocator);
+
+ /* start with some fairly simple deterministic tests */
+
+ wmem_test_allocator_det(allocator, verify, 8);
+
+ wmem_test_allocator_det(allocator, verify, 64);
+
+ wmem_test_allocator_det(allocator, verify, 512);
+
+ for (i=0; i<MAX_SIMULTANEOUS_ALLOCS; i++) {
+ ptrs[i] = wmem_alloc0_array(allocator, char, 32);
+ }
+
+ if (verify) (*verify)(allocator);
+ wmem_free_all(allocator);
+ wmem_gc(allocator);
+ if (verify) (*verify)(allocator);
+
+ /* now do some random fuzz-like tests */
+
+ /* reset our ptr array */
+ for (i=0; i<MAX_SIMULTANEOUS_ALLOCS; i++) {
+ ptrs[i] = NULL;
+ }
+
+ /* Run enough iterations to fill the array 32 times */
+ for (i=0; i<iterations; i++) {
+ int ptrs_index;
+ int new_size;
+
+ /* returns value 0 <= x < MAX_SIMULTANEOUS_ALLOCS which is a valid
+ * index into ptrs */
+ ptrs_index = g_test_rand_int_range(0, MAX_SIMULTANEOUS_ALLOCS);
+
+ if (ptrs[ptrs_index] == NULL) {
+ /* if that index is unused, allocate some random amount of memory
+ * between 0 and MAX_ALLOC_SIZE */
+ new_size = g_test_rand_int_range(0, MAX_ALLOC_SIZE);
+
+ ptrs[ptrs_index] = (char *) wmem_alloc0(allocator, new_size);
+ }
+ else if (g_test_rand_bit()) {
+ /* the index is used, and our random bit has determined we will be
+ * reallocating instead of freeing. Do so to some random size
+ * between 0 and MAX_ALLOC_SIZE, then manually zero the
+ * new memory */
+ new_size = g_test_rand_int_range(0, MAX_ALLOC_SIZE);
+
+ ptrs[ptrs_index] = (char *) wmem_realloc(allocator,
+ ptrs[ptrs_index], new_size);
+
+ if (new_size)
+ memset(ptrs[ptrs_index], 0, new_size);
+ }
+ else {
+ /* the index is used, and our random bit has determined we will be
+ * freeing instead of reallocating. Do so and NULL the pointer for
+ * the next iteration. */
+ wmem_free(allocator, ptrs[ptrs_index]);
+ ptrs[ptrs_index] = NULL;
+ }
+ if (verify) (*verify)(allocator);
+ }
+
+ wmem_destroy_allocator(allocator);
+}
+
+static void
+wmem_test_allocator_block(void)
+{
+ wmem_test_allocator(WMEM_ALLOCATOR_BLOCK, &wmem_block_verify,
+ MAX_SIMULTANEOUS_ALLOCS*64);
+ wmem_test_allocator_jumbo(WMEM_ALLOCATOR_BLOCK, &wmem_block_verify);
+}
+
+static void
+wmem_test_allocator_block_fast(void)
+{
+ wmem_test_allocator(WMEM_ALLOCATOR_BLOCK_FAST, NULL,
+ MAX_SIMULTANEOUS_ALLOCS*4);
+ wmem_test_allocator_jumbo(WMEM_ALLOCATOR_BLOCK, NULL);
+}
+
+static void
+wmem_test_allocator_simple(void)
+{
+ wmem_test_allocator(WMEM_ALLOCATOR_SIMPLE, NULL,
+ MAX_SIMULTANEOUS_ALLOCS*64);
+ wmem_test_allocator_jumbo(WMEM_ALLOCATOR_SIMPLE, NULL);
+}
+
+static void
+wmem_test_allocator_strict(void)
+{
+ wmem_test_allocator(WMEM_ALLOCATOR_STRICT, &wmem_strict_check_canaries,
+ MAX_SIMULTANEOUS_ALLOCS*64);
+ wmem_test_allocator_jumbo(WMEM_ALLOCATOR_STRICT, &wmem_strict_check_canaries);
+}
+
+/* UTILITY TESTING FUNCTIONS (/wmem/utils/) */
+
+static void
+wmem_test_miscutls(void)
+{
+ wmem_allocator_t *allocator;
+ const char *source = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ char *ret;
+
+ allocator = wmem_allocator_new(WMEM_ALLOCATOR_STRICT);
+
+ ret = (char*) wmem_memdup(allocator, NULL, 0);
+ g_assert_true(ret == NULL);
+
+ ret = (char*) wmem_memdup(allocator, source, 5);
+ ret[4] = '\0';
+ g_assert_cmpstr(ret, ==, "ABCD");
+
+ ret = (char*) wmem_memdup(allocator, source, 1);
+ g_assert_true(ret[0] == 'A');
+ wmem_strict_check_canaries(allocator);
+
+ ret = (char*) wmem_memdup(allocator, source, 10);
+ ret[9] = '\0';
+ g_assert_cmpstr(ret, ==, "ABCDEFGHI");
+
+ wmem_destroy_allocator(allocator);
+}
+
+static void
+wmem_test_strutls(void)
+{
+ wmem_allocator_t *allocator;
+ const char *orig_str;
+ char *new_str;
+
+ allocator = wmem_allocator_new(WMEM_ALLOCATOR_STRICT);
+
+ orig_str = "TEST1";
+ new_str = wmem_strdup(allocator, orig_str);
+ g_assert_cmpstr(new_str, ==, orig_str);
+ new_str[0] = 'X';
+ g_assert_cmpstr(new_str, >, orig_str);
+ wmem_strict_check_canaries(allocator);
+
+ orig_str = "TEST123456789";
+ new_str = wmem_strndup(allocator, orig_str, 6);
+ g_assert_cmpstr(new_str, ==, "TEST12");
+ g_assert_cmpstr(new_str, <, orig_str);
+ new_str[0] = 'X';
+ g_assert_cmpstr(new_str, >, orig_str);
+ wmem_strict_check_canaries(allocator);
+
+ new_str = wmem_strdup_printf(allocator, "abc %s %% %d", "boo", 23);
+ g_assert_cmpstr(new_str, ==, "abc boo % 23");
+ new_str = wmem_strdup_printf(allocator, "%s", STRING_80);
+ g_assert_cmpstr(new_str, ==, STRING_80);
+ wmem_strict_check_canaries(allocator);
+
+ orig_str = "Short String";
+ new_str = wmem_strdup_printf(allocator, "TEST %s", orig_str);
+ g_assert_cmpstr(new_str, ==, "TEST Short String");
+
+ orig_str = "Very Long..............................."
+ "........................................"
+ "........................................"
+ "........................................"
+ "........................................"
+ "........................................"
+ "..................................String";
+ new_str = wmem_strdup_printf(allocator, "TEST %s", orig_str);
+ g_assert_cmpstr(new_str, ==,
+ "TEST Very Long..............................."
+ "........................................"
+ "........................................"
+ "........................................"
+ "........................................"
+ "........................................"
+ "..................................String");
+
+ wmem_destroy_allocator(allocator);
+}
+
+#define RESOURCE_USAGE_START get_resource_usage(&start_utime, &start_stime)
+
+#define RESOURCE_USAGE_END \
+ get_resource_usage(&end_utime, &end_stime); \
+ utime_ms = (end_utime - start_utime) * 1000.0; \
+ stime_ms = (end_stime - start_stime) * 1000.0
+
+/* NOTE: You have to run "wmem_test -m perf" to run the performance tests. */
+static void
+wmem_test_stringperf(void)
+{
+#define LOOP_COUNT (1 * 1000 * 1000)
+ wmem_allocator_t *allocator;
+#ifdef _WIN32
+ char buffer[1];
+#endif
+ char **str_ptr = g_new(char *, LOOP_COUNT);
+ char *s_val = "test string";
+ double d_val = 1000.2;
+ unsigned u_val = 54321;
+ int i_val = -12345;
+ int i;
+ double start_utime, start_stime, end_utime, end_stime, utime_ms, stime_ms;
+
+ allocator = wmem_allocator_new(WMEM_ALLOCATOR_BLOCK);
+
+/* C99 snprintf */
+
+ RESOURCE_USAGE_START;
+ for (i = 0; i < LOOP_COUNT; i++) {
+ snprintf(NULL, 0, "%s", s_val);
+ }
+ RESOURCE_USAGE_END;
+ g_test_minimized_result(utime_ms + stime_ms,
+ "snprintf 1 string: u %.3f ms s %.3f ms", utime_ms, stime_ms);
+
+ RESOURCE_USAGE_START;
+ for (i = 0; i < LOOP_COUNT; i++) {
+ snprintf(NULL, 0, "%s%s%s%s%s", s_val, s_val, s_val, s_val, s_val);
+ }
+ RESOURCE_USAGE_END;
+ g_test_minimized_result(utime_ms + stime_ms,
+ "snprintf 5 strings: u %.3f ms s %.3f ms", utime_ms, stime_ms);
+
+ RESOURCE_USAGE_START;
+ for (i = 0; i < LOOP_COUNT; i++) {
+ snprintf(NULL, 0, "%s%u%3.5f%02d", s_val, u_val, d_val, i_val);
+ }
+ RESOURCE_USAGE_END;
+ g_test_minimized_result(utime_ms + stime_ms,
+ "snprintf mixed args: u %.3f ms s %.3f ms", utime_ms, stime_ms);
+
+/* GLib g_snprintf (can use C99 or Gnulib) */
+
+ RESOURCE_USAGE_START;
+ for (i = 0; i < LOOP_COUNT; i++) {
+ g_snprintf(NULL, 0, "%s", s_val);
+ }
+ RESOURCE_USAGE_END;
+ g_test_minimized_result(utime_ms + stime_ms,
+ "g_printf_string_upper_bound (via g_snprintf) 1 string: u %.3f ms s %.3f ms", utime_ms, stime_ms);
+
+ RESOURCE_USAGE_START;
+ for (i = 0; i < LOOP_COUNT; i++) {
+ g_snprintf(NULL, 0, "%s%s%s%s%s", s_val, s_val, s_val, s_val, s_val);
+ }
+ RESOURCE_USAGE_END;
+ g_test_minimized_result(utime_ms + stime_ms,
+ "g_printf_string_upper_bound (via g_snprintf) 5 strings: u %.3f ms s %.3f ms", utime_ms, stime_ms);
+
+ RESOURCE_USAGE_START;
+ for (i = 0; i < LOOP_COUNT; i++) {
+ g_snprintf(NULL, 0, "%s%u%3.5f%02d", s_val, u_val, d_val, i_val);
+ }
+ RESOURCE_USAGE_END;
+ g_test_minimized_result(utime_ms + stime_ms,
+ "g_printf_string_upper_bound (via g_snprintf) mixed args: u %.3f ms s %.3f ms", utime_ms, stime_ms);
+
+/* Windows _snprintf_s */
+
+#ifdef _WIN32
+ RESOURCE_USAGE_START;
+ for (i = 0; i < LOOP_COUNT; i++) {
+ _snprintf_s(buffer, 1, _TRUNCATE, "%s", s_val);
+ }
+ RESOURCE_USAGE_END;
+ g_test_minimized_result(utime_ms + stime_ms,
+ "_snprintf_s upper bound 1 string: u %.3f ms s %.3f ms", utime_ms, stime_ms);
+
+ RESOURCE_USAGE_START;
+ for (i = 0; i < LOOP_COUNT; i++) {
+ _snprintf_s(buffer, 1, _TRUNCATE, "%s%s%s%s%s", s_val, s_val, s_val, s_val, s_val);
+ }
+ RESOURCE_USAGE_END;
+ g_test_minimized_result(utime_ms + stime_ms,
+ "_snprintf_s upper bound 5 strings: u %.3f ms s %.3f ms", utime_ms, stime_ms);
+
+ RESOURCE_USAGE_START;
+ for (i = 0; i < LOOP_COUNT; i++) {
+ _snprintf_s(buffer, 1, _TRUNCATE, "%s%u%3.5f%02d", s_val, u_val, d_val, i_val);
+ }
+ RESOURCE_USAGE_END;
+ g_test_minimized_result(utime_ms + stime_ms,
+ "_snprintf_s upper bound mixed args: u %.3f ms s %.3f ms", utime_ms, stime_ms);
+#endif
+
+/* GLib strdup */
+
+ RESOURCE_USAGE_START;
+ for (i = 0; i < LOOP_COUNT; i++) {
+ str_ptr[i] = g_strdup_printf("%s%s", s_val, s_val);
+ }
+ RESOURCE_USAGE_END;
+ g_test_minimized_result(utime_ms + stime_ms,
+ "g_strdup_printf 2 strings: u %.3f ms s %.3f ms", utime_ms, stime_ms);
+ for (i = 0; i < LOOP_COUNT; i++) {
+ g_free(str_ptr[i]);
+ }
+
+ RESOURCE_USAGE_START;
+ for (i = 0; i < LOOP_COUNT; i++) {
+ str_ptr[i] = g_strdup_printf("%s%s%s%s%s", s_val, s_val, s_val, s_val, s_val);
+ }
+ RESOURCE_USAGE_END;
+ g_test_minimized_result(utime_ms + stime_ms,
+ "g_strdup_printf 5 strings: u %.3f ms s %.3f ms", utime_ms, stime_ms);
+ for (i = 0; i < LOOP_COUNT; i++) {
+ g_free(str_ptr[i]);
+ }
+
+/* wmem strdup null allocator */
+
+ RESOURCE_USAGE_START;
+ for (i = 0; i < LOOP_COUNT; i++) {
+ str_ptr[i] = wmem_strdup_printf(NULL, "%s%s", s_val, s_val);
+ }
+ RESOURCE_USAGE_END;
+ g_test_minimized_result(utime_ms + stime_ms,
+ "wmem_strdup_printf() 2 strings: u %.3f ms s %.3f ms", utime_ms, stime_ms);
+ for (i = 0; i < LOOP_COUNT; i++) {
+ g_free(str_ptr[i]);
+ }
+
+ RESOURCE_USAGE_START;
+ for (i = 0; i < LOOP_COUNT; i++) {
+ str_ptr[i] = wmem_strdup_printf(NULL, "%s%s%s%s%s", s_val, s_val, s_val, s_val, s_val);
+ }
+ RESOURCE_USAGE_END;
+ g_test_minimized_result(utime_ms + stime_ms,
+ "wmem_strdup_printf(NULL) 5 strings: u %.3f ms s %.3f ms", utime_ms, stime_ms);
+ for (i = 0; i < LOOP_COUNT; i++) {
+ g_free(str_ptr[i]);
+ }
+
+/* wmem strdup strict allocator */
+
+ RESOURCE_USAGE_START;
+ for (i = 0; i < LOOP_COUNT; i++) {
+ wmem_strdup_printf(allocator, "%s%s", s_val, s_val);
+ }
+ RESOURCE_USAGE_END;
+ g_test_minimized_result(utime_ms + stime_ms,
+ "wmem_strdup_printf(allocator) 2 strings: u %.3f ms s %.3f ms", utime_ms, stime_ms);
+
+ RESOURCE_USAGE_START;
+ for (i = 0; i < LOOP_COUNT; i++) {
+ wmem_strdup_printf(allocator, "%s%s%s%s%s", s_val, s_val, s_val, s_val, s_val);
+ }
+ RESOURCE_USAGE_END;
+ g_test_minimized_result(utime_ms + stime_ms,
+ "wmem_strdup_printf(allocator) 5 strings: u %.3f ms s %.3f ms", utime_ms, stime_ms);
+
+ wmem_destroy_allocator(allocator);
+ g_free(str_ptr);
+}
+
+/* DATA STRUCTURE TESTING FUNCTIONS (/wmem/datastruct/) */
+
+static void
+wmem_test_array(void)
+{
+ wmem_allocator_t *allocator;
+ wmem_array_t *array;
+ unsigned int i, j, k;
+ uint32_t val, *buf;
+ uint32_t vals[8];
+ uint32_t *raw;
+ uint32_t lastint;
+
+ allocator = wmem_allocator_new(WMEM_ALLOCATOR_STRICT);
+
+ array = wmem_array_new(allocator, sizeof(uint32_t));
+ g_assert_true(array);
+ g_assert_true(wmem_array_get_count(array) == 0);
+
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ val = i;
+ wmem_array_append_one(array, val);
+ g_assert_true(wmem_array_get_count(array) == i+1);
+
+ val = *(uint32_t*)wmem_array_index(array, i);
+ g_assert_true(val == i);
+ g_assert_true(wmem_array_try_index(array, i, &val) == 0);
+ g_assert_true(val == i);
+ g_assert_true(wmem_array_try_index(array, i+1, &val) < 0);
+
+ }
+ wmem_strict_check_canaries(allocator);
+
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ val = *(uint32_t*)wmem_array_index(array, i);
+ g_assert_true(val == i);
+ g_assert_true(wmem_array_try_index(array, i, &val) == 0);
+ g_assert_true(val == i);
+ }
+
+ wmem_destroy_array(array);
+
+ array = wmem_array_sized_new(allocator, sizeof(uint32_t), 73);
+ wmem_array_set_null_terminator(array);
+ for (i=0; i<75; i++)
+ g_assert_true(wmem_array_try_index(array, i, &val) < 0);
+
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ for (j=0; j<8; j++) {
+ vals[j] = i+j;
+ }
+
+ wmem_array_append(array, vals, 8);
+ g_assert_true(wmem_array_get_count(array) == 8*(i+1));
+ }
+ wmem_strict_check_canaries(allocator);
+
+ buf = (uint32_t*)wmem_array_get_raw(array);
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ for (j=0; j<8; j++) {
+ g_assert_true(buf[i*8 + j] == i+j);
+ }
+ }
+
+ wmem_array_sort(array, wmem_test_compare_guint32);
+ for (i=0, k=0; i<8; i++) {
+ for (j=0; j<=i; j++, k++) {
+ val = *(uint32_t*)wmem_array_index(array, k);
+ g_assert_true(val == i);
+ g_assert_true(wmem_array_try_index(array, k, &val) == 0);
+ g_assert_true(val == i);
+ }
+ }
+ for (j=k; k<8*(CONTAINER_ITERS+1)-j; k++) {
+ val = *(uint32_t*)wmem_array_index(array, k);
+ g_assert_true(val == ((k-j)/8)+8);
+ g_assert_true(wmem_array_try_index(array, k, &val) == 0);
+ g_assert_true(val == ((k-j)/8)+8);
+ }
+ for (i=0; i<7; i++) {
+ for (j=0; j<7-i; j++, k++) {
+ val = *(uint32_t*)wmem_array_index(array, k);
+ g_assert_true(val == CONTAINER_ITERS+i);
+ g_assert_true(wmem_array_try_index(array, k, &val) == 0);
+ g_assert_true(val == CONTAINER_ITERS+i);
+ }
+ }
+ g_assert_true(k == wmem_array_get_count(array));
+
+ lastint = 77;
+ wmem_array_append_one(array, lastint);
+
+ raw = (uint32_t*)wmem_array_get_raw(array);
+ g_assert_true(raw[wmem_array_get_count(array)] == 0);
+ g_assert_true(raw[wmem_array_get_count(array) - 1] == lastint);
+
+ wmem_destroy_array(array);
+
+ wmem_destroy_allocator(allocator);
+}
+
+static void
+check_val_list(void * val, void * val_to_check)
+{
+ g_assert_true(val == val_to_check);
+}
+
+static int
+str_compare(gconstpointer a, gconstpointer b)
+{
+ return strcmp((const char*)a, (const char*)b);
+}
+
+static void
+wmem_test_list(void)
+{
+ wmem_allocator_t *allocator;
+ wmem_list_t *list;
+ wmem_list_frame_t *frame;
+ unsigned int i;
+ int int1;
+ int int2;
+ char* str1;
+ char* str2;
+
+ allocator = wmem_allocator_new(WMEM_ALLOCATOR_STRICT);
+
+ list = wmem_list_new(allocator);
+ g_assert_true(list);
+ g_assert_true(wmem_list_count(list) == 0);
+
+ frame = wmem_list_head(list);
+ g_assert_true(frame == NULL);
+
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ wmem_list_prepend(list, GINT_TO_POINTER(i));
+ g_assert_true(wmem_list_count(list) == i+1);
+ g_assert_true(wmem_list_find(list, GINT_TO_POINTER(i)));
+
+ frame = wmem_list_head(list);
+ g_assert_true(frame);
+ g_assert_true(wmem_list_frame_data(frame) == GINT_TO_POINTER(i));
+ }
+ wmem_strict_check_canaries(allocator);
+
+ i = CONTAINER_ITERS - 1;
+ frame = wmem_list_head(list);
+ while (frame) {
+ g_assert_true(wmem_list_frame_data(frame) == GINT_TO_POINTER(i));
+ i--;
+ frame = wmem_list_frame_next(frame);
+ }
+
+ i = 0;
+ frame = wmem_list_tail(list);
+ while (frame) {
+ g_assert_true(wmem_list_frame_data(frame) == GINT_TO_POINTER(i));
+ i++;
+ frame = wmem_list_frame_prev(frame);
+ }
+
+ i = CONTAINER_ITERS - 2;
+ while (wmem_list_count(list) > 1) {
+ wmem_list_remove(list, GINT_TO_POINTER(i));
+ i--;
+ }
+ wmem_list_remove(list, GINT_TO_POINTER(CONTAINER_ITERS - 1));
+ g_assert_true(wmem_list_count(list) == 0);
+ g_assert_true(wmem_list_head(list) == NULL);
+ g_assert_true(wmem_list_tail(list) == NULL);
+
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ wmem_list_append(list, GINT_TO_POINTER(i));
+ g_assert_true(wmem_list_count(list) == i+1);
+
+ frame = wmem_list_head(list);
+ g_assert_true(frame);
+ }
+ wmem_strict_check_canaries(allocator);
+
+ i = 0;
+ frame = wmem_list_head(list);
+ while (frame) {
+ g_assert_true(wmem_list_frame_data(frame) == GINT_TO_POINTER(i));
+ i++;
+ frame = wmem_list_frame_next(frame);
+ }
+
+ i = CONTAINER_ITERS - 1;
+ frame = wmem_list_tail(list);
+ while (frame) {
+ g_assert_true(wmem_list_frame_data(frame) == GINT_TO_POINTER(i));
+ i--;
+ frame = wmem_list_frame_prev(frame);
+ }
+
+ wmem_destroy_allocator(allocator);
+
+ list = wmem_list_new(NULL);
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ wmem_list_prepend(list, GINT_TO_POINTER(i));
+ }
+ g_assert_true(wmem_list_count(list) == CONTAINER_ITERS);
+ wmem_destroy_list(list);
+
+ list = wmem_list_new(NULL);
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ wmem_list_append(list, GINT_TO_POINTER(1));
+ }
+ wmem_list_foreach(list, check_val_list, GINT_TO_POINTER(1));
+ wmem_destroy_list(list);
+
+ list = wmem_list_new(NULL);
+ wmem_list_insert_sorted(list, GINT_TO_POINTER(5), wmem_compare_int);
+ wmem_list_insert_sorted(list, GINT_TO_POINTER(8), wmem_compare_int);
+ wmem_list_insert_sorted(list, GINT_TO_POINTER(1), wmem_compare_int);
+ wmem_list_insert_sorted(list, GINT_TO_POINTER(2), wmem_compare_int);
+ wmem_list_insert_sorted(list, GINT_TO_POINTER(9), wmem_compare_int);
+ g_assert_true(wmem_list_count(list) == 5);
+ frame = wmem_list_head(list);
+ int1 = GPOINTER_TO_INT(wmem_list_frame_data(frame));
+ while ((frame = wmem_list_frame_next(frame))) {
+ int2 = GPOINTER_TO_INT(wmem_list_frame_data(frame));
+ g_assert_true(int1 <= int2);
+ int1 = int2;
+ }
+ wmem_destroy_list(list);
+
+ list = wmem_list_new(NULL);
+ wmem_list_insert_sorted(list, GINT_TO_POINTER(5), wmem_compare_int);
+ wmem_list_insert_sorted(list, GINT_TO_POINTER(1), wmem_compare_int);
+ wmem_list_insert_sorted(list, GINT_TO_POINTER(7), wmem_compare_int);
+ wmem_list_insert_sorted(list, GINT_TO_POINTER(3), wmem_compare_int);
+ wmem_list_insert_sorted(list, GINT_TO_POINTER(2), wmem_compare_int);
+ wmem_list_insert_sorted(list, GINT_TO_POINTER(2), wmem_compare_int);
+ g_assert_true(wmem_list_count(list) == 6);
+ frame = wmem_list_head(list);
+ int1 = GPOINTER_TO_INT(wmem_list_frame_data(frame));
+ while ((frame = wmem_list_frame_next(frame))) {
+ int2 = GPOINTER_TO_INT(wmem_list_frame_data(frame));
+ g_assert_true(int1 <= int2);
+ int1 = int2;
+ }
+ wmem_destroy_list(list);
+
+ list = wmem_list_new(NULL);
+ wmem_list_insert_sorted(list, "abc", str_compare);
+ wmem_list_insert_sorted(list, "bcd", str_compare);
+ wmem_list_insert_sorted(list, "aaa", str_compare);
+ wmem_list_insert_sorted(list, "bbb", str_compare);
+ wmem_list_insert_sorted(list, "zzz", str_compare);
+ wmem_list_insert_sorted(list, "ggg", str_compare);
+ g_assert_true(wmem_list_count(list) == 6);
+ frame = wmem_list_head(list);
+ str1 = (char*)wmem_list_frame_data(frame);
+ while ((frame = wmem_list_frame_next(frame))) {
+ str2 = (char*)wmem_list_frame_data(frame);
+ g_assert_true(strcmp(str1, str2) <= 0);
+ str1 = str2;
+ }
+ wmem_destroy_list(list);
+}
+
+static void
+check_val_map(void * key _U_, void * val, void * user_data)
+{
+ g_assert_true(val == user_data);
+}
+
+static gboolean
+equal_val_map(void * key _U_, void * val, void * user_data)
+{
+ return val == user_data;
+}
+
+static void
+wmem_test_map(void)
+{
+ wmem_allocator_t *allocator, *extra_allocator;
+ wmem_map_t *map;
+ char *str_key;
+ const void *str_key_ret;
+ unsigned int i;
+ unsigned int *key_ret;
+ unsigned int *value_ret;
+ void *ret;
+
+ allocator = wmem_allocator_new(WMEM_ALLOCATOR_STRICT);
+ extra_allocator = wmem_allocator_new(WMEM_ALLOCATOR_STRICT);
+
+ /* insertion, lookup and removal of simple integer keys */
+ map = wmem_map_new(allocator, g_direct_hash, g_direct_equal);
+ g_assert_true(map);
+
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ ret = wmem_map_insert(map, GINT_TO_POINTER(i), GINT_TO_POINTER(777777));
+ g_assert_true(ret == NULL);
+ ret = wmem_map_insert(map, GINT_TO_POINTER(i), GINT_TO_POINTER(i));
+ g_assert_true(ret == GINT_TO_POINTER(777777));
+ ret = wmem_map_insert(map, GINT_TO_POINTER(i), GINT_TO_POINTER(i));
+ g_assert_true(ret == GINT_TO_POINTER(i));
+ }
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ ret = wmem_map_lookup(map, GINT_TO_POINTER(i));
+ g_assert_true(ret == GINT_TO_POINTER(i));
+ g_assert_true(wmem_map_contains(map, GINT_TO_POINTER(i)) == true);
+ g_assert_true(wmem_map_lookup_extended(map, GINT_TO_POINTER(i), NULL, NULL));
+ key_ret = NULL;
+ g_assert_true(wmem_map_lookup_extended(map, GINT_TO_POINTER(i), GINT_TO_POINTER(&key_ret), NULL));
+ g_assert_true(key_ret == GINT_TO_POINTER(i));
+ value_ret = NULL;
+ g_assert_true(wmem_map_lookup_extended(map, GINT_TO_POINTER(i), NULL, GINT_TO_POINTER(&value_ret)));
+ g_assert_true(value_ret == GINT_TO_POINTER(i));
+ key_ret = NULL;
+ value_ret = NULL;
+ g_assert_true(wmem_map_lookup_extended(map, GINT_TO_POINTER(i), GINT_TO_POINTER(&key_ret), GINT_TO_POINTER(&value_ret)));
+ g_assert_true(key_ret == GINT_TO_POINTER(i));
+ g_assert_true(value_ret == GINT_TO_POINTER(i));
+ ret = wmem_map_remove(map, GINT_TO_POINTER(i));
+ g_assert_true(ret == GINT_TO_POINTER(i));
+ g_assert_true(wmem_map_contains(map, GINT_TO_POINTER(i)) == false);
+ ret = wmem_map_lookup(map, GINT_TO_POINTER(i));
+ g_assert_true(ret == NULL);
+ ret = wmem_map_remove(map, GINT_TO_POINTER(i));
+ g_assert_true(ret == NULL);
+ }
+ wmem_free_all(allocator);
+
+ /* test auto-reset functionality */
+ map = wmem_map_new_autoreset(allocator, extra_allocator, g_direct_hash, g_direct_equal);
+ g_assert_true(map);
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ ret = wmem_map_insert(map, GINT_TO_POINTER(i), GINT_TO_POINTER(777777));
+ g_assert_true(ret == NULL);
+ ret = wmem_map_insert(map, GINT_TO_POINTER(i), GINT_TO_POINTER(i));
+ g_assert_true(ret == GINT_TO_POINTER(777777));
+ ret = wmem_map_insert(map, GINT_TO_POINTER(i), GINT_TO_POINTER(i));
+ g_assert_true(ret == GINT_TO_POINTER(i));
+ }
+ wmem_free_all(extra_allocator);
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ g_assert_true(wmem_map_lookup(map, GINT_TO_POINTER(i)) == NULL);
+ }
+ wmem_free_all(allocator);
+
+ map = wmem_map_new(allocator, wmem_str_hash, g_str_equal);
+ g_assert_true(map);
+
+ /* string keys and for-each */
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ str_key = wmem_test_rand_string(allocator, 1, 64);
+ wmem_map_insert(map, str_key, GINT_TO_POINTER(i));
+ ret = wmem_map_lookup(map, str_key);
+ g_assert_true(ret == GINT_TO_POINTER(i));
+ g_assert_true(wmem_map_contains(map, str_key) == true);
+ str_key_ret = NULL;
+ value_ret = NULL;
+ g_assert_true(wmem_map_lookup_extended(map, str_key, &str_key_ret, GINT_TO_POINTER(&value_ret)) == true);
+ g_assert_true(g_str_equal(str_key_ret, str_key));
+ g_assert_true(value_ret == GINT_TO_POINTER(i));
+ }
+
+ /* test foreach */
+ map = wmem_map_new(allocator, wmem_str_hash, g_str_equal);
+ g_assert_true(map);
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ str_key = wmem_test_rand_string(allocator, 1, 64);
+ wmem_map_insert(map, str_key, GINT_TO_POINTER(2));
+ }
+ wmem_map_foreach(map, check_val_map, GINT_TO_POINTER(2));
+
+ wmem_map_foreach_remove(map, equal_val_map, GINT_TO_POINTER(2));
+ g_assert_true(wmem_map_size(map) == 0);
+
+ /* test size */
+ map = wmem_map_new(allocator, g_direct_hash, g_direct_equal);
+ g_assert_true(map);
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ wmem_map_insert(map, GINT_TO_POINTER(i), GINT_TO_POINTER(i));
+ }
+ g_assert_true(wmem_map_size(map) == CONTAINER_ITERS);
+
+ for (i=0; i<CONTAINER_ITERS; i+=2) {
+ wmem_map_foreach_remove(map, equal_val_map, GINT_TO_POINTER(i));
+ }
+ g_assert_true(wmem_map_size(map) == CONTAINER_ITERS/2);
+
+ wmem_destroy_allocator(extra_allocator);
+ wmem_destroy_allocator(allocator);
+}
+
+static void
+wmem_test_queue(void)
+{
+ wmem_allocator_t *allocator;
+ wmem_queue_t *queue;
+ unsigned int i;
+
+ allocator = wmem_allocator_new(WMEM_ALLOCATOR_STRICT);
+
+ queue = wmem_queue_new(allocator);
+ g_assert_true(queue);
+ g_assert_true(wmem_queue_count(queue) == 0);
+
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ wmem_queue_push(queue, GINT_TO_POINTER(i));
+
+ g_assert_true(wmem_queue_count(queue) == i+1);
+ g_assert_true(wmem_queue_peek(queue) == GINT_TO_POINTER(0));
+ }
+ wmem_strict_check_canaries(allocator);
+
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ g_assert_true(wmem_queue_peek(queue) == GINT_TO_POINTER(i));
+ g_assert_true(wmem_queue_pop(queue) == GINT_TO_POINTER(i));
+ g_assert_true(wmem_queue_count(queue) == CONTAINER_ITERS-i-1);
+ }
+ g_assert_true(wmem_queue_count(queue) == 0);
+
+ wmem_destroy_queue(queue);
+
+ wmem_destroy_allocator(allocator);
+}
+
+static void
+wmem_test_stack(void)
+{
+ wmem_allocator_t *allocator;
+ wmem_stack_t *stack;
+ unsigned int i;
+
+ allocator = wmem_allocator_new(WMEM_ALLOCATOR_STRICT);
+
+ stack = wmem_stack_new(allocator);
+ g_assert_true(stack);
+ g_assert_true(wmem_stack_count(stack) == 0);
+
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ wmem_stack_push(stack, GINT_TO_POINTER(i));
+
+ g_assert_true(wmem_stack_count(stack) == i+1);
+ g_assert_true(wmem_stack_peek(stack) == GINT_TO_POINTER(i));
+ }
+ wmem_strict_check_canaries(allocator);
+
+ for (i=CONTAINER_ITERS; i>0; i--) {
+ g_assert_true(wmem_stack_peek(stack) == GINT_TO_POINTER(i-1));
+ g_assert_true(wmem_stack_pop(stack) == GINT_TO_POINTER(i-1));
+ g_assert_true(wmem_stack_count(stack) == i-1);
+ }
+ g_assert_true(wmem_stack_count(stack) == 0);
+
+ wmem_destroy_stack(stack);
+
+ wmem_destroy_allocator(allocator);
+}
+
+static void
+wmem_test_strbuf(void)
+{
+ wmem_allocator_t *allocator;
+ wmem_strbuf_t *strbuf;
+ int i;
+
+ allocator = wmem_allocator_new(WMEM_ALLOCATOR_STRICT);
+
+ strbuf = wmem_strbuf_new(allocator, "TEST");
+ g_assert_true(strbuf);
+ g_assert_cmpstr(wmem_strbuf_get_str(strbuf), ==, "TEST");
+ g_assert_cmpuint(wmem_strbuf_get_len(strbuf), ==, 4);
+
+ wmem_strbuf_append(strbuf, "FUZZ");
+ g_assert_cmpstr(wmem_strbuf_get_str(strbuf), ==, "TESTFUZZ");
+ g_assert_cmpuint(wmem_strbuf_get_len(strbuf), ==, 8);
+
+ wmem_strbuf_append_printf(strbuf, "%d%s", 3, "a");
+ g_assert_cmpstr(wmem_strbuf_get_str(strbuf), ==, "TESTFUZZ3a");
+ g_assert_cmpuint(wmem_strbuf_get_len(strbuf), ==, 10);
+
+ wmem_strbuf_append_c(strbuf, 'q');
+ g_assert_cmpstr(wmem_strbuf_get_str(strbuf), ==, "TESTFUZZ3aq");
+ g_assert_cmpuint(wmem_strbuf_get_len(strbuf), ==, 11);
+
+ wmem_strbuf_append_unichar(strbuf, g_utf8_get_char("\xC2\xA9"));
+ g_assert_cmpstr(wmem_strbuf_get_str(strbuf), ==, "TESTFUZZ3aq\xC2\xA9");
+ g_assert_cmpuint(wmem_strbuf_get_len(strbuf), ==, 13);
+
+ wmem_strbuf_append_c_count(strbuf, '+', 8);
+ g_assert_cmpstr(wmem_strbuf_get_str(strbuf), ==, "TESTFUZZ3aq\xC2\xA9++++++++");
+ g_assert_cmpuint(wmem_strbuf_get_len(strbuf), ==, 21);
+
+ wmem_strbuf_truncate(strbuf, 32);
+ wmem_strbuf_truncate(strbuf, 24);
+ wmem_strbuf_truncate(strbuf, 16);
+ wmem_strbuf_truncate(strbuf, 13);
+ g_assert_cmpstr(wmem_strbuf_get_str(strbuf), ==, "TESTFUZZ3aq\xC2\xA9");
+ g_assert_cmpuint(wmem_strbuf_get_len(strbuf), ==, 13);
+
+ wmem_strbuf_truncate(strbuf, 3);
+ g_assert_cmpstr(wmem_strbuf_get_str(strbuf), ==, "TES");
+ g_assert_cmpuint(wmem_strbuf_get_len(strbuf), ==, 3);
+
+ wmem_strbuf_append_len(strbuf, "TFUZZ1234", 5);
+ g_assert_cmpstr(wmem_strbuf_get_str(strbuf), ==, "TESTFUZZ");
+ g_assert_cmpuint(wmem_strbuf_get_len(strbuf), ==, 8);
+
+ wmem_free_all(allocator);
+
+ strbuf = wmem_strbuf_new(allocator, "TEST");
+ for (i=0; i<1024; i++) {
+ if (g_test_rand_bit()) {
+ wmem_strbuf_append(strbuf, "ABC");
+ }
+ else {
+ wmem_strbuf_append_printf(strbuf, "%d%d", 3, 777);
+ }
+ wmem_strict_check_canaries(allocator);
+ }
+ g_assert_true(strlen(wmem_strbuf_get_str(strbuf)) ==
+ wmem_strbuf_get_len(strbuf));
+
+ wmem_destroy_allocator(allocator);
+}
+
+static void
+wmem_test_strbuf_validate(void)
+{
+ wmem_strbuf_t *strbuf;
+ const char *endptr;
+
+ strbuf = wmem_strbuf_new(NULL, "TEST\xEF ABC");
+ g_assert_false(wmem_strbuf_utf8_validate(strbuf, &endptr));
+ g_assert_true(endptr == &strbuf->str[4]);
+ wmem_strbuf_destroy(strbuf);
+
+ strbuf = wmem_strbuf_new(NULL, NULL);
+ wmem_strbuf_append_len(strbuf, "TEST\x00\x00 ABC", 10);
+ g_assert_true(wmem_strbuf_utf8_validate(strbuf, &endptr));
+ wmem_strbuf_destroy(strbuf);
+
+ strbuf = wmem_strbuf_new(NULL, NULL);
+ wmem_strbuf_append_len(strbuf, "TEST\x00\xEF ABC", 10);
+ g_assert_false(wmem_strbuf_utf8_validate(strbuf, &endptr));
+ g_assert_true(endptr == &strbuf->str[5]);
+ wmem_strbuf_destroy(strbuf);
+
+ strbuf = wmem_strbuf_new(NULL, NULL);
+ wmem_strbuf_append_len(strbuf, "TEST\x00 ABC \x00 DEF \x00", 17);
+ g_assert_true(wmem_strbuf_utf8_validate(strbuf, &endptr));
+ wmem_strbuf_destroy(strbuf);
+}
+
+static void
+wmem_test_tree(void)
+{
+ wmem_allocator_t *allocator, *extra_allocator;
+ wmem_tree_t *tree;
+ uint32_t i;
+ int seen_values = 0;
+ int j;
+ char *str_key;
+#define WMEM_TREE_MAX_KEY_COUNT 8
+#define WMEM_TREE_MAX_KEY_LEN 4
+ int key_count;
+ wmem_tree_key_t keys[WMEM_TREE_MAX_KEY_COUNT];
+
+ allocator = wmem_allocator_new(WMEM_ALLOCATOR_STRICT);
+ extra_allocator = wmem_allocator_new(WMEM_ALLOCATOR_STRICT);
+
+ tree = wmem_tree_new(allocator);
+ g_assert_true(tree);
+ g_assert_true(wmem_tree_is_empty(tree));
+
+ /* test basic 32-bit key operations */
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ g_assert_true(wmem_tree_lookup32(tree, i) == NULL);
+ if (i > 0) {
+ g_assert_true(wmem_tree_lookup32_le(tree, i) == GINT_TO_POINTER(i-1));
+ }
+ wmem_tree_insert32(tree, i, GINT_TO_POINTER(i));
+ g_assert_true(wmem_tree_lookup32(tree, i) == GINT_TO_POINTER(i));
+ g_assert_true(!wmem_tree_is_empty(tree));
+ }
+ g_assert_true(wmem_tree_count(tree) == CONTAINER_ITERS);
+ wmem_free_all(allocator);
+
+ tree = wmem_tree_new(allocator);
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ uint32_t rand_int;
+ do {
+ rand_int = g_test_rand_int();
+ } while (wmem_tree_lookup32(tree, rand_int));
+ wmem_tree_insert32(tree, rand_int, GINT_TO_POINTER(i));
+ g_assert_true(wmem_tree_lookup32(tree, rand_int) == GINT_TO_POINTER(i));
+ }
+ g_assert_true(wmem_tree_count(tree) == CONTAINER_ITERS);
+ wmem_free_all(allocator);
+
+ /* test auto-reset functionality */
+ tree = wmem_tree_new_autoreset(allocator, extra_allocator);
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ g_assert_true(wmem_tree_lookup32(tree, i) == NULL);
+ wmem_tree_insert32(tree, i, GINT_TO_POINTER(i));
+ g_assert_true(wmem_tree_lookup32(tree, i) == GINT_TO_POINTER(i));
+ }
+ g_assert_true(wmem_tree_count(tree) == CONTAINER_ITERS);
+ wmem_free_all(extra_allocator);
+ g_assert_true(wmem_tree_count(tree) == 0);
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ g_assert_true(wmem_tree_lookup32(tree, i) == NULL);
+ g_assert_true(wmem_tree_lookup32_le(tree, i) == NULL);
+ }
+ wmem_free_all(allocator);
+
+ /* test array key functionality */
+ tree = wmem_tree_new(allocator);
+ key_count = g_random_int_range(1, WMEM_TREE_MAX_KEY_COUNT);
+ for (j=0; j<key_count; j++) {
+ keys[j].length = g_random_int_range(1, WMEM_TREE_MAX_KEY_LEN);
+ }
+ keys[key_count].length = 0;
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ for (j=0; j<key_count; j++) {
+ keys[j].key = (uint32_t*)wmem_test_rand_string(allocator,
+ (keys[j].length*4), (keys[j].length*4)+1);
+ }
+ wmem_tree_insert32_array(tree, keys, GINT_TO_POINTER(i));
+ g_assert_true(wmem_tree_lookup32_array(tree, keys) == GINT_TO_POINTER(i));
+ }
+ wmem_free_all(allocator);
+
+ tree = wmem_tree_new(allocator);
+ keys[0].length = 1;
+ keys[0].key = wmem_new(allocator, uint32_t);
+ *(keys[0].key) = 0;
+ keys[1].length = 0;
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ wmem_tree_insert32_array(tree, keys, GINT_TO_POINTER(i));
+ *(keys[0].key) += 4;
+ }
+ *(keys[0].key) = 0;
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ g_assert_true(wmem_tree_lookup32_array(tree, keys) == GINT_TO_POINTER(i));
+ for (j=0; j<3; j++) {
+ (*(keys[0].key)) += 1;
+ g_assert_true(wmem_tree_lookup32_array_le(tree, keys) ==
+ GINT_TO_POINTER(i));
+ }
+ *(keys[0].key) += 1;
+ }
+ wmem_free_all(allocator);
+
+ /* test string key functionality */
+ tree = wmem_tree_new(allocator);
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ str_key = wmem_test_rand_string(allocator, 1, 64);
+ wmem_tree_insert_string(tree, str_key, GINT_TO_POINTER(i), 0);
+ g_assert_true(wmem_tree_lookup_string(tree, str_key, 0) ==
+ GINT_TO_POINTER(i));
+ }
+ wmem_free_all(allocator);
+
+ tree = wmem_tree_new(allocator);
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ str_key = wmem_test_rand_string(allocator, 1, 64);
+ wmem_tree_insert_string(tree, str_key, GINT_TO_POINTER(i),
+ WMEM_TREE_STRING_NOCASE);
+ g_assert_true(wmem_tree_lookup_string(tree, str_key,
+ WMEM_TREE_STRING_NOCASE) == GINT_TO_POINTER(i));
+ }
+ wmem_free_all(allocator);
+
+ /* test for-each functionality */
+ tree = wmem_tree_new(allocator);
+ expected_user_data = GINT_TO_POINTER(g_test_rand_int());
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ int tmp;
+ do {
+ tmp = g_test_rand_int();
+ } while (wmem_tree_lookup32(tree, tmp));
+ value_seen[i] = false;
+ wmem_tree_insert32(tree, tmp, GINT_TO_POINTER(i));
+ }
+
+ cb_called_count = 0;
+ cb_continue_count = CONTAINER_ITERS;
+ wmem_tree_foreach(tree, wmem_test_foreach_cb, expected_user_data);
+ g_assert_true(cb_called_count == CONTAINER_ITERS);
+ g_assert_true(cb_continue_count == 0);
+
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ g_assert_true(value_seen[i]);
+ value_seen[i] = false;
+ }
+
+ cb_called_count = 0;
+ cb_continue_count = 10;
+ wmem_tree_foreach(tree, wmem_test_foreach_cb, expected_user_data);
+ g_assert_true(cb_called_count == 10);
+ g_assert_true(cb_continue_count == 0);
+
+ for (i=0; i<CONTAINER_ITERS; i++) {
+ if (value_seen[i]) {
+ seen_values++;
+ }
+ }
+ g_assert_true(seen_values == 10);
+
+ wmem_destroy_allocator(extra_allocator);
+ wmem_destroy_allocator(allocator);
+}
+
+
+/* to be used as userdata in the callback wmem_test_itree_check_overlap_cb*/
+typedef struct wmem_test_itree_user_data {
+ wmem_range_t range;
+ unsigned counter;
+} wmem_test_itree_user_data_t;
+
+
+/* increase userData counter in case the range match the userdata range */
+static bool
+wmem_test_itree_check_overlap_cb (const void *key, void *value _U_, void *userData)
+{
+ const wmem_range_t *ckey = (const wmem_range_t *)key;
+ struct wmem_test_itree_user_data * d = (struct wmem_test_itree_user_data *)userData;
+ g_assert_true(key);
+ g_assert_true(d);
+
+ if(wmem_itree_range_overlap(ckey, &d->range)) {
+ d->counter++;
+ }
+
+ return false;
+}
+
+
+static bool
+wmem_test_overlap(uint64_t low, uint64_t high, uint64_t lowbis, uint64_t highbis)
+{
+ wmem_range_t r1 = {low, high, 0};
+ wmem_range_t r2 = {lowbis, highbis, 0};
+ return wmem_itree_range_overlap(&r1, &r2);
+}
+
+static void
+wmem_test_itree(void)
+{
+ wmem_allocator_t *allocator, *extra_allocator;
+ wmem_itree_t *tree;
+ int i = 0;
+ int32_t max_rand = 0;
+ wmem_test_itree_user_data_t userData;
+ wmem_range_t range, r2;
+
+ allocator = wmem_allocator_new(WMEM_ALLOCATOR_STRICT);
+ extra_allocator = wmem_allocator_new(WMEM_ALLOCATOR_STRICT);
+
+ tree = wmem_itree_new(allocator);
+ g_assert_true(tree);
+ g_assert_true(wmem_itree_is_empty(tree));
+
+ wmem_free_all(allocator);
+
+ /* make sure that wmem_test_overlap is correct (well it's no proof but...)*/
+ g_assert_true(wmem_test_overlap(0, 10, 0, 4));
+ g_assert_true(wmem_test_overlap(0, 10, 9, 14));
+ g_assert_true(wmem_test_overlap(5, 10, 3, 8));
+ g_assert_true(wmem_test_overlap(5, 10, 1, 12));
+ g_assert_true(!wmem_test_overlap(0, 10, 11, 12));
+
+ /* Generate a reference range, then fill an itree with random ranges,
+ then we count greedily the number of overlapping ranges and compare
+ the result with the optimized result
+ */
+
+ userData.counter = 0;
+
+ tree = wmem_itree_new(allocator);
+
+ /* even though keys are uint64_t, we use INT32_MAX as a max because of the type returned by
+ g_test_rand_int_range.
+ */
+ max_rand = INT32_MAX;
+ r2.max_edge = range.max_edge = 0;
+ range.low = g_test_rand_int_range(0, max_rand);
+ range.high = g_test_rand_int_range( (int32_t)range.low, (int32_t)max_rand);
+ userData.range = range;
+
+ for (i=0; i<CONTAINER_ITERS; i++) {
+
+ wmem_list_t *results = NULL;
+
+ /* reset the search */
+ userData.counter = 0;
+ r2.low = (uint64_t)g_test_rand_int_range(0, 100);
+ r2.high = (uint64_t)g_test_rand_int_range( (int32_t)r2.low, 100);
+
+ wmem_itree_insert(tree, r2.low, r2.high, GINT_TO_POINTER(i));
+
+ /* greedy search */
+ wmem_tree_foreach(tree, wmem_test_itree_check_overlap_cb, &userData);
+
+ /* Optimized search */
+ results = wmem_itree_find_intervals(tree, allocator, range.low, range.high);
+
+ /* keep it as a loop instead of wmem_list_count in case one */
+ g_assert_true(wmem_list_count(results) == userData.counter);
+ }
+
+ wmem_destroy_allocator(extra_allocator);
+ wmem_destroy_allocator(allocator);
+}
+
+
+int
+main(int argc, char **argv)
+{
+ int ret;
+
+ wmem_init();
+
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add_func("/wmem/allocator/block", wmem_test_allocator_block);
+ g_test_add_func("/wmem/allocator/blk_fast", wmem_test_allocator_block_fast);
+ g_test_add_func("/wmem/allocator/simple", wmem_test_allocator_simple);
+ g_test_add_func("/wmem/allocator/strict", wmem_test_allocator_strict);
+ g_test_add_func("/wmem/allocator/callbacks", wmem_test_allocator_callbacks);
+
+ g_test_add_func("/wmem/utils/misc", wmem_test_miscutls);
+ g_test_add_func("/wmem/utils/strings", wmem_test_strutls);
+
+ if (g_test_perf()) {
+ g_test_add_func("/wmem/utils/stringperf", wmem_test_stringperf);
+ }
+
+ g_test_add_func("/wmem/datastruct/array", wmem_test_array);
+ g_test_add_func("/wmem/datastruct/list", wmem_test_list);
+ g_test_add_func("/wmem/datastruct/map", wmem_test_map);
+ g_test_add_func("/wmem/datastruct/queue", wmem_test_queue);
+ g_test_add_func("/wmem/datastruct/stack", wmem_test_stack);
+ g_test_add_func("/wmem/datastruct/strbuf", wmem_test_strbuf);
+ g_test_add_func("/wmem/datastruct/strbuf/validate", wmem_test_strbuf_validate);
+ g_test_add_func("/wmem/datastruct/tree", wmem_test_tree);
+ g_test_add_func("/wmem/datastruct/itree", wmem_test_itree);
+
+ ret = g_test_run();
+
+ wmem_cleanup();
+
+ 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:
+ */
diff --git a/wsutil/wmem/wmem_tree-int.h b/wsutil/wmem/wmem_tree-int.h
new file mode 100644
index 00000000..d6b6cc10
--- /dev/null
+++ b/wsutil/wmem/wmem_tree-int.h
@@ -0,0 +1,85 @@
+/** @file
+ *
+ * Definitions for the Wireshark Memory Manager Red-Black Tree
+ * Copyright 2013, Evan Huus <eapache@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WMEM_TREE_INT_H__
+#define __WMEM_TREE_INT_H__
+
+#include "wmem_tree.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef enum _wmem_node_color_t {
+ WMEM_NODE_COLOR_RED,
+ WMEM_NODE_COLOR_BLACK
+} wmem_node_color_t;
+
+
+struct _wmem_tree_node_t {
+ struct _wmem_tree_node_t *parent;
+ struct _wmem_tree_node_t *left;
+ struct _wmem_tree_node_t *right;
+
+ const void *key;
+ void *data;
+
+ wmem_node_color_t color;
+ bool is_subtree;
+ bool is_removed;
+
+
+};
+
+typedef struct _wmem_tree_node_t wmem_tree_node_t;
+
+
+typedef struct _wmem_itree_node_t wmem_itree_node_t;
+
+struct _wmem_tree_t {
+ wmem_allocator_t *metadata_allocator;
+ wmem_allocator_t *data_allocator;
+ wmem_tree_node_t *root;
+ unsigned metadata_scope_cb_id;
+ unsigned data_scope_cb_id;
+
+ void (*post_rotation_cb)(wmem_tree_node_t *);
+};
+
+typedef int (*compare_func)(const void *a, const void *b);
+
+wmem_tree_node_t *
+wmem_tree_insert(wmem_tree_t *tree, const void *key, void *data, compare_func cmp);
+
+typedef struct _wmem_range_t wmem_range_t;
+
+bool
+wmem_itree_range_overlap(const wmem_range_t *r1, const wmem_range_t *r2);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WMEM_TREE__INTERNALS_H__ */
+
+/*
+ * 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/wsutil/wmem/wmem_tree.c b/wsutil/wmem/wmem_tree.c
new file mode 100644
index 00000000..05fa638e
--- /dev/null
+++ b/wsutil/wmem/wmem_tree.c
@@ -0,0 +1,869 @@
+/* wmem_tree.c
+ * Wireshark Memory Manager Red-Black Tree
+ * Based on the red-black tree implementation in epan/emem.*
+ * Copyright 2013, Evan Huus <eapache@gmail.com>
+ *
+ * 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 <string.h>
+#include <stdio.h>
+#include <glib.h>
+
+#include "wmem-int.h"
+#include "wmem_core.h"
+#include "wmem_strutl.h"
+#include "wmem_tree.h"
+#include "wmem_tree-int.h"
+#include "wmem_user_cb.h"
+
+
+static wmem_tree_node_t *
+node_uncle(wmem_tree_node_t *node)
+{
+ wmem_tree_node_t *parent, *grandparent;
+
+ parent = node->parent;
+ if (parent == NULL) {
+ return NULL;
+ }
+
+ grandparent = parent->parent;
+ if (grandparent == NULL) {
+ return NULL;
+ }
+
+ if (parent == grandparent->left) {
+ return grandparent->right;
+ }
+ else {
+ return grandparent->left;
+ }
+}
+
+static void rb_insert_case1(wmem_tree_t *tree, wmem_tree_node_t *node);
+static void rb_insert_case2(wmem_tree_t *tree, wmem_tree_node_t *node);
+
+static void
+rotate_left(wmem_tree_t *tree, wmem_tree_node_t *node)
+{
+ if (node->parent) {
+ if (node->parent->left == node) {
+ node->parent->left = node->right;
+ }
+ else {
+ node->parent->right = node->right;
+ }
+ }
+ else {
+ tree->root = node->right;
+ }
+
+ node->right->parent = node->parent;
+ node->parent = node->right;
+ node->right = node->right->left;
+ if (node->right) {
+ node->right->parent = node;
+ }
+ node->parent->left = node;
+
+ if (tree->post_rotation_cb) {
+ tree->post_rotation_cb (node);
+ }
+}
+
+static void
+rotate_right(wmem_tree_t *tree, wmem_tree_node_t *node)
+{
+ if (node->parent) {
+ if (node->parent->left == node) {
+ node->parent->left = node->left;
+ }
+ else {
+ node->parent->right = node->left;
+ }
+ }
+ else {
+ tree->root = node->left;
+ }
+
+ node->left->parent = node->parent;
+ node->parent = node->left;
+ node->left = node->left->right;
+ if (node->left) {
+ node->left->parent = node;
+ }
+ node->parent->right = node;
+
+
+ if (tree->post_rotation_cb) {
+ tree->post_rotation_cb (node);
+ }
+}
+
+static void
+rb_insert_case5(wmem_tree_t *tree, wmem_tree_node_t *node)
+{
+ wmem_tree_node_t *parent, *grandparent;
+
+ parent = node->parent;
+ grandparent = parent->parent;
+
+ parent->color = WMEM_NODE_COLOR_BLACK;
+ grandparent->color = WMEM_NODE_COLOR_RED;
+
+ if (node == parent->left && parent == grandparent->left) {
+ rotate_right(tree, grandparent);
+ }
+ else {
+ rotate_left(tree, grandparent);
+ }
+}
+
+static void
+rb_insert_case4(wmem_tree_t *tree, wmem_tree_node_t *node)
+{
+ wmem_tree_node_t *parent, *grandparent;
+
+ parent = node->parent;
+ grandparent = parent->parent;
+ if (!grandparent) {
+ return;
+ }
+
+ if (node == parent->right && parent == grandparent->left) {
+ rotate_left(tree, parent);
+ node = node->left;
+ }
+ else if (node == parent->left && parent == grandparent->right) {
+ rotate_right(tree, parent);
+ node = node->right;
+ }
+
+ rb_insert_case5(tree, node);
+}
+
+static void
+rb_insert_case3(wmem_tree_t *tree, wmem_tree_node_t *node)
+{
+ wmem_tree_node_t *parent, *grandparent, *uncle;
+
+ uncle = node_uncle(node);
+
+ if (uncle && uncle->color == WMEM_NODE_COLOR_RED) {
+ parent = node->parent;
+ grandparent = parent->parent;
+
+ parent->color = WMEM_NODE_COLOR_BLACK;
+ uncle->color = WMEM_NODE_COLOR_BLACK;
+ grandparent->color = WMEM_NODE_COLOR_RED;
+
+ rb_insert_case1(tree, grandparent);
+ }
+ else {
+ rb_insert_case4(tree, node);
+ }
+}
+
+static void
+rb_insert_case2(wmem_tree_t *tree, wmem_tree_node_t *node)
+{
+ /* parent is always non-NULL here */
+ if (node->parent->color == WMEM_NODE_COLOR_RED) {
+ rb_insert_case3(tree, node);
+ }
+}
+
+static void
+rb_insert_case1(wmem_tree_t *tree, wmem_tree_node_t *node)
+{
+ wmem_tree_node_t *parent = node->parent;
+
+ if (parent == NULL) {
+ node->color = WMEM_NODE_COLOR_BLACK;
+ }
+ else {
+ rb_insert_case2(tree, node);
+ }
+}
+
+wmem_tree_t *
+wmem_tree_new(wmem_allocator_t *allocator)
+{
+ wmem_tree_t *tree;
+
+ tree = wmem_new0(allocator, wmem_tree_t);
+ tree->metadata_allocator = allocator;
+ tree->data_allocator = allocator;
+
+ return tree;
+}
+
+static bool
+wmem_tree_reset_cb(wmem_allocator_t *allocator _U_, wmem_cb_event_t event,
+ void *user_data)
+{
+ wmem_tree_t *tree = (wmem_tree_t *)user_data;
+
+ tree->root = NULL;
+
+ if (event == WMEM_CB_DESTROY_EVENT) {
+ wmem_unregister_callback(tree->metadata_allocator, tree->metadata_scope_cb_id);
+ wmem_free(tree->metadata_allocator, tree);
+ }
+
+ return true;
+}
+
+static bool
+wmem_tree_destroy_cb(wmem_allocator_t *allocator _U_, wmem_cb_event_t event _U_,
+ void *user_data)
+{
+ wmem_tree_t *tree = (wmem_tree_t *)user_data;
+
+ wmem_unregister_callback(tree->data_allocator, tree->data_scope_cb_id);
+
+ return false;
+}
+
+wmem_tree_t *
+wmem_tree_new_autoreset(wmem_allocator_t *metadata_scope, wmem_allocator_t *data_scope)
+{
+ wmem_tree_t *tree;
+
+ tree = wmem_new0(metadata_scope, wmem_tree_t);
+ tree->metadata_allocator = metadata_scope;
+ tree->data_allocator = data_scope;
+
+ tree->metadata_scope_cb_id = wmem_register_callback(metadata_scope, wmem_tree_destroy_cb,
+ tree);
+ tree->data_scope_cb_id = wmem_register_callback(data_scope, wmem_tree_reset_cb,
+ tree);
+
+ return tree;
+}
+
+static void
+free_tree_node(wmem_allocator_t *allocator, wmem_tree_node_t* node, bool free_keys, bool free_values)
+{
+ if (node == NULL) {
+ return;
+ }
+
+ if (node->left) {
+ free_tree_node(allocator, node->left, free_keys, free_values);
+ }
+
+ if (node->is_subtree) {
+ wmem_tree_destroy((wmem_tree_t *)node->data, free_keys, free_values);
+ node->data = NULL;
+ }
+
+ if (node->right) {
+ free_tree_node(allocator, node->right, free_keys, free_values);
+ }
+
+ if (free_keys) {
+ wmem_free(allocator, (void*)node->key);
+ }
+
+ if (free_values) {
+ wmem_free(allocator, node->data);
+ }
+ wmem_free(allocator, node);
+}
+
+void
+wmem_tree_destroy(wmem_tree_t *tree, bool free_keys, bool free_values)
+{
+ free_tree_node(tree->data_allocator, tree->root, free_keys, free_values);
+ if (tree->metadata_allocator) {
+ wmem_unregister_callback(tree->metadata_allocator, tree->metadata_scope_cb_id);
+ }
+ if (tree->data_allocator) {
+ wmem_unregister_callback(tree->data_allocator, tree->data_scope_cb_id);
+ }
+ wmem_free(tree->metadata_allocator, tree);
+}
+
+bool
+wmem_tree_is_empty(wmem_tree_t *tree)
+{
+ return tree->root == NULL;
+}
+
+static bool
+count_nodes(const void *key _U_, void *value _U_, void *userdata)
+{
+ unsigned* count = (unsigned*)userdata;
+ (*count)++;
+ return false;
+}
+
+unsigned
+wmem_tree_count(wmem_tree_t* tree)
+{
+ unsigned count = 0;
+
+ /* Recursing through the tree counting each node is the simplest approach.
+ We don't keep track of the count within the tree because it can get
+ complicated with subtrees within the tree */
+ wmem_tree_foreach(tree, count_nodes, &count);
+
+ return count;
+}
+
+static wmem_tree_node_t *
+create_node(wmem_allocator_t *allocator, wmem_tree_node_t *parent, const void *key,
+ void *data, wmem_node_color_t color, bool is_subtree)
+{
+ wmem_tree_node_t *node;
+
+ node = wmem_new(allocator, wmem_tree_node_t);
+
+ node->left = NULL;
+ node->right = NULL;
+ node->parent = parent;
+
+ node->key = key;
+ node->data = data;
+
+ node->color = color;
+ node->is_subtree = is_subtree;
+ node->is_removed = false;
+
+ return node;
+}
+
+#define CREATE_DATA(TRANSFORM, DATA) ((TRANSFORM) ? (TRANSFORM)(DATA) : (DATA))
+
+
+/**
+ * return inserted node
+ */
+static wmem_tree_node_t *
+lookup_or_insert32_node(wmem_tree_t *tree, uint32_t key,
+ void*(*func)(void*), void* data, bool is_subtree, bool replace)
+{
+ wmem_tree_node_t *node = tree->root;
+ wmem_tree_node_t *new_node = NULL;
+
+ /* is this the first node ?*/
+ if (!node) {
+ new_node = create_node(tree->data_allocator, NULL, GUINT_TO_POINTER(key),
+ CREATE_DATA(func, data), WMEM_NODE_COLOR_BLACK, is_subtree);
+ tree->root = new_node;
+ return new_node;
+ }
+
+ /* it was not the new root so walk the tree until we find where to
+ * insert this new leaf.
+ */
+ while (!new_node) {
+ /* this node already exists, so just return the data pointer*/
+ if (key == GPOINTER_TO_UINT(node->key)) {
+ if (replace) {
+ node->data = CREATE_DATA(func, data);
+ }
+ return node;
+ }
+ else if (key < GPOINTER_TO_UINT(node->key)) {
+ if (node->left) {
+ node = node->left;
+ }
+ else {
+ /* new node to the left */
+ new_node = create_node(tree->data_allocator, node, GUINT_TO_POINTER(key),
+ CREATE_DATA(func, data), WMEM_NODE_COLOR_RED,
+ is_subtree);
+ node->left = new_node;
+ }
+ }
+ else if (key > GPOINTER_TO_UINT(node->key)) {
+ if (node->right) {
+ node = node->right;
+ }
+ else {
+ /* new node to the right */
+ new_node = create_node(tree->data_allocator, node, GUINT_TO_POINTER(key),
+ CREATE_DATA(func, data), WMEM_NODE_COLOR_RED,
+ is_subtree);
+ node->right = new_node;
+ }
+ }
+ }
+
+ /* node will now point to the newly created node */
+ rb_insert_case1(tree, new_node);
+
+ return new_node;
+}
+
+
+static void *
+lookup_or_insert32(wmem_tree_t *tree, uint32_t key,
+ void*(*func)(void*), void* data, bool is_subtree, bool replace)
+{
+ wmem_tree_node_t *node = lookup_or_insert32_node(tree, key, func, data, is_subtree, replace);
+ return node->data;
+}
+
+static void *
+wmem_tree_lookup(wmem_tree_t *tree, const void *key, compare_func cmp)
+{
+ wmem_tree_node_t *node;
+
+ if (tree == NULL || key == NULL) {
+ return NULL;
+ }
+
+ node = tree->root;
+
+ while (node) {
+ int result = cmp(key, node->key);
+ if (result == 0) {
+ return node->data;
+ }
+ else if (result < 0) {
+ node = node->left;
+ }
+ else if (result > 0) {
+ node = node->right;
+ }
+ }
+
+ return NULL;
+}
+
+wmem_tree_node_t *
+wmem_tree_insert(wmem_tree_t *tree, const void *key, void *data, compare_func cmp)
+{
+ wmem_tree_node_t *node = tree->root;
+ wmem_tree_node_t *new_node = NULL;
+
+ /* is this the first node ?*/
+ if (!node) {
+ tree->root = create_node(tree->data_allocator, node, key,
+ data, WMEM_NODE_COLOR_BLACK, false);
+ return tree->root;
+ }
+
+ /* it was not the new root so walk the tree until we find where to
+ * insert this new leaf.
+ */
+ while (!new_node) {
+ int result = cmp(key, node->key);
+ if (result == 0) {
+ node->data = data;
+ node->is_removed = data ? false : true;
+ return node;
+ }
+ else if (result < 0) {
+ if (node->left) {
+ node = node->left;
+ }
+ else {
+ new_node = create_node(tree->data_allocator, node, key,
+ data, WMEM_NODE_COLOR_RED, false);
+ node->left = new_node;
+ }
+ }
+ else if (result > 0) {
+ if (node->right) {
+ node = node->right;
+ }
+ else {
+ /* new node to the right */
+ new_node = create_node(tree->data_allocator, node, key,
+ data, WMEM_NODE_COLOR_RED, false);
+ node->right = new_node;
+ }
+ }
+ }
+
+ /* node will now point to the newly created node */
+ rb_insert_case1(tree, new_node);
+
+ return new_node;
+}
+
+void
+wmem_tree_insert32(wmem_tree_t *tree, uint32_t key, void *data)
+{
+ lookup_or_insert32(tree, key, NULL, data, false, true);
+}
+
+bool wmem_tree_contains32(wmem_tree_t *tree, uint32_t key)
+{
+ if (!tree) {
+ return false;
+ }
+
+ wmem_tree_node_t *node = tree->root;
+
+ while (node) {
+ if (key == GPOINTER_TO_UINT(node->key)) {
+ return true;
+ }
+ else if (key < GPOINTER_TO_UINT(node->key)) {
+ node = node->left;
+ }
+ else if (key > GPOINTER_TO_UINT(node->key)) {
+ node = node->right;
+ }
+ }
+
+ return false;
+}
+
+void *
+wmem_tree_lookup32(wmem_tree_t *tree, uint32_t key)
+{
+ if (!tree) {
+ return NULL;
+ }
+
+ wmem_tree_node_t *node = tree->root;
+
+ while (node) {
+ if (key == GPOINTER_TO_UINT(node->key)) {
+ return node->data;
+ }
+ else if (key < GPOINTER_TO_UINT(node->key)) {
+ node = node->left;
+ }
+ else if (key > GPOINTER_TO_UINT(node->key)) {
+ node = node->right;
+ }
+ }
+
+ return NULL;
+}
+
+void *
+wmem_tree_lookup32_le(wmem_tree_t *tree, uint32_t key)
+{
+ if (!tree) {
+ return NULL;
+ }
+
+ wmem_tree_node_t *node = tree->root;
+
+ while (node) {
+ if (key == GPOINTER_TO_UINT(node->key)) {
+ return node->data;
+ }
+ else if (key < GPOINTER_TO_UINT(node->key)) {
+ if (node->left == NULL) {
+ break;
+ }
+ node = node->left;
+ }
+ else if (key > GPOINTER_TO_UINT(node->key)) {
+ if (node->right == NULL) {
+ break;
+ }
+ node = node->right;
+ }
+ }
+
+ if (!node) {
+ return NULL;
+ }
+
+ /* If we are still at the root of the tree this means that this node
+ * is either smaller than the search key and then we return this
+ * node or else there is no smaller key available and then
+ * we return NULL.
+ */
+ if (node->parent == NULL) {
+ if (key > GPOINTER_TO_UINT(node->key)) {
+ return node->data;
+ } else {
+ return NULL;
+ }
+ }
+
+ if (GPOINTER_TO_UINT(node->key) <= key) {
+ /* if our key is <= the search key, we have the right node */
+ return node->data;
+ }
+ else if (node == node->parent->left) {
+ /* our key is bigger than the search key and we're a left child,
+ * we have to check if any of our ancestors are smaller. */
+ while (node) {
+ if (key > GPOINTER_TO_UINT(node->key)) {
+ return node->data;
+ }
+ node=node->parent;
+ }
+ return NULL;
+ }
+ else {
+ /* our key is bigger than the search key and we're a right child,
+ * our parent is the one we want */
+ return node->parent->data;
+ }
+}
+
+void *
+wmem_tree_remove32(wmem_tree_t *tree, uint32_t key)
+{
+ void *ret = wmem_tree_lookup32(tree, key);
+ if (ret) {
+ /* Not really a remove, but set data to NULL to mark node with is_removed */
+ wmem_tree_insert32(tree, key, NULL);
+ }
+ return ret;
+}
+
+void
+wmem_tree_insert_string(wmem_tree_t* tree, const char* k, void* v, uint32_t flags)
+{
+ char *key;
+ compare_func cmp;
+
+ key = wmem_strdup(tree->data_allocator, k);
+
+ if (flags & WMEM_TREE_STRING_NOCASE) {
+ cmp = (compare_func)g_ascii_strcasecmp;
+ } else {
+ cmp = (compare_func)strcmp;
+ }
+
+ wmem_tree_insert(tree, key, v, cmp);
+}
+
+void *
+wmem_tree_lookup_string(wmem_tree_t* tree, const char* k, uint32_t flags)
+{
+ compare_func cmp;
+
+ if (flags & WMEM_TREE_STRING_NOCASE) {
+ cmp = (compare_func)g_ascii_strcasecmp;
+ } else {
+ cmp = (compare_func)strcmp;
+ }
+
+ return wmem_tree_lookup(tree, k, cmp);
+}
+
+void *
+wmem_tree_remove_string(wmem_tree_t* tree, const char* k, uint32_t flags)
+{
+ void *ret = wmem_tree_lookup_string(tree, k, flags);
+ if (ret) {
+ /* Not really a remove, but set data to NULL to mark node with is_removed */
+ wmem_tree_insert_string(tree, k, NULL, flags);
+ }
+ return ret;
+}
+
+static void *
+create_sub_tree(void* d)
+{
+ return wmem_tree_new(((wmem_tree_t *)d)->data_allocator);
+}
+
+void
+wmem_tree_insert32_array(wmem_tree_t *tree, wmem_tree_key_t *key, void *data)
+{
+ wmem_tree_t *insert_tree = NULL;
+ wmem_tree_key_t *cur_key;
+ uint32_t i, insert_key32 = 0;
+
+ for (cur_key = key; cur_key->length > 0; cur_key++) {
+ for (i = 0; i < cur_key->length; i++) {
+ /* Insert using the previous key32 */
+ if (!insert_tree) {
+ insert_tree = tree;
+ } else {
+ insert_tree = (wmem_tree_t *)lookup_or_insert32(insert_tree,
+ insert_key32, create_sub_tree, tree, true, false);
+ }
+ insert_key32 = cur_key->key[i];
+ }
+ }
+
+ ws_assert(insert_tree);
+
+ wmem_tree_insert32(insert_tree, insert_key32, data);
+}
+
+static void *
+wmem_tree_lookup32_array_helper(wmem_tree_t *tree, wmem_tree_key_t *key,
+ void*(*helper)(wmem_tree_t*, uint32_t))
+{
+ wmem_tree_t *lookup_tree = NULL;
+ wmem_tree_key_t *cur_key;
+ uint32_t i, lookup_key32 = 0;
+
+ if (!tree || !key) {
+ return NULL;
+ }
+
+ for (cur_key = key; cur_key->length > 0; cur_key++) {
+ for (i = 0; i < cur_key->length; i++) {
+ /* Lookup using the previous key32 */
+ if (!lookup_tree) {
+ lookup_tree = tree;
+ }
+ else {
+ lookup_tree =
+ (wmem_tree_t *)(*helper)(lookup_tree, lookup_key32);
+ if (!lookup_tree) {
+ return NULL;
+ }
+ }
+ lookup_key32 = cur_key->key[i];
+ }
+ }
+
+ /* Assert if we didn't get any valid keys */
+ ws_assert(lookup_tree);
+
+ return (*helper)(lookup_tree, lookup_key32);
+}
+
+void *
+wmem_tree_lookup32_array(wmem_tree_t *tree, wmem_tree_key_t *key)
+{
+ return wmem_tree_lookup32_array_helper(tree, key, wmem_tree_lookup32);
+}
+
+void *
+wmem_tree_lookup32_array_le(wmem_tree_t *tree, wmem_tree_key_t *key)
+{
+ return wmem_tree_lookup32_array_helper(tree, key, wmem_tree_lookup32_le);
+}
+
+static bool
+wmem_tree_foreach_nodes(wmem_tree_node_t* node, wmem_foreach_func callback,
+ void *user_data)
+{
+ bool stop_traverse = false;
+
+ if (!node) {
+ return false;
+ }
+
+ if (node->left) {
+ if (wmem_tree_foreach_nodes(node->left, callback, user_data)) {
+ return true;
+ }
+ }
+
+ if (node->is_subtree) {
+ stop_traverse = wmem_tree_foreach((wmem_tree_t *)node->data,
+ callback, user_data);
+ } else if (!node->is_removed) {
+ /* No callback for "removed" nodes */
+ stop_traverse = callback(node->key, node->data, user_data);
+ }
+
+ if (stop_traverse) {
+ return true;
+ }
+
+ if(node->right) {
+ if (wmem_tree_foreach_nodes(node->right, callback, user_data)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+wmem_tree_foreach(wmem_tree_t* tree, wmem_foreach_func callback,
+ void *user_data)
+{
+ if(!tree->root)
+ return false;
+
+ return wmem_tree_foreach_nodes(tree->root, callback, user_data);
+}
+
+static void wmem_print_subtree(wmem_tree_t *tree, uint32_t level, wmem_printer_func key_printer, wmem_printer_func data_printer);
+
+static void
+wmem_print_indent(uint32_t level) {
+ uint32_t i;
+ for (i=0; i<level; i++) {
+ printf(" ");
+ }
+}
+
+static void
+wmem_tree_print_nodes(const char *prefix, wmem_tree_node_t *node, uint32_t level,
+ wmem_printer_func key_printer, wmem_printer_func data_printer)
+{
+ if (!node)
+ return;
+
+ wmem_print_indent(level);
+
+ printf("%sNODE:%p parent:%p left:%p right:%p colour:%s key:%p %s:%p\n",
+ prefix,
+ (void *)node, (void *)node->parent,
+ (void *)node->left, (void *)node->right,
+ node->color?"Black":"Red", node->key,
+ node->is_subtree?"tree":"data", node->data);
+ if (key_printer) {
+ wmem_print_indent(level);
+ key_printer(node->key);
+ printf("\n");
+ }
+ if (data_printer && !node->is_subtree) {
+ wmem_print_indent(level);
+ data_printer(node->data);
+ printf("\n");
+ }
+
+ if (node->left)
+ wmem_tree_print_nodes("L-", node->left, level+1, key_printer, data_printer);
+ if (node->right)
+ wmem_tree_print_nodes("R-", node->right, level+1, key_printer, data_printer);
+
+ if (node->is_subtree)
+ wmem_print_subtree((wmem_tree_t *)node->data, level+1, key_printer, data_printer);
+}
+
+
+static void
+wmem_print_subtree(wmem_tree_t *tree, uint32_t level, wmem_printer_func key_printer, wmem_printer_func data_printer)
+{
+ if (!tree)
+ return;
+
+ wmem_print_indent(level);
+
+ printf("WMEM tree:%p root:%p\n", (void *)tree, (void *)tree->root);
+ if (tree->root) {
+ wmem_tree_print_nodes("Root-", tree->root, level, key_printer, data_printer);
+ }
+}
+
+void
+wmem_print_tree(wmem_tree_t *tree, wmem_printer_func key_printer, wmem_printer_func data_printer)
+{
+ wmem_print_subtree(tree, 0, key_printer, data_printer);
+}
+/*
+ * 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/wsutil/wmem/wmem_tree.h b/wsutil/wmem/wmem_tree.h
new file mode 100644
index 00000000..f639ff9a
--- /dev/null
+++ b/wsutil/wmem/wmem_tree.h
@@ -0,0 +1,263 @@
+/** @file
+ * Definitions for the Wireshark Memory Manager Red-Black Tree
+ * Based on the red-black tree implementation in epan/emem.*
+ * Copyright 2013, Evan Huus <eapache@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WMEM_TREE_H__
+#define __WMEM_TREE_H__
+
+#include "wmem_core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/** @addtogroup wmem
+ * @{
+ * @defgroup wmem-tree Red/Black Tree
+ *
+ * Binary trees are a well-known and popular device in computer science to
+ * handle storage of objects based on a search key or identity. The
+ * particular binary tree style implemented here is the red/black tree, which
+ * has the nice property of being self-balancing. This guarantees O(log(n))
+ * time for lookups, compared to linked lists that are O(n). This means
+ * red/black trees scale very well when many objects are being stored.
+ *
+ * @{
+ */
+
+struct _wmem_tree_t;
+typedef struct _wmem_tree_t wmem_tree_t;
+
+/** Creates a tree with the given allocator scope. When the scope is emptied,
+ * the tree is fully destroyed. */
+WS_DLL_PUBLIC
+wmem_tree_t *
+wmem_tree_new(wmem_allocator_t *allocator)
+G_GNUC_MALLOC;
+
+/** Creates a tree with two allocator scopes. The base structure lives in the
+ * metadata scope, and the tree data lives in the data scope. Every time free_all
+ * occurs in the data scope the tree is transparently emptied without affecting
+ * the location of the base / metadata structure.
+ *
+ * WARNING: None of the tree (even the part in the metadata scope) can be used
+ * after the data scope has been *destroyed*.
+ *
+ * The primary use for this function is to create trees that reset for each new
+ * capture file that is loaded. This can be done by specifying wmem_epan_scope()
+ * as the metadata scope and wmem_file_scope() as the data scope.
+ */
+WS_DLL_PUBLIC
+wmem_tree_t *
+wmem_tree_new_autoreset(wmem_allocator_t *metadata_scope, wmem_allocator_t *data_scope)
+G_GNUC_MALLOC;
+
+/** Cleanup memory used by tree. Intended for NULL scope allocated trees */
+WS_DLL_PUBLIC
+void
+wmem_tree_destroy(wmem_tree_t *tree, bool free_keys, bool free_values);
+
+/** Returns true if the tree is empty (has no nodes). */
+WS_DLL_PUBLIC
+bool
+wmem_tree_is_empty(wmem_tree_t *tree);
+
+/** Returns number of nodes in tree */
+WS_DLL_PUBLIC
+unsigned
+wmem_tree_count(wmem_tree_t* tree);
+
+/** Insert a node indexed by a uint32_t key value.
+ *
+ * Data is a pointer to the structure you want to be able to retrieve by
+ * searching for the same key later.
+ *
+ * NOTE: If you insert a node to a key that already exists in the tree this
+ * function will simply overwrite the old value. If the structures you are
+ * storing are allocated in a wmem pool this is not a problem as they will still
+ * be freed with the pool. If you are managing them manually however, you must
+ * either ensure the key is unique, or do a lookup before each insert.
+ */
+WS_DLL_PUBLIC
+void
+wmem_tree_insert32(wmem_tree_t *tree, uint32_t key, void *data);
+
+/** Look up a node in the tree indexed by a uint32_t integer value. Return true
+ * if present.
+ */
+WS_DLL_PUBLIC
+bool
+wmem_tree_contains32(wmem_tree_t *tree, uint32_t key);
+
+/** Look up a node in the tree indexed by a uint32_t integer value. If no node is
+ * found the function will return NULL.
+ */
+WS_DLL_PUBLIC
+void *
+wmem_tree_lookup32(wmem_tree_t *tree, uint32_t key);
+
+/** Look up a node in the tree indexed by a uint32_t integer value.
+ * Returns the node that has the largest key that is less than or equal
+ * to the search key, or NULL if no such key exists.
+ */
+WS_DLL_PUBLIC
+void *
+wmem_tree_lookup32_le(wmem_tree_t *tree, uint32_t key);
+
+/** Remove a node in the tree indexed by a uint32_t integer value. This is not
+ * really a remove, but the value is set to NULL so that wmem_tree_lookup32
+ * not will find it.
+ */
+WS_DLL_PUBLIC
+void *
+wmem_tree_remove32(wmem_tree_t *tree, uint32_t key);
+
+/** case insensitive strings as keys */
+#define WMEM_TREE_STRING_NOCASE 0x00000001
+/** Insert a new value under a string key. Like wmem_tree_insert32 but where the
+ * key is a null-terminated string instead of a uint32_t. You may pass
+ * WMEM_TREE_STRING_NOCASE to the flags argument in order to make it store the
+ * key in a case-insensitive way. (Note that "case-insensitive" refers
+ * only to the ASCII letters A-Z and a-z; it is locale-independent.
+ * Do not expect it to honor the rules of your language; for example, "I"
+ * will always be mapped to "i". */
+WS_DLL_PUBLIC
+void
+wmem_tree_insert_string(wmem_tree_t *tree, const char* key, void *data,
+ uint32_t flags);
+
+/** Lookup the value under a string key, like wmem_tree_lookup32 but where the
+ * keye is a null-terminated string instead of a uint32_t. See
+ * wmem_tree_insert_string for an explanation of flags. */
+WS_DLL_PUBLIC
+void *
+wmem_tree_lookup_string(wmem_tree_t* tree, const char* key, uint32_t flags);
+
+/** Remove the value under a string key. This is not really a remove, but the
+ * value is set to NULL so that wmem_tree_lookup_string not will find it.
+ * See wmem_tree_insert_string for an explanation of flags. */
+WS_DLL_PUBLIC
+void *
+wmem_tree_remove_string(wmem_tree_t* tree, const char* key, uint32_t flags);
+
+typedef struct _wmem_tree_key_t {
+ uint32_t length; /**< length in uint32_t words */
+ uint32_t *key;
+} wmem_tree_key_t;
+
+/** Insert a node indexed by a sequence of uint32_t key values.
+ *
+ * Takes as key an array of uint32_t vectors of type wmem_tree_key_t. It will
+ * iterate through each key to search further down the tree until it reaches an
+ * element where length==0, indicating the end of the array. You MUST terminate
+ * the key array by {0, NULL} or this will crash.
+ *
+ * NOTE: length indicates the number of uint32_t values in the vector, not the
+ * number of bytes.
+ *
+ * NOTE: all the "key" members of the "key" argument MUST be aligned on
+ * 32-bit boundaries; otherwise, this code will crash on platforms such
+ * as SPARC that require aligned pointers.
+ *
+ * If you use ...32_array() calls you MUST make sure that every single node
+ * you add to a specific tree always has a key of exactly the same number of
+ * keylen words or it will crash. Or at least that every single item that sits
+ * behind the same top level node always has exactly the same number of words.
+ *
+ * One way to guarantee this is the way that NFS does this for the
+ * nfs_name_snoop_known tree which holds filehandles for both v2 and v3.
+ * v2 filehandles are always 32 bytes (8 words) while v3 filehandles can have
+ * any length (though 32 bytes are most common).
+ * The NFS dissector handles this by providing a uint32_t containing the length
+ * as the very first item in this vector :
+ *
+ * wmem_tree_key_t fhkey[3];
+ *
+ * fhlen=nns->fh_length;
+ * fhkey[0].length=1;
+ * fhkey[0].key=&fhlen;
+ * fhkey[1].length=fhlen/4;
+ * fhkey[1].key=nns->fh;
+ * fhkey[2].length=0;
+ */
+WS_DLL_PUBLIC
+void
+wmem_tree_insert32_array(wmem_tree_t *tree, wmem_tree_key_t *key, void *data);
+
+/** Look up a node in the tree indexed by a sequence of uint32_t integer values.
+ * See wmem_tree_insert32_array for details on the key.
+ */
+WS_DLL_PUBLIC
+void *
+wmem_tree_lookup32_array(wmem_tree_t *tree, wmem_tree_key_t *key);
+
+/** Look up a node in the tree indexed by a multi-part tree value.
+ * The function will return the node that has the largest key that is
+ * equal to or smaller than the search key, or NULL if no such key was
+ * found.
+ *
+ * NOTE: The key returned will be "less" in key order. The usefulness
+ * of the returned node must be verified prior to use.
+ *
+ * See wmem_tree_insert32_array for details on the key.
+ */
+WS_DLL_PUBLIC
+void *
+wmem_tree_lookup32_array_le(wmem_tree_t *tree, wmem_tree_key_t *key);
+
+/** Function type for processing one node of a tree during a traversal. Value is
+ * the value of the node, userdata is whatever was passed to the traversal
+ * function. If the function returns true the traversal will end prematurely.
+ */
+typedef bool (*wmem_foreach_func)(const void *key, void *value, void *userdata);
+
+
+/** Function type to print key/data of nodes in wmem_print_tree_verbose */
+typedef void (*wmem_printer_func)(const void *data);
+
+
+/** Inorder traversal (left/parent/right) of the tree and call
+ * callback(value, userdata) for each value found.
+ *
+ * Returns true if the traversal was ended prematurely by the callback.
+ */
+WS_DLL_PUBLIC
+bool
+wmem_tree_foreach(wmem_tree_t* tree, wmem_foreach_func callback,
+ void *user_data);
+
+
+/* Accepts callbacks to print the key and/or data (both printers can be null) */
+WS_DLL_PUBLIC
+void
+wmem_print_tree(wmem_tree_t *tree, wmem_printer_func key_printer, wmem_printer_func data_printer);
+
+/** @}
+ * @} */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WMEM_TREE_H__ */
+
+/*
+ * 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/wsutil/wmem/wmem_user_cb.c b/wsutil/wmem/wmem_user_cb.c
new file mode 100644
index 00000000..232b1692
--- /dev/null
+++ b/wsutil/wmem/wmem_user_cb.c
@@ -0,0 +1,104 @@
+/* wmem_user_cb.c
+ * Wireshark Memory Manager User Callbacks
+ * Copyright 2012, Evan Huus <eapache@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "wmem_core.h"
+#include "wmem_allocator.h"
+
+#include "wmem_user_cb.h"
+#include "wmem_user_cb_int.h"
+
+typedef struct _wmem_user_cb_container_t {
+ wmem_user_cb_t cb;
+ void *user_data;
+ struct _wmem_user_cb_container_t *next;
+ unsigned id;
+} wmem_user_cb_container_t;
+
+void
+wmem_call_callbacks(wmem_allocator_t *allocator, wmem_cb_event_t event)
+{
+ wmem_user_cb_container_t **prev, *cur;
+ bool again;
+
+ prev = &(allocator->callbacks);
+ cur = allocator->callbacks;
+
+ while (cur) {
+
+ /* call it */
+ again = cur->cb(allocator, event, cur->user_data);
+
+ /* if the callback requested deregistration, or this is being triggered
+ * by the final destruction of the allocator, remove the callback */
+ if (! again || event == WMEM_CB_DESTROY_EVENT) {
+ *prev = cur->next;
+ wmem_free(NULL, cur);
+ cur = *prev;
+ }
+ else {
+ prev = &(cur->next);
+ cur = cur->next;
+ }
+ }
+}
+
+unsigned
+wmem_register_callback(wmem_allocator_t *allocator,
+ wmem_user_cb_t callback, void *user_data)
+{
+ wmem_user_cb_container_t *container;
+ static unsigned next_id = 1;
+
+ container = wmem_new(NULL, wmem_user_cb_container_t);
+
+ container->cb = callback;
+ container->user_data = user_data;
+ container->next = allocator->callbacks;
+ container->id = next_id++;
+
+ allocator->callbacks = container;
+
+ return container->id;
+}
+
+void
+wmem_unregister_callback(wmem_allocator_t *allocator, unsigned id)
+{
+ wmem_user_cb_container_t **prev, *cur;
+
+ prev = &(allocator->callbacks);
+ cur = allocator->callbacks;
+
+ while (cur) {
+
+ if (cur->id == id) {
+ *prev = cur->next;
+ wmem_free(NULL, cur);
+ return;
+ }
+
+ prev = &(cur->next);
+ cur = cur->next;
+ }
+}
+
+/*
+ * 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/wsutil/wmem/wmem_user_cb.h b/wsutil/wmem/wmem_user_cb.h
new file mode 100644
index 00000000..7d2a37e3
--- /dev/null
+++ b/wsutil/wmem/wmem_user_cb.h
@@ -0,0 +1,92 @@
+/** @file
+ * Definitions for the Wireshark Memory Manager User Callbacks
+ * Copyright 2012, Evan Huus <eapache@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WMEM_USER_CB_H__
+#define __WMEM_USER_CB_H__
+
+#include <glib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/** @addtogroup wmem
+ * @{
+ * @defgroup wmem-user-cb User Callbacks
+ *
+ * User callbacks.
+ *
+ * @{
+ */
+
+/** The events that can trigger a callback. */
+typedef enum _wmem_cb_event_t {
+ WMEM_CB_FREE_EVENT, /**< wmem_free_all() */
+ WMEM_CB_DESTROY_EVENT /**< wmem_destroy_allocator() */
+} wmem_cb_event_t;
+
+/** Function signature for registered user callbacks.
+ *
+ * allocator The allocator that triggered this callback.
+ * event The event type that triggered this callback.
+ * user_data Whatever user_data was originally passed to the call to
+ * wmem_register_callback().
+ * @return false to unregister the callback, true otherwise.
+ */
+typedef bool (*wmem_user_cb_t) (wmem_allocator_t*, wmem_cb_event_t, void*);
+
+/** Register a callback function with the given allocator pool.
+ *
+ * @param allocator The allocator with which to register the callback.
+ * @param callback The function to be called as the callback.
+ * @param user_data An arbitrary data pointer that is passed to the callback as
+ * a way to specify extra parameters or store extra data. Note
+ * that this pointer is not freed when a callback is finished,
+ * you have to do that yourself in the callback, or just
+ * allocate it in the appropriate wmem pool.
+ * @return ID of this callback that can be passed back to
+ * wmem_unregister_callback().
+ */
+WS_DLL_PUBLIC
+unsigned
+wmem_register_callback(wmem_allocator_t *allocator, wmem_user_cb_t callback,
+ void *user_data);
+
+/** Unregister the callback function with the given ID.
+ *
+ * @param allocator The allocator from which to unregister the callback.
+ * @param id The callback id as returned from wmem_register_callback().
+ */
+WS_DLL_PUBLIC
+void
+wmem_unregister_callback(wmem_allocator_t *allocator, unsigned id);
+
+/** @}
+ * @} */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WMEM_USER_CB_H__ */
+
+/*
+ * 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/wsutil/wmem/wmem_user_cb_int.h b/wsutil/wmem/wmem_user_cb_int.h
new file mode 100644
index 00000000..9f3ed58a
--- /dev/null
+++ b/wsutil/wmem/wmem_user_cb_int.h
@@ -0,0 +1,45 @@
+/** @file
+ *
+ * Definitions for the Wireshark Memory Manager User Callback Internals
+ * Copyright 2012, Evan Huus <eapache@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WMEM_USER_CB_INT_H__
+#define __WMEM_USER_CB_INT_H__
+
+#include <glib.h>
+
+#include "wmem_user_cb.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+WS_DLL_LOCAL
+void
+wmem_call_callbacks(wmem_allocator_t *allocator, wmem_cb_event_t event);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WMEM_USER_CB_INT_H__ */
+
+/*
+ * 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/wsutil/ws_assert.h b/wsutil/ws_assert.h
new file mode 100644
index 00000000..f4880fd8
--- /dev/null
+++ b/wsutil/ws_assert.h
@@ -0,0 +1,129 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WS_ASSERT_H__
+#define __WS_ASSERT_H__
+
+#include <ws_symbol_export.h>
+#include <ws_attributes.h>
+#include <stdbool.h>
+#include <string.h>
+#include <wsutil/wslog.h>
+#include <wsutil/wmem/wmem.h>
+
+#if defined(ENABLE_ASSERT)
+#define WS_ASSERT_ENABLED 1
+#elif defined(NDEBUG)
+#define WS_ASSERT_ENABLED 0
+#else
+#define WS_ASSERT_ENABLED 1
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * We don't want to execute the expression without assertions because
+ * it might be time and space costly and the goal here is to optimize for
+ * that case. However removing it completely is not good enough
+ * because it might generate many unused variable warnings. So we use
+ * if (false) and let the compiler optimize away the dead execution branch.
+ */
+#define ws_assert_if_active(active, expr) \
+ do { \
+ if ((active) && !(expr)) \
+ ws_error("assertion failed: %s", #expr); \
+ } while (0)
+
+/*
+ * ws_abort_if_fail() is not conditional on having assertions enabled.
+ * Usually used to appease a static analyzer.
+ */
+#define ws_abort_if_fail(expr) \
+ ws_assert_if_active(true, expr)
+
+/*
+ * ws_assert() cannot produce side effects, otherwise code will
+ * behave differently because of having assertions enabled/disabled, and
+ * probably introduce some difficult to track bugs.
+ */
+#define ws_assert(expr) \
+ ws_assert_if_active(WS_ASSERT_ENABLED, expr)
+
+
+#define ws_assert_streq(s1, s2) \
+ ws_assert((s1) && (s2) && strcmp((s1), (s2)) == 0)
+
+#define ws_assert_utf8(str, len) \
+ do { \
+ const char *__assert_endptr; \
+ if (WS_ASSERT_ENABLED && \
+ !g_utf8_validate(str, len, &__assert_endptr)) { \
+ ws_log_utf8_full(LOG_DOMAIN_UTF_8, LOG_LEVEL_ERROR, \
+ __FILE__, __LINE__, __func__, \
+ str, len, __assert_endptr); \
+ } \
+ } while (0)
+
+/*
+ * We don't want to disable ws_assert_not_reached() with (optional) assertions
+ * disabled.
+ * That would blast compiler warnings everywhere for no benefit, not
+ * even a miniscule performance gain. Reaching this function is always
+ * a programming error and will unconditionally abort execution.
+ *
+ * Note: With g_assert_not_reached() if the compiler supports unreachable
+ * built-ins (which recent versions of GCC and MSVC do) there is no warning
+ * blast with g_assert_not_reached() and G_DISABLE_ASSERT. However if that
+ * is not the case then g_assert_not_reached() is simply (void)0 and that
+ * causes the spurious warnings, because the compiler can't tell anymore
+ * that a certain code path is not used. We avoid that with
+ * ws_assert_not_reached(). There is no reason to ever use a no-op here.
+ */
+#define ws_assert_not_reached() \
+ ws_error("assertion \"not reached\" failed")
+
+/*
+ * These macros can be used as an alternative to ws_assert() to
+ * assert some condition on function arguments. This must only be used
+ * to catch programming errors, in situations where an assertion is
+ * appropriate. And it should only be used if failing the condition
+ * doesn't necessarily lead to an inconsistent state for the program.
+ *
+ * It is possible to set the fatal log domain to "InvalidArg" to abort
+ * execution for debugging purposes, if one of these checks fail.
+ */
+
+#define ws_warn_badarg(str) \
+ ws_log_full(LOG_DOMAIN_EINVAL, LOG_LEVEL_WARNING, \
+ __FILE__, __LINE__, __func__, \
+ "invalid argument: %s", str)
+
+#define ws_return_str_if(expr, scope) \
+ do { \
+ if (WS_ASSERT_ENABLED && (expr)) { \
+ ws_warn_badarg(#expr); \
+ return wmem_strdup_printf(scope, "(invalid argument: %s)", #expr); \
+ } \
+ } while (0)
+
+#define ws_return_val_if(expr, val) \
+ do { \
+ if (WS_ASSERT_ENABLED && (expr)) { \
+ ws_warn_badarg(#expr); \
+ return (val); \
+ } \
+ } while (0)
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WS_ASSERT_H__ */
diff --git a/wsutil/ws_cpuid.h b/wsutil/ws_cpuid.h
new file mode 100644
index 00000000..cc17e2a5
--- /dev/null
+++ b/wsutil/ws_cpuid.h
@@ -0,0 +1,141 @@
+/** @file
+ * Get the CPU info on x86 processors that support it
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/*
+ * Get CPU info on platforms where the x86 cpuid instruction can be used.
+ *
+ * Skip 32-bit versions for GCC and Clang, as older IA-32 processors don't
+ * have cpuid.
+ *
+ * Intel has documented the CPUID instruction in the "Intel(r) 64 and IA-32
+ * Architectures Developer's Manual" at
+ *
+ * https://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-vol-2a-manual.html
+ *
+ * The ws_cpuid() routine will return 0 if cpuinfo isn't available, including
+ * on non-x86 platforms and on 32-bit x86 platforms with GCC and Clang, as
+ * well as non-MSVC and non-GCC-or-Clang platforms.
+ *
+ * The "selector" argument to ws_cpuid() is the "initial EAX value" for the
+ * instruction. The initial ECX value is 0.
+ *
+ * The "CPUInfo" argument points to 4 32-bit values into which the
+ * resulting values of EAX, EBX, ECX, and EDX are store, in order.
+ */
+
+#include "ws_attributes.h"
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#if defined(_MSC_VER) /* MSVC */
+
+/*
+ * XXX - do the same IA-32 (which doesn't have CPUID prior to some versions
+ * of the 80486 and all versions of the 80586^Woriginal Pentium) vs.
+ * x86-64 (which always has CPUID) stuff that we do with GCC/Clang?
+ *
+ * You will probably not be happy running current versions of Wireshark
+ * on an 80386 or 80486 machine, and we're dropping support for IA-32
+ * on Windows anyway, so the answer is probably "no".
+ */
+#if defined(_M_IX86) || defined(_M_X64)
+static bool
+ws_cpuid(uint32_t *CPUInfo, uint32_t selector)
+{
+ /* https://docs.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex */
+
+ CPUInfo[0] = CPUInfo[1] = CPUInfo[2] = CPUInfo[3] = 0;
+ __cpuid((int *) CPUInfo, selector);
+ /* XXX, how to check if it's supported on MSVC? just in case clear all flags above */
+ return true;
+}
+#else /* not x86 */
+static bool
+ws_cpuid(uint32_t *CPUInfo _U_, int selector _U_)
+{
+ /* Not x86, so no cpuid instruction */
+ return false;
+}
+#endif
+
+#elif defined(__GNUC__) /* GCC/clang */
+
+#if defined(__x86_64__)
+static inline bool
+ws_cpuid(uint32_t *CPUInfo, int selector)
+{
+ __asm__ __volatile__("cpuid"
+ : "=a" (CPUInfo[0]),
+ "=b" (CPUInfo[1]),
+ "=c" (CPUInfo[2]),
+ "=d" (CPUInfo[3])
+ : "a" (selector),
+ "c" (0));
+ return true;
+}
+#elif defined(__i386__)
+static bool
+ws_cpuid(uint32_t *CPUInfo _U_, int selector _U_)
+{
+ /*
+ * TODO: need a test if older processors have the cpuid instruction.
+ *
+ * The correct way to test for this, according to the Intel64/IA-32
+ * documentation from Intel, in section 17.1 "USING THE CPUID
+ * INSTRUCTION", is to try to change the ID bit (bit 21) in
+ * EFLAGS. If it can be changed, the machine supports CPUID,
+ * otherwise it doesn't.
+ *
+ * Some 486's, and all subsequent processors, support CPUID.
+ *
+ * For those who are curious, the way you distinguish between
+ * an 80386 and an 80486 is to try to set the flag in EFLAGS
+ * that causes unaligned accesses to fault - that's bit 18.
+ * However, if the SMAP bit is set in CR4, that bit controls
+ * whether explicit supervisor-mode access to user-mode pages
+ * are allowed, so that should presumably only be done in a
+ * very controlled environment, such as the system boot process.
+ *
+ * So, if you want to find out what type of CPU the system has,
+ * it's probably best to ask the OS, if it supplies the result
+ * of any CPU type testing it's done.
+ */
+ return false;
+}
+#else /* not x86 */
+static bool
+ws_cpuid(uint32_t *CPUInfo _U_, int selector _U_)
+{
+ /* Not x86, so no cpuid instruction */
+ return false;
+}
+#endif
+
+#else /* Other compilers */
+
+static bool
+ws_cpuid(uint32_t *CPUInfo _U_, int selector _U_)
+{
+ return false;
+}
+#endif
+
+static int
+ws_cpuid_sse42(void)
+{
+ uint32_t CPUInfo[4];
+
+ if (!ws_cpuid(CPUInfo, 1))
+ return 0;
+
+ /* in ECX bit 20 toggled on */
+ return (CPUInfo[2] & (1 << 20));
+}
diff --git a/wsutil/ws_getopt.c b/wsutil/ws_getopt.c
new file mode 100644
index 00000000..ced6e177
--- /dev/null
+++ b/wsutil/ws_getopt.c
@@ -0,0 +1,271 @@
+/*
+ * musl as a whole is licensed under the following standard MIT license:
+ *
+ * ----------------------------------------------------------------------
+ * Copyright © 2005-2020 Rich Felker, et al.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * ----------------------------------------------------------------------
+ */
+
+#include <wsutil/ws_getopt.h>
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <wchar.h>
+
+#include <ws_codepoints.h>
+
+char *ws_optarg;
+int ws_optind=1, ws_opterr=1, ws_optopt, ws_optpos, ws_optreset=0;
+
+static void __getopt_msg(const char *prog, const char *errstr,
+ const char *optbuf, size_t optsize)
+{
+ FILE *f = stderr;
+ if ((fputs(prog, f) < 0) ||
+ (fputs(errstr, f) < 0) ||
+ (fwrite(optbuf, sizeof(char), optsize, f) != optsize)) {
+ return;
+ }
+ putc('\n', f);
+}
+
+static void permute(char *const *argv, int dest, int src)
+{
+ char **av = (char **)argv;
+ char *tmp = av[src];
+ int i;
+ for (i=src; i>dest; i--)
+ av[i] = av[i-1];
+ av[dest] = tmp;
+}
+
+int ws_getopt(int argc, char * const argv[], const char *optstring)
+{
+ int i;
+ wchar_t c, d;
+ int k, l;
+ char *optchar;
+
+ if (!ws_optind || ws_optreset) {
+ ws_optreset = 0;
+ ws_optpos = 0;
+ ws_optind = 1;
+ }
+
+ if (ws_optind >= argc || !argv[ws_optind])
+ return -1;
+
+ if (argv[ws_optind][0] != '-') {
+ if (optstring[0] == '-') {
+ ws_optarg = argv[ws_optind++];
+ return 1;
+ }
+ return -1;
+ }
+
+ if (!argv[ws_optind][1])
+ return -1;
+
+ if (argv[ws_optind][1] == '-' && !argv[ws_optind][2]) {
+ ws_optind++;
+ return -1;
+ }
+
+ if (!ws_optpos) ws_optpos++;
+ if ((k = mbtowc(&c, argv[ws_optind]+ws_optpos, MB_LEN_MAX)) < 0) {
+ k = 1;
+ c = UNICODE_REPLACEMENT_CHARACTER; /* replacement char */
+ }
+ optchar = argv[ws_optind]+ws_optpos;
+ ws_optpos += k;
+
+ if (!argv[ws_optind][ws_optpos]) {
+ ws_optind++;
+ ws_optpos = 0;
+ }
+
+ if (optstring[0] == '-' || optstring[0] == '+')
+ optstring++;
+
+ i = 0;
+ d = 0;
+ do {
+ l = mbtowc(&d, optstring+i, MB_LEN_MAX);
+ if (l>0) i+=l; else i++;
+ } while (l && d != c);
+
+ if (d != c || c == ':') {
+ ws_optopt = c;
+ if (optstring[0] != ':' && ws_opterr)
+ __getopt_msg(argv[0], ": unrecognized option: ", optchar, k);
+ return '?';
+ }
+ if (optstring[i] == ':') {
+ ws_optarg = 0;
+ if (optstring[i+1] != ':' || ws_optpos) {
+ ws_optarg = argv[ws_optind++] + ws_optpos;
+ ws_optpos = 0;
+ }
+ if (ws_optind > argc) {
+ ws_optopt = c;
+ if (optstring[0] == ':') return ':';
+ if (ws_opterr) __getopt_msg(argv[0],
+ ": option requires an argument: ",
+ optchar, k);
+ return '?';
+ }
+ }
+ return c;
+}
+
+static int __getopt_long_core(int argc, char *const *argv, const char *optstring, const struct ws_option *longopts, int *idx, int longonly);
+
+static int __getopt_long(int argc, char *const *argv, const char *optstring, const struct ws_option *longopts, int *idx, int longonly)
+{
+ int ret, skipped, resumed;
+ if (!ws_optind || ws_optreset) {
+ ws_optreset = 0;
+ ws_optpos = 0;
+ ws_optind = 1;
+ }
+ if (ws_optind >= argc || !argv[ws_optind]) return -1;
+ skipped = ws_optind;
+ if (optstring[0] != '+' && optstring[0] != '-') {
+ int i;
+ for (i=ws_optind; ; i++) {
+ if (i >= argc || !argv[i]) return -1;
+ if (argv[i][0] == '-' && argv[i][1]) break;
+ }
+ ws_optind = i;
+ }
+ resumed = ws_optind;
+ ret = __getopt_long_core(argc, argv, optstring, longopts, idx, longonly);
+ if (resumed > skipped) {
+ int i, cnt = ws_optind-resumed;
+ for (i=0; i<cnt; i++)
+ permute(argv, skipped, ws_optind-1);
+ ws_optind = skipped + cnt;
+ }
+ return ret;
+}
+
+static int __getopt_long_core(int argc, char *const *argv, const char *optstring, const struct ws_option *longopts, int *idx, int longonly)
+{
+ ws_optarg = 0;
+ if (longopts && argv[ws_optind][0] == '-' &&
+ ((longonly && argv[ws_optind][1] && argv[ws_optind][1] != '-') ||
+ (argv[ws_optind][1] == '-' && argv[ws_optind][2])))
+ {
+ int colon = optstring[optstring[0]=='+'||optstring[0]=='-']==':';
+ int i, cnt, match = -1;
+ char *arg = NULL, *opt, *start = argv[ws_optind]+1;
+ for (cnt=i=0; longopts[i].name; i++) {
+ const char *name = longopts[i].name;
+ opt = start;
+ if (*opt == '-') opt++;
+ while (*opt && *opt != '=' && *opt == *name) {
+ name++;
+ opt++;
+ }
+ if (*opt && *opt != '=') continue;
+ arg = opt;
+ match = i;
+ if (!*name) {
+ cnt = 1;
+ break;
+ }
+ cnt++;
+ }
+ if (cnt==1 && longonly && arg-start == mblen(start, MB_LEN_MAX)) {
+ ptrdiff_t l = arg - start;
+ for (i=0; optstring[i]; i++) {
+ ptrdiff_t j;
+ for (j=0; j<l && start[j]==optstring[i+j]; j++);
+ if (j==l) {
+ cnt++;
+ break;
+ }
+ }
+ }
+ if (cnt==1) {
+ i = match;
+ opt = arg;
+ ws_optind++;
+ if (*opt == '=') {
+ if (!longopts[i].has_arg) {
+ ws_optopt = longopts[i].val;
+ if (colon || !ws_opterr)
+ return '?';
+ __getopt_msg(argv[0],
+ ": option does not take an argument: ",
+ longopts[i].name,
+ strlen(longopts[i].name));
+ return '?';
+ }
+ ws_optarg = opt+1;
+ } else if (longopts[i].has_arg == ws_required_argument) {
+ if (!(ws_optarg = argv[ws_optind])) {
+ ws_optopt = longopts[i].val;
+ if (colon) return ':';
+ if (!ws_opterr) return '?';
+ __getopt_msg(argv[0],
+ ": option requires an argument: ",
+ longopts[i].name,
+ strlen(longopts[i].name));
+ return '?';
+ }
+ ws_optind++;
+ }
+ if (idx) *idx = i;
+ if (longopts[i].flag) {
+ *longopts[i].flag = longopts[i].val;
+ return 0;
+ }
+ return longopts[i].val;
+ }
+ if (argv[ws_optind][1] == '-') {
+ ws_optopt = 0;
+ if (!colon && ws_opterr)
+ __getopt_msg(argv[0], cnt ?
+ ": option is ambiguous: " :
+ ": unrecognized option: ",
+ argv[ws_optind]+2,
+ strlen(argv[ws_optind]+2));
+ ws_optind++;
+ return '?';
+ }
+ }
+ return ws_getopt(argc, argv, optstring);
+}
+
+int ws_getopt_long(int argc, char *const *argv, const char *optstring, const struct ws_option *longopts, int *idx)
+{
+ return __getopt_long(argc, argv, optstring, longopts, idx, 0);
+}
+
+int ws_getopt_long_only(int argc, char *const *argv, const char *optstring, const struct ws_option *longopts, int *idx)
+{
+ return __getopt_long(argc, argv, optstring, longopts, idx, 1);
+}
diff --git a/wsutil/ws_getopt.h b/wsutil/ws_getopt.h
new file mode 100644
index 00000000..b32badce
--- /dev/null
+++ b/wsutil/ws_getopt.h
@@ -0,0 +1,60 @@
+/** @file
+ *
+ * musl as a whole is licensed under the following standard MIT license:
+ *
+ * ----------------------------------------------------------------------
+ * Copyright © 2005-2020 Rich Felker, et al.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * ----------------------------------------------------------------------
+ */
+
+#ifndef _WS_GETOPT_H_
+#define _WS_GETOPT_H_
+
+#include <ws_symbol_export.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+WS_DLL_PUBLIC int ws_getopt(int, char * const [], const char *);
+WS_DLL_PUBLIC char *ws_optarg;
+WS_DLL_PUBLIC int ws_optind, ws_opterr, ws_optopt, ws_optpos, ws_optreset;
+
+struct ws_option {
+ const char *name;
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+WS_DLL_PUBLIC int ws_getopt_long(int, char *const *, const char *, const struct ws_option *, int *);
+WS_DLL_PUBLIC int ws_getopt_long_only(int, char *const *, const char *, const struct ws_option *, int *);
+
+#define ws_no_argument 0
+#define ws_required_argument 1
+#define ws_optional_argument 2
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/wsutil/ws_mempbrk.c b/wsutil/ws_mempbrk.c
new file mode 100644
index 00000000..f3f99b71
--- /dev/null
+++ b/wsutil/ws_mempbrk.c
@@ -0,0 +1,87 @@
+/* ws_mempbrk.c
+ *
+ * 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"
+
+/* see bug 10798: there is a bug in the compiler the buildbots use for Mac OSX
+ and SSE4.2, so we're not going to use SSE4.2 with Mac OSX right now, for
+ older Mac OSX compilers.
+ */
+#ifdef __APPLE__
+#if defined(__clang__) && (__clang_major__ >= 6)
+/* allow HAVE_SSE4_2 to be used for clang 6.0+ case because we know it works */
+#else
+/* don't allow it otherwise, for Mac OSX */
+#undef HAVE_SSE4_2
+#endif
+#endif
+
+#include "ws_mempbrk.h"
+#include "ws_mempbrk_int.h"
+
+#include <string.h>
+
+void
+ws_mempbrk_compile(ws_mempbrk_pattern* pattern, const char *needles)
+{
+ const char *n = needles;
+ memset(pattern->patt, 0, 256);
+ while (*n) {
+ pattern->patt[(int)*n] = 1;
+ n++;
+ }
+
+#ifdef HAVE_SSE4_2
+ ws_mempbrk_sse42_compile(pattern, needles);
+#endif
+}
+
+
+const uint8_t *
+ws_mempbrk_portable_exec(const uint8_t* haystack, size_t haystacklen, const ws_mempbrk_pattern* pattern, unsigned char *found_needle)
+{
+ const uint8_t *haystack_end = haystack + haystacklen;
+
+ while (haystack < haystack_end) {
+ if (pattern->patt[*haystack]) {
+ if (found_needle)
+ *found_needle = *haystack;
+ return haystack;
+ }
+ haystack++;
+ }
+
+ return NULL;
+}
+
+
+WS_DLL_PUBLIC const uint8_t *
+ws_mempbrk_exec(const uint8_t* haystack, size_t haystacklen, const ws_mempbrk_pattern* pattern, unsigned char *found_needle)
+{
+#ifdef HAVE_SSE4_2
+ if (haystacklen >= 16 && pattern->use_sse42)
+ return ws_mempbrk_sse42_exec(haystack, haystacklen, pattern, found_needle);
+#endif
+
+ return ws_mempbrk_portable_exec(haystack, haystacklen, pattern, found_needle);
+}
+
+
+/*
+ * 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:
+ */
diff --git a/wsutil/ws_mempbrk.h b/wsutil/ws_mempbrk.h
new file mode 100644
index 00000000..7aff505f
--- /dev/null
+++ b/wsutil/ws_mempbrk.h
@@ -0,0 +1,37 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WS_MEMPBRK_H__
+#define __WS_MEMPBRK_H__
+
+#include <wireshark.h>
+
+#ifdef HAVE_SSE4_2
+#include <emmintrin.h>
+#endif
+
+/** The pattern object used for ws_mempbrk_exec().
+ */
+typedef struct {
+ char patt[256];
+#ifdef HAVE_SSE4_2
+ bool use_sse42;
+ __m128i mask;
+#endif
+} ws_mempbrk_pattern;
+
+/** Compile the pattern for the needles to find using ws_mempbrk_exec().
+ */
+WS_DLL_PUBLIC void ws_mempbrk_compile(ws_mempbrk_pattern* pattern, const char *needles);
+
+/** Scan for the needles specified by the compiled pattern.
+ */
+WS_DLL_PUBLIC const uint8_t *ws_mempbrk_exec(const uint8_t* haystack, size_t haystacklen, const ws_mempbrk_pattern* pattern, unsigned char *found_needle);
+
+#endif /* __WS_MEMPBRK_H__ */
diff --git a/wsutil/ws_mempbrk_int.h b/wsutil/ws_mempbrk_int.h
new file mode 100644
index 00000000..223cb64d
--- /dev/null
+++ b/wsutil/ws_mempbrk_int.h
@@ -0,0 +1,20 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WS_MEMPBRK_INT_H__
+#define __WS_MEMPBRK_INT_H__
+
+const uint8_t *ws_mempbrk_portable_exec(const uint8_t* haystack, size_t haystacklen, const ws_mempbrk_pattern* pattern, unsigned char *found_needle);
+
+#ifdef HAVE_SSE4_2
+void ws_mempbrk_sse42_compile(ws_mempbrk_pattern* pattern, const char *needles);
+const char *ws_mempbrk_sse42_exec(const char* haystack, size_t haystacklen, const ws_mempbrk_pattern* pattern, unsigned char *found_needle);
+#endif
+
+#endif /* __WS_MEMPBRK_INT_H__ */
diff --git a/wsutil/ws_mempbrk_sse42.c b/wsutil/ws_mempbrk_sse42.c
new file mode 100644
index 00000000..9de75aa6
--- /dev/null
+++ b/wsutil/ws_mempbrk_sse42.c
@@ -0,0 +1,173 @@
+/* strcspn with SSE4.2 intrinsics
+ Copyright (C) 2009-2014 Free Software Foundation, Inc.
+ Contributed by Intel Corporation.
+ This file is part of the GNU C Library.
+
+ SPDX-License-Identifier: LGPL-2.1-or-later
+*/
+
+
+#include "config.h"
+
+#ifdef HAVE_SSE4_2
+
+#include <glib.h>
+#include "ws_cpuid.h"
+
+#ifdef _WIN32
+ #include <tmmintrin.h>
+#endif
+
+#include <nmmintrin.h>
+#include <string.h>
+#include "ws_mempbrk.h"
+#include "ws_mempbrk_int.h"
+
+/* __has_feature(address_sanitizer) is used later for Clang, this is for
+ * compatibility with other compilers (such as GCC and MSVC) */
+#ifndef __has_feature
+# define __has_feature(x) 0
+#endif
+
+#define cast_128aligned__m128i(p) ((const __m128i *) (const void *) (p))
+
+/* Helper for variable shifts of SSE registers.
+ Copyright (C) 2010 Free Software Foundation, Inc.
+ */
+
+static const int8_t ___m128i_shift_right[31] =
+ {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+ };
+
+static inline __m128i
+__m128i_shift_right (__m128i value, unsigned long int offset)
+{
+ /* _mm_loadu_si128() works with unaligned data, cast safe */
+ return _mm_shuffle_epi8 (value,
+ _mm_loadu_si128 (cast_128aligned__m128i(___m128i_shift_right + offset)));
+}
+
+
+void
+ws_mempbrk_sse42_compile(ws_mempbrk_pattern* pattern, const char *needles)
+{
+ size_t length = strlen(needles);
+
+ pattern->use_sse42 = ws_cpuid_sse42() && (length <= 16);
+
+ if (pattern->use_sse42) {
+ pattern->mask = _mm_setzero_si128();
+ memcpy(&(pattern->mask), needles, length);
+ }
+}
+
+/* We use 0x2:
+ _SIDD_SBYTE_OPS
+ | _SIDD_CMP_EQUAL_ANY
+ | _SIDD_POSITIVE_POLARITY
+ | _SIDD_LEAST_SIGNIFICANT
+ on pcmpistri to compare xmm/mem128
+
+ 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ X X X X X X X X X X X X X X X X
+
+ against xmm
+
+ 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ A A A A A A A A A A A A A A A A
+
+ to find out if the first 16byte data element has any byte A and
+ the offset of the first byte. There are 3 cases:
+
+ 1. The first 16byte data element has the byte A at the offset X.
+ 2. The first 16byte data element has EOS and doesn't have the byte A.
+ 3. The first 16byte data element is valid and doesn't have the byte A.
+
+ Here is the table of ECX, CFlag, ZFlag and SFlag for 2 cases:
+
+ 1 X 1 0/1 0
+ 2 16 0 1 0
+ 3 16 0 0 0
+
+ We exit from the loop for cases 1 and 2 with jbe which branches
+ when either CFlag or ZFlag is 1. If CFlag == 1, ECX has the offset
+ X for case 1. */
+
+const char *
+ws_mempbrk_sse42_exec(const char *haystack, size_t haystacklen, const ws_mempbrk_pattern* pattern, unsigned char *found_needle)
+{
+ const char *aligned;
+ int offset;
+
+ offset = (int) ((size_t) haystack & 15);
+ aligned = (const char *) ((size_t) haystack & -16L);
+ if (offset != 0)
+ {
+ /* Check partial string. cast safe it's 16B aligned */
+ __m128i value = __m128i_shift_right (_mm_load_si128 (cast_128aligned__m128i(aligned)), offset);
+
+ int length = _mm_cmpistri (pattern->mask, value, 0x2);
+ /* No need to check ZFlag since ZFlag is always 1. */
+ int cflag = _mm_cmpistrc (pattern->mask, value, 0x2);
+ /* XXX: why does this compare value with value? */
+ int idx = _mm_cmpistri (value, value, 0x3a);
+
+ if (cflag) {
+ if (found_needle)
+ *found_needle = *(haystack + length);
+ return haystack + length;
+ }
+
+ /* Find where the NULL terminator is. */
+ if (idx < 16 - offset)
+ {
+ /* found NUL @ 'idx', need to switch to slower mempbrk */
+ return ws_mempbrk_portable_exec(haystack + idx + 1, haystacklen - idx - 1, pattern, found_needle); /* haystacklen is bigger than 16 & idx < 16 so no underflow here */
+ }
+ aligned += 16;
+ haystacklen -= (16 - offset);
+ }
+ else
+ aligned = haystack;
+
+ while (haystacklen >= 16)
+ {
+ __m128i value = _mm_load_si128 (cast_128aligned__m128i(aligned));
+ int idx = _mm_cmpistri (pattern->mask, value, 0x2);
+ int cflag = _mm_cmpistrc (pattern->mask, value, 0x2);
+ int zflag = _mm_cmpistrz (pattern->mask, value, 0x2);
+
+ if (cflag) {
+ if (found_needle)
+ *found_needle = *(aligned + idx);
+ return aligned + idx;
+ }
+
+ if (zflag)
+ {
+ /* found NUL, need to switch to slower mempbrk */
+ return ws_mempbrk_portable_exec(aligned, haystacklen, pattern, found_needle);
+ }
+ aligned += 16;
+ haystacklen -= 16;
+ }
+
+ /* XXX, use mempbrk_slow here? */
+ return ws_mempbrk_portable_exec(aligned, haystacklen, pattern, found_needle);
+}
+
+#endif /* HAVE_SSE4_2 */
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 2
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=2 tabstop=8 expandtab:
+ * :indentSize=2:tabSize=8:noTabs=true:
+ */
diff --git a/wsutil/ws_pipe.c b/wsutil/ws_pipe.c
new file mode 100644
index 00000000..7689f933
--- /dev/null
+++ b/wsutil/ws_pipe.c
@@ -0,0 +1,856 @@
+/* ws_pipe.c
+ *
+ * Routines for handling pipes.
+ *
+ * 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>
+#define WS_LOG_DOMAIN LOG_DOMAIN_CAPTURE
+#include "wsutil/ws_pipe.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#include <io.h>
+#include <fcntl.h> /* for _O_BINARY */
+#include <wsutil/win32-utils.h>
+#else
+#include <unistd.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#endif
+
+#ifdef __linux__
+#define HAS_G_SPAWN_LINUX_THREAD_SAFETY_BUG
+#include <fcntl.h>
+#include <sys/syscall.h> /* for syscall and SYS_getdents64 */
+#include <wsutil/file_util.h> /* for ws_open -> open to pacify checkAPIs.pl */
+#endif
+
+#include "wsutil/filesystem.h"
+#include "wsutil/wslog.h"
+
+#ifdef HAS_G_SPAWN_LINUX_THREAD_SAFETY_BUG
+struct linux_dirent64 {
+ uint64_t d_ino; /* 64-bit inode number */
+ uint64_t d_off; /* 64-bit offset to next structure */
+ unsigned short d_reclen; /* Size of this dirent */
+ unsigned char d_type; /* File type */
+ char d_name[]; /* Filename (null-terminated) */
+};
+
+/* Async-signal-safe string to integer conversion. */
+static int
+filename_to_fd(const char *p)
+{
+ char c;
+ int fd = 0;
+ const int cutoff = INT_MAX / 10;
+ const int cutlim = INT_MAX % 10;
+
+ if (*p == '\0')
+ return -1;
+
+ while ((c = *p++) != '\0') {
+ if (!g_ascii_isdigit(c))
+ return -1;
+ c -= '0';
+
+ /* Check for overflow. */
+ if (fd > cutoff || (fd == cutoff && c > cutlim))
+ return -1;
+
+ fd = fd * 10 + c;
+ }
+
+ return fd;
+}
+
+static void
+close_non_standard_fds_linux(void * user_data _U_)
+{
+ /*
+ * GLib 2.14.2 and newer (up to at least GLib 2.58.1) on Linux with multiple
+ * threads can deadlock in the child process due to use of opendir (which
+ * is not async-signal-safe). To avoid this, disable the broken code path
+ * and manually close file descriptors using async-signal-safe code only.
+ * Use CLOEXEC to allow reporting of execve errors to the parent via a pipe.
+ * https://gitlab.gnome.org/GNOME/glib/issues/1014
+ * https://gitlab.gnome.org/GNOME/glib/merge_requests/490
+ */
+ int dir_fd = ws_open("/proc/self/fd", O_RDONLY | O_DIRECTORY);
+ if (dir_fd >= 0) {
+ char buf[4096];
+ int nread, fd;
+ struct linux_dirent64 *de;
+
+ while ((nread = (int) syscall(SYS_getdents64, dir_fd, buf, sizeof(buf))) > 0) {
+ for (int pos = 0; pos < nread; pos += de->d_reclen) {
+ de = (struct linux_dirent64 *)(buf + pos);
+ fd = filename_to_fd(de->d_name);
+ if (fd > STDERR_FILENO && fd != dir_fd) {
+ /* Close all other (valid) file descriptors above stderr. */
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+ }
+ }
+ }
+
+ close(dir_fd);
+ } else {
+ /* Slow fallback in case /proc is not mounted */
+ for (int fd = STDERR_FILENO + 1; fd < getdtablesize(); fd++) {
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+ }
+ }
+}
+#endif
+
+#ifdef _WIN32
+static ULONG pipe_serial_number;
+
+/* Alternative for CreatePipe() where read handle is opened with FILE_FLAG_OVERLAPPED */
+static bool
+ws_pipe_create_overlapped_read(HANDLE *read_pipe_handle, HANDLE *write_pipe_handle,
+ SECURITY_ATTRIBUTES *sa, DWORD suggested_buffer_size)
+{
+ HANDLE read_pipe, write_pipe;
+ unsigned char *name = ws_strdup_printf("\\\\.\\Pipe\\WiresharkWsPipe.%08lx.%08lx",
+ GetCurrentProcessId(),
+ InterlockedIncrement(&pipe_serial_number));
+ gunichar2 *wname = g_utf8_to_utf16(name, -1, NULL, NULL, NULL);
+
+ g_free(name);
+
+ read_pipe = CreateNamedPipe(wname, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
+ PIPE_TYPE_BYTE | PIPE_WAIT, 1,
+ suggested_buffer_size, suggested_buffer_size,
+ 0, sa);
+ if (INVALID_HANDLE_VALUE == read_pipe)
+ {
+ g_free(wname);
+ return false;
+ }
+
+ write_pipe = CreateFile(wname, GENERIC_WRITE, 0, sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (INVALID_HANDLE_VALUE == write_pipe)
+ {
+ DWORD error = GetLastError();
+ CloseHandle(read_pipe);
+ SetLastError(error);
+ g_free(wname);
+ return false;
+ }
+
+ *read_pipe_handle = read_pipe;
+ *write_pipe_handle = write_pipe;
+ g_free(wname);
+ return(true);
+}
+#endif
+
+/**
+ * Helper to convert a command and argument list to an NULL-terminated 'argv'
+ * array, suitable for g_spawn_sync and friends. Free with g_strfreev.
+ */
+static char **
+convert_to_argv(const char *command, int args_count, char *const *args)
+{
+ char **argv = g_new(char *, args_count + 2);
+ // The caller does not seem to modify this, but g_spawn_sync uses 'char **'
+ // as opposed to 'const char **', so just to be sure clone it.
+ argv[0] = g_strdup(command);
+ for (int i = 0; i < args_count; i++) {
+ // Empty arguments may indicate a bug in Wireshark. Extcap for example
+ // omits arguments when their string value is empty. On Windows, empty
+ // arguments would silently be ignored because protect_arg returns an
+ // empty string, therefore we print a warning here.
+ if (!*args[i]) {
+ ws_warning("Empty argument %d in arguments list", i);
+ }
+ argv[1 + i] = g_strdup(args[i]);
+ }
+ argv[args_count + 1] = NULL;
+ return argv;
+}
+
+/**
+ * Convert a non-empty NULL-terminated array of command and arguments to a
+ * string for displaying purposes. On Windows, the returned string is properly
+ * escaped and can be executed directly.
+ */
+static char *
+convert_to_command_line(char **argv)
+{
+ GString *command_line = g_string_sized_new(200);
+#ifdef _WIN32
+ // The first argument must always be quoted even if it does not contain
+ // special characters or else CreateProcess might consider arguments as part
+ // of the executable.
+ char *quoted_arg = protect_arg(argv[0]);
+ if (quoted_arg[0] != '"') {
+ g_string_append_c(command_line, '"');
+ g_string_append(command_line, quoted_arg);
+ g_string_append_c(command_line, '"');
+ } else {
+ g_string_append(command_line, quoted_arg);
+ }
+ g_free(quoted_arg);
+
+ for (int i = 1; argv[i]; i++) {
+ quoted_arg = protect_arg(argv[i]);
+ g_string_append_c(command_line, ' ');
+ g_string_append(command_line, quoted_arg);
+ g_free(quoted_arg);
+ }
+#else
+ for (int i = 0; argv[i]; i++) {
+ char *quoted_arg = g_shell_quote(argv[i]);
+ if (i != 0) {
+ g_string_append_c(command_line, ' ');
+ }
+ g_string_append(command_line, quoted_arg);
+ g_free(quoted_arg);
+ }
+#endif
+ return g_string_free(command_line, false);
+}
+
+bool ws_pipe_spawn_sync(const char *working_directory, const char *command, int argc, char **args, char **command_output)
+{
+ bool status = false;
+ bool result = false;
+ char *local_output = NULL;
+#ifdef _WIN32
+
+#define BUFFER_SIZE 16384
+
+ STARTUPINFO info;
+ PROCESS_INFORMATION processInfo;
+
+ SECURITY_ATTRIBUTES sa;
+ HANDLE child_stdout_rd = NULL;
+ HANDLE child_stdout_wr = NULL;
+ HANDLE child_stderr_rd = NULL;
+ HANDLE child_stderr_wr = NULL;
+ HANDLE inherit_handles[2];
+
+ OVERLAPPED stdout_overlapped;
+ OVERLAPPED stderr_overlapped;
+#else
+ int exit_status = 0;
+#endif
+
+ char **argv = convert_to_argv(command, argc, args);
+ char *command_line = convert_to_command_line(argv);
+
+ ws_debug("command line: %s", command_line);
+
+ uint64_t start_time = g_get_monotonic_time();
+
+#ifdef _WIN32
+ /* Setup overlapped structures. Create Manual Reset events, initially not signalled */
+ memset(&stdout_overlapped, 0, sizeof(OVERLAPPED));
+ memset(&stderr_overlapped, 0, sizeof(OVERLAPPED));
+ stdout_overlapped.hEvent = CreateEvent(NULL, true, false, NULL);
+ if (!stdout_overlapped.hEvent)
+ {
+ g_free(command_line);
+ g_strfreev(argv);
+ ws_debug("Could not create stdout overlapped event");
+ return false;
+ }
+ stderr_overlapped.hEvent = CreateEvent(NULL, true, false, NULL);
+ if (!stderr_overlapped.hEvent)
+ {
+ CloseHandle(stdout_overlapped.hEvent);
+ g_free(command_line);
+ g_strfreev(argv);
+ ws_debug("Could not create stderr overlapped event");
+ return false;
+ }
+
+ memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES));
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.bInheritHandle = false;
+ sa.lpSecurityDescriptor = NULL;
+
+ if (!ws_pipe_create_overlapped_read(&child_stdout_rd, &child_stdout_wr, &sa, 0))
+ {
+ CloseHandle(stdout_overlapped.hEvent);
+ CloseHandle(stderr_overlapped.hEvent);
+ g_free(command_line);
+ g_strfreev(argv);
+ ws_debug("Could not create stdout handle");
+ return false;
+ }
+
+ if (!ws_pipe_create_overlapped_read(&child_stderr_rd, &child_stderr_wr, &sa, 0))
+ {
+ CloseHandle(stdout_overlapped.hEvent);
+ CloseHandle(stderr_overlapped.hEvent);
+ CloseHandle(child_stdout_rd);
+ CloseHandle(child_stdout_wr);
+ g_free(command_line);
+ g_strfreev(argv);
+ ws_debug("Could not create stderr handle");
+ return false;
+ }
+
+ inherit_handles[0] = child_stderr_wr;
+ inherit_handles[1] = child_stdout_wr;
+
+ memset(&processInfo, 0, sizeof(PROCESS_INFORMATION));
+ memset(&info, 0, sizeof(STARTUPINFO));
+
+ info.cb = sizeof(STARTUPINFO);
+ info.hStdError = child_stderr_wr;
+ info.hStdOutput = child_stdout_wr;
+ info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+ info.wShowWindow = SW_HIDE;
+
+ if (win32_create_process(NULL, command_line, NULL, NULL, G_N_ELEMENTS(inherit_handles), inherit_handles,
+ CREATE_NEW_CONSOLE, NULL, working_directory, &info, &processInfo))
+ {
+ char* stdout_buffer = (char*)g_malloc(BUFFER_SIZE);
+ char* stderr_buffer = (char*)g_malloc(BUFFER_SIZE);
+ DWORD dw;
+ DWORD bytes_read;
+ GString *output_string = g_string_new(NULL);
+ bool process_finished = false;
+ bool pending_stdout = true;
+ bool pending_stderr = true;
+
+ /* Start asynchronous reads from child process stdout and stderr */
+ if (!ReadFile(child_stdout_rd, stdout_buffer, BUFFER_SIZE, NULL, &stdout_overlapped))
+ {
+ if (GetLastError() != ERROR_IO_PENDING)
+ {
+ ws_debug("ReadFile on child stdout pipe failed. Error %ld", GetLastError());
+ pending_stdout = false;
+ }
+ }
+
+ if (!ReadFile(child_stderr_rd, stderr_buffer, BUFFER_SIZE, NULL, &stderr_overlapped))
+ {
+ if (GetLastError() != ERROR_IO_PENDING)
+ {
+ ws_debug("ReadFile on child stderr pipe failed. Error %ld", GetLastError());
+ pending_stderr = false;
+ }
+ }
+
+ for (;;)
+ {
+ HANDLE handles[3];
+ DWORD n_handles = 0;
+ if (!process_finished)
+ {
+ handles[n_handles++] = processInfo.hProcess;
+ }
+ if (pending_stdout)
+ {
+ handles[n_handles++] = stdout_overlapped.hEvent;
+ }
+ if (pending_stderr)
+ {
+ handles[n_handles++] = stderr_overlapped.hEvent;
+ }
+
+ if (!n_handles)
+ {
+ /* No more things to wait */
+ break;
+ }
+
+ dw = WaitForMultipleObjects(n_handles, handles, false, INFINITE);
+ if (dw < (WAIT_OBJECT_0 + n_handles))
+ {
+ int i = dw - WAIT_OBJECT_0;
+ if (handles[i] == processInfo.hProcess)
+ {
+ /* Process finished but there might still be unread data in the pipe.
+ * Close the write pipes, so ReadFile does not wait indefinitely.
+ */
+ CloseHandle(child_stdout_wr);
+ CloseHandle(child_stderr_wr);
+ process_finished = true;
+ }
+ else if (handles[i] == stdout_overlapped.hEvent)
+ {
+ bytes_read = 0;
+ if (!GetOverlappedResult(child_stdout_rd, &stdout_overlapped, &bytes_read, true))
+ {
+ if (GetLastError() == ERROR_BROKEN_PIPE)
+ {
+ pending_stdout = false;
+ continue;
+ }
+ ws_debug("GetOverlappedResult on stdout failed. Error %ld", GetLastError());
+ }
+ if (process_finished && (bytes_read == 0))
+ {
+ /* We have drained the pipe and there isn't any process that holds active write handle to the pipe. */
+ pending_stdout = false;
+ continue;
+ }
+ g_string_append_len(output_string, stdout_buffer, bytes_read);
+ if (!ReadFile(child_stdout_rd, stdout_buffer, BUFFER_SIZE, NULL, &stdout_overlapped))
+ {
+ if (GetLastError() != ERROR_IO_PENDING)
+ {
+ ws_debug("ReadFile on child stdout pipe failed. Error %ld", GetLastError());
+ pending_stdout = false;
+ }
+ }
+ }
+ else if (handles[i] == stderr_overlapped.hEvent)
+ {
+ /* Discard the stderr data just like non-windows version of this function does. */
+ bytes_read = 0;
+ if (!GetOverlappedResult(child_stderr_rd, &stderr_overlapped, &bytes_read, true))
+ {
+ if (GetLastError() == ERROR_BROKEN_PIPE)
+ {
+ pending_stderr = false;
+ continue;
+ }
+ ws_debug("GetOverlappedResult on stderr failed. Error %ld", GetLastError());
+ }
+ if (process_finished && (bytes_read == 0))
+ {
+ pending_stderr = false;
+ continue;
+ }
+ if (!ReadFile(child_stderr_rd, stderr_buffer, BUFFER_SIZE, NULL, &stderr_overlapped))
+ {
+ if (GetLastError() != ERROR_IO_PENDING)
+ {
+ ws_debug("ReadFile on child stderr pipe failed. Error %ld", GetLastError());
+ pending_stderr = false;
+ }
+ }
+ }
+ }
+ else
+ {
+ ws_debug("WaitForMultipleObjects returned 0x%08lX. Error %ld", dw, GetLastError());
+ }
+ }
+
+ g_free(stdout_buffer);
+ g_free(stderr_buffer);
+
+ status = GetExitCodeProcess(processInfo.hProcess, &dw);
+ if (status && dw != 0)
+ {
+ status = false;
+ }
+
+ local_output = g_string_free(output_string, false);
+
+ CloseHandle(child_stdout_rd);
+ CloseHandle(child_stderr_rd);
+
+ CloseHandle(processInfo.hProcess);
+ CloseHandle(processInfo.hThread);
+ }
+ else
+ {
+ status = false;
+
+ CloseHandle(child_stdout_rd);
+ CloseHandle(child_stdout_wr);
+ CloseHandle(child_stderr_rd);
+ CloseHandle(child_stderr_wr);
+ }
+
+ CloseHandle(stdout_overlapped.hEvent);
+ CloseHandle(stderr_overlapped.hEvent);
+#else
+
+ GSpawnFlags flags = (GSpawnFlags)0;
+ GSpawnChildSetupFunc child_setup = NULL;
+#ifdef HAS_G_SPAWN_LINUX_THREAD_SAFETY_BUG
+ flags = (GSpawnFlags)(flags | G_SPAWN_LEAVE_DESCRIPTORS_OPEN);
+ child_setup = close_non_standard_fds_linux;
+#endif
+ status = g_spawn_sync(working_directory, argv, NULL,
+ flags, child_setup, NULL, &local_output, NULL, &exit_status, NULL);
+
+ if (status && exit_status != 0)
+ status = false;
+#endif
+
+ ws_debug("%s finished in %.3fms", argv[0], (g_get_monotonic_time() - start_time) / 1000.0);
+
+ if (status)
+ {
+ if (local_output != NULL) {
+ ws_noisy("spawn output: %s", local_output);
+ if (command_output != NULL)
+ *command_output = g_strdup(local_output);
+ }
+ result = true;
+ }
+
+ g_free(local_output);
+ g_free(command_line);
+ g_strfreev(argv);
+
+ return result;
+}
+
+void ws_pipe_init(ws_pipe_t *ws_pipe)
+{
+ if (!ws_pipe) return;
+ memset(ws_pipe, 0, sizeof(ws_pipe_t));
+ ws_pipe->pid = WS_INVALID_PID;
+}
+
+GPid ws_pipe_spawn_async(ws_pipe_t *ws_pipe, GPtrArray *args)
+{
+ GPid pid = WS_INVALID_PID;
+ int stdin_fd, stdout_fd, stderr_fd;
+#ifdef _WIN32
+ STARTUPINFO info;
+ PROCESS_INFORMATION processInfo;
+
+ SECURITY_ATTRIBUTES sa;
+ HANDLE child_stdin_rd = NULL;
+ HANDLE child_stdin_wr = NULL;
+ HANDLE child_stdout_rd = NULL;
+ HANDLE child_stdout_wr = NULL;
+ HANDLE child_stderr_rd = NULL;
+ HANDLE child_stderr_wr = NULL;
+ HANDLE inherit_handles[3];
+#endif
+
+ // XXX harmonize handling of command arguments for the sync/async functions
+ // and make them const? This array ends with a trailing NULL by the way.
+ char **args_array = (char **)args->pdata;
+ char **argv = convert_to_argv(args_array[0], args->len - 2, args_array + 1);
+ char *command_line = convert_to_command_line(argv);
+
+ ws_debug("command line: %s", command_line);
+
+#ifdef _WIN32
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.bInheritHandle = false;
+ sa.lpSecurityDescriptor = NULL;
+
+ if (!CreatePipe(&child_stdin_rd, &child_stdin_wr, &sa, 0))
+ {
+ g_free(command_line);
+ g_strfreev(argv);
+ ws_debug("Could not create stdin handle");
+ return WS_INVALID_PID;
+ }
+
+ if (!CreatePipe(&child_stdout_rd, &child_stdout_wr, &sa, 0))
+ {
+ CloseHandle(child_stdin_rd);
+ CloseHandle(child_stdin_wr);
+ g_free(command_line);
+ g_strfreev(argv);
+ ws_debug("Could not create stdout handle");
+ return WS_INVALID_PID;
+ }
+
+ if (!CreatePipe(&child_stderr_rd, &child_stderr_wr, &sa, 0))
+ {
+ CloseHandle(child_stdin_rd);
+ CloseHandle(child_stdin_wr);
+ CloseHandle(child_stdout_rd);
+ CloseHandle(child_stdout_wr);
+ g_free(command_line);
+ g_strfreev(argv);
+ ws_debug("Could not create stderr handle");
+ return WS_INVALID_PID;
+ }
+
+ inherit_handles[0] = child_stdin_rd;
+ inherit_handles[1] = child_stderr_wr;
+ inherit_handles[2] = child_stdout_wr;
+
+ memset(&processInfo, 0, sizeof(PROCESS_INFORMATION));
+ memset(&info, 0, sizeof(STARTUPINFO));
+
+ info.cb = sizeof(STARTUPINFO);
+ info.hStdInput = child_stdin_rd;
+ info.hStdError = child_stderr_wr;
+ info.hStdOutput = child_stdout_wr;
+ info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+ info.wShowWindow = SW_HIDE;
+
+ if (win32_create_process(NULL, command_line, NULL, NULL, G_N_ELEMENTS(inherit_handles), inherit_handles,
+ CREATE_NEW_CONSOLE, NULL, NULL, &info, &processInfo))
+ {
+ stdin_fd = _open_osfhandle((intptr_t)(child_stdin_wr), _O_BINARY);
+ stdout_fd = _open_osfhandle((intptr_t)(child_stdout_rd), _O_BINARY);
+ stderr_fd = _open_osfhandle((intptr_t)(child_stderr_rd), _O_BINARY);
+ pid = processInfo.hProcess;
+ CloseHandle(processInfo.hThread);
+ }
+ else
+ {
+ CloseHandle(child_stdin_wr);
+ CloseHandle(child_stdout_rd);
+ CloseHandle(child_stderr_rd);
+ }
+
+ /* We no longer need other (child) end of pipes. The child process holds
+ * its own handles that will be closed on process exit. However, we have
+ * to close *our* handles as otherwise read() on stdout_fd and stderr_fd
+ * will block indefinitely after the process exits.
+ */
+ CloseHandle(child_stdin_rd);
+ CloseHandle(child_stdout_wr);
+ CloseHandle(child_stderr_wr);
+#else
+
+ GError *error = NULL;
+ GSpawnFlags flags = G_SPAWN_DO_NOT_REAP_CHILD;
+ GSpawnChildSetupFunc child_setup = NULL;
+#ifdef HAS_G_SPAWN_LINUX_THREAD_SAFETY_BUG
+ flags = (GSpawnFlags)(flags | G_SPAWN_LEAVE_DESCRIPTORS_OPEN);
+ child_setup = close_non_standard_fds_linux;
+#endif
+ bool spawned = g_spawn_async_with_pipes(NULL, argv, NULL,
+ flags, child_setup, NULL,
+ &pid, &stdin_fd, &stdout_fd, &stderr_fd, &error);
+ if (!spawned) {
+ ws_debug("Error creating async pipe: %s", error->message);
+ g_free(error->message);
+ }
+#endif
+
+ g_free(command_line);
+ g_strfreev(argv);
+
+ ws_pipe->pid = pid;
+
+ if (pid != WS_INVALID_PID) {
+#ifdef _WIN32
+ ws_pipe->stdin_io = g_io_channel_win32_new_fd(stdin_fd);
+ ws_pipe->stdout_io = g_io_channel_win32_new_fd(stdout_fd);
+ ws_pipe->stderr_io = g_io_channel_win32_new_fd(stderr_fd);
+#else
+ ws_pipe->stdin_io = g_io_channel_unix_new(stdin_fd);
+ ws_pipe->stdout_io = g_io_channel_unix_new(stdout_fd);
+ ws_pipe->stderr_io = g_io_channel_unix_new(stderr_fd);
+#endif
+ g_io_channel_set_encoding(ws_pipe->stdin_io, NULL, NULL);
+ g_io_channel_set_encoding(ws_pipe->stdout_io, NULL, NULL);
+ g_io_channel_set_encoding(ws_pipe->stderr_io, NULL, NULL);
+ g_io_channel_set_buffered(ws_pipe->stdin_io, false);
+ g_io_channel_set_buffered(ws_pipe->stdout_io, false);
+ g_io_channel_set_buffered(ws_pipe->stderr_io, false);
+ g_io_channel_set_close_on_unref(ws_pipe->stdin_io, true);
+ g_io_channel_set_close_on_unref(ws_pipe->stdout_io, true);
+ g_io_channel_set_close_on_unref(ws_pipe->stderr_io, true);
+ }
+
+ return pid;
+}
+
+#ifdef _WIN32
+
+typedef struct
+{
+ HANDLE pipeHandle;
+ OVERLAPPED ol;
+ BOOL pendingIO;
+} PIPEINTS;
+
+bool
+ws_pipe_wait_for_pipe(HANDLE * pipe_handles, int num_pipe_handles, HANDLE pid)
+{
+ PIPEINTS pipeinsts[3];
+ HANDLE handles[4];
+ bool result = true;
+
+ SecureZeroMemory(pipeinsts, sizeof(pipeinsts));
+
+ if (num_pipe_handles == 0 || num_pipe_handles > 3)
+ {
+ ws_debug("Invalid number of pipes given as argument.");
+ return false;
+ }
+
+ for (int i = 0; i < num_pipe_handles; ++i)
+ {
+ pipeinsts[i].ol.hEvent = CreateEvent(NULL, true, false, NULL);
+ if (!pipeinsts[i].ol.hEvent)
+ {
+ ws_debug("Could not create overlapped event");
+ for (int j = 0; j < i; j++)
+ {
+ CloseHandle(pipeinsts[j].ol.hEvent);
+ }
+ return false;
+ }
+ }
+
+ for (int i = 0; i < num_pipe_handles; ++i)
+ {
+ pipeinsts[i].pipeHandle = pipe_handles[i];
+ pipeinsts[i].ol.Pointer = 0;
+ pipeinsts[i].pendingIO = false;
+ if (!ConnectNamedPipe(pipeinsts[i].pipeHandle, &pipeinsts[i].ol))
+ {
+ DWORD error = GetLastError();
+ switch (error)
+ {
+ case ERROR_IO_PENDING:
+ pipeinsts[i].pendingIO = true;
+ break;
+
+ case ERROR_PIPE_CONNECTED:
+ SetEvent(pipeinsts[i].ol.hEvent);
+ break;
+
+ default:
+ ws_debug("ConnectNamedPipe failed with %ld\n.", error);
+ result = false;
+ }
+ }
+ }
+
+ while (result)
+ {
+ DWORD dw;
+ int num_handles = 0;
+ for (int i = 0; i < num_pipe_handles; ++i)
+ {
+ if (pipeinsts[i].pendingIO)
+ {
+ handles[num_handles] = pipeinsts[i].ol.hEvent;
+ num_handles++;
+ }
+ }
+ if (num_handles == 0)
+ {
+ /* All pipes have been successfully connected */
+ break;
+ }
+ /* Wait for process in case it exits before the pipes have connected */
+ handles[num_handles] = pid;
+ num_handles++;
+
+ dw = WaitForMultipleObjects(num_handles, handles, false, 30000);
+ int handle_idx = dw - WAIT_OBJECT_0;
+ if (dw == WAIT_TIMEOUT)
+ {
+ ws_debug("extcap didn't connect to pipe within 30 seconds.");
+ result = false;
+ break;
+ }
+ // If index points to our handles array
+ else if (handle_idx >= 0 && handle_idx < num_handles)
+ {
+ if (handles[handle_idx] == pid)
+ {
+ ws_debug("extcap terminated without connecting to pipe.");
+ result = false;
+ }
+ for (int i = 0; i < num_pipe_handles; ++i)
+ {
+ if (handles[handle_idx] == pipeinsts[i].ol.hEvent)
+ {
+ DWORD cbRet;
+ BOOL success = GetOverlappedResult(
+ pipeinsts[i].pipeHandle, // handle to pipe
+ &pipeinsts[i].ol, // OVERLAPPED structure
+ &cbRet, // bytes transferred
+ true); // wait
+ if (!success)
+ {
+ ws_debug("Error %ld \n.", GetLastError());
+ result = false;
+ }
+ pipeinsts[i].pendingIO = false;
+ }
+ }
+ }
+ else
+ {
+ ws_debug("WaitForMultipleObjects returned 0x%08lX. Error %ld", dw, GetLastError());
+ result = false;
+ }
+ }
+
+ for (int i = 0; i < num_pipe_handles; ++i)
+ {
+ if (pipeinsts[i].pendingIO)
+ {
+ CancelIoEx(pipeinsts[i].pipeHandle, &pipeinsts[i].ol);
+ WaitForSingleObject(pipeinsts[i].ol.hEvent, INFINITE);
+ }
+ CloseHandle(pipeinsts[i].ol.hEvent);
+ }
+
+ return result;
+}
+#endif
+
+bool
+ws_pipe_data_available(int pipe_fd)
+{
+#ifdef _WIN32 /* PeekNamedPipe */
+ HANDLE hPipe = (HANDLE) _get_osfhandle(pipe_fd);
+ DWORD bytes_avail;
+
+ if (hPipe == INVALID_HANDLE_VALUE)
+ {
+ return false;
+ }
+
+ if (! PeekNamedPipe(hPipe, NULL, 0, NULL, &bytes_avail, NULL))
+ {
+ return false;
+ }
+
+ if (bytes_avail > 0)
+ {
+ return true;
+ }
+ return false;
+#else /* select */
+ fd_set rfds;
+ struct timeval timeout;
+
+ FD_ZERO(&rfds);
+ FD_SET(pipe_fd, &rfds);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+
+ if (select(pipe_fd + 1, &rfds, NULL, NULL, &timeout) > 0)
+ {
+ return true;
+ }
+
+ return false;
+#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/wsutil/ws_pipe.h b/wsutil/ws_pipe.h
new file mode 100644
index 00000000..968e0579
--- /dev/null
+++ b/wsutil/ws_pipe.h
@@ -0,0 +1,105 @@
+/** @file
+ *
+ * Routines for handling pipes.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WS_PIPE_H__
+#define __WS_PIPE_H__
+
+#include <stdbool.h>
+
+// ws_symbol_export and WS_INVALID_PID
+#include "wsutil/processes.h"
+
+#include <glib.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#include <io.h>
+#define ws_pipe_handle HANDLE
+#define ws_get_pipe_handle(pipe_fd) ((HANDLE)_get_osfhandle(pipe_fd))
+#else
+#define ws_pipe_handle int
+#define ws_get_pipe_handle(pipe_fd) (pipe_fd)
+#endif
+
+typedef struct _ws_pipe_t {
+ GPid pid;
+ GIOChannel *stdin_io;
+ GIOChannel *stdout_io;
+ GIOChannel *stderr_io;
+} ws_pipe_t;
+
+/**
+ * @brief Run a process using g_spawn_sync on UNIX and Linux, and
+ * CreateProcess on Windows. Wait for it to finish.
+ * @param [IN] working_directory Initial working directory.
+ * @param [IN] command Command to run.
+ * @param [IN] argc Number of arguments for the command, not including the command itself.
+ * @param [IN] args Arguments for the command, not including the command itself.
+ * The last element must be NULL.
+ * @param [OUT] command_output If not NULL, receives a copy of the command output. Must be g_freed.
+ * @return true on success or false on failure.
+ */
+WS_DLL_PUBLIC bool ws_pipe_spawn_sync(const char * working_directory, const char * command, int argc, char ** args, char ** command_output);
+
+/**
+ * @brief Initialize a ws_pipe_t struct. Sets .pid to WS_INVALID_PID and all other members to 0 or NULL.
+ * @param ws_pipe [IN] The pipe to initialize.
+ */
+WS_DLL_PUBLIC void ws_pipe_init(ws_pipe_t *ws_pipe);
+
+/**
+ * @brief Checks whether a pipe is valid (for reading or writing).
+ */
+static inline bool ws_pipe_valid(ws_pipe_t *ws_pipe)
+{
+ return ws_pipe && ws_pipe->pid && ws_pipe->pid != WS_INVALID_PID;
+}
+
+/**
+ * @brief Start a process using g_spawn_sync on UNIX and Linux, and CreateProcess on Windows.
+ * @param ws_pipe The process PID, stdio descriptors, etc.
+ * @param args The command to run along with its arguments.
+ * @return A valid PID on success, otherwise WS_INVALID_PID.
+ */
+WS_DLL_PUBLIC GPid ws_pipe_spawn_async (ws_pipe_t * ws_pipe, GPtrArray * args );
+
+#ifdef _WIN32
+/**
+ * @brief Wait for a set of handles using WaitForMultipleObjects. Windows only.
+ * @param pipe_handles An array of handles
+ * @param num_pipe_handles The size of the array.
+ * @param pid Child process PID.
+ * @return true on success or false on failure.
+ */
+WS_DLL_PUBLIC bool ws_pipe_wait_for_pipe(HANDLE * pipe_handles, int num_pipe_handles, HANDLE pid);
+#endif
+
+/**
+ * @brief Check to see if a file descriptor has data available.
+ * @param pipe_fd File descriptor.
+ * @return true if data is available or false otherwise.
+ */
+WS_DLL_PUBLIC bool ws_pipe_data_available(int pipe_fd);
+
+#endif /* __WS_PIPE_H__ */
+
+/*
+ * 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/wsutil/ws_roundup.h b/wsutil/ws_roundup.h
new file mode 100644
index 00000000..f7177a8a
--- /dev/null
+++ b/wsutil/ws_roundup.h
@@ -0,0 +1,22 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WS_ROUNDUP_H__
+#define __WS_ROUNDUP_H__
+
+/*
+ * Round up to various powers of 2.
+ */
+#define WS_ROUNDUP_2(n) (((n) + ((unsigned)(2U-1U))) & (~((unsigned)(2U-1U))))
+#define WS_ROUNDUP_4(n) (((n) + ((unsigned)(4U-1U))) & (~((unsigned)(4U-1U))))
+#define WS_ROUNDUP_8(n) (((n) + ((unsigned)(8U-1U))) & (~((unsigned)(8U-1U))))
+#define WS_ROUNDUP_16(n) (((n) + ((unsigned)(16U-1U))) & (~((unsigned)(16U-1U))))
+#define WS_ROUNDUP_32(n) (((n) + ((unsigned)(32U-1U))) & (~((unsigned)(32U-1U))))
+
+#endif /* __WS_ROUNDUP_H__ */
diff --git a/wsutil/ws_strptime.c b/wsutil/ws_strptime.c
new file mode 100644
index 00000000..3c55d93e
--- /dev/null
+++ b/wsutil/ws_strptime.c
@@ -0,0 +1,906 @@
+/*-
+ * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code was contributed to The NetBSD Foundation by Klaus Klein.
+ * Heavily optimised by David Laight
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#define _GNU_SOURCE
+#include "config.h"
+#include "ws_strptime.h"
+#include <time.h>
+#include <wsutil/time_util.h> /* For ws_localtime_r() */
+#include <wsutil/strtoi.h>
+
+#ifdef _WIN32
+#define tzset _tzset
+#define tzname _tzname
+#define timezone _timezone
+#define daylight _daylight
+#endif
+
+static const unsigned char *conv_num(const unsigned char *, int *, unsigned, unsigned);
+static const unsigned char *find_string(const unsigned char *, int *, const char * const *,
+ const char * const *, int);
+
+#define SECSPERMIN 60
+#define MINSPERHOUR 60
+#define HOURSPERDAY 24
+#define DAYSPERWEEK 7
+#define DAYSPERNYEAR 365
+#define DAYSPERLYEAR 366
+#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
+#define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY)
+#define MONSPERYEAR 12
+
+#define TM_SUNDAY 0
+#define TM_MONDAY 1
+#define TM_TUESDAY 2
+#define TM_WEDNESDAY 3
+#define TM_THURSDAY 4
+#define TM_FRIDAY 5
+#define TM_SATURDAY 6
+
+#define TM_JANUARY 0
+#define TM_FEBRUARY 1
+#define TM_MARCH 2
+#define TM_APRIL 3
+#define TM_MAY 4
+#define TM_JUNE 5
+#define TM_JULY 6
+#define TM_AUGUST 7
+#define TM_SEPTEMBER 8
+#define TM_OCTOBER 9
+#define TM_NOVEMBER 10
+#define TM_DECEMBER 11
+
+#define TM_YEAR_BASE 1900
+
+#define EPOCH_YEAR 1970
+#define EPOCH_WDAY TM_THURSDAY
+
+#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
+
+/*
+** Since everything in isleap is modulo 400 (or a factor of 400), we know that
+** isleap(y) == isleap(y % 400)
+** and so
+** isleap(a + b) == isleap((a + b) % 400)
+** or
+** isleap(a + b) == isleap(a % 400 + b % 400)
+** This is true even if % means modulo rather than Fortran remainder
+** (which is allowed by C89 but not C99).
+** We use this to avoid addition overflow problems.
+*/
+
+#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
+
+/*
+ * We do not implement alternate representations. However, we always
+ * check whether a given modifier is allowed for a certain conversion.
+ */
+#define ALT_E 0x01
+#define ALT_O 0x02
+#define LEGAL_ALT(x) { if (alt_format & ~(x)) return NULL; }
+
+#define S_YEAR (1 << 0)
+#define S_MON (1 << 1)
+#define S_YDAY (1 << 2)
+#define S_MDAY (1 << 3)
+#define S_WDAY (1 << 4)
+#define S_HOUR (1 << 5)
+
+#define HAVE_MDAY(s) (s & S_MDAY)
+#define HAVE_MON(s) (s & S_MON)
+#define HAVE_WDAY(s) (s & S_WDAY)
+#define HAVE_YDAY(s) (s & S_YDAY)
+#define HAVE_YEAR(s) (s & S_YEAR)
+#define HAVE_HOUR(s) (s & S_HOUR)
+
+static char utc[] = { "UTC" };
+/* RFC-822/RFC-2822 */
+static const char * const nast[5] = {
+ "EST", "CST", "MST", "PST", "\0\0\0"
+};
+static const char * const nadt[5] = {
+ "EDT", "CDT", "MDT", "PDT", "\0\0\0"
+};
+
+static const char * const cloc_am_pm[] = {"AM", "PM", NULL};
+
+static const char * const cloc_abday[] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
+};
+
+static const char * const cloc_day[] = {
+ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
+ "Saturday", NULL
+};
+
+static const char * const cloc_abmon[] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
+ "Oct", "Nov", "Dec", NULL
+};
+
+static const char * const cloc_mon[] = {
+ "January", "February", "March", "April", "May", "June", "July",
+ "August", "September", "October", "November", "December", NULL
+};
+
+/*
+ * Table to determine the ordinal date for the start of a month.
+ * Ref: http://en.wikipedia.org/wiki/ISO_week_date
+ */
+static const int start_of_month[2][13] = {
+ /* non-leap year */
+ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
+ /* leap year */
+ { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
+};
+
+/*
+ * Calculate the week day of the first day of a year. Valid for
+ * the Gregorian calendar, which began Sept 14, 1752 in the UK
+ * and its colonies. Ref:
+ * http://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week
+ */
+
+static int
+first_wday_of(int yr)
+{
+ return ((2 * (3 - (yr / 100) % 4)) + (yr % 100) + ((yr % 100) / 4) +
+ (isleap(yr) ? 6 : 0) + 1) % 7;
+}
+
+#define delim(p) ((p) == '\0' || g_ascii_isspace((unsigned char)(p)))
+
+#define SET_ZONEP(p, off, zone) \
+ do { if (p) { p->tm_gmtoff = off; p->tm_zone = zone; } } while (0)
+
+/*
+ * This is spectacularly ugly.
+ *
+ * POSIX require that there be a variable named "timezone", which contains
+ * "the difference, in seconds, between Coordinated Universal Time (UTC)
+ * and local standard time.".
+ *
+ * Most of the platforms on which we run have this.
+ *
+ * FreeBSD, however, does not. Instead, it provides a function named
+ * "timezone", which takes two integer arguments, "zone" and "dst",
+ * and "returns a pointer to a time zone abbreviation for the specified
+ * zone and dst values. The zone argument is the number of minutes west
+ * of GMT and dst is non-zero if daylight savings time is in effect."
+ *
+ * So we need a way to get "the difference, in seconds, between Coordinated
+ * Universal Time (UTC) and local standard time."
+ *
+ * The FreeBSD Wireshark port, as of 2023-12-05, does so by handing
+ * a time_t value of 0, meaning 1970-01-01 00:00:00 UTC (the Unix Epoch),
+ * to localtime() and using the tm_gmtoff value from the resulting
+ * struct tm. That works in countries that were in standard time
+ * then, but doesn't work in countries that were not in standard time
+ * then, meaning it doesn't work correctly in countries in the Southern
+ * Hemisphere that were in Daylight Saving Tie at that point, and may or
+ * may not work correctly in Ireland, depending on how "standard time"
+ * is defined (don't ask).
+ *
+ * For now, we use a similar mechanism to the one above, but we check
+ * whether tm_isdst is greater than 0 in the resulting struct tm and,
+ * if it is, use a time_t value of 86400*(365/2), in the hopes that,
+ * halfway through 1970, the location in question was in standard
+ * time.
+ *
+ * Also, for now, we test for FreeBSD rather than doing a configure-
+ * time check; checking whether the symbol "timezone" is defined
+ * won't work, as it's defined in FreeBSD as a function, so we'd
+ * have to check *how* it's defined.
+ *
+ * So we have a function to return the difference in question. It
+ * returns a long because timezone is defined to be a long in POSIX
+ * and because the tm_gmtoff member of a struct tm, if such a member
+ * is present, is also a long.
+ */
+static long
+utc_offset(void)
+{
+#if defined(__FreeBSD__)
+ /*
+ * We only calculate the standard time UTC offset once, under the
+ * assumption that we won't change what time zone we're in.
+ *
+ * XXX - that assumption is violated if:
+ *
+ * you're running on an OS where you can set the current
+ * time zone and that will affect all running programs,
+ * or where the OS tries to determine where you're located
+ * and changes the time zone to match (for example, macOS,
+ * in which both of those are the case);
+ *
+ * you're in a location that has moved between time zones
+ * since 1970-01-01 00:00:00 UTC (there are some, and the
+ * IANA time zone database, at least, takes that into
+ * account);
+ *
+ * we add support for the if_iana_tzname Interface
+ * Description Block option, so that, when looking
+ * at a file with that option for one or more
+ * interfaces, and using the timezone from that
+ * option rather than the local timezone, the
+ * offset from UTC may change from file to file.
+ *
+ * This *probably* won't make much of a difference, as
+ * we have to do this sort of hackery only when parsing
+ * a date that doesn't use the "Obsolete Date and Time",
+ * as it's called in RFC 2822.
+ */
+ static bool got_utcoffset = false;
+ static struct tm *gtm;
+ time_t then = 0;
+
+ if (got_utcoffset) {
+ if (gtm != NULL)
+ return gtm->tm_gmtoff;
+ else
+ return 0; /* localtime() failed on us */
+ }
+
+ gtm = localtime(&then);
+ got_utcoffset = true;
+ if (gtm == NULL) {
+ /*
+ * Oh, heck, it can't convert the Epoch. Just
+ * return 0 and say to hell with it.
+ */
+ return 0;
+ }
+ if (gtm->tm_isdst > 0) {
+ /*
+ * Sorry, we were in Daylight Saving Time on
+ * 1970-01-01 at 00:00:00 UTC. Try the middle
+ * of the year. (We don't bother making sure
+ * we weren't in DST then.)
+ */
+ then = 86400*(365/2);
+ gtm = localtime(&then);
+ if (gtm == NULL) {
+ /* See above. */
+ return 0;
+ }
+ }
+ return gtm->tm_gmtoff;
+#else
+ return timezone;
+#endif
+}
+
+char *
+ws_strptime_p(const char *buf, const char *format, struct tm *tm)
+{
+#ifdef HAVE_STRPTIME
+ return strptime(buf, format, tm);
+#else
+ return ws_strptime(buf, format, tm, NULL);
+#endif
+}
+
+char *
+ws_strptime(const char *buf, const char *fmt, struct tm *tm, struct ws_timezone *zonep)
+{
+ unsigned char c;
+ const unsigned char *bp, *ep, *zname;
+ int alt_format, i, split_year = 0, neg = 0, state = 0,
+ day_offset = -1, week_offset = 0, offs, mandatory;
+ const char *new_fmt;
+ long tm_gmtoff;
+ const char *tm_zone;
+
+ bp = (const unsigned char *)buf;
+
+ while (bp != NULL && (c = *fmt++) != '\0') {
+ /* Clear `alternate' modifier prior to new conversion. */
+ alt_format = 0;
+ i = 0;
+
+ /* Eat up white-space. */
+ if (g_ascii_isspace(c)) {
+ while (g_ascii_isspace(*bp))
+ bp++;
+ continue;
+ }
+
+ if (c != '%')
+ goto literal;
+
+
+again: switch (c = *fmt++) {
+ case '%': /* "%%" is converted to "%". */
+literal:
+ if (c != *bp++)
+ return NULL;
+ LEGAL_ALT(0);
+ continue;
+
+ /*
+ * "Alternative" modifiers. Just set the appropriate flag
+ * and start over again.
+ */
+ case 'E': /* "%E?" alternative conversion modifier. */
+ LEGAL_ALT(0);
+ alt_format |= ALT_E;
+ goto again;
+
+ case 'O': /* "%O?" alternative conversion modifier. */
+ LEGAL_ALT(0);
+ alt_format |= ALT_O;
+ goto again;
+
+ /*
+ * "Complex" conversion rules, implemented through recursion.
+ */
+ case 'c': /* Date and time, using the locale's format. */
+ new_fmt = "%a %b %e %H:%M:%S %Y";
+ state |= S_WDAY | S_MON | S_MDAY | S_YEAR;
+ goto recurse;
+
+ case 'D': /* The date as "%m/%d/%y". */
+ new_fmt = "%m/%d/%y";
+ LEGAL_ALT(0);
+ state |= S_MON | S_MDAY | S_YEAR;
+ goto recurse;
+
+ case 'F': /* The date as "%Y-%m-%d". */
+ new_fmt = "%Y-%m-%d";
+ LEGAL_ALT(0);
+ state |= S_MON | S_MDAY | S_YEAR;
+ goto recurse;
+
+ case 'R': /* The time as "%H:%M". */
+ new_fmt = "%H:%M";
+ LEGAL_ALT(0);
+ goto recurse;
+
+ case 'r': /* The time in 12-hour clock representation. */
+ new_fmt = "%I:%M:%S %p";
+ LEGAL_ALT(0);
+ goto recurse;
+
+ case 'T': /* The time as "%H:%M:%S". */
+ new_fmt = "%H:%M:%S";
+ LEGAL_ALT(0);
+ goto recurse;
+
+ case 'X': /* The time, using the locale's format. */
+ new_fmt = "%H:%M:%S";
+ goto recurse;
+
+ case 'x': /* The date, using the locale's format. */
+ new_fmt = "%m/%d/%y";
+ state |= S_MON | S_MDAY | S_YEAR;
+ recurse:
+ bp = (const unsigned char *)ws_strptime((const char *)bp,
+ new_fmt, tm, zonep);
+ LEGAL_ALT(ALT_E);
+ continue;
+
+ /*
+ * "Elementary" conversion rules.
+ */
+ case 'A': /* The day of week, using the locale's form. */
+ case 'a':
+ bp = find_string(bp, &tm->tm_wday, cloc_day, cloc_abday, 7);
+ LEGAL_ALT(0);
+ state |= S_WDAY;
+ continue;
+
+ case 'B': /* The month, using the locale's form. */
+ case 'b':
+ case 'h':
+ bp = find_string(bp, &tm->tm_mon, cloc_mon, cloc_abmon, 12);
+ LEGAL_ALT(0);
+ state |= S_MON;
+ continue;
+
+ case 'C': /* The century number. */
+ i = 20;
+ bp = conv_num(bp, &i, 0, 99);
+
+ i = i * 100 - TM_YEAR_BASE;
+ if (split_year)
+ i += tm->tm_year % 100;
+ split_year = 1;
+ tm->tm_year = i;
+ LEGAL_ALT(ALT_E);
+ state |= S_YEAR;
+ continue;
+
+ case 'd': /* The day of month. */
+ case 'e':
+ bp = conv_num(bp, &tm->tm_mday, 1, 31);
+ LEGAL_ALT(ALT_O);
+ state |= S_MDAY;
+ continue;
+
+ case 'k': /* The hour (24-hour clock representation). */
+ LEGAL_ALT(0);
+ /* FALLTHROUGH */
+ case 'H':
+ bp = conv_num(bp, &tm->tm_hour, 0, 23);
+ LEGAL_ALT(ALT_O);
+ state |= S_HOUR;
+ continue;
+
+ case 'l': /* The hour (12-hour clock representation). */
+ LEGAL_ALT(0);
+ /* FALLTHROUGH */
+ case 'I':
+ bp = conv_num(bp, &tm->tm_hour, 1, 12);
+ if (tm->tm_hour == 12)
+ tm->tm_hour = 0;
+ LEGAL_ALT(ALT_O);
+ state |= S_HOUR;
+ continue;
+
+ case 'j': /* The day of year. */
+ i = 1;
+ bp = conv_num(bp, &i, 1, 366);
+ tm->tm_yday = i - 1;
+ LEGAL_ALT(0);
+ state |= S_YDAY;
+ continue;
+
+ case 'M': /* The minute. */
+ bp = conv_num(bp, &tm->tm_min, 0, 59);
+ LEGAL_ALT(ALT_O);
+ continue;
+
+ case 'm': /* The month. */
+ i = 1;
+ bp = conv_num(bp, &i, 1, 12);
+ tm->tm_mon = i - 1;
+ LEGAL_ALT(ALT_O);
+ state |= S_MON;
+ continue;
+
+ case 'p': /* The locale's equivalent of AM/PM. */
+ bp = find_string(bp, &i, cloc_am_pm,
+ NULL, 2);
+ if (HAVE_HOUR(state) && tm->tm_hour > 11)
+ return NULL;
+ tm->tm_hour += i * 12;
+ LEGAL_ALT(0);
+ continue;
+
+ case 'S': /* The seconds. */
+ bp = conv_num(bp, &tm->tm_sec, 0, 61);
+ LEGAL_ALT(ALT_O);
+ continue;
+
+ case 's': /* seconds since the epoch */
+ {
+ int64_t secs;
+ const char *endptr;
+ time_t sse;
+
+ /* Extract the seconds as a 64-bit signed number. */
+ if (!ws_strtoi64(bp, &endptr, &secs)) {
+ bp = NULL;
+ continue;
+ }
+ bp = endptr;
+
+ /* For now, reject times before the Epoch. */
+ if (secs < 0) {
+ bp = NULL;
+ continue;
+ }
+
+ /* Make sure it fits. */
+ sse = (time_t)secs;
+ if (sse != secs) {
+ bp = NULL;
+ continue;
+ }
+
+ if (ws_localtime_r(&sse, tm) == NULL)
+ bp = NULL;
+ else
+ state |= S_YDAY | S_WDAY |
+ S_MON | S_MDAY | S_YEAR;
+ }
+ continue;
+
+ case 'U': /* The week of year, beginning on sunday. */
+ case 'W': /* The week of year, beginning on monday. */
+ /*
+ * This is bogus, as we can not assume any valid
+ * information present in the tm structure at this
+ * point to calculate a real value, so save the
+ * week for now in case it can be used later.
+ */
+ bp = conv_num(bp, &i, 0, 53);
+ LEGAL_ALT(ALT_O);
+ if (c == 'U')
+ day_offset = TM_SUNDAY;
+ else
+ day_offset = TM_MONDAY;
+ week_offset = i;
+ continue;
+
+ case 'w': /* The day of week, beginning on sunday. */
+ bp = conv_num(bp, &tm->tm_wday, 0, 6);
+ LEGAL_ALT(ALT_O);
+ state |= S_WDAY;
+ continue;
+
+ case 'u': /* The day of week, monday = 1. */
+ bp = conv_num(bp, &i, 1, 7);
+ tm->tm_wday = i % 7;
+ LEGAL_ALT(ALT_O);
+ state |= S_WDAY;
+ continue;
+
+ case 'g': /* The year corresponding to the ISO week
+ * number but without the century.
+ */
+ bp = conv_num(bp, &i, 0, 99);
+ continue;
+
+ case 'G': /* The year corresponding to the ISO week
+ * number with century.
+ */
+ do
+ bp++;
+ while (g_ascii_isdigit(*bp));
+ continue;
+
+ case 'V': /* The ISO 8601:1988 week number as decimal */
+ bp = conv_num(bp, &i, 1, 53);
+ continue;
+
+ case 'Y': /* The year. */
+ i = TM_YEAR_BASE; /* just for data sanity... */
+ bp = conv_num(bp, &i, 0, 9999);
+ tm->tm_year = i - TM_YEAR_BASE;
+ LEGAL_ALT(ALT_E);
+ state |= S_YEAR;
+ continue;
+
+ case 'y': /* The year within 100 years of the epoch. */
+ /* LEGAL_ALT(ALT_E | ALT_O); */
+ bp = conv_num(bp, &i, 0, 99);
+
+ if (split_year)
+ /* preserve century */
+ i += (tm->tm_year / 100) * 100;
+ else {
+ split_year = 1;
+ if (i <= 68)
+ i = i + 2000 - TM_YEAR_BASE;
+ else
+ i = i + 1900 - TM_YEAR_BASE;
+ }
+ tm->tm_year = i;
+ state |= S_YEAR;
+ continue;
+
+ case 'Z':
+ case 'z':
+ tzset();
+ mandatory = c == 'z';
+ /*
+ * We recognize all ISO 8601 formats:
+ * Z = Zulu time/UTC
+ * [+-]hhmm
+ * [+-]hh:mm
+ * [+-]hh
+ * We recognize all RFC-822/RFC-2822 formats:
+ * UT|GMT
+ * North American : UTC offsets
+ * E[DS]T = Eastern : -4 | -5
+ * C[DS]T = Central : -5 | -6
+ * M[DS]T = Mountain: -6 | -7
+ * P[DS]T = Pacific : -7 | -8
+ * Nautical/Military
+ * [A-IL-M] = -1 ... -9 (J not used)
+ * [N-Y] = +1 ... +12
+ * Note: J maybe used to denote non-nautical
+ * local time
+ */
+ if (mandatory)
+ while (g_ascii_isspace(*bp))
+ bp++;
+
+ zname = bp;
+ switch (*bp++) {
+ case 'G':
+ if (*bp++ != 'M')
+ goto namedzone;
+ /*FALLTHROUGH*/
+ case 'U':
+ if (*bp++ != 'T')
+ goto namedzone;
+ else if (!delim(*bp) && *bp++ != 'C')
+ goto namedzone;
+ /*FALLTHROUGH*/
+ case 'Z':
+ if (!delim(*bp))
+ goto namedzone;
+ tm->tm_isdst = 0;
+ tm_gmtoff = 0;
+ tm_zone = utc;
+ SET_ZONEP(zonep, tm_gmtoff, tm_zone);
+ continue;
+ case '+':
+ neg = 0;
+ break;
+ case '-':
+ neg = 1;
+ break;
+ default:
+namedzone:
+ bp = zname;
+
+ /* Nautical / Military style */
+ if (delim(bp[1]) &&
+ ((*bp >= 'A' && *bp <= 'I') ||
+ (*bp >= 'L' && *bp <= 'Y'))) {
+ /* Argh! No 'J'! */
+ if (*bp >= 'A' && *bp <= 'I')
+ tm_gmtoff =
+ (int)*bp - ('A' - 1);
+ else if (*bp >= 'L' && *bp <= 'M')
+ tm_gmtoff = (int)*bp - 'A';
+ else if (*bp >= 'N' && *bp <= 'Y')
+ tm_gmtoff = 'M' - (int)*bp;
+ else {
+ /* Not reached. */
+ ws_critical("Not reached!");
+ goto out;
+ }
+ tm_gmtoff *= SECSPERHOUR;
+ tm_zone = NULL; /* XXX */
+ SET_ZONEP(zonep, tm_gmtoff, tm_zone);
+ bp++;
+ continue;
+ }
+ /* 'J' is local time */
+ if (delim(bp[1]) && *bp == 'J') {
+ tm_gmtoff = -utc_offset();
+ tm_zone = NULL; /* XXX */
+ SET_ZONEP(zonep, tm_gmtoff, tm_zone);
+ bp++;
+ continue;
+ }
+
+ /*
+ * From our 3 letter hard-coded table
+ */
+ ep = find_string(bp, &i, nast, NULL, 4);
+ if (ep != NULL) {
+ tm_gmtoff = (-5 - i) * SECSPERHOUR;
+ tm_zone = nast[i];
+ SET_ZONEP(zonep, tm_gmtoff, tm_zone);
+ bp = ep;
+ continue;
+ }
+ ep = find_string(bp, &i, nadt, NULL, 4);
+ if (ep != NULL) {
+ tm->tm_isdst = 1;
+ tm_gmtoff = (-4 - i) * SECSPERHOUR;
+ tm_zone = nadt[i];
+ SET_ZONEP(zonep, tm_gmtoff, tm_zone);
+ bp = ep;
+ continue;
+ }
+ /*
+ * Our current timezone
+ */
+ ep = find_string(bp, &i,
+ (const char * const *)tzname,
+ NULL, 2);
+ if (ep != NULL) {
+ tm->tm_isdst = i;
+ tm_gmtoff = -utc_offset();
+ tm_zone = tzname[i];
+ SET_ZONEP(zonep, tm_gmtoff, tm_zone);
+ bp = ep;
+ continue;
+ }
+ goto out;
+ }
+ offs = 0;
+ for (i = 0; i < 4; ) {
+ if (g_ascii_isdigit(*bp)) {
+ offs = offs * 10 + (*bp++ - '0');
+ i++;
+ continue;
+ }
+ if (i == 2 && *bp == ':') {
+ bp++;
+ continue;
+ }
+ break;
+ }
+ if (g_ascii_isdigit(*bp))
+ goto out;
+ switch (i) {
+ case 2:
+ offs *= SECSPERHOUR;
+ break;
+ case 4:
+ i = offs % 100;
+ offs /= 100;
+ if (i >= SECSPERMIN)
+ goto out;
+ /* Convert minutes into decimal */
+ offs = offs * SECSPERHOUR + i * SECSPERMIN;
+ break;
+ default:
+ out:
+ if (mandatory)
+ return NULL;
+ bp = zname;
+ continue;
+ }
+ /* ISO 8601 & RFC 3339 limit to 23:59 max */
+ if (offs >= (HOURSPERDAY * SECSPERHOUR))
+ goto out;
+ if (neg)
+ offs = -offs;
+ tm->tm_isdst = 0; /* XXX */
+ tm_gmtoff = offs;
+ tm_zone = NULL; /* XXX */
+ SET_ZONEP(zonep, tm_gmtoff, tm_zone);
+ continue;
+
+ /*
+ * Miscellaneous conversions.
+ */
+ case 'n': /* Any kind of white-space. */
+ case 't':
+ while (g_ascii_isspace(*bp))
+ bp++;
+ LEGAL_ALT(0);
+ continue;
+
+
+ default: /* Unknown/unsupported conversion. */
+ return NULL;
+ }
+ }
+
+ if (!HAVE_YDAY(state) && HAVE_YEAR(state)) {
+ if (HAVE_MON(state) && HAVE_MDAY(state)) {
+ /* calculate day of year (ordinal date) */
+ tm->tm_yday = start_of_month[isleap_sum(tm->tm_year,
+ TM_YEAR_BASE)][tm->tm_mon] + (tm->tm_mday - 1);
+ state |= S_YDAY;
+ } else if (day_offset != -1) {
+ /*
+ * Set the date to the first Sunday (or Monday)
+ * of the specified week of the year.
+ */
+ if (!HAVE_WDAY(state)) {
+ tm->tm_wday = day_offset;
+ state |= S_WDAY;
+ }
+ tm->tm_yday = (7 -
+ first_wday_of(tm->tm_year + TM_YEAR_BASE) +
+ day_offset) % 7 + (week_offset - 1) * 7 +
+ tm->tm_wday - day_offset;
+ state |= S_YDAY;
+ }
+ }
+
+ if (HAVE_YDAY(state) && HAVE_YEAR(state)) {
+ int isleap;
+
+ if (!HAVE_MON(state)) {
+ /* calculate month of day of year */
+ i = 0;
+ isleap = isleap_sum(tm->tm_year, TM_YEAR_BASE);
+ while (tm->tm_yday >= start_of_month[isleap][i])
+ i++;
+ if (i > 12) {
+ i = 1;
+ tm->tm_yday -= start_of_month[isleap][12];
+ tm->tm_year++;
+ }
+ tm->tm_mon = i - 1;
+ state |= S_MON;
+ }
+
+ if (!HAVE_MDAY(state)) {
+ /* calculate day of month */
+ isleap = isleap_sum(tm->tm_year, TM_YEAR_BASE);
+ tm->tm_mday = tm->tm_yday -
+ start_of_month[isleap][tm->tm_mon] + 1;
+ state |= S_MDAY;
+ }
+
+ if (!HAVE_WDAY(state)) {
+ /* calculate day of week */
+ i = 0;
+ week_offset = first_wday_of(tm->tm_year);
+ while (i++ <= tm->tm_yday) {
+ if (week_offset++ >= 6)
+ week_offset = 0;
+ }
+ tm->tm_wday = week_offset;
+ }
+ }
+
+ return (char *)bp;
+}
+
+
+static const unsigned char *
+conv_num(const unsigned char *buf, int *dest, unsigned llim, unsigned ulim)
+{
+ unsigned result = 0;
+ unsigned char ch;
+
+ /* The limit also determines the number of valid digits. */
+ unsigned rulim = ulim;
+
+ ch = *buf;
+ if (ch < '0' || ch > '9')
+ return NULL;
+
+ do {
+ result *= 10;
+ result += ch - '0';
+ rulim /= 10;
+ ch = *++buf;
+ } while ((result * 10 <= ulim) && rulim && ch >= '0' && ch <= '9');
+
+ if (result < llim || result > ulim)
+ return NULL;
+
+ *dest = result;
+ return buf;
+}
+
+static const unsigned char *
+find_string(const unsigned char *bp, int *tgt, const char * const *n1,
+ const char * const *n2, int c)
+{
+ int i;
+ size_t len;
+
+ /* check full name - then abbreviated ones */
+ for (; n1 != NULL; n1 = n2, n2 = NULL) {
+ for (i = 0; i < c; i++, n1++) {
+ len = strlen(*n1);
+ if (g_ascii_strncasecmp(*n1, (const char *)bp, len) == 0) {
+ *tgt = i;
+ return bp + len;
+ }
+ }
+ }
+
+ /* Nothing matched */
+ return NULL;
+}
diff --git a/wsutil/ws_strptime.h b/wsutil/ws_strptime.h
new file mode 100644
index 00000000..0eb0c56a
--- /dev/null
+++ b/wsutil/ws_strptime.h
@@ -0,0 +1,46 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WS_STRPTIME_H__
+#define __WS_STRPTIME_H__
+
+#include <wireshark.h>
+#include <time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Struct to pass the "tm_gmtoff" and "tm_zone" fields, for systems whose
+ * libc struct tm type lacks these non-standard extensions. */
+struct ws_timezone {
+ long tm_gmtoff;
+ const char *tm_zone;
+};
+
+/*
+ * This is the NetBSD strptime(), modified to always use the "C" locale.
+ */
+WS_DLL_PUBLIC
+char *
+ws_strptime(const char *buf, const char *format, struct tm *tm,
+ struct ws_timezone *zonep);
+
+/*
+ * Portability wrapper around the system's strptime().
+ */
+WS_DLL_PUBLIC
+char *
+ws_strptime_p(const char *buf, const char *format, struct tm *tm);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WS_STRPTIME_H__ */
diff --git a/wsutil/wsgcrypt.c b/wsutil/wsgcrypt.c
new file mode 100644
index 00000000..3fbf9e58
--- /dev/null
+++ b/wsutil/wsgcrypt.c
@@ -0,0 +1,214 @@
+/* wsgcrypt.c
+ * Helper functions for libgcrypt
+ * By Erik de Jong <erikdejong@gmail.com>
+ * Copyright 2017 Erik de Jong
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "wsgcrypt.h"
+
+gcry_error_t ws_hmac_buffer(int algo, void *digest, const void *buffer, size_t length, const void *key, size_t keylen)
+{
+ gcry_md_hd_t hmac_handle;
+ gcry_error_t result = gcry_md_open(&hmac_handle, algo, GCRY_MD_FLAG_HMAC);
+ if (result) {
+ return result;
+ }
+ result = gcry_md_setkey(hmac_handle, key, keylen);
+ if (result) {
+ gcry_md_close(hmac_handle);
+ return result;
+ }
+ gcry_md_write(hmac_handle, buffer, length);
+ memcpy(digest, gcry_md_read(hmac_handle, 0), gcry_md_get_algo_dlen(algo));
+ gcry_md_close(hmac_handle);
+ return GPG_ERR_NO_ERROR;
+}
+
+gcry_error_t ws_cmac_buffer(int algo, void *digest, const void *buffer, size_t length, const void *key, size_t keylen)
+{
+ gcry_mac_hd_t cmac_handle;
+ gcry_error_t result = gcry_mac_open(&cmac_handle, algo, 0, NULL);
+ if (result) {
+ return result;
+ }
+ result = gcry_mac_setkey(cmac_handle, key, keylen);
+ if (result) {
+ gcry_mac_close(cmac_handle);
+ return result;
+ }
+ gcry_mac_write(cmac_handle, buffer, length);
+ result = gcry_mac_read(cmac_handle, digest, &keylen);
+ gcry_mac_close(cmac_handle);
+ return result;
+}
+
+void crypt_des_ecb(uint8_t *output, const uint8_t *buffer, const uint8_t *key56)
+{
+ uint8_t key64[8];
+ gcry_cipher_hd_t handle;
+
+ memset(output, 0x00, 8);
+
+ /* Transform 56 bits key into 64 bits DES key */
+ key64[0] = key56[0];
+ key64[1] = (key56[0] << 7) | (key56[1] >> 1);
+ key64[2] = (key56[1] << 6) | (key56[2] >> 2);
+ key64[3] = (key56[2] << 5) | (key56[3] >> 3);
+ key64[4] = (key56[3] << 4) | (key56[4] >> 4);
+ key64[5] = (key56[4] << 3) | (key56[5] >> 5);
+ key64[6] = (key56[5] << 2) | (key56[6] >> 6);
+ key64[7] = (key56[6] << 1);
+
+ if (gcry_cipher_open(&handle, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0)) {
+ return;
+ }
+ if (gcry_cipher_setkey(handle, key64, 8)) {
+ gcry_cipher_close(handle);
+ return;
+ }
+ gcry_cipher_encrypt(handle, output, 8, buffer, 8);
+ gcry_cipher_close(handle);
+}
+
+size_t rsa_decrypt_inplace(const unsigned len, unsigned char* data, gcry_sexp_t pk, bool pkcs1_padding, char **err)
+{
+ int rc = 0;
+ size_t decr_len = 0, i = 0;
+ gcry_sexp_t s_data = NULL, s_plain = NULL;
+ gcry_mpi_t encr_mpi = NULL, text = NULL;
+
+ *err = NULL;
+
+ /* create mpi representation of encrypted data */
+ rc = gcry_mpi_scan(&encr_mpi, GCRYMPI_FMT_USG, data, len, NULL);
+ if (rc != 0 ) {
+ *err = ws_strdup_printf("can't convert data to mpi (size %d):%s", len, gcry_strerror(rc));
+ return 0;
+ }
+
+ /* put the data into a simple list */
+ rc = gcry_sexp_build(&s_data, NULL, "(enc-val(rsa(a%m)))", encr_mpi);
+ if (rc != 0) {
+ *err = ws_strdup_printf("can't build encr_sexp:%s", gcry_strerror(rc));
+ decr_len = 0;
+ goto out;
+ }
+
+ /* pass it to libgcrypt */
+ rc = gcry_pk_decrypt(&s_plain, s_data, pk);
+ if (rc != 0)
+ {
+ *err = ws_strdup_printf("can't decrypt key:%s", gcry_strerror(rc));
+ decr_len = 0;
+ goto out;
+ }
+
+ /* convert plain text sexp to mpi format */
+ text = gcry_sexp_nth_mpi(s_plain, 0, 0);
+ if (! text) {
+ *err = g_strdup("can't convert sexp to mpi");
+ decr_len = 0;
+ goto out;
+ }
+
+ /* compute size requested for plaintext buffer */
+ rc = gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &decr_len, text);
+ if (rc != 0) {
+ *err = ws_strdup_printf("can't compute decr size:%s", gcry_strerror(rc));
+ decr_len = 0;
+ goto out;
+ }
+
+ /* sanity check on out buffer */
+ if (decr_len > len) {
+ *err = ws_strdup_printf("decrypted data is too long ?!? (%zu max %d)", decr_len, len);
+ decr_len = 0;
+ goto out;
+ }
+
+ /* write plain text to newly allocated buffer */
+ rc = gcry_mpi_print(GCRYMPI_FMT_USG, data, len, &decr_len, text);
+ if (rc != 0) {
+ *err = ws_strdup_printf("can't print decr data to mpi (size %zu):%s", decr_len, gcry_strerror(rc));
+ decr_len = 0;
+ goto out;
+ }
+
+ if (pkcs1_padding) {
+ /* strip the padding*/
+ rc = 0;
+ for (i = 1; i < decr_len; i++) {
+ if (data[i] == 0) {
+ rc = (int) i+1;
+ break;
+ }
+ }
+
+ decr_len -= rc;
+ memmove(data, data+rc, decr_len);
+ }
+
+out:
+ gcry_sexp_release(s_data);
+ gcry_sexp_release(s_plain);
+ gcry_mpi_release(encr_mpi);
+ gcry_mpi_release(text);
+ return decr_len;
+}
+
+gcry_error_t
+hkdf_expand(int hashalgo, const uint8_t *prk, unsigned prk_len, const uint8_t *info, unsigned info_len,
+ uint8_t *out, unsigned out_len)
+{
+ // Current maximum hash output size: 48 bytes for SHA-384.
+ unsigned char lastoutput[48];
+ gcry_md_hd_t h;
+ gcry_error_t err;
+ const unsigned hash_len = gcry_md_get_algo_dlen(hashalgo);
+
+ /* Some sanity checks */
+ if (!(out_len > 0 && out_len <= 255 * hash_len) ||
+ !(hash_len > 0 && hash_len <= sizeof(lastoutput))) {
+ return GPG_ERR_INV_ARG;
+ }
+
+ err = gcry_md_open(&h, hashalgo, GCRY_MD_FLAG_HMAC);
+ if (err) {
+ return err;
+ }
+
+ for (unsigned offset = 0; offset < out_len; offset += hash_len) {
+ gcry_md_reset(h);
+ gcry_md_setkey(h, prk, prk_len); /* Set PRK */
+ if (offset > 0) {
+ gcry_md_write(h, lastoutput, hash_len); /* T(1..N) */
+ }
+ gcry_md_write(h, info, info_len); /* info */
+ gcry_md_putc(h, (uint8_t) (offset / hash_len + 1)); /* constant 0x01..N */
+
+ memcpy(lastoutput, gcry_md_read(h, hashalgo), hash_len);
+ memcpy(out + offset, lastoutput, MIN(hash_len, out_len - offset));
+ }
+
+ gcry_md_close(h);
+ return 0;
+}
+
+/*
+ * 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:
+ */
diff --git a/wsutil/wsgcrypt.h b/wsutil/wsgcrypt.h
new file mode 100644
index 00000000..0f09f3cd
--- /dev/null
+++ b/wsutil/wsgcrypt.h
@@ -0,0 +1,74 @@
+/** @file
+ *
+ * Wrapper around libgcrypt's include file gcrypt.h.
+ * For libgcrypt 1.5.0, including gcrypt.h directly brings up lots of
+ * compiler warnings about deprecated definitions.
+ * Try to work around these warnings to ensure a clean build with -Werror.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2007 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WSGCRYPT_H__
+#define __WSGCRYPT_H__
+
+#include <wireshark.h>
+#include <gcrypt.h>
+
+#define HASH_MD5_LENGTH 16
+#define HASH_SHA1_LENGTH 20
+#define HASH_SHA2_224_LENGTH 28
+#define HASH_SHA2_256_LENGTH 32
+#define HASH_SHA2_384_LENGTH 48
+#define HASH_SHA2_512_LENGTH 64
+
+/* Convenience function to calculate the HMAC from the data in BUFFER
+ of size LENGTH with key KEY of size KEYLEN using the algorithm ALGO avoiding the creating of a
+ hash object. The hash is returned in the caller provided buffer
+ DIGEST which must be large enough to hold the digest of the given
+ algorithm. */
+WS_DLL_PUBLIC gcry_error_t ws_hmac_buffer(int algo, void *digest, const void *buffer, size_t length, const void *key, size_t keylen);
+
+WS_DLL_PUBLIC gcry_error_t ws_cmac_buffer(int algo, void *digest, const void *buffer, size_t length, const void *key, size_t keylen);
+
+/* Convenience function to encrypt 8 bytes in BUFFER with DES using the 56 bits KEY expanded to
+ 64 bits as key, encrypted data is returned in OUTPUT which must be at least 8 bytes large */
+WS_DLL_PUBLIC void crypt_des_ecb(uint8_t *output, const uint8_t *buffer, const uint8_t *key56);
+
+/* Convenience function for RSA decryption. Returns decrypted length on success, 0 on failure */
+WS_DLL_PUBLIC size_t rsa_decrypt_inplace(const unsigned len, unsigned char* data, gcry_sexp_t pk, bool pkcs1_padding, char **err);
+
+/**
+ * RFC 5869 HMAC-based Extract-and-Expand Key Derivation Function (HKDF):
+ * HKDF-Expand(PRK, info, L) -> OKM
+ *
+ * @param hashalgo [in] Libgcrypt hash algorithm identifier.
+ * @param prk [in] Pseudo-random key.
+ * @param prk_len [in] Length of prk.
+ * @param info [in] Optional context (can be NULL if info_len is zero).
+ * @param info_len [in] Length of info.
+ * @param out [out] Output keying material.
+ * @param out_len [in] Size of output keying material.
+ * @return 0 on success and an error code otherwise.
+ */
+WS_DLL_PUBLIC gcry_error_t
+hkdf_expand(int hashalgo, const uint8_t *prk, unsigned prk_len, const uint8_t *info, unsigned info_len,
+ uint8_t *out, unsigned out_len);
+
+/*
+ * Calculate HKDF-Extract(salt, IKM) -> PRK according to RFC 5869.
+ * Caller MUST ensure that 'prk' is large enough to store the digest from hash
+ * algorithm 'hashalgo' (e.g. 32 bytes for SHA-256).
+ */
+static inline gcry_error_t
+hkdf_extract(int hashalgo, const uint8_t *salt, size_t salt_len, const uint8_t *ikm, size_t ikm_len, uint8_t *prk)
+{
+ /* PRK = HMAC-Hash(salt, IKM) where salt is key, and IKM is input. */
+ return ws_hmac_buffer(hashalgo, prk, ikm, ikm_len, salt, salt_len);
+}
+
+
+#endif /* __WSGCRYPT_H__ */
diff --git a/wsutil/wsjson.c b/wsutil/wsjson.c
new file mode 100644
index 00000000..d2e55771
--- /dev/null
+++ b/wsutil/wsjson.c
@@ -0,0 +1,312 @@
+/* wsjson.c
+ * JSON parsing functions.
+ *
+ * Copyright 2016, Dario Lombardo
+ *
+ * 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"
+#define WS_LOG_DOMAIN LOG_DOMAIN_MAIN
+
+#include "wsjson.h"
+
+#include <string.h>
+#include <errno.h>
+#include <wsutil/jsmn.h>
+#include <wsutil/str_util.h>
+#include <wsutil/unicode-utils.h>
+#include <wsutil/wslog.h>
+
+bool
+json_validate(const uint8_t *buf, const size_t len)
+{
+ bool ret = true;
+ /* We expect no more than 1024 tokens */
+ unsigned max_tokens = 1024;
+ jsmntok_t* t;
+ jsmn_parser p;
+ int rcode;
+
+ /*
+ * Make sure the buffer isn't empty and the first octet isn't a NUL;
+ * otherwise, the parser will immediately stop parsing and not validate
+ * anything after that, so it'll just think it was handed an empty string.
+ *
+ * XXX - should we check for NULs anywhere in the buffer?
+ */
+ if (len == 0) {
+ ws_debug("JSON string is empty");
+ return false;
+ }
+ if (buf[0] == '\0') {
+ ws_debug("invalid character inside JSON string");
+ return false;
+ }
+
+ t = g_new0(jsmntok_t, max_tokens);
+
+ if (!t)
+ return false;
+
+ jsmn_init(&p);
+ rcode = jsmn_parse(&p, buf, len, t, max_tokens);
+ if (rcode < 0) {
+ switch (rcode) {
+ case JSMN_ERROR_NOMEM:
+ ws_debug("not enough tokens were provided");
+ break;
+ case JSMN_ERROR_INVAL:
+ ws_debug("invalid character inside JSON string");
+ break;
+ case JSMN_ERROR_PART:
+ ws_debug("the string is not a full JSON packet, "
+ "more bytes expected");
+ break;
+ default:
+ ws_debug("unexpected error");
+ break;
+ }
+ ret = false;
+ }
+
+ g_free(t);
+ return ret;
+}
+
+int
+json_parse(const char *buf, jsmntok_t *tokens, unsigned int max_tokens)
+{
+ jsmn_parser p;
+
+ jsmn_init(&p);
+ return jsmn_parse(&p, buf, strlen(buf), tokens, max_tokens);
+}
+
+static
+jsmntok_t *json_get_next_object(jsmntok_t *cur)
+{
+ int i;
+ jsmntok_t *next = cur+1;
+
+ for (i = 0; i < cur->size; i++) {
+ next = json_get_next_object(next);
+ }
+ return next;
+}
+
+jsmntok_t *json_get_object(const char *buf, jsmntok_t *parent, const char *name)
+{
+ int i;
+ jsmntok_t *cur = parent+1;
+
+ for (i = 0; i < parent->size; i++) {
+ if (cur->type == JSMN_STRING &&
+ !strncmp(&buf[cur->start], name, cur->end - cur->start)
+ && strlen(name) == (size_t)(cur->end - cur->start) &&
+ cur->size == 1 && (cur+1)->type == JSMN_OBJECT) {
+ return cur+1;
+ }
+ cur = json_get_next_object(cur);
+ }
+ return NULL;
+}
+
+jsmntok_t *json_get_array(const char *buf, jsmntok_t *parent, const char *name)
+{
+ int i;
+ jsmntok_t *cur = parent+1;
+
+ for (i = 0; i < parent->size; i++) {
+ if (cur->type == JSMN_STRING &&
+ !strncmp(&buf[cur->start], name, cur->end - cur->start)
+ && strlen(name) == (size_t)(cur->end - cur->start) &&
+ cur->size == 1 && (cur+1)->type == JSMN_ARRAY) {
+ return cur+1;
+ }
+ cur = json_get_next_object(cur);
+ }
+ return NULL;
+}
+
+int json_get_array_len(jsmntok_t *array)
+{
+ if (array->type != JSMN_ARRAY)
+ return -1;
+ return array->size;
+}
+
+jsmntok_t *json_get_array_index(jsmntok_t *array, int idx)
+{
+ int i;
+ jsmntok_t *cur = array+1;
+
+
+ if (array->type != JSMN_ARRAY || idx < 0 || idx >= array->size)
+ return NULL;
+ for (i = 0; i < idx; i++)
+ cur = json_get_next_object(cur);
+ return cur;
+}
+
+char *json_get_string(char *buf, jsmntok_t *parent, const char *name)
+{
+ int i;
+ jsmntok_t *cur = parent+1;
+
+ for (i = 0; i < parent->size; i++) {
+ if (cur->type == JSMN_STRING &&
+ !strncmp(&buf[cur->start], name, cur->end - cur->start)
+ && strlen(name) == (size_t)(cur->end - cur->start) &&
+ cur->size == 1 && (cur+1)->type == JSMN_STRING) {
+ buf[(cur+1)->end] = '\0';
+ if (!json_decode_string_inplace(&buf[(cur+1)->start]))
+ return NULL;
+ return &buf[(cur+1)->start];
+ }
+ cur = json_get_next_object(cur);
+ }
+ return NULL;
+}
+
+bool json_get_double(char *buf, jsmntok_t *parent, const char *name, double *val)
+{
+ int i;
+ jsmntok_t *cur = parent+1;
+
+ for (i = 0; i < parent->size; i++) {
+ if (cur->type == JSMN_STRING &&
+ !strncmp(&buf[cur->start], name, cur->end - cur->start)
+ && strlen(name) == (size_t)(cur->end - cur->start) &&
+ cur->size == 1 && (cur+1)->type == JSMN_PRIMITIVE) {
+ buf[(cur+1)->end] = '\0';
+ *val = g_ascii_strtod(&buf[(cur+1)->start], NULL);
+ if (errno != 0)
+ return false;
+ return true;
+ }
+ cur = json_get_next_object(cur);
+ }
+ return false;
+}
+
+bool
+json_decode_string_inplace(char *text)
+{
+ const char *input = text;
+ char *output = text;
+ while (*input) {
+ char ch = *input++;
+
+ if (ch == '\\') {
+ ch = *input++;
+
+ switch (ch) {
+ case '\"':
+ case '\\':
+ case '/':
+ *output++ = ch;
+ break;
+
+ case 'b':
+ *output++ = '\b';
+ break;
+ case 'f':
+ *output++ = '\f';
+ break;
+ case 'n':
+ *output++ = '\n';
+ break;
+ case 'r':
+ *output++ = '\r';
+ break;
+ case 't':
+ *output++ = '\t';
+ break;
+
+ case 'u':
+ {
+ uint32_t unicode_hex = 0;
+ int k;
+ int bin;
+
+ for (k = 0; k < 4; k++) {
+ unicode_hex <<= 4;
+
+ ch = *input++;
+ bin = ws_xton(ch);
+ if (bin == -1)
+ return false;
+ unicode_hex |= bin;
+ }
+
+ if ((IS_LEAD_SURROGATE(unicode_hex))) {
+ uint16_t lead_surrogate = unicode_hex;
+ uint16_t trail_surrogate = 0;
+
+ if (input[0] != '\\' || input[1] != 'u')
+ return false;
+ input += 2;
+
+ for (k = 0; k < 4; k++) {
+ trail_surrogate <<= 4;
+
+ ch = *input++;
+ bin = ws_xton(ch);
+ if (bin == -1)
+ return false;
+ trail_surrogate |= bin;
+ }
+
+ if ((!IS_TRAIL_SURROGATE(trail_surrogate)))
+ return false;
+
+ unicode_hex = SURROGATE_VALUE(lead_surrogate,trail_surrogate);
+
+ } else if ((IS_TRAIL_SURROGATE(unicode_hex))) {
+ return false;
+ }
+
+ if (!g_unichar_validate(unicode_hex))
+ return false;
+
+ /* Don't allow NUL byte injection. */
+ if (unicode_hex == 0)
+ return false;
+
+ /* \uXXXX => 6 bytes, and g_unichar_to_utf8() requires to have output buffer at least 6 bytes -> OK. */
+ k = g_unichar_to_utf8(unicode_hex, output);
+ output += k;
+ break;
+ }
+
+ default:
+ return false;
+ }
+
+ } else {
+ *output = ch;
+ output++;
+ }
+ }
+
+ *output = '\0';
+ return true;
+}
+
+/*
+ * 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/wsutil/wsjson.h b/wsutil/wsjson.h
new file mode 100644
index 00000000..be232767
--- /dev/null
+++ b/wsutil/wsjson.h
@@ -0,0 +1,95 @@
+/** @file
+ *
+ * JSON parsing functions.
+ *
+ * Copyright 2016, Dario Lombardo
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WSJSON_H__
+#define __WSJSON_H__
+
+#include "ws_symbol_export.h"
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include "jsmn.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Check if a buffer is json an returns true if it is.
+ */
+WS_DLL_PUBLIC bool json_validate(const uint8_t *buf, const size_t len);
+
+WS_DLL_PUBLIC int json_parse(const char *buf, jsmntok_t *tokens, unsigned int max_tokens);
+
+/**
+ * Get the pointer to an object belonging to parent object and named as the name variable.
+ * Returns NULL if not found.
+ */
+WS_DLL_PUBLIC jsmntok_t *json_get_object(const char *buf, jsmntok_t *parent, const char *name);
+
+/**
+ * Get the pointer to an array belonging to parent object and named as the name variable.
+ * Returns NULL if not found.
+ */
+WS_DLL_PUBLIC jsmntok_t *json_get_array(const char *buf, jsmntok_t *parent, const char *name);
+
+/**
+ * Get the number of elements of an array.
+ * Returns -1 if the JSON objecct is not an array.
+ */
+WS_DLL_PUBLIC int json_get_array_len(jsmntok_t *array);
+
+/**
+ * Get the pointer to idx element of an array.
+ * Returns NULL if not found.
+ */
+WS_DLL_PUBLIC jsmntok_t *json_get_array_index(jsmntok_t *parent, int idx);
+
+/**
+ * Get the unescaped value of a string object belonging to parent object and named as the name variable.
+ * Returns NULL if not found. Caution: it modifies input buffer.
+ */
+WS_DLL_PUBLIC char *json_get_string(char *buf, jsmntok_t *parent, const char *name);
+
+/**
+ * Get the value of a number object belonging to parent object and named as the name variable.
+ * Returns false if not found. Caution: it modifies input buffer.
+ * Scientific notation not supported yet.
+ */
+WS_DLL_PUBLIC bool json_get_double(char *buf, jsmntok_t *parent, const char *name, double *val);
+
+/**
+ * Decode the contents of a JSON string value by overwriting the input data.
+ * Returns true on success and false if invalid characters were encountered.
+ */
+WS_DLL_PUBLIC bool json_decode_string_inplace(char *text);
+
+#ifdef __cplusplus
+}
+#endif
+
+#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/wsutil/wslog.c b/wsutil/wslog.c
new file mode 100644
index 00000000..e181e5ed
--- /dev/null
+++ b/wsutil/wslog.c
@@ -0,0 +1,1442 @@
+/*
+ * Copyright 2021, João Valverde <j@v6e.pt>
+ *
+ * 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 "wslog.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+/* Because ws_assert() dependes on ws_error() we do not use it
+ * here and fall back on assert() instead. */
+#include <assert.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef _WIN32
+#include <process.h>
+#include <windows.h>
+#include <conio.h>
+#endif
+
+#include "file_util.h"
+#include "time_util.h"
+#include "to_str.h"
+#include "strtoi.h"
+#ifdef _WIN32
+#include "console_win32.h"
+#endif
+
+#define ASSERT(expr) assert(expr)
+
+/* Runtime log level. */
+#define ENV_VAR_LEVEL "WIRESHARK_LOG_LEVEL"
+
+/* Log domains enabled/disabled. */
+#define ENV_VAR_DOMAIN "WIRESHARK_LOG_DOMAIN"
+
+/* Alias "domain" and "domains". */
+#define ENV_VAR_DOMAIN_S "WIRESHARK_LOG_DOMAINS"
+
+/* Log level that generates a trap and aborts. Can be "critical"
+ * or "warning". */
+#define ENV_VAR_FATAL "WIRESHARK_LOG_FATAL"
+
+/* Log domains that are fatal. */
+#define ENV_VAR_FATAL_DOMAIN "WIRESHARK_LOG_FATAL_DOMAIN"
+
+/* Alias "domain" and "domains". */
+#define ENV_VAR_FATAL_DOMAIN_S "WIRESHARK_LOG_FATAL_DOMAINS"
+
+/* Domains that will produce debug output, regardless of log level or
+ * domain filter. */
+#define ENV_VAR_DEBUG "WIRESHARK_LOG_DEBUG"
+
+/* Domains that will produce noisy output, regardless of log level or
+ * domain filter. */
+#define ENV_VAR_NOISY "WIRESHARK_LOG_NOISY"
+
+#define DEFAULT_LOG_LEVEL LOG_LEVEL_MESSAGE
+
+#define DEFAULT_PROGNAME "PID"
+
+#define DOMAIN_UNDEFED(domain) ((domain) == NULL || *(domain) == '\0')
+#define DOMAIN_DEFINED(domain) (!DOMAIN_UNDEFED(domain))
+
+/*
+ * Note: I didn't measure it but I assume using a string array is faster than
+ * a GHashTable for small number N of domains.
+ */
+typedef struct {
+ char **domainv;
+ bool positive; /* positive or negative match */
+ enum ws_log_level min_level; /* for level filters */
+} log_filter_t;
+
+
+/* If the module is not initialized by calling ws_log_init() all messages
+ * will be printed regardless of log level. This is a feature, not a bug. */
+static enum ws_log_level current_log_level = LOG_LEVEL_NONE;
+
+static bool stdout_color_enabled = false;
+
+static bool stderr_color_enabled = false;
+
+/* Use stdout for levels "info" and below, for backward compatibility
+ * with GLib. */
+static bool stdout_logging_enabled = false;
+
+static const char *registered_progname = DEFAULT_PROGNAME;
+
+/* List of domains to filter. */
+static log_filter_t *domain_filter = NULL;
+
+/* List of domains to output debug level unconditionally. */
+static log_filter_t *debug_filter = NULL;
+
+/* List of domains to output noisy level unconditionally. */
+static log_filter_t *noisy_filter = NULL;
+
+/* List of domains that are fatal. */
+static log_filter_t *fatal_filter = NULL;
+
+static ws_log_writer_cb *registered_log_writer = NULL;
+
+static void *registered_log_writer_data = NULL;
+
+static ws_log_writer_free_data_cb *registered_log_writer_data_free = NULL;
+
+static FILE *custom_log = NULL;
+
+static enum ws_log_level fatal_log_level = LOG_LEVEL_ERROR;
+
+#ifdef WS_DEBUG
+static bool init_complete = false;
+#endif
+
+ws_log_console_open_pref ws_log_console_open = LOG_CONSOLE_OPEN_NEVER;
+
+
+static void print_err(void (*vcmdarg_err)(const char *, va_list ap),
+ int exit_failure,
+ const char *fmt, ...) G_GNUC_PRINTF(3,4);
+
+static void ws_log_cleanup(void);
+
+
+const char *ws_log_level_to_string(enum ws_log_level level)
+{
+ switch (level) {
+ case LOG_LEVEL_NONE:
+ return "(zero)";
+ case LOG_LEVEL_ECHO:
+ return "ECHO";
+ case LOG_LEVEL_ERROR:
+ return "ERROR";
+ case LOG_LEVEL_CRITICAL:
+ return "CRITICAL";
+ case LOG_LEVEL_WARNING:
+ return "WARNING";
+ case LOG_LEVEL_MESSAGE:
+ return "MESSAGE";
+ case LOG_LEVEL_INFO:
+ return "INFO";
+ case LOG_LEVEL_DEBUG:
+ return "DEBUG";
+ case LOG_LEVEL_NOISY:
+ return "NOISY";
+ default:
+ return "(BOGUS LOG LEVEL)";
+ }
+}
+
+
+static enum ws_log_level string_to_log_level(const char *str_level)
+{
+ if (!str_level)
+ return LOG_LEVEL_NONE;
+
+ if (g_ascii_strcasecmp(str_level, "noisy") == 0)
+ return LOG_LEVEL_NOISY;
+ else if (g_ascii_strcasecmp(str_level, "debug") == 0)
+ return LOG_LEVEL_DEBUG;
+ else if (g_ascii_strcasecmp(str_level, "info") == 0)
+ return LOG_LEVEL_INFO;
+ else if (g_ascii_strcasecmp(str_level, "message") == 0)
+ return LOG_LEVEL_MESSAGE;
+ else if (g_ascii_strcasecmp(str_level, "warning") == 0)
+ return LOG_LEVEL_WARNING;
+ else if (g_ascii_strcasecmp(str_level, "critical") == 0)
+ return LOG_LEVEL_CRITICAL;
+ else if (g_ascii_strcasecmp(str_level, "error") == 0)
+ return LOG_LEVEL_ERROR;
+ else if (g_ascii_strcasecmp(str_level, "echo") == 0)
+ return LOG_LEVEL_ECHO;
+ else
+ return LOG_LEVEL_NONE;
+}
+
+
+WS_RETNONNULL
+static inline const char *domain_to_string(const char *domain)
+{
+ return DOMAIN_UNDEFED(domain) ? "(none)" : domain;
+}
+
+
+static inline bool filter_contains(log_filter_t *filter,
+ const char *domain)
+{
+ if (filter == NULL || DOMAIN_UNDEFED(domain))
+ return false;
+
+ for (char **domv = filter->domainv; *domv != NULL; domv++) {
+ if (g_ascii_strcasecmp(*domv, domain) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+static inline bool level_filter_matches(log_filter_t *filter,
+ const char *domain,
+ enum ws_log_level level,
+ bool *active_ptr)
+{
+ if (filter == NULL || DOMAIN_UNDEFED(domain))
+ return false;
+
+ if (!filter_contains(filter, domain))
+ return false;
+
+ if (filter->positive) {
+ if (active_ptr)
+ *active_ptr = level >= filter->min_level;
+ return true;
+ }
+
+ /* negative match */
+ if (level <= filter->min_level) {
+ if (active_ptr)
+ *active_ptr = false;
+ return true;
+ }
+
+ return false;
+}
+
+
+static inline void
+get_timestamp(struct timespec *ts)
+{
+ bool ok = false;
+
+#if defined(HAVE_CLOCK_GETTIME)
+ ok = (clock_gettime(CLOCK_REALTIME, ts) == 0);
+#elif defined(HAVE_TIMESPEC_GET)
+ ok = (timespec_get(ts, TIME_UTC) == TIME_UTC);
+#endif
+ if (ok)
+ return;
+
+ /* Fall back on time(). */
+ ts->tv_sec = time(NULL);
+ ts->tv_nsec = -1;
+}
+
+
+static inline void fill_manifest(ws_log_manifest_t *mft)
+{
+ struct timespec ts;
+ get_timestamp(&ts);
+ ws_localtime_r(&ts.tv_sec, &mft->tstamp_secs);
+ mft->nanosecs = ts.tv_nsec;
+ mft->pid = getpid();
+}
+
+
+static inline bool msg_is_active(const char *domain, enum ws_log_level level,
+ ws_log_manifest_t *mft)
+{
+ bool is_active = ws_log_msg_is_active(domain, level);
+ if (is_active)
+ fill_manifest(mft);
+ return is_active;
+}
+
+
+bool ws_log_msg_is_active(const char *domain, enum ws_log_level level)
+{
+ /*
+ * Higher numerical levels have higher priority. Critical and above
+ * are always enabled.
+ */
+ if (level >= LOG_LEVEL_CRITICAL)
+ return true;
+
+ /*
+ * Check if the level has been configured as fatal.
+ */
+ if (level >= fatal_log_level)
+ return true;
+
+ /*
+ * Check if the domain has been configured as fatal.
+ */
+ if (DOMAIN_DEFINED(domain) && fatal_filter != NULL) {
+ if (filter_contains(fatal_filter, domain) && fatal_filter->positive) {
+ return true;
+ }
+ }
+
+ /*
+ * The debug/noisy filter overrides the other parameters.
+ */
+ if (DOMAIN_DEFINED(domain)) {
+ bool active;
+
+ if (level_filter_matches(noisy_filter, domain, level, &active))
+ return active;
+ if (level_filter_matches(debug_filter, domain, level, &active))
+ return active;
+ }
+
+ /*
+ * If the priority is lower than the current minimum drop the
+ * message.
+ */
+ if (level < current_log_level)
+ return false;
+
+ /*
+ * If we don't have domain filtering enabled we are done.
+ */
+ if (domain_filter == NULL)
+ return true;
+
+ /*
+ * We have a filter but we don't use it with the undefined domain,
+ * pretty much every permanent call to ws_log should be using a
+ * chosen domain.
+ */
+ if (DOMAIN_UNDEFED(domain))
+ return true;
+
+ /* Check if the domain filter matches. */
+ if (filter_contains(domain_filter, domain))
+ return domain_filter->positive;
+
+ /* We have a domain filter but it didn't match. */
+ return !domain_filter->positive;
+}
+
+
+enum ws_log_level ws_log_get_level(void)
+{
+ return current_log_level;
+}
+
+
+enum ws_log_level ws_log_set_level(enum ws_log_level level)
+{
+ if (level <= LOG_LEVEL_NONE || level >= _LOG_LEVEL_LAST)
+ return LOG_LEVEL_NONE;
+ if (level > LOG_LEVEL_CRITICAL)
+ level = LOG_LEVEL_CRITICAL;
+
+ current_log_level = level;
+ return current_log_level;
+}
+
+
+enum ws_log_level ws_log_set_level_str(const char *str_level)
+{
+ enum ws_log_level level;
+
+ level = string_to_log_level(str_level);
+ return ws_log_set_level(level);
+}
+
+
+static const char *opt_level = "--log-level";
+static const char *opt_domain = "--log-domain";
+/* Alias "domain" and "domains". */
+static const char *opt_domain_s = "--log-domains";
+static const char *opt_file = "--log-file";
+static const char *opt_fatal = "--log-fatal";
+static const char *opt_fatal_domain = "--log-fatal-domain";
+/* Alias "domain" and "domains". */
+static const char *opt_fatal_domain_s = "--log-fatal-domains";
+static const char *opt_debug = "--log-debug";
+static const char *opt_noisy = "--log-noisy";
+
+
+static void print_err(void (*vcmdarg_err)(const char *, va_list ap),
+ int exit_failure,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (vcmdarg_err)
+ vcmdarg_err(fmt, ap);
+ else
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ if (exit_failure != LOG_ARGS_NOEXIT)
+ exit(exit_failure);
+}
+
+
+/*
+ * This tries to convert old log level preference to a wslog
+ * configuration. The string must start with "console.log.level:"
+ * It receives an argv for { '-o', 'console.log.level:nnn', ...} or
+ * { '-oconsole.log.level:nnn', ...}.
+ */
+static void
+parse_console_compat_option(char *argv[],
+ void (*vcmdarg_err)(const char *, va_list ap),
+ int exit_failure)
+{
+ const char *mask_str;
+ uint32_t mask;
+ enum ws_log_level level;
+
+ ASSERT(argv != NULL);
+
+ if (argv[0] == NULL)
+ return;
+
+ if (strcmp(argv[0], "-o") == 0) {
+ if (argv[1] == NULL ||
+ !g_str_has_prefix(argv[1], "console.log.level:")) {
+ /* Not what we were looking for. */
+ return;
+ }
+ mask_str = argv[1] + strlen("console.log.level:");
+ }
+ else if (g_str_has_prefix(argv[0], "-oconsole.log.level:")) {
+ mask_str = argv[0] + strlen("-oconsole.log.level:");
+ }
+ else {
+ /* Not what we were looking for. */
+ return;
+ }
+
+ print_err(vcmdarg_err, LOG_ARGS_NOEXIT,
+ "Option 'console.log.level' is deprecated, consult '--help' "
+ "for diagnostic message options.");
+
+ if (*mask_str == '\0') {
+ print_err(vcmdarg_err, exit_failure,
+ "Missing value to 'console.log.level' option.");
+ return;
+ }
+
+ if (!ws_basestrtou32(mask_str, NULL, &mask, 10)) {
+ print_err(vcmdarg_err, exit_failure,
+ "%s is not a valid decimal number.", mask_str);
+ return;
+ }
+
+ /*
+ * The lowest priority bit in the mask defines the level.
+ */
+ if (mask & G_LOG_LEVEL_DEBUG)
+ level = LOG_LEVEL_DEBUG;
+ else if (mask & G_LOG_LEVEL_INFO)
+ level = LOG_LEVEL_INFO;
+ else if (mask & G_LOG_LEVEL_MESSAGE)
+ level = LOG_LEVEL_MESSAGE;
+ else if (mask & G_LOG_LEVEL_WARNING)
+ level = LOG_LEVEL_WARNING;
+ else if (mask & G_LOG_LEVEL_CRITICAL)
+ level = LOG_LEVEL_CRITICAL;
+ else if (mask & G_LOG_LEVEL_ERROR)
+ level = LOG_LEVEL_ERROR;
+ else
+ level = LOG_LEVEL_NONE;
+
+ if (level == LOG_LEVEL_NONE) {
+ /* Some values (like zero) might not contain any meaningful bits.
+ * Throwing an error in that case seems appropriate. */
+ print_err(vcmdarg_err, exit_failure,
+ "Value %s is not a valid log mask.", mask_str);
+ return;
+ }
+
+ ws_log_set_level(level);
+}
+
+/* Match "arg_name=value" or "arg_name value" to opt_name. */
+static bool optequal(const char *arg, const char *opt)
+{
+ ASSERT(arg);
+ ASSERT(opt);
+#define ARGEND(arg) (*(arg) == '\0' || *(arg) == ' ' || *(arg) == '=')
+
+ while (!ARGEND(arg) && *opt != '\0') {
+ if (*arg != *opt) {
+ return false;
+ }
+ arg += 1;
+ opt += 1;
+ }
+ if (ARGEND(arg) && *opt == '\0') {
+ return true;
+ }
+ return false;
+}
+
+int ws_log_parse_args(int *argc_ptr, char *argv[],
+ void (*vcmdarg_err)(const char *, va_list ap),
+ int exit_failure)
+{
+ char **ptr = argv;
+ int count = *argc_ptr;
+ int ret = 0;
+ size_t optlen;
+ const char *option, *value;
+ int extra;
+
+ if (argc_ptr == NULL || argv == NULL)
+ return -1;
+
+#ifdef WS_DEBUG
+ /* Assert ws_log_init() was called before ws_log_parse_args(). */
+ ASSERT(init_complete);
+#endif
+
+ /* Configure from command line. */
+
+ while (*ptr != NULL) {
+ if (optequal(*ptr, opt_level)) {
+ option = opt_level;
+ optlen = strlen(opt_level);
+ }
+ else if (optequal(*ptr, opt_domain)) {
+ option = opt_domain;
+ optlen = strlen(opt_domain);
+ }
+ else if (optequal(*ptr, opt_domain_s)) {
+ option = opt_domain; /* Alias */
+ optlen = strlen(opt_domain_s);
+ }
+ else if (optequal(*ptr, opt_fatal_domain)) {
+ option = opt_fatal_domain;
+ optlen = strlen(opt_fatal_domain);
+ }
+ else if (optequal(*ptr, opt_fatal_domain_s)) {
+ option = opt_fatal_domain; /* Alias */
+ optlen = strlen(opt_fatal_domain_s);
+ }
+ else if (optequal(*ptr, opt_file)) {
+ option = opt_file;
+ optlen = strlen(opt_file);
+ }
+ else if (optequal(*ptr, opt_fatal)) {
+ option = opt_fatal;
+ optlen = strlen(opt_fatal);
+ }
+ else if (optequal(*ptr, opt_debug)) {
+ option = opt_debug;
+ optlen = strlen(opt_debug);
+ }
+ else if (optequal(*ptr, opt_noisy)) {
+ option = opt_noisy;
+ optlen = strlen(opt_noisy);
+ }
+ else {
+ /* Check is we have the old '-o console.log.level' flag,
+ * or '-oconsole.log.level', for backward compatibility.
+ * Then if we do ignore it after processing and let the
+ * preferences module handle it later. */
+ if (*(*ptr + 0) == '-' && *(*ptr + 1) == 'o') {
+ parse_console_compat_option(ptr, vcmdarg_err, exit_failure);
+ }
+ ptr += 1;
+ count -= 1;
+ continue;
+ }
+
+ value = *ptr + optlen;
+ /* Two possibilities:
+ * --<option> <value>
+ * or
+ * --<option>=<value>
+ */
+ if (value[0] == '\0') {
+ /* value is separated with blank space */
+ value = *(ptr + 1);
+ extra = 1;
+
+ if (value == NULL || !*value || *value == '-') {
+ /* If the option value after the blank starts with '-' assume
+ * it is another option. */
+ print_err(vcmdarg_err, exit_failure,
+ "Option \"%s\" requires a value.\n", *ptr);
+ option = NULL;
+ extra = 0;
+ ret += 1;
+ }
+ }
+ else if (value[0] == '=') {
+ /* value is after equals */
+ value += 1;
+ extra = 0;
+ }
+ else {
+ /* Option isn't known. */
+ ptr += 1;
+ count -= 1;
+ continue;
+ }
+
+ if (option == opt_level) {
+ if (ws_log_set_level_str(value) == LOG_LEVEL_NONE) {
+ print_err(vcmdarg_err, exit_failure,
+ "Invalid log level \"%s\".\n", value);
+ ret += 1;
+ }
+ }
+ else if (option == opt_domain) {
+ ws_log_set_domain_filter(value);
+ }
+ else if (option == opt_fatal_domain) {
+ ws_log_set_fatal_domain_filter(value);
+ }
+ else if (option == opt_file) {
+ if (value == NULL) {
+ print_err(vcmdarg_err, exit_failure,
+ "Option '%s' requires an argument.\n",
+ option);
+ ret += 1;
+ }
+ else {
+ FILE *fp = ws_fopen(value, "w");
+ if (fp == NULL) {
+ print_err(vcmdarg_err, exit_failure,
+ "Error opening file '%s' for writing: %s.\n",
+ value, g_strerror(errno));
+ ret += 1;
+ }
+ else {
+ ws_log_add_custom_file(fp);
+ }
+ }
+ }
+ else if (option == opt_fatal) {
+ if (ws_log_set_fatal_level_str(value) == LOG_LEVEL_NONE) {
+ print_err(vcmdarg_err, exit_failure,
+ "Fatal log level must be \"critical\" or "
+ "\"warning\", not \"%s\".\n", value);
+ ret += 1;
+ }
+ }
+ else if (option == opt_debug) {
+ ws_log_set_debug_filter(value);
+ }
+ else if (option == opt_noisy) {
+ ws_log_set_noisy_filter(value);
+ }
+ else {
+ /* Option value missing or invalid, do nothing. */
+ }
+
+ /*
+ * We found a log option. We will remove it from
+ * the argv by moving up the other strings in the array. This is
+ * so that it doesn't generate an unrecognized option
+ * error further along in the initialization process.
+ */
+ /* Include the terminating NULL in the memmove. */
+ memmove(ptr, ptr + 1 + extra, (count - extra) * sizeof(*ptr));
+ /* No need to increment ptr here. */
+ count -= (1 + extra);
+ *argc_ptr -= (1 + extra);
+ }
+
+ return ret;
+}
+
+
+static void free_log_filter(log_filter_t **filter_ptr)
+{
+ if (filter_ptr == NULL || *filter_ptr == NULL)
+ return;
+ g_strfreev((*filter_ptr)->domainv);
+ g_free(*filter_ptr);
+ *filter_ptr = NULL;
+}
+
+
+static void tokenize_filter_str(log_filter_t **filter_ptr,
+ const char *str_filter,
+ enum ws_log_level min_level)
+{
+ const char *sep = ",;";
+ bool negated = false;
+ log_filter_t *filter;
+
+ ASSERT(filter_ptr);
+ ASSERT(*filter_ptr == NULL);
+
+ if (str_filter == NULL)
+ return;
+
+ if (str_filter[0] == '!') {
+ negated = true;
+ str_filter += 1;
+ }
+ if (*str_filter == '\0')
+ return;
+
+ filter = g_new(log_filter_t, 1);
+ filter->domainv = g_strsplit_set(str_filter, sep, -1);
+ filter->positive = !negated;
+ filter->min_level = min_level;
+ *filter_ptr = filter;
+}
+
+
+void ws_log_set_domain_filter(const char *str_filter)
+{
+ free_log_filter(&domain_filter);
+ tokenize_filter_str(&domain_filter, str_filter, LOG_LEVEL_NONE);
+}
+
+
+void ws_log_set_fatal_domain_filter(const char *str_filter)
+{
+ free_log_filter(&fatal_filter);
+ tokenize_filter_str(&fatal_filter, str_filter, LOG_LEVEL_NONE);
+}
+
+
+void ws_log_set_debug_filter(const char *str_filter)
+{
+ free_log_filter(&debug_filter);
+ tokenize_filter_str(&debug_filter, str_filter, LOG_LEVEL_DEBUG);
+}
+
+
+void ws_log_set_noisy_filter(const char *str_filter)
+{
+ free_log_filter(&noisy_filter);
+ tokenize_filter_str(&noisy_filter, str_filter, LOG_LEVEL_NOISY);
+}
+
+
+enum ws_log_level ws_log_set_fatal_level(enum ws_log_level level)
+{
+ if (level <= LOG_LEVEL_NONE || level >= _LOG_LEVEL_LAST)
+ return LOG_LEVEL_NONE;
+ if (level > LOG_LEVEL_ERROR)
+ level = LOG_LEVEL_ERROR;
+ if (level < LOG_LEVEL_WARNING)
+ level = LOG_LEVEL_WARNING;
+
+ fatal_log_level = level;
+ return fatal_log_level;
+}
+
+
+enum ws_log_level ws_log_set_fatal_level_str(const char *str_level)
+{
+ enum ws_log_level level;
+
+ level = string_to_log_level(str_level);
+ return ws_log_set_fatal_level(level);
+}
+
+
+void ws_log_set_writer(ws_log_writer_cb *writer)
+{
+ if (registered_log_writer_data_free)
+ registered_log_writer_data_free(registered_log_writer_data);
+
+ registered_log_writer = writer;
+ registered_log_writer_data = NULL;
+ registered_log_writer_data_free = NULL;
+}
+
+
+void ws_log_set_writer_with_data(ws_log_writer_cb *writer,
+ void *user_data,
+ ws_log_writer_free_data_cb *free_user_data)
+{
+ if (registered_log_writer_data_free)
+ registered_log_writer_data_free(registered_log_writer_data);
+
+ registered_log_writer = writer;
+ registered_log_writer_data = user_data;
+ registered_log_writer_data_free = free_user_data;
+}
+
+
+static void glib_log_handler(const char *domain, GLogLevelFlags flags,
+ const char *message, void * user_data _U_)
+{
+ enum ws_log_level level;
+
+ /*
+ * The highest priority bit in the mask defines the level. We
+ * ignore the GLib fatal log level mask and use our own fatal
+ * log level setting instead.
+ */
+
+ if (flags & G_LOG_LEVEL_ERROR)
+ level = LOG_LEVEL_ERROR;
+ else if (flags & G_LOG_LEVEL_CRITICAL)
+ level = LOG_LEVEL_CRITICAL;
+ else if (flags & G_LOG_LEVEL_WARNING)
+ level = LOG_LEVEL_WARNING;
+ else if (flags & G_LOG_LEVEL_MESSAGE)
+ level = LOG_LEVEL_MESSAGE;
+ else if (flags & G_LOG_LEVEL_INFO)
+ level = LOG_LEVEL_INFO;
+ else if (flags & G_LOG_LEVEL_DEBUG)
+ level = LOG_LEVEL_DEBUG;
+ else
+ level = LOG_LEVEL_NONE; /* Should not happen. */
+
+ ws_log(domain, level, "%s", message);
+}
+
+
+#ifdef _WIN32
+static void load_registry(void)
+{
+ LONG lResult;
+ DWORD ptype;
+ DWORD data;
+ DWORD data_size = sizeof(DWORD);
+
+ lResult = RegGetValueA(HKEY_CURRENT_USER,
+ "Software\\Wireshark",
+ LOG_HKCU_CONSOLE_OPEN,
+ RRF_RT_REG_DWORD,
+ &ptype,
+ &data,
+ &data_size);
+ if (lResult != ERROR_SUCCESS || ptype != REG_DWORD) {
+ return;
+ }
+
+ ws_log_console_open = (ws_log_console_open_pref)data;
+}
+#endif
+
+
+/*
+ * We can't write to stderr in ws_log_init() because dumpcap uses stderr
+ * to communicate with the parent and it will block. We have to use
+ * vcmdarg_err to report errors.
+ */
+void ws_log_init(const char *progname,
+ void (*vcmdarg_err)(const char *, va_list ap))
+{
+ const char *env;
+ int fd;
+
+ if (progname != NULL) {
+ registered_progname = progname;
+ g_set_prgname(progname);
+ }
+
+ ws_tzset();
+
+ current_log_level = DEFAULT_LOG_LEVEL;
+
+ if ((fd = fileno(stdout)) >= 0)
+ stdout_color_enabled = g_log_writer_supports_color(fd);
+ if ((fd = fileno(stderr)) >= 0)
+ stderr_color_enabled = g_log_writer_supports_color(fd);
+
+ /* Set the GLib log handler for the default domain. */
+ g_log_set_handler(NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL,
+ glib_log_handler, NULL);
+
+ /* Set the GLib log handler for GLib itself. */
+ g_log_set_handler("GLib", G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL,
+ glib_log_handler, NULL);
+
+#ifdef _WIN32
+ load_registry();
+
+ /* if the user wants a console to be always there, well, we should open one for him */
+ if (ws_log_console_open == LOG_CONSOLE_OPEN_ALWAYS) {
+ create_console();
+ }
+#endif
+
+ atexit(ws_log_cleanup);
+
+ /* Configure from environment. */
+
+ env = g_getenv(ENV_VAR_LEVEL);
+ if (env != NULL) {
+ if (ws_log_set_level_str(env) == LOG_LEVEL_NONE) {
+ print_err(vcmdarg_err, LOG_ARGS_NOEXIT,
+ "Ignoring invalid environment value %s=\"%s\"",
+ ENV_VAR_LEVEL, env);
+ }
+ }
+
+ env = g_getenv(ENV_VAR_FATAL);
+ if (env != NULL) {
+ if (ws_log_set_fatal_level_str(env) == LOG_LEVEL_NONE) {
+ print_err(vcmdarg_err, LOG_ARGS_NOEXIT,
+ "Ignoring invalid environment value %s=\"%s\"",
+ ENV_VAR_FATAL, env);
+ }
+ }
+
+ /* Alias "domain" and "domains". The plural form wins. */
+ if ((env = g_getenv(ENV_VAR_DOMAIN_S)) != NULL)
+ ws_log_set_domain_filter(env);
+ else if ((env = g_getenv(ENV_VAR_DOMAIN)) != NULL)
+ ws_log_set_domain_filter(env);
+
+ /* Alias "domain" and "domains". The plural form wins. */
+ if ((env = g_getenv(ENV_VAR_FATAL_DOMAIN_S)) != NULL)
+ ws_log_set_fatal_domain_filter(env);
+ else if ((env = g_getenv(ENV_VAR_FATAL_DOMAIN)) != NULL)
+ ws_log_set_fatal_domain_filter(env);
+
+ env = g_getenv(ENV_VAR_DEBUG);
+ if (env != NULL)
+ ws_log_set_debug_filter(env);
+
+ env = g_getenv(ENV_VAR_NOISY);
+ if (env != NULL)
+ ws_log_set_noisy_filter(env);
+
+#ifdef WS_DEBUG
+ init_complete = true;
+#endif
+}
+
+
+void ws_log_init_with_writer(const char *progname,
+ ws_log_writer_cb *writer,
+ void (*vcmdarg_err)(const char *, va_list ap))
+{
+ registered_log_writer = writer;
+ ws_log_init(progname, vcmdarg_err);
+}
+
+
+void ws_log_init_with_writer_and_data(const char *progname,
+ ws_log_writer_cb *writer,
+ void *user_data,
+ ws_log_writer_free_data_cb *free_user_data,
+ void (*vcmdarg_err)(const char *, va_list ap))
+{
+ registered_log_writer_data = user_data;
+ registered_log_writer_data_free = free_user_data;
+ ws_log_init_with_writer(progname, writer, vcmdarg_err);
+}
+
+
+#define MAGENTA "\033[35m"
+#define BLUE "\033[34m"
+#define CYAN "\033[36m"
+#define GREEN "\033[32m"
+#define YELLOW "\033[33m"
+#define RED "\033[31m"
+#define RESET "\033[0m"
+
+static inline const char *level_color_on(bool enable, enum ws_log_level level)
+{
+ if (!enable)
+ return "";
+
+ switch (level) {
+ case LOG_LEVEL_NOISY:
+ case LOG_LEVEL_DEBUG:
+ return GREEN;
+ case LOG_LEVEL_INFO:
+ case LOG_LEVEL_MESSAGE:
+ return CYAN;
+ case LOG_LEVEL_WARNING:
+ return YELLOW;
+ case LOG_LEVEL_CRITICAL:
+ return MAGENTA;
+ case LOG_LEVEL_ERROR:
+ return RED;
+ case LOG_LEVEL_ECHO:
+ return YELLOW;
+ default:
+ break;
+ }
+ return "";
+}
+
+static inline const char *color_off(bool enable)
+{
+ return enable ? RESET : "";
+}
+
+#define NANOSECS_IN_MICROSEC 1000
+
+/*
+ * We must not call anything that might log a message
+ * in the log handler context (GLib might log a message if we register
+ * our own handler for the GLib domain).
+ */
+static void log_write_do_work(FILE *fp, bool use_color,
+ struct tm *when, long nanosecs, intmax_t pid,
+ const char *domain, enum ws_log_level level,
+ const char *file, long line, const char *func,
+ const char *user_format, va_list user_ap)
+{
+ fputs(" **", fp);
+
+#ifdef WS_DEBUG
+ if (!init_complete)
+ fputs(" no init!", fp);
+#endif
+
+ /* Process */
+ fprintf(fp, " (%s:%"PRIdMAX")", registered_progname, pid);
+
+ /* Timestamp */
+ if (when != NULL) {
+ fprintf(fp, " %02d:%02d:%02d",
+ when->tm_hour, when->tm_min, when->tm_sec);
+ if (nanosecs >= 0) {
+ fprintf(fp, ".%06ld", nanosecs / NANOSECS_IN_MICROSEC);
+ }
+ }
+
+ /* Domain/level */
+ fprintf(fp, " [%s %s%s%s]", domain_to_string(domain),
+ level_color_on(use_color, level),
+ ws_log_level_to_string(level),
+ color_off(use_color));
+
+ /* File/line */
+ if (file != NULL) {
+ fprintf(fp, " %s", file);
+ if (line >= 0) {
+ fprintf(fp, ":%ld", line);
+ }
+ }
+
+ /* Any formatting changes here need to be synced with ui/capture.c:capture_input_closed. */
+ fputs(" --", fp);
+
+ /* Function name */
+ if (func != NULL)
+ fprintf(fp, " %s():", func);
+
+ /* User message */
+ fputc(' ', fp);
+ vfprintf(fp, user_format, user_ap);
+ fputc('\n', fp);
+ fflush(fp);
+}
+
+
+static inline FILE *console_file(enum ws_log_level level)
+{
+ if (level <= LOG_LEVEL_INFO && stdout_logging_enabled)
+ return stdout;
+ return stderr;
+}
+
+
+static inline bool console_color_enabled(enum ws_log_level level)
+{
+ if (level <= LOG_LEVEL_INFO && stdout_logging_enabled)
+ return stdout_color_enabled;
+ return stderr_color_enabled;
+}
+
+
+static void log_write_fatal_msg(FILE *fp, intmax_t pid, const char *msg)
+{
+ /* Process */
+ fprintf(fp, " ** (%s:%"PRIdMAX") %s", registered_progname, pid, msg);
+}
+
+
+/*
+ * We must not call anything that might log a message
+ * in the log handler context (GLib might log a message if we register
+ * our own handler for the GLib domain).
+ */
+static void log_write_dispatch(const char *domain, enum ws_log_level level,
+ const char *file, long line, const char *func,
+ ws_log_manifest_t *mft,
+ const char *user_format, va_list user_ap)
+{
+ bool fatal_event = false;
+ const char *fatal_msg = NULL;
+ va_list user_ap_copy;
+
+ if (level >= fatal_log_level && level != LOG_LEVEL_ECHO) {
+ fatal_event = true;
+ fatal_msg = "Aborting on fatal log level exception\n";
+ }
+ else if (fatal_filter != NULL) {
+ if (filter_contains(fatal_filter, domain) && fatal_filter->positive) {
+ fatal_event = true;
+ fatal_msg = "Aborting on fatal log domain exception\n";
+ }
+ }
+
+#ifdef _WIN32
+ if (ws_log_console_open != LOG_CONSOLE_OPEN_NEVER) {
+ create_console();
+ }
+#endif /* _WIN32 */
+
+ if (custom_log) {
+ va_copy(user_ap_copy, user_ap);
+ log_write_do_work(custom_log, false,
+ &mft->tstamp_secs, mft->nanosecs, mft->pid,
+ domain, level, file, line, func,
+ user_format, user_ap_copy);
+ va_end(user_ap_copy);
+ if (fatal_msg) {
+ log_write_fatal_msg(custom_log, mft->pid, fatal_msg);
+ }
+ }
+
+ if (registered_log_writer) {
+ registered_log_writer(domain, level, file, line, func, fatal_msg, mft,
+ user_format, user_ap, registered_log_writer_data);
+ }
+ else {
+ log_write_do_work(console_file(level), console_color_enabled(level),
+ &mft->tstamp_secs, mft->nanosecs, mft->pid,
+ domain, level, file, line, func,
+ user_format, user_ap);
+ if (fatal_msg) {
+ log_write_fatal_msg(console_file(level), mft->pid, fatal_msg);
+ }
+ }
+
+#ifdef _WIN32
+ if (fatal_event && ws_log_console_open != LOG_CONSOLE_OPEN_NEVER) {
+ /* wait for a key press before the following error handler will terminate the program
+ this way the user at least can read the error message */
+ printf("\n\nPress any key to exit\n");
+ _getch();
+ }
+#endif /* _WIN32 */
+
+ if (fatal_event) {
+ abort();
+ }
+}
+
+
+void ws_logv(const char *domain, enum ws_log_level level,
+ const char *format, va_list ap)
+{
+ ws_log_manifest_t mft;
+ if (!msg_is_active(domain, level, &mft))
+ return;
+
+ log_write_dispatch(domain, level, NULL, -1, NULL, &mft, format, ap);
+}
+
+
+void ws_logv_full(const char *domain, enum ws_log_level level,
+ const char *file, long line, const char *func,
+ const char *format, va_list ap)
+{
+ ws_log_manifest_t mft;
+ if (!msg_is_active(domain, level, &mft))
+ return;
+
+ log_write_dispatch(domain, level, file, line, func, &mft, format, ap);
+}
+
+
+void ws_log(const char *domain, enum ws_log_level level,
+ const char *format, ...)
+{
+ ws_log_manifest_t mft;
+ if (!msg_is_active(domain, level, &mft))
+ return;
+
+ va_list ap;
+
+ va_start(ap, format);
+ log_write_dispatch(domain, level, NULL, -1, NULL, &mft, format, ap);
+ va_end(ap);
+}
+
+
+void ws_log_full(const char *domain, enum ws_log_level level,
+ const char *file, long line, const char *func,
+ const char *format, ...)
+{
+ ws_log_manifest_t mft;
+ if (!msg_is_active(domain, level, &mft))
+ return;
+
+ va_list ap;
+
+ va_start(ap, format);
+ log_write_dispatch(domain, level, file, line, func, &mft, format, ap);
+ va_end(ap);
+}
+
+
+void ws_log_fatal_full(const char *domain, enum ws_log_level level,
+ const char *file, long line, const char *func,
+ const char *format, ...)
+{
+ ws_log_manifest_t mft;
+ va_list ap;
+
+ fill_manifest(&mft);
+ va_start(ap, format);
+ log_write_dispatch(domain, level, file, line, func, &mft, format, ap);
+ va_end(ap);
+ abort();
+}
+
+
+void ws_log_write_always_full(const char *domain, enum ws_log_level level,
+ const char *file, long line, const char *func,
+ const char *format, ...)
+{
+ ws_log_manifest_t mft;
+ va_list ap;
+
+ fill_manifest(&mft);
+ va_start(ap, format);
+ log_write_dispatch(domain, level, file, line, func, &mft, format, ap);
+ va_end(ap);
+}
+
+
+static void
+append_trailer(const char *src, size_t src_length, wmem_strbuf_t *display, wmem_strbuf_t *underline)
+{
+ gunichar ch;
+ size_t hex_len;
+
+ while (src_length > 0) {
+ ch = g_utf8_get_char_validated(src, src_length);
+ if (ch == (gunichar)-1 || ch == (gunichar)-2) {
+ wmem_strbuf_append_hex(display, *src);
+ wmem_strbuf_append_c_count(underline, '^', 4);
+ src += 1;
+ src_length -= 1;
+ }
+ else {
+ if (g_unichar_isprint(ch)) {
+ wmem_strbuf_append_unichar(display, ch);
+ wmem_strbuf_append_c_count(underline, ' ', 1);
+ }
+ else {
+ hex_len = wmem_strbuf_append_hex_unichar(display, ch);
+ wmem_strbuf_append_c_count(underline, ' ', hex_len);
+ }
+ const char *tmp = g_utf8_next_char(src);
+ src_length -= tmp - src;
+ src = tmp;
+ }
+ }
+}
+
+
+static char *
+make_utf8_display(const char *src, size_t src_length, size_t good_length)
+{
+ wmem_strbuf_t *display;
+ wmem_strbuf_t *underline;
+ gunichar ch;
+ size_t hex_len;
+
+ display = wmem_strbuf_create(NULL);
+ underline = wmem_strbuf_create(NULL);
+
+ for (const char *s = src; s < src + good_length; s = g_utf8_next_char(s)) {
+ ch = g_utf8_get_char(s);
+
+ if (g_unichar_isprint(ch)) {
+ wmem_strbuf_append_unichar(display, ch);
+ wmem_strbuf_append_c(underline, ' ');
+ }
+ else {
+ hex_len = wmem_strbuf_append_hex_unichar(display, ch);
+ wmem_strbuf_append_c_count(underline, ' ', hex_len);
+ }
+ }
+
+ append_trailer(&src[good_length], src_length - good_length, display, underline);
+
+ wmem_strbuf_append_c(display, '\n');
+ wmem_strbuf_append(display, underline->str);
+ wmem_strbuf_destroy(underline);
+
+ return wmem_strbuf_finalize(display);
+}
+
+
+void ws_log_utf8_full(const char *domain, enum ws_log_level level,
+ const char *file, long line, const char *func,
+ const char *string, ssize_t _length, const char *endptr)
+{
+ if (!ws_log_msg_is_active(domain, level))
+ return;
+
+ char *display;
+ size_t length;
+ size_t good_length;
+
+ if (_length < 0)
+ length = strlen(string);
+ else
+ length = _length;
+
+ if (endptr == NULL || endptr < string) {
+ /* Find the pointer to the first invalid byte. */
+ if (g_utf8_validate(string, length, &endptr)) {
+ /* Valid string - should not happen. */
+ return;
+ }
+ }
+ good_length = endptr - string;
+
+ display = make_utf8_display(string, length, good_length);
+
+ ws_log_write_always_full(domain, level, file, line, func,
+ "Invalid UTF-8 at address %p offset %zu (length = %zu):\n%s",
+ string, good_length, length, display);
+
+ g_free(display);
+}
+
+
+void ws_log_buffer_full(const char *domain, enum ws_log_level level,
+ const char *file, long line, const char *func,
+ const uint8_t *ptr, size_t size, size_t max_bytes_len,
+ const char *msg)
+{
+ if (!ws_log_msg_is_active(domain, level))
+ return;
+
+ char *bufstr = bytes_to_str_maxlen(NULL, ptr, size, max_bytes_len);
+
+ if (G_UNLIKELY(msg == NULL))
+ ws_log_write_always_full(domain, level, file, line, func,
+ "<buffer:%p>: %s (%zu bytes)",
+ ptr, bufstr, size);
+ else
+ ws_log_write_always_full(domain, level, file, line, func,
+ "%s: %s (%zu bytes)",
+ msg, bufstr, size);
+ wmem_free(NULL, bufstr);
+}
+
+
+void ws_log_file_writer(FILE *fp, const char *domain, enum ws_log_level level,
+ const char *file, long line, const char *func,
+ ws_log_manifest_t *mft,
+ const char *user_format, va_list user_ap)
+{
+ log_write_do_work(fp, false,
+ &mft->tstamp_secs, mft->nanosecs, mft->pid,
+ domain, level, file, line, func,
+ user_format, user_ap);
+}
+
+
+void ws_log_console_writer(const char *domain, enum ws_log_level level,
+ const char *file, long line, const char *func,
+ ws_log_manifest_t *mft,
+ const char *user_format, va_list user_ap)
+{
+ log_write_do_work(console_file(level), console_color_enabled(level),
+ &mft->tstamp_secs, mft->nanosecs, mft->pid,
+ domain, level, file, line, func,
+ user_format, user_ap);
+}
+
+
+WS_DLL_PUBLIC
+void ws_log_console_writer_set_use_stdout(bool use_stdout)
+{
+ stdout_logging_enabled = use_stdout;
+}
+
+
+static void ws_log_cleanup(void)
+{
+ if (registered_log_writer_data_free) {
+ registered_log_writer_data_free(registered_log_writer_data);
+ registered_log_writer_data = NULL;
+ }
+ if (custom_log) {
+ fclose(custom_log);
+ custom_log = NULL;
+ }
+ free_log_filter(&domain_filter);
+ free_log_filter(&debug_filter);
+ free_log_filter(&noisy_filter);
+ free_log_filter(&fatal_filter);
+}
+
+
+void ws_log_add_custom_file(FILE *fp)
+{
+ if (custom_log != NULL) {
+ fclose(custom_log);
+ }
+ custom_log = fp;
+}
+
+
+#define USAGE_LEVEL \
+ "sets the active log level (\"critical\", \"warning\", etc.)"
+
+#define USAGE_FATAL \
+ "sets level to abort the program (\"critical\" or \"warning\")"
+
+#define USAGE_DOMAINS \
+ "comma-separated list of the active log domains"
+
+#define USAGE_FATAL_DOMAINS \
+ "list of domains that cause the program to abort"
+
+#define USAGE_DEBUG \
+ "list of domains with \"debug\" level"
+
+#define USAGE_NOISY \
+ "list of domains with \"noisy\" level"
+
+#define USAGE_FILE \
+ "file to output messages to (in addition to stderr)"
+
+void ws_log_print_usage(FILE *fp)
+{
+ fprintf(fp, "Diagnostic output:\n");
+ fprintf(fp, " --log-level <level> " USAGE_LEVEL "\n");
+ fprintf(fp, " --log-fatal <level> " USAGE_FATAL "\n");
+ fprintf(fp, " --log-domains <[!]list> " USAGE_DOMAINS "\n");
+ fprintf(fp, " --log-fatal-domains <list>\n");
+ fprintf(fp, " " USAGE_FATAL_DOMAINS "\n");
+ fprintf(fp, " --log-debug <[!]list> " USAGE_DEBUG "\n");
+ fprintf(fp, " --log-noisy <[!]list> " USAGE_NOISY "\n");
+ fprintf(fp, " --log-file <path> " USAGE_FILE "\n");
+}
diff --git a/wsutil/wslog.h b/wsutil/wslog.h
new file mode 100644
index 00000000..8044ad10
--- /dev/null
+++ b/wsutil/wslog.h
@@ -0,0 +1,458 @@
+/** @file
+ *
+ * Copyright 2021, João Valverde <j@v6e.pt>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WSLOG_H__
+#define __WSLOG_H__
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <time.h>
+#include <glib.h>
+
+#include <ws_symbol_export.h>
+#include <ws_attributes.h>
+#include <ws_log_defs.h>
+#include <ws_posix_compat.h>
+
+#ifdef WS_LOG_DOMAIN
+#define _LOG_DOMAIN WS_LOG_DOMAIN
+#else
+#define _LOG_DOMAIN ""
+#endif
+
+#ifdef WS_DEBUG
+#define _LOG_DEBUG_ENABLED true
+#else
+#define _LOG_DEBUG_ENABLED false
+#endif
+
+/*
+ * Define the macro WS_LOG_DOMAIN *before* including this header,
+ * for example:
+ * #define WS_LOG_DOMAIN LOG_DOMAIN_MAIN
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * Console open preference is stored in the Windows registry.
+ * HKEY_CURRENT_USER\Software\Wireshark\ConsoleOpen
+ */
+#define LOG_HKCU_CONSOLE_OPEN "ConsoleOpen"
+
+typedef enum {
+ LOG_CONSOLE_OPEN_NEVER,
+ LOG_CONSOLE_OPEN_AUTO, /* On demand. */
+ LOG_CONSOLE_OPEN_ALWAYS, /* Open during startup. */
+} ws_log_console_open_pref;
+
+WSUTIL_EXPORT
+ws_log_console_open_pref ws_log_console_open;
+
+typedef struct {
+ struct tm tstamp_secs;
+ long nanosecs;
+ intmax_t pid;
+} ws_log_manifest_t;
+
+/** Callback for registering a log writer. */
+typedef void (ws_log_writer_cb)(const char *domain, enum ws_log_level level,
+ const char *file, long line, const char *func,
+ const char *fatal_msg, ws_log_manifest_t *mft,
+ const char *user_format, va_list user_ap,
+ void *user_data);
+
+
+/** Callback for freeing a user data pointer. */
+typedef void (ws_log_writer_free_data_cb)(void *user_data);
+
+
+WS_DLL_PUBLIC
+void ws_log_file_writer(FILE *fp, const char *domain, enum ws_log_level level,
+ const char *file, long line, const char *func,
+ ws_log_manifest_t *mft,
+ const char *user_format, va_list user_ap);
+
+
+WS_DLL_PUBLIC
+void ws_log_console_writer(const char *domain, enum ws_log_level level,
+ const char *file, long line, const char *func,
+ ws_log_manifest_t *mft,
+ const char *user_format, va_list user_ap);
+
+
+/** Configure log levels "info" and below to use stdout.
+ *
+ * Normally all log messages are written to stderr. For backward compatibility
+ * with GLib calling this function with true configures log levels "info",
+ * "debug" and "noisy" to be written to stdout.
+ */
+WS_DLL_PUBLIC
+void ws_log_console_writer_set_use_stdout(bool use_stdout);
+
+
+/** Convert a numerical level to its string representation. */
+WS_DLL_PUBLIC
+WS_RETNONNULL
+const char *ws_log_level_to_string(enum ws_log_level level);
+
+
+/** Checks if a domain and level combination generate output.
+ *
+ * Returns true if a message will be printed for the domain/level combo.
+ */
+WS_DLL_PUBLIC
+bool ws_log_msg_is_active(const char *domain, enum ws_log_level level);
+
+
+/** Return the currently active log level. */
+WS_DLL_PUBLIC
+enum ws_log_level ws_log_get_level(void);
+
+
+/** Set the active log level. Returns the active level or LOG_LEVEL_NONE
+ * if level is invalid. */
+WS_DLL_PUBLIC
+enum ws_log_level ws_log_set_level(enum ws_log_level level);
+
+
+/** Set the active log level from a string.
+ *
+ * String levels are "error", "critical", "warning", "message", "info",
+ * "debug" and "noisy" (case insensitive).
+ * Returns the new log level or LOG_LEVEL NONE if the string representation
+ * is invalid.
+ */
+WS_DLL_PUBLIC
+enum ws_log_level ws_log_set_level_str(const char *str_level);
+
+
+/** Set a domain filter from a string.
+ *
+ * Domain filter is a case insensitive list separated by ',' or ';'. Only
+ * the domains in the filter will generate output; the others will be muted.
+ * Filter expressions can be preceded by '!' to invert the sense of the match.
+ * In this case only non-matching domains will generate output.
+ */
+WS_DLL_PUBLIC
+void ws_log_set_domain_filter(const char *domain_filter);
+
+/** Set a fatal domain filter from a string.
+ *
+ * Domain filter is a case insensitive list separated by ',' or ';'. Domains
+ * in the filter will cause the program to abort.
+ */
+WS_DLL_PUBLIC
+void ws_log_set_fatal_domain_filter(const char *domain_filter);
+
+
+/** Set a debug filter from a string.
+ *
+ * A debug filter lists all domains that should have debug level output turned
+ * on, regardless of the global log level and domain filter. If negated
+ * then debug (and below) will be disabled and the others unaffected by
+ * the filter.
+ */
+WS_DLL_PUBLIC
+void ws_log_set_debug_filter(const char *str_filter);
+
+
+/** Set a noisy filter from a string.
+ *
+ * Same as ws_log_set_debug_filter() for "noisy" level.
+ */
+WS_DLL_PUBLIC
+void ws_log_set_noisy_filter(const char *str_filter);
+
+
+/** Set the fatal log level.
+ *
+ * Sets the log level at which calls to ws_log() will abort the program. The
+ * argument can be LOG_LEVEL_ERROR, LOG_LEVEL_CRITICAL or LOG_LEVEL_WARNING.
+ * Level LOG_LEVEL_ERROR is always fatal.
+ */
+WS_DLL_PUBLIC
+enum ws_log_level ws_log_set_fatal_level(enum ws_log_level level);
+
+
+/** Set the fatal log level from a string.
+ *
+ * Same as ws_log_set_fatal(), but accepts the strings "error", critical" or
+ * "warning" instead as arguments.
+ */
+WS_DLL_PUBLIC
+enum ws_log_level ws_log_set_fatal_level_str(const char *str_level);
+
+
+/** Set the active log writer.
+ *
+ * The parameter 'writer' can be NULL to use the default writer.
+ */
+WS_DLL_PUBLIC
+void ws_log_set_writer(ws_log_writer_cb *writer);
+
+
+/** Set the active log writer.
+ *
+ * The parameter 'writer' can be NULL to use the default writer.
+ * Accepts an extra user_data parameter that will be passed to
+ * the log writer.
+ */
+WS_DLL_PUBLIC
+void ws_log_set_writer_with_data(ws_log_writer_cb *writer,
+ void *user_data,
+ ws_log_writer_free_data_cb *free_user_data);
+
+
+#define LOG_ARGS_NOEXIT -1
+
+/** Parses the command line arguments for log options.
+ *
+ * Returns zero for no error, non-zero for one or more invalid options.
+ */
+WS_DLL_PUBLIC
+int ws_log_parse_args(int *argc_ptr, char *argv[],
+ void (*vcmdarg_err)(const char *, va_list ap),
+ int exit_failure);
+
+
+/** Initializes the logging code.
+ *
+ * Must be called at startup before using the log API. If provided
+ * vcmdarg_err is used to print initialization errors. This usually means
+ * a misconfigured environment variable.
+ */
+WS_DLL_PUBLIC
+void ws_log_init(const char *progname,
+ void (*vcmdarg_err)(const char *, va_list ap));
+
+
+/** Initializes the logging code.
+ *
+ * Can be used instead of wslog_init(). Takes an extra writer argument.
+ * If provided this callback will be used instead of the default writer.
+ */
+WS_DLL_PUBLIC
+void ws_log_init_with_writer(const char *progname,
+ ws_log_writer_cb *writer,
+ void (*vcmdarg_err)(const char *, va_list ap));
+
+
+/** Initializes the logging code.
+ *
+ * Accepts a user data pointer in addition to the writer. This pointer will
+ * be provided to the writer with every invocation. If provided
+ * free_user_data will be called during cleanup.
+ */
+WS_DLL_PUBLIC
+void ws_log_init_with_writer_and_data(const char *progname,
+ ws_log_writer_cb *writer,
+ void *user_data,
+ ws_log_writer_free_data_cb *free_user_data,
+ void (*vcmdarg_err)(const char *, va_list ap));
+
+
+/** This function is called to output a message to the log.
+ *
+ * Takes a format string and a variable number of arguments.
+ */
+WS_DLL_PUBLIC
+void ws_log(const char *domain, enum ws_log_level level,
+ const char *format, ...) G_GNUC_PRINTF(3,4);
+
+
+/** This function is called to output a message to the log.
+ *
+ * Takes a format string and a 'va_list'.
+ */
+WS_DLL_PUBLIC
+void ws_logv(const char *domain, enum ws_log_level level,
+ const char *format, va_list ap);
+
+
+/** This function is called to output a message to the log.
+ *
+ * In addition to the message this function accepts file/line/function
+ * information.
+ */
+WS_DLL_PUBLIC
+void ws_log_full(const char *domain, enum ws_log_level level,
+ const char *file, long line, const char *func,
+ const char *format, ...) G_GNUC_PRINTF(6,7);
+
+
+/** This function is called to output a message to the log.
+ *
+ * In addition to the message this function accepts file/line/function
+ * information.
+ */
+WS_DLL_PUBLIC
+void ws_logv_full(const char *domain, enum ws_log_level level,
+ const char *file, long line, const char *func,
+ const char *format, va_list ap);
+
+
+WS_DLL_PUBLIC
+WS_NORETURN
+void ws_log_fatal_full(const char *domain, enum ws_log_level level,
+ const char *file, long line, const char *func,
+ const char *format, ...) G_GNUC_PRINTF(6,7);
+
+
+/*
+ * The if condition avoids -Wunused warnings for variables used only with
+ * WS_DEBUG, typically inside a ws_debug() call. The compiler will
+ * optimize away the dead execution branch.
+ */
+#define _LOG_IF_ACTIVE(active, level, file, line, func, ...) \
+ do { \
+ if (active) { \
+ ws_log_full(_LOG_DOMAIN, level, \
+ file, line, func, \
+ __VA_ARGS__); \
+ } \
+ } while (0)
+
+#define _LOG_FULL(active, level, ...) \
+ _LOG_IF_ACTIVE(active, level, __FILE__, __LINE__, __func__, __VA_ARGS__)
+
+#define _LOG_SIMPLE(active, level, ...) \
+ _LOG_IF_ACTIVE(active, level, NULL, -1, NULL, __VA_ARGS__)
+
+/** Logs with "error" level.
+ *
+ * Accepts a format string and includes the file and function name.
+ *
+ * "error" is always fatal and terminates the program with a coredump.
+ */
+#define ws_error(...) \
+ ws_log_fatal_full(_LOG_DOMAIN, LOG_LEVEL_ERROR, \
+ __FILE__, __LINE__, __func__, __VA_ARGS__)
+
+/** Logs with "critical" level.
+ *
+ * Accepts a format string and includes the file and function name.
+ */
+#define ws_critical(...) \
+ _LOG_FULL(true, LOG_LEVEL_CRITICAL, __VA_ARGS__)
+
+/** Logs with "warning" level.
+ *
+ * Accepts a format string and includes the file and function name.
+ */
+#define ws_warning(...) \
+ _LOG_FULL(true, LOG_LEVEL_WARNING, __VA_ARGS__)
+
+/** Logs with "message" level.
+ *
+ * Accepts a format string and *does not* include the file and function
+ * name. This is the default log level.
+ */
+#define ws_message(...) \
+ _LOG_SIMPLE(true, LOG_LEVEL_MESSAGE, __VA_ARGS__)
+
+/** Logs with "info" level.
+ *
+ * Accepts a format string and includes the file and function name.
+ */
+#define ws_info(...) \
+ _LOG_FULL(true, LOG_LEVEL_INFO, __VA_ARGS__)
+
+/** Logs with "debug" level.
+ *
+ * Accepts a format string and includes the file and function name.
+ */
+#define ws_debug(...) \
+ _LOG_FULL(_LOG_DEBUG_ENABLED, LOG_LEVEL_DEBUG, __VA_ARGS__)
+
+/** Logs with "noisy" level.
+ *
+ * Accepts a format string and includes the file and function name.
+ */
+#define ws_noisy(...) \
+ _LOG_FULL(_LOG_DEBUG_ENABLED, LOG_LEVEL_NOISY, __VA_ARGS__)
+
+/** Used for temporary debug print outs, always active. */
+#define WS_DEBUG_HERE(...) \
+ _LOG_FULL(true, LOG_LEVEL_ECHO, __VA_ARGS__)
+
+
+WS_DLL_PUBLIC
+void ws_log_utf8_full(const char *domain, enum ws_log_level level,
+ const char *file, long line, const char *func,
+ const char *string, ssize_t length, const char *endptr);
+
+
+#define ws_log_utf8(str, len, endptr) \
+ do { \
+ if (_LOG_DEBUG_ENABLED) { \
+ ws_log_utf8_full(LOG_DOMAIN_UTF_8, LOG_LEVEL_DEBUG, \
+ __FILE__, __LINE__, __func__, \
+ str, len, endptr); \
+ } \
+ } while (0)
+
+
+/** This function is called to log a buffer (bytes array).
+ *
+ * Accepts an optional 'msg' argument to provide a description.
+ */
+WS_DLL_PUBLIC
+void ws_log_buffer_full(const char *domain, enum ws_log_level level,
+ const char *file, long line, const char *func,
+ const uint8_t *buffer, size_t size,
+ size_t max_bytes_len, const char *msg);
+
+
+#define ws_log_buffer(buf, size) \
+ do { \
+ if (_LOG_DEBUG_ENABLED) { \
+ ws_log_buffer_full(_LOG_DOMAIN, LOG_LEVEL_DEBUG, \
+ __FILE__, __LINE__, __func__, \
+ buf, size, 36, #buf); \
+ } \
+ } while (0)
+
+
+/** Auxiliary function to write custom logging functions.
+ *
+ * This function is the same as ws_log_full() but does not perform any
+ * domain/level filtering to avoid a useless double activation check.
+ * It should only be used in conjunction with a pre-check using
+ * ws_log_msg_is_active().
+ */
+WS_DLL_PUBLIC
+void ws_log_write_always_full(const char *domain, enum ws_log_level level,
+ const char *file, long line, const char *func,
+ const char *format, ...) G_GNUC_PRINTF(6,7);
+
+
+/** Define an auxiliary file pointer where messages should be written.
+ *
+ * This file, if set, functions in addition to the registered or
+ * default log writer.
+ */
+WS_DLL_PUBLIC
+void ws_log_add_custom_file(FILE *fp);
+
+
+WS_DLL_PUBLIC
+void ws_log_print_usage(FILE *fp);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WSLOG_H__ */
diff --git a/wsutil/xtea.c b/wsutil/xtea.c
new file mode 100644
index 00000000..b98de5f5
--- /dev/null
+++ b/wsutil/xtea.c
@@ -0,0 +1,70 @@
+/* xtea.c
+ * Implementation of XTEA cipher
+ * By Ahmad Fatoum <ahmad[AT]a3f.at>
+ * Copyright 2017 Ahmad Fatoum
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "xtea.h"
+
+#include <string.h>
+
+#include "pint.h"
+
+void decrypt_xtea_ecb(uint8_t plaintext[8], const uint8_t ciphertext[8], const uint32_t key[4], unsigned num_rounds)
+{
+ unsigned i;
+ uint32_t v[2], delta = 0x9E3779B9, sum = delta * num_rounds;
+
+ v[0] = pntoh32(&ciphertext[0]);
+ v[1] = pntoh32(&ciphertext[4]);
+
+ for (i = 0; i < num_rounds; i++) {
+ v[1] -= (((v[0] << 4) ^ (v[0] >> 5)) + v[0]) ^ (sum + key[(sum >> 11) & 3]);
+ sum -= delta;
+ v[0] -= (((v[1] << 4) ^ (v[1] >> 5)) + v[1]) ^ (sum + key[sum & 3]);
+ }
+
+ v[0] = GUINT32_TO_BE(v[0]);
+ v[1] = GUINT32_TO_BE(v[1]);
+
+ memcpy(plaintext, v, sizeof v);
+}
+
+void decrypt_xtea_le_ecb(uint8_t plaintext[8], const uint8_t ciphertext[8], const uint32_t key[4], unsigned num_rounds)
+{
+ unsigned i;
+ uint32_t v[2], delta = 0x9E3779B9, sum = delta * num_rounds;
+
+ v[0] = pletoh32(&ciphertext[0]);
+ v[1] = pletoh32(&ciphertext[4]);
+
+ for (i = 0; i < num_rounds; i++) {
+ v[1] -= (((v[0] << 4) ^ (v[0] >> 5)) + v[0]) ^ (sum + key[(sum >> 11) & 3]);
+ sum -= delta;
+ v[0] -= (((v[1] << 4) ^ (v[1] >> 5)) + v[1]) ^ (sum + key[sum & 3]);
+ }
+
+ v[0] = GUINT32_TO_LE(v[0]);
+ v[1] = GUINT32_TO_LE(v[1]);
+
+ memcpy(plaintext, v, sizeof v);
+}
+
+/*
+ * 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/wsutil/xtea.h b/wsutil/xtea.h
new file mode 100644
index 00000000..e166160c
--- /dev/null
+++ b/wsutil/xtea.h
@@ -0,0 +1,39 @@
+/** @file
+ *
+ * Implementation of XTEA cipher
+ * By Ahmad Fatoum <ahmad[AT]a3f.at>
+ * Copyright 2017 Ahmad Fatoum
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __XTEA_H__
+#define __XTEA_H__
+
+/* Actual XTEA is big-endian, nevertheless there exist protocols that treat every block
+ * as little endian, so we provide both
+ */
+#include "wireshark.h"
+
+WS_DLL_PUBLIC void decrypt_xtea_ecb(uint8_t plaintext[8], const uint8_t ciphertext[8], const uint32_t key[4], unsigned num_rounds);
+
+WS_DLL_PUBLIC void decrypt_xtea_le_ecb(uint8_t plaintext[8], const uint8_t ciphertext[8], const uint32_t key[4], unsigned num_rounds);
+
+#endif /* __XTEA_H__ */
+
+/*
+ * 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:
+ */