summaryrefslogtreecommitdiffstats
path: root/ui/qt/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--ui/qt/main.cpp1149
1 files changed, 1149 insertions, 0 deletions
diff --git a/ui/qt/main.cpp b/ui/qt/main.cpp
new file mode 100644
index 00000000..582855f2
--- /dev/null
+++ b/ui/qt/main.cpp
@@ -0,0 +1,1149 @@
+/* main.cpp
+ *
+ * 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 <glib.h>
+
+#include <locale.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#include <tchar.h>
+#include <wchar.h>
+#include <shellapi.h>
+#include <wsutil/console_win32.h>
+#endif
+
+#include <ws_exit_codes.h>
+#include <wsutil/clopts_common.h>
+#include <wsutil/cmdarg_err.h>
+#include <ui/urls.h>
+#include <wsutil/time_util.h>
+#include <wsutil/filesystem.h>
+#include <wsutil/privileges.h>
+#include <wsutil/socket.h>
+#include <wsutil/wslog.h>
+#ifdef HAVE_PLUGINS
+#include <wsutil/plugins.h>
+#endif
+#include <wsutil/report_message.h>
+#include <wsutil/please_report_bug.h>
+#include <wsutil/unicode-utils.h>
+#include <wsutil/version_info.h>
+
+#include <epan/addr_resolv.h>
+#include <epan/ex-opt.h>
+#include <epan/tap.h>
+#include <epan/stat_tap_ui.h>
+#include <epan/column.h>
+#include <epan/disabled_protos.h>
+#include <epan/prefs.h>
+
+#ifdef HAVE_KERBEROS
+#include <epan/packet.h>
+#include <epan/asn1.h>
+#include <epan/dissectors/packet-kerberos.h>
+#endif
+
+#include <wsutil/codecs.h>
+
+#include <extcap.h>
+
+/* general (not Qt specific) */
+#include "file.h"
+#include "epan/color_filters.h"
+
+#include "epan/rtd_table.h"
+#include "epan/srt_table.h"
+
+#include "ui/alert_box.h"
+#include "ui/iface_lists.h"
+#include "ui/language.h"
+#include "ui/persfilepath_opt.h"
+#include "ui/recent.h"
+#include "ui/simple_dialog.h"
+#include "ui/util.h"
+#include "ui/dissect_opts.h"
+#include "ui/commandline.h"
+#include "ui/capture_ui_utils.h"
+#include "ui/preference_utils.h"
+#include "ui/software_update.h"
+#include "ui/taps.h"
+
+#include "ui/qt/conversation_dialog.h"
+#include "ui/qt/utils/color_utils.h"
+#include "ui/qt/coloring_rules_dialog.h"
+#include "ui/qt/endpoint_dialog.h"
+#include "ui/qt/glib_mainloop_on_qeventloop.h"
+#include "ui/qt/wireshark_main_window.h"
+#include "ui/qt/response_time_delay_dialog.h"
+#include "ui/qt/service_response_time_dialog.h"
+#include "ui/qt/simple_dialog.h"
+#include "ui/qt/simple_statistics_dialog.h"
+#include <ui/qt/widgets/splash_overlay.h>
+#include "ui/qt/wireshark_application.h"
+
+#include "capture/capture-pcap-util.h"
+
+#include <QMessageBox>
+#include <QScreen>
+
+#ifdef _WIN32
+# include "capture/capture-wpcap.h"
+# include <wsutil/file_util.h>
+#endif /* _WIN32 */
+
+#ifdef HAVE_AIRPCAP
+# include <capture/airpcap.h>
+# include <capture/airpcap_loader.h>
+//# include "airpcap_dlg.h"
+//# include "airpcap_gui_utils.h"
+#endif
+
+#include "epan/crypt/dot11decrypt_ws.h"
+
+/* Handle the addition of View menu items without request */
+#if defined(Q_OS_MAC)
+#include <ui/macosx/cocoa_bridge.h>
+#endif
+
+#include <ui/qt/utils/qt_ui_utils.h>
+
+//#define DEBUG_STARTUP_TIME 1
+
+/* update the main window */
+void main_window_update(void)
+{
+ WiresharkApplication::processEvents();
+}
+
+void exit_application(int status) {
+ if (wsApp) {
+ wsApp->quit();
+ }
+ exit(status);
+}
+
+/*
+ * Report an error in command-line arguments.
+ *
+ * On Windows, Wireshark is built for the Windows subsystem, and runs
+ * without a console, so we create a console on Windows to receive the
+ * output.
+ *
+ * See create_console(), in ui/win32/console_win32.c, for an example
+ * of code to check whether we need to create a console.
+ *
+ * On UN*Xes:
+ *
+ * If Wireshark is run from the command line, its output either goes
+ * to the terminal or to wherever the standard error was redirected.
+ *
+ * If Wireshark is run by executing it as a remote command, e.g. with
+ * ssh, its output either goes to whatever socket was set up for the
+ * remote command's standard error or to wherever the standard error
+ * was redirected.
+ *
+ * If Wireshark was run from the GUI, e.g. by double-clicking on its
+ * icon or on a file that it opens, there are no guarantees as to
+ * where the standard error went. It could be going to /dev/null
+ * (current macOS), or to a socket to systemd for the journal, or
+ * to a log file in the user's home directory, or to the "console
+ * device" ("workstation console"), or....
+ *
+ * Part of determining that, at least for locally-run Wireshark,
+ * is to try to open /dev/tty to determine whether the process
+ * has a controlling terminal. (It fails, at a minimum, for
+ * Wireshark launched from the GUI under macOS, Ubuntu with GNOME,
+ * and Ubuntu with KDE; in all cases, an attempt to open /dev/tty
+ * fails with ENXIO.) If it does have a controlling terminal,
+ * write to the standard error, otherwise assume that the standard
+ * error might not go anywhere that the user will be able to see.
+ * That doesn't handle the "run by ssh" case, however; that will
+ * not have a controlling terminal. (This means running it by
+ * remote execution, not by remote login.) Perhaps there's an
+ * environment variable to check there.
+ */
+// xxx copied from ../gtk/main.c
+static void
+wireshark_cmdarg_err(const char *fmt, va_list ap)
+{
+#ifdef _WIN32
+ create_console();
+#endif
+ fprintf(stderr, "wireshark: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+}
+
+/*
+ * Report additional information for an error in command-line arguments.
+ * Creates a console on Windows.
+ */
+// xxx copied from ../gtk/main.c
+static void
+wireshark_cmdarg_err_cont(const char *fmt, va_list ap)
+{
+#ifdef _WIN32
+ create_console();
+#endif
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+}
+
+void
+gather_wireshark_qt_compiled_info(feature_list l)
+{
+#ifdef QT_VERSION
+ with_feature(l, "Qt %s", QT_VERSION_STR);
+#else
+ with_feature(l, "Qt (version unknown)");
+#endif
+ gather_caplibs_compile_info(l);
+ epan_gather_compile_info(l);
+#ifdef QT_MULTIMEDIA_LIB
+ with_feature(l, "QtMultimedia");
+#else
+ without_feature(l, "QtMultimedia");
+#endif
+
+ const char *update_info = software_update_info();
+ if (update_info) {
+ with_feature(l, "automatic updates using %s", update_info);
+ } else {
+ without_feature(l, "automatic updates");
+ }
+#ifdef _WIN32
+#ifdef HAVE_AIRPCAP
+ gather_airpcap_compile_info(l);
+#else
+ without_feature(l, "AirPcap");
+#endif
+#endif /* _WIN32 */
+
+#ifdef HAVE_MINIZIP
+ with_feature(l, "Minizip");
+#else
+ without_feature(l, "Minizip");
+#endif
+}
+
+void
+gather_wireshark_runtime_info(feature_list l)
+{
+ with_feature(l, "Qt %s", qVersion());
+#ifdef HAVE_LIBPCAP
+ gather_caplibs_runtime_info(l);
+#endif
+ epan_gather_runtime_info(l);
+
+#ifdef HAVE_AIRPCAP
+ gather_airpcap_runtime_info(l);
+#endif
+
+ if (mainApp) {
+ // Display information
+ const char *display_mode = ColorUtils::themeIsDark() ? "dark" : "light";
+ with_feature(l, "%s display mode", display_mode);
+
+ int hidpi_count = 0;
+ foreach (QScreen *screen, mainApp->screens()) {
+ if (screen->devicePixelRatio() > 1.0) {
+ hidpi_count++;
+ }
+ }
+ if (hidpi_count == mainApp->screens().count()) {
+ with_feature(l, "HiDPI");
+ } else if (hidpi_count) {
+ with_feature(l, "mixed DPI");
+ } else {
+ without_feature(l, "HiDPI");
+ }
+ QString session = qEnvironmentVariable("XDG_SESSION_TYPE");
+ if (!session.isEmpty()) {
+ if (session == "wayland") {
+ with_feature(l, "Wayland");
+ } else if (session == "x11") {
+ with_feature(l, "Xorg");
+ } else {
+ with_feature(l, "XDG_SESSION_TYPE=%s", qUtf8Printable(session));
+ }
+ }
+ QString platform = qApp->platformName();
+ if (!platform.isEmpty()) {
+ with_feature(l, "QPA plugin \"%s\"", qUtf8Printable(platform));
+ }
+ }
+}
+
+static void
+qt_log_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
+{
+ enum ws_log_level log_level = LOG_LEVEL_NONE;
+
+ // QMessageLogContext may contain null/zero for release builds.
+ const char *file = context.file;
+ int line = context.line > 0 ? context.line : -1;
+ const char *func = context.function;
+
+ switch (type) {
+ case QtInfoMsg:
+ log_level = LOG_LEVEL_INFO;
+ // Omit the file/line/function for this level.
+ file = nullptr;
+ line = -1;
+ func = nullptr;
+ break;
+ case QtWarningMsg:
+ log_level = LOG_LEVEL_WARNING;
+ break;
+ case QtCriticalMsg:
+ log_level = LOG_LEVEL_CRITICAL;
+ break;
+ case QtFatalMsg:
+ log_level = LOG_LEVEL_ERROR;
+ break;
+ // We want qDebug() messages to show up always for temporary print-outs.
+ case QtDebugMsg:
+ log_level = LOG_LEVEL_ECHO;
+ break;
+ }
+
+ // Qt gives the full method declaration as the function. Our convention
+ // (following the C/C++ standards) is to display only the function name.
+ // Hack the name into the message as a workaround to avoid formatting
+ // issues.
+ if (func != nullptr) {
+ ws_log_full(LOG_DOMAIN_QTUI, log_level, file, line, nullptr,
+ "%s -- %s", func, qUtf8Printable(msg));
+ }
+ else {
+ ws_log_full(LOG_DOMAIN_QTUI, log_level, file, line, nullptr,
+ "%s", qUtf8Printable(msg));
+ }
+}
+
+#ifdef HAVE_LIBPCAP
+/* Check if there's something important to tell the user during startup.
+ * We want to do this *after* showing the main window so that any windows
+ * we pop up will be above the main window.
+ */
+static void
+check_and_warn_user_startup()
+{
+ gchar *cur_user, *cur_group;
+
+ /* Tell the user not to run as root. */
+ if (running_with_special_privs() && recent.privs_warn_if_elevated) {
+ cur_user = get_cur_username();
+ cur_group = get_cur_groupname();
+ simple_message_box(ESD_TYPE_WARN, &recent.privs_warn_if_elevated,
+ "Running as user \"%s\" and group \"%s\".\n"
+ "This could be dangerous.\n\n"
+ "If you're running Wireshark this way in order to perform live capture, "
+ "you may want to be aware that there is a better way documented at\n"
+ WS_WIKI_URL("CaptureSetup/CapturePrivileges"), cur_user, cur_group);
+ g_free(cur_user);
+ g_free(cur_group);
+ }
+}
+#endif
+
+#if defined(_WIN32) && !defined(__MINGW32__)
+// Try to avoid library search path collisions. QCoreApplication will
+// search QT_INSTALL_PREFIX/plugins for platform DLLs before searching
+// the application directory. If
+//
+// - You have Qt version 5.x.y installed in the default location
+// (C:\Qt\5.x) on your machine.
+//
+// and
+//
+// - You install Wireshark that was built on a machine with Qt version
+// 5.x.z installed in the default location.
+//
+// Qt5Core.dll will load qwindows.dll from your local C:\Qt\5.x\...\plugins
+// directory. This may not be compatible with qwindows.dll from that
+// same path on the build machine. At any rate, loading DLLs from paths
+// you don't control is ill-advised. We work around this by removing every
+// path except our application directory.
+//
+// NOTE: This does not apply to MinGW-w64 using MSYS2. In that case we use
+// the system's Qt plugins with the default search paths.
+//
+// NOTE 2: When using MinGW-w64 without MSYS2 we also search for the system DLLS,
+// because our installer might not have deployed them.
+
+static inline void
+win32_reset_library_path(void)
+{
+ QString app_path = QDir(get_progfile_dir()).path();
+ foreach (QString path, QCoreApplication::libraryPaths()) {
+ QCoreApplication::removeLibraryPath(path);
+ }
+ QCoreApplication::addLibraryPath(app_path);
+}
+#endif
+
+#ifdef Q_OS_MAC
+// Try to work around
+//
+// https://gitlab.com/wireshark/wireshark/-/issues/17075
+//
+// aka
+//
+// https://bugreports.qt.io/browse/QTBUG-87014
+//
+// The fix at
+//
+// https://codereview.qt-project.org/c/qt/qtbase/+/322228/3/src/plugins/platforms/cocoa/qnsview_drawing.mm
+//
+// enables layer backing if we're running on Big Sur OR we're running on
+// Catalina AND we were built with the Catalina SDK. Enable layer backing
+// here by setting QT_MAC_WANTS_LAYER=1, but only if we're running on Big
+// Sur and our version of Qt doesn't have a fix for QTBUG-87014.
+#include <QOperatingSystemVersion>
+static inline void
+macos_enable_layer_backing(void)
+{
+ // At the time of this writing, the QTBUG-87014 for layerEnabledByMacOS is...
+ //
+ // ...in https://github.com/qt/qtbase/blob/5.12/src/plugins/platforms/cocoa/qnsview_drawing.mm
+ // ...not in https://github.com/qt/qtbase/blob/5.12.10/src/plugins/platforms/cocoa/qnsview_drawing.mm
+ // ...in https://github.com/qt/qtbase/blob/5.15/src/plugins/platforms/cocoa/qnsview_drawing.mm
+ // ...not in https://github.com/qt/qtbase/blob/5.15.2/src/plugins/platforms/cocoa/qnsview_drawing.mm
+ // ...not in https://github.com/qt/qtbase/blob/6.0/src/plugins/platforms/cocoa/qnsview_drawing.mm
+ // ...not in https://github.com/qt/qtbase/blob/6.0.0/src/plugins/platforms/cocoa/qnsview_drawing.mm
+ //
+ // We'll assume that it will be fixed in 5.12.11, 5.15.3, and 6.0.1.
+ // Note that we only ship LTS versions of Qt with our macOS packages.
+ // Feel free to add other versions if needed.
+#if \
+ (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) && QT_VERSION < QT_VERSION_CHECK(5, 12, 11) \
+ || (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) && QT_VERSION < QT_VERSION_CHECK(5, 15, 3)) \
+ || (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) && QT_VERSION < QT_VERSION_CHECK(6, 0, 1)) \
+ )
+ QOperatingSystemVersion os_ver = QOperatingSystemVersion::current();
+ int major_ver = os_ver.majorVersion();
+ int minor_ver = os_ver.minorVersion();
+ if ( (major_ver == 10 && minor_ver >= 16) || major_ver >= 11 ) {
+ if (qgetenv("QT_MAC_WANTS_LAYER").isEmpty()) {
+ qputenv("QT_MAC_WANTS_LAYER", "1");
+ }
+ }
+#endif
+}
+#endif
+
+#ifdef HAVE_LIBPCAP
+static GList *
+capture_opts_get_interface_list(int *err, char **err_str)
+{
+ /*
+ * XXX - should this pass an update callback?
+ * We already have a window up by the time we start parsing
+ * the majority of the command-line arguments, because
+ * we need to do a bunch of initialization work before
+ * parsing those arguments, and we want to let the user
+ * know that we're doing that initialization, given that
+ * it can take a while.
+ */
+ return capture_interface_list(err, err_str, NULL);
+}
+#endif
+
+/* And now our feature presentation... [ fade to music ] */
+int main(int argc, char *qt_argv[])
+{
+ WiresharkMainWindow *main_w;
+
+#ifdef _WIN32
+ LPWSTR *wc_argv;
+ int wc_argc;
+#endif
+ int ret_val = EXIT_SUCCESS;
+ char **argv = qt_argv;
+
+ char *rf_path;
+ int rf_open_errno;
+#ifdef HAVE_LIBPCAP
+ gchar *err_str, *err_str_secondary;;
+#else
+#ifdef _WIN32
+#ifdef HAVE_AIRPCAP
+ gchar *err_str;
+#endif
+#endif
+#endif
+ gchar *err_msg = NULL;
+ df_error_t *df_err = NULL;
+
+ QString dfilter, read_filter;
+#ifdef HAVE_LIBPCAP
+ int caps_queries = 0;
+#endif
+ /* Start time in microseconds */
+ guint64 start_time = g_get_monotonic_time();
+ static const struct report_message_routines wireshark_report_routines = {
+ vfailure_alert_box,
+ vwarning_alert_box,
+ open_failure_alert_box,
+ read_failure_alert_box,
+ write_failure_alert_box,
+ cfile_open_failure_alert_box,
+ cfile_dump_open_failure_alert_box,
+ cfile_read_failure_alert_box,
+ cfile_write_failure_alert_box,
+ cfile_close_failure_alert_box
+ };
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ /*
+ * See:
+ *
+ * issue #16908;
+ *
+ * https://doc.qt.io/qt-5/qvector.html#maximum-size-and-out-of-memory-conditions
+ *
+ * https://forum.qt.io/topic/114950/qvector-realloc-throwing-sigsegv-when-very-large-surface3d-is-rendered
+ *
+ * for why we're doing this; the widget we use for the packet list
+ * uses QVector, so those limitations apply to it.
+ *
+ * Apparently, this will be fixed in Qt 6:
+ *
+ * https://github.com/qt/qtbase/commit/215ca735341b9487826023a7983382851ce8bf26
+ *
+ * https://github.com/qt/qtbase/commit/2a6cdec718934ca2cc7f6f9c616ebe62f6912123#diff-724f419b0bb0487c2629bb16cf534c4b268ddcee89b5177189b607f940cfd83dR192
+ *
+ * Hopefully QList won't cause any performance hits relative to
+ * QVector.
+ *
+ * We pick 53 million records as a value that should avoid the problem;
+ * see the Wireshark issue for why that value was chosen.
+ */
+ cf_set_max_records(53000000);
+#endif
+
+#ifdef Q_OS_MAC
+ macos_enable_layer_backing();
+#endif
+
+ cmdarg_err_init(wireshark_cmdarg_err, wireshark_cmdarg_err_cont);
+
+ /* Initialize log handler early so we can have proper logging during startup. */
+ ws_log_init("wireshark", vcmdarg_err);
+ /* For backward compatibility with GLib logging and Wireshark 3.4. */
+ ws_log_console_writer_set_use_stdout(TRUE);
+
+ qInstallMessageHandler(qt_log_message_handler);
+
+#ifdef _WIN32
+ restore_pipes();
+#endif
+
+#ifdef DEBUG_STARTUP_TIME
+ prefs.gui_console_open = console_open_always;
+#endif /* DEBUG_STARTUP_TIME */
+
+#if defined(Q_OS_MAC)
+ /* Disable automatic addition of tab menu entries in view menu */
+ CocoaBridge::cleanOSGeneratedMenuItems();
+#endif
+
+ /*
+ * Set the C-language locale to the native environment and set the
+ * code page to UTF-8 on Windows.
+ */
+#ifdef _WIN32
+ setlocale(LC_ALL, ".UTF-8");
+#else
+ setlocale(LC_ALL, "");
+#endif
+
+ ws_tzset();
+
+#ifdef _WIN32
+ //
+ // On Windows, QCoreApplication has its own WinMain(), which gets the
+ // command line using GetCommandLineW(), breaks it into individual
+ // arguments using CommandLineToArgvW(), and then "helpfully"
+ // converts those UTF-16LE arguments into strings in the local code
+ // page.
+ //
+ // We don't want that, because not all file names can be represented
+ // in the local code page, so we do the same, but we convert the
+ // strings into UTF-8.
+ //
+ wc_argv = CommandLineToArgvW(GetCommandLineW(), &wc_argc);
+ if (wc_argv) {
+ argc = wc_argc;
+ argv = arg_list_utf_16to8(wc_argc, wc_argv);
+ LocalFree(wc_argv);
+ } /* XXX else bail because something is horribly, horribly wrong? */
+
+ create_app_running_mutex();
+#endif /* _WIN32 */
+
+ /* Early logging command-line initialization. */
+ ws_log_parse_args(&argc, argv, vcmdarg_err, WS_EXIT_INVALID_OPTION);
+ ws_noisy("Finished log init and parsing command line log arguments");
+
+ /*
+ * Get credential information for later use, and drop privileges
+ * before doing anything else.
+ * Let the user know if anything happened.
+ */
+ init_process_policies();
+ relinquish_special_privs_perm();
+
+ /*
+ * Attempt to get the pathname of the directory containing the
+ * executable file.
+ */
+ /* configuration_init_error = */ configuration_init(argv[0], NULL);
+ /* ws_log(NULL, LOG_LEVEL_DEBUG, "progfile_dir: %s", get_progfile_dir()); */
+
+#ifdef _WIN32
+ ws_init_dll_search_path();
+ /* Load wpcap if possible. Do this before collecting the run-time version information */
+ load_wpcap();
+
+#ifdef HAVE_AIRPCAP
+ /* Load the airpcap.dll. This must also be done before collecting
+ * run-time version information. */
+ load_airpcap();
+#if 0
+ airpcap_dll_ret_val = load_airpcap();
+
+ switch (airpcap_dll_ret_val) {
+ case AIRPCAP_DLL_OK:
+ /* load the airpcap interfaces */
+ g_airpcap_if_list = get_airpcap_interface_list(&err, &err_str);
+
+ if (g_airpcap_if_list == NULL || g_list_length(g_airpcap_if_list) == 0) {
+ if (err == CANT_GET_AIRPCAP_INTERFACE_LIST && err_str != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", "Failed to open Airpcap Adapters.");
+ g_free(err_str);
+ }
+ airpcap_if_active = NULL;
+
+ } else {
+
+ /* select the first as default (THIS SHOULD BE CHANGED) */
+ airpcap_if_active = airpcap_get_default_if(airpcap_if_list);
+ }
+ break;
+ /*
+ * XXX - Maybe we need to warn the user if one of the following happens???
+ */
+ case AIRPCAP_DLL_OLD:
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s","AIRPCAP_DLL_OLD\n");
+ break;
+
+ case AIRPCAP_DLL_ERROR:
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s","AIRPCAP_DLL_ERROR\n");
+ break;
+
+ case AIRPCAP_DLL_NOT_FOUND:
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s","AIRPCAP_DDL_NOT_FOUND\n");
+ break;
+ }
+#endif
+#endif /* HAVE_AIRPCAP */
+#endif /* _WIN32 */
+
+ /* Get the compile-time version information string */
+ ws_init_version_info("Wireshark", gather_wireshark_qt_compiled_info,
+ gather_wireshark_runtime_info);
+
+ init_report_message("Wireshark", &wireshark_report_routines);
+
+ /* Create the user profiles directory */
+ if (create_profiles_dir(&rf_path) == -1) {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "Could not create profiles directory\n\"%s\": %s.",
+ rf_path, g_strerror(errno));
+ g_free (rf_path);
+ }
+
+ profile_store_persconffiles(TRUE);
+ recent_init();
+
+ /* Read the profile independent recent file. We have to do this here so we can */
+ /* set the profile before it can be set from the command line parameter */
+ if (!recent_read_static(&rf_path, &rf_open_errno)) {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "Could not open common recent file\n\"%s\": %s.",
+ rf_path, g_strerror(rf_open_errno));
+ g_free(rf_path);
+ }
+
+ commandline_early_options(argc, argv);
+
+#if defined(_WIN32) && !defined(__MINGW32__)
+ win32_reset_library_path();
+#endif
+
+ // Handle DPI scaling on Windows. This causes problems in at least
+ // one case on X11 and we don't yet support Android.
+ // We do the equivalent on macOS by setting NSHighResolutionCapable
+ // in Info.plist.
+ // Note that this enables Windows 8.1-style Per-monitor DPI
+ // awareness but not Windows 10-style Per-monitor v2 awareness.
+ // https://doc.qt.io/qt-5/scalability.html
+ // https://doc.qt.io/qt-5/highdpi.html
+ // https://bugreports.qt.io/browse/QTBUG-53022 - The device pixel ratio is pretty much bogus on Windows.
+ // https://bugreports.qt.io/browse/QTBUG-55510 - Windows have wrong size
+ //
+ // Deprecated in Qt6.
+ // warning: 'Qt::AA_EnableHighDpiScaling' is deprecated: High-DPI scaling is always enabled.
+ // This attribute no longer has any effect.
+#if defined(Q_OS_WIN) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+#endif
+
+ /* Create The Wireshark app */
+ WiresharkApplication ws_app(argc, qt_argv);
+
+ // Default value is 400ms = "quickly typing" when searching in Preferences->Protocols
+ // 1000ms allows a more "hunt/peck" typing speed. 2000ms tested - too long.
+ QApplication::setKeyboardInputInterval(1000);
+
+ /* initialize the funnel mini-api */
+ // xxx qtshark
+ //initialize_funnel_ops();
+
+ Dot11DecryptInitContext(&dot11decrypt_ctx);
+
+ QString cf_name;
+ unsigned int in_file_type = WTAP_TYPE_AUTO;
+
+ err_msg = ws_init_sockets();
+ if (err_msg != NULL)
+ {
+ cmdarg_err("%s", err_msg);
+ g_free(err_msg);
+ cmdarg_err_cont("%s", please_report_bug());
+ ret_val = WS_EXIT_INIT_FAILED;
+ goto clean_exit;
+ }
+
+ /* Read the profile dependent (static part) of the recent file. */
+ /* Only the static part of it will be read, as we don't have the gui now to fill the */
+ /* recent lists which is done in the dynamic part. */
+ /* We have to do this already here, so command line parameters can overwrite these values. */
+ if (!recent_read_profile_static(&rf_path, &rf_open_errno)) {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "Could not open recent file\n\"%s\": %s.",
+ rf_path, g_strerror(rf_open_errno));
+ g_free(rf_path);
+ }
+ wsApp->applyCustomColorsFromRecent();
+
+ // Initialize our language
+ read_language_prefs();
+ wsApp->loadLanguage(language);
+
+ /* ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_DEBUG, "Translator %s", language); */
+
+ // Init the main window (and splash)
+ main_w = new(WiresharkMainWindow);
+ main_w->show();
+ // Setup GLib mainloop on Qt event loop to enable GLib and GIO watches
+ GLibMainloopOnQEventLoop::setup(main_w);
+ // We may not need a queued connection here but it would seem to make sense
+ // to force the issue.
+ main_w->connect(&ws_app, SIGNAL(openCaptureFile(QString,QString,unsigned int)),
+ main_w, SLOT(openCaptureFile(QString,QString,unsigned int)));
+ main_w->connect(&ws_app, &WiresharkApplication::openCaptureOptions,
+ main_w, &WiresharkMainWindow::showCaptureOptionsDialog);
+
+ /*
+ * If we have a saved "last directory in which a file was opened"
+ * in the recent file, set it as the one for the app.
+ *
+ * (do this after the path settings are processed)
+ */
+ if (recent.gui_fileopen_remembered_dir &&
+ test_for_directory(recent.gui_fileopen_remembered_dir) == EISDIR) {
+ set_last_open_dir(recent.gui_fileopen_remembered_dir);
+ }
+
+#ifdef DEBUG_STARTUP_TIME
+ ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "set_console_log_handler, elapsed time %" PRIu64 " us \n", g_get_monotonic_time() - start_time);
+#endif
+
+#ifdef HAVE_LIBPCAP
+ /* Set the initial values in the capture options. This might be overwritten
+ by preference settings and then again by the command line parameters. */
+ capture_opts_init(&global_capture_opts, capture_opts_get_interface_list);
+#endif
+
+ /*
+ * Libwiretap must be initialized before libwireshark is, so that
+ * dissection-time handlers for file-type-dependent blocks can
+ * register using the file type/subtype value for the file type.
+ */
+ wtap_init(TRUE);
+
+ splash_update(RA_DISSECTORS, NULL, NULL);
+#ifdef DEBUG_STARTUP_TIME
+ ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "Calling epan init, elapsed time %" PRIu64 " us \n", g_get_monotonic_time() - start_time);
+#endif
+ /* Register all dissectors; we must do this before checking for the
+ "-G" flag, as the "-G" flag dumps information registered by the
+ dissectors, and we must do it before we read the preferences, in
+ case any dissectors register preferences. */
+ if (!epan_init(splash_update, NULL, TRUE)) {
+ SimpleDialog::displayQueuedMessages(main_w);
+ ret_val = WS_EXIT_INIT_FAILED;
+ goto clean_exit;
+ }
+#ifdef DEBUG_STARTUP_TIME
+ /* epan_init resets the preferences */
+ prefs.gui_console_open = console_open_always;
+ ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "epan done, elapsed time %" PRIu64 " us \n", g_get_monotonic_time() - start_time);
+#endif
+
+ /* Register all audio codecs. */
+ codecs_init();
+
+ // Read the dynamic part of the recent file. This determines whether or
+ // not the recent list appears in the main window so the earlier we can
+ // call this the better.
+ if (!recent_read_dynamic(&rf_path, &rf_open_errno)) {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "Could not open recent file\n\"%s\": %s.",
+ rf_path, g_strerror(rf_open_errno));
+ g_free(rf_path);
+ }
+ wsApp->refreshRecentCaptures();
+
+ splash_update(RA_LISTENERS, NULL, NULL);
+#ifdef DEBUG_STARTUP_TIME
+ ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "Register all tap listeners, elapsed time %" PRIu64 " us \n", g_get_monotonic_time() - start_time);
+#endif
+ /* Register all tap listeners; we do this before we parse the arguments,
+ as the "-z" argument can specify a registered tap. */
+
+ register_all_tap_listeners(tap_reg_listener);
+
+ conversation_table_set_gui_info(init_conversation_table);
+ endpoint_table_set_gui_info(init_endpoint_table);
+ srt_table_iterate_tables(register_service_response_tables, NULL);
+ rtd_table_iterate_tables(register_response_time_delay_tables, NULL);
+ stat_tap_iterate_tables(register_simple_stat_tables, NULL);
+
+ if (ex_opt_count("read_format") > 0) {
+ in_file_type = open_info_name_to_type(ex_opt_get_next("read_format"));
+ }
+
+#ifdef DEBUG_STARTUP_TIME
+ ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "Calling extcap_register_preferences, elapsed time %" PRIu64 " us \n", g_get_monotonic_time() - start_time);
+#endif
+ splash_update(RA_EXTCAP, NULL, NULL);
+ extcap_register_preferences();
+ splash_update(RA_PREFERENCES, NULL, NULL);
+#ifdef DEBUG_STARTUP_TIME
+ ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "Calling module preferences, elapsed time %" PRIu64 " us \n", g_get_monotonic_time() - start_time);
+#endif
+
+ /* Read the preferences, but don't apply them yet. */
+ global_commandline_info.prefs_p = ws_app.readConfigurationFiles(false);
+
+ /* Now let's see if any of preferences were overridden at the command
+ * line, and store them. We have to do this before applying the
+ * preferences to the capture options.
+ */
+ commandline_override_prefs(argc, argv, TRUE);
+
+ /* Some of the preferences affect the capture options. Apply those
+ * before getting the other command line arguments, which can also
+ * affect the capture options. The command line arguments should be
+ * applied last to take precedence (at least until the user saves
+ * preferences, or switches profiles.)
+ */
+ prefs_to_capture_opts();
+
+ /* Now get our remaining args */
+ commandline_other_options(argc, argv, TRUE);
+
+ /* Convert some command-line parameters to QStrings */
+ if (global_commandline_info.cf_name != NULL)
+ cf_name = QString(global_commandline_info.cf_name);
+ if (global_commandline_info.rfilter != NULL)
+ read_filter = QString(global_commandline_info.rfilter);
+ if (global_commandline_info.dfilter != NULL)
+ dfilter = QString(global_commandline_info.dfilter);
+
+ timestamp_set_type(recent.gui_time_format);
+ timestamp_set_precision(recent.gui_time_precision);
+ timestamp_set_seconds_type (recent.gui_seconds_format);
+
+#ifdef HAVE_LIBPCAP
+#ifdef DEBUG_STARTUP_TIME
+ ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "Calling fill_in_local_interfaces, elapsed time %" PRIu64 " us \n", g_get_monotonic_time() - start_time);
+#endif
+ splash_update(RA_INTERFACES, NULL, NULL);
+
+ if (!global_commandline_info.cf_name && !prefs.capture_no_interface_load)
+ fill_in_local_interfaces(main_window_update);
+
+ if (global_commandline_info.list_link_layer_types)
+ caps_queries |= CAPS_QUERY_LINK_TYPES;
+ if (global_commandline_info.list_timestamp_types)
+ caps_queries |= CAPS_QUERY_TIMESTAMP_TYPES;
+
+ if (global_commandline_info.start_capture || caps_queries) {
+ /* We're supposed to do a live capture or get a list of link-layer/timestamp
+ types for a live capture device; if the user didn't specify an
+ interface to use, pick a default. */
+ ret_val = capture_opts_default_iface_if_necessary(&global_capture_opts,
+ ((global_commandline_info.prefs_p->capture_device) && (*global_commandline_info.prefs_p->capture_device != '\0')) ? get_if_name(global_commandline_info.prefs_p->capture_device) : NULL);
+ if (ret_val != 0) {
+ goto clean_exit;
+ }
+ }
+
+ /*
+ * If requested, list the link layer types and/or time stamp types
+ * and exit.
+ */
+ if (caps_queries) {
+ guint i;
+
+#ifdef _WIN32
+ create_console();
+#endif /* _WIN32 */
+ /* Get the list of link-layer types for the capture devices. */
+ ret_val = EXIT_SUCCESS;
+ for (i = 0; i < global_capture_opts.ifaces->len; i++) {
+ interface_options *interface_opts;
+ if_capabilities_t *caps;
+ char *auth_str = NULL;
+
+ interface_opts = &g_array_index(global_capture_opts.ifaces, interface_options, i);
+#ifdef HAVE_PCAP_REMOTE
+ if (interface_opts->auth_type == CAPTURE_AUTH_PWD) {
+ auth_str = ws_strdup_printf("%s:%s", interface_opts->auth_username, interface_opts->auth_password);
+ }
+#endif
+ caps = capture_get_if_capabilities(interface_opts->name, interface_opts->monitor_mode,
+ auth_str, &err_str, &err_str_secondary, NULL);
+ g_free(auth_str);
+ if (caps == NULL) {
+ cmdarg_err("%s%s%s", err_str, err_str_secondary ? "\n" : "", err_str_secondary ? err_str_secondary : "");
+ g_free(err_str);
+ g_free(err_str_secondary);
+ ret_val = WS_EXIT_INVALID_CAPABILITY;
+ break;
+ }
+ ret_val = capture_opts_print_if_capabilities(caps, interface_opts,
+ caps_queries);
+ free_if_capabilities(caps);
+ if (ret_val != EXIT_SUCCESS) {
+ break;
+ }
+ }
+#ifdef _WIN32
+ destroy_console();
+#endif /* _WIN32 */
+ goto clean_exit;
+ }
+
+ capture_opts_trim_snaplen(&global_capture_opts, MIN_PACKET_SIZE);
+ capture_opts_trim_ring_num_files(&global_capture_opts);
+#endif /* HAVE_LIBPCAP */
+
+ /* Notify all registered modules that have had any of their preferences
+ changed either from one of the preferences file or from the command
+ line that their preferences have changed. */
+#ifdef DEBUG_STARTUP_TIME
+ ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "Calling prefs_apply_all, elapsed time %" PRIu64 " us \n", g_get_monotonic_time() - start_time);
+#endif
+ splash_update(RA_PREFERENCES_APPLY, NULL, NULL);
+ prefs_apply_all();
+ wsApp->emitAppSignal(WiresharkApplication::PreferencesChanged);
+
+#ifdef HAVE_LIBPCAP
+ if ((global_capture_opts.num_selected == 0) &&
+ (prefs.capture_device != NULL)) {
+ guint i;
+ interface_t *device;
+ for (i = 0; i < global_capture_opts.all_ifaces->len; i++) {
+ device = &g_array_index(global_capture_opts.all_ifaces, interface_t, i);
+ if (!device->hidden && strcmp(device->display_name, prefs.capture_device) == 0) {
+ device->selected = TRUE;
+ global_capture_opts.num_selected++;
+ break;
+ }
+ }
+ }
+#endif
+
+ /*
+ * Enabled and disabled protocols and heuristic dissectors as per
+ * command-line options.
+ */
+ if (!setup_enabled_and_disabled_protocols()) {
+ ret_val = WS_EXIT_INVALID_OPTION;
+ goto clean_exit;
+ }
+
+ build_column_format_array(&CaptureFile::globalCapFile()->cinfo, global_commandline_info.prefs_p->num_cols, TRUE);
+ wsApp->emitAppSignal(WiresharkApplication::ColumnsChanged); // We read "recent" widths above.
+ wsApp->emitAppSignal(WiresharkApplication::RecentPreferencesRead); // Must be emitted after PreferencesChanged.
+
+ wsApp->setMonospaceFont(prefs.gui_font_name);
+
+ /* For update of WindowTitle (When use gui.window_title preference) */
+ main_w->setWSWindowTitle();
+
+ if (!color_filters_init(&err_msg, color_filter_add_cb)) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_msg);
+ g_free(err_msg);
+ }
+
+ /* allSystemsGo() emits appInitialized(), which signals the WelcomePage to
+ * delete the splash overlay. However, it doesn't get redrawn until
+ * processEvents() is called. If we're opening a capture file that happens
+ * either when we finish reading the file or when the progress bar appears.
+ * It's probably better to leave the splash overlay up until that happens
+ * rather than showing the user the welcome page, so we don't call
+ * processEvents() here.
+ */
+ wsApp->allSystemsGo();
+ ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "Wireshark is up and ready to go, elapsed time %.3fs", (float) (g_get_monotonic_time() - start_time) / 1000000);
+ SimpleDialog::displayQueuedMessages(main_w);
+
+ /* User could specify filename, or display filter, or both */
+ if (!dfilter.isEmpty())
+ main_w->filterPackets(dfilter, false);
+ if (!cf_name.isEmpty()) {
+ if (main_w->openCaptureFile(cf_name, read_filter, in_file_type)) {
+
+ /* Open stat windows; we do so after creating the main window,
+ to avoid Qt warnings, and after successfully opening the
+ capture file, so we know we have something to compute stats
+ on, and after registering all dissectors, so that MATE will
+ have registered its field array and we can have a tap filter
+ with one of MATE's late-registered fields as part of the
+ filter. */
+ start_requested_stats();
+
+ if (global_commandline_info.go_to_packet != 0) {
+ /* Jump to the specified frame number, kept for backward
+ compatibility. */
+ cf_goto_frame(CaptureFile::globalCapFile(), global_commandline_info.go_to_packet);
+ } else if (global_commandline_info.jfilter != NULL) {
+ dfilter_t *jump_to_filter = NULL;
+ /* try to compile given filter */
+ if (!dfilter_compile(global_commandline_info.jfilter, &jump_to_filter, &df_err)) {
+ // Similar code in MainWindow::mergeCaptureFile().
+ QMessageBox::warning(main_w, QObject::tr("Invalid Display Filter"),
+ QObject::tr("The filter expression %1 isn't a valid display filter. (%2).")
+ .arg(global_commandline_info.jfilter, df_err->msg),
+ QMessageBox::Ok);
+ df_error_free(&df_err);
+ } else {
+ /* Filter ok, jump to the first packet matching the filter
+ conditions. Default search direction is forward, but if
+ option d was given, search backwards */
+ cf_find_packet_dfilter(CaptureFile::globalCapFile(), jump_to_filter, global_commandline_info.jump_backwards);
+ }
+ }
+ }
+ }
+#ifdef HAVE_LIBPCAP
+ else {
+ if (global_commandline_info.start_capture) {
+ if (global_capture_opts.save_file != NULL) {
+ /* Save the directory name for future file dialogs. */
+ /* (get_dirname overwrites filename) */
+ gchar *s = g_strdup(global_capture_opts.save_file);
+ set_last_open_dir(get_dirname(s));
+ g_free(s);
+ }
+ /* "-k" was specified; start a capture. */
+ check_and_warn_user_startup();
+
+ /* If no user interfaces were specified on the command line,
+ copy the list of selected interfaces to the set of interfaces
+ to use for this capture. */
+ if (global_capture_opts.ifaces->len == 0)
+ collect_ifaces(&global_capture_opts);
+ CaptureFile::globalCapFile()->window = main_w;
+ if (capture_start(&global_capture_opts, global_commandline_info.capture_comments,
+ main_w->captureSession(), main_w->captureInfoData(),
+ main_window_update)) {
+ /* The capture started. Open stat windows; we do so after creating
+ the main window, to avoid GTK warnings, and after successfully
+ opening the capture file, so we know we have something to compute
+ stats on, and after registering all dissectors, so that MATE will
+ have registered its field array and we can have a tap filter with
+ one of MATE's late-registered fields as part of the filter. */
+ start_requested_stats();
+ }
+ }
+ /* if the user didn't supply a capture filter, use the one to filter out remote connections like SSH */
+ if (!global_commandline_info.start_capture && !global_capture_opts.default_options.cfilter) {
+ global_capture_opts.default_options.cfilter = g_strdup(get_conn_cfilter());
+ }
+ }
+#endif /* HAVE_LIBPCAP */
+
+ // UAT and UI settings files used in configuration profiles which are used
+ // in Qt dialogs are not registered during startup because they only get
+ // loaded when the dialog is shown. Register them here.
+ profile_register_persconffile("io_graphs");
+ profile_register_persconffile("import_hexdump.json");
+
+ profile_store_persconffiles(FALSE);
+
+ // If the wsApp->exec() event loop exits cleanly, we call
+ // WiresharkApplication::cleanup().
+ ret_val = wsApp->exec();
+ wsApp = NULL;
+
+ // Many widgets assume that they always have valid epan data, so this
+ // must be called before epan_cleanup().
+ // XXX We need to clean up the Lua GUI here. We currently paper over
+ // this in FunnelStatistics::~FunnelStatistics, which leaks memory.
+ delete main_w;
+
+ recent_cleanup();
+ epan_cleanup();
+
+ extcap_cleanup();
+
+ Dot11DecryptDestroyContext(&dot11decrypt_ctx);
+
+ ws_cleanup_sockets();
+
+#ifdef _WIN32
+ /* For some unknown reason, the "atexit()" call in "create_console()"
+ doesn't arrange that "destroy_console()" be called when we exit,
+ so we call it here if a console was created. */
+ destroy_console();
+#endif /* _WIN32 */
+
+clean_exit:
+#ifdef HAVE_LIBPCAP
+ capture_opts_cleanup(&global_capture_opts);
+#endif
+ col_cleanup(&CaptureFile::globalCapFile()->cinfo);
+ codecs_cleanup();
+ wtap_cleanup();
+ free_progdirs();
+ commandline_options_free();
+ exit_application(ret_val);
+}