1 /* main.cpp
2  *
3  * Wireshark - Network traffic analyzer
4  * By Gerald Combs <gerald@wireshark.org>
5  * Copyright 1998 Gerald Combs
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  */
9 
10 #include <config.h>
11 
12 #include <glib.h>
13 
14 #include <locale.h>
15 
16 #ifdef _WIN32
17 #include <windows.h>
18 #include <tchar.h>
19 #include <wchar.h>
20 #include <shellapi.h>
21 #include "ui/win32/console_win32.h"
22 #endif
23 
24 #include <ui/clopts_common.h>
25 #include <ui/cmdarg_err.h>
26 #include <ui/exit_codes.h>
27 #include <ui/urls.h>
28 #include <wsutil/filesystem.h>
29 #include <wsutil/privileges.h>
30 #include <wsutil/socket.h>
31 #include <wsutil/wslog.h>
32 #ifdef HAVE_PLUGINS
33 #include <wsutil/plugins.h>
34 #endif
35 #include <wsutil/report_message.h>
36 #include <wsutil/please_report_bug.h>
37 #include <wsutil/unicode-utils.h>
38 #include <ui/version_info.h>
39 
40 #include <epan/addr_resolv.h>
41 #include <epan/ex-opt.h>
42 #include <epan/tap.h>
43 #include <epan/stat_tap_ui.h>
44 #include <epan/column.h>
45 #include <epan/disabled_protos.h>
46 #include <epan/prefs.h>
47 
48 #ifdef HAVE_KERBEROS
49 #include <epan/packet.h>
50 #include <epan/asn1.h>
51 #include <epan/dissectors/packet-kerberos.h>
52 #endif
53 
54 #include <wsutil/codecs.h>
55 
56 #include <extcap.h>
57 
58 /* general (not Qt specific) */
59 #include "file.h"
60 #include "epan/color_filters.h"
61 
62 #include "epan/rtd_table.h"
63 #include "epan/srt_table.h"
64 
65 #include "ui/alert_box.h"
66 #include "ui/console.h"
67 #include "ui/iface_lists.h"
68 #include "ui/language.h"
69 #include "ui/persfilepath_opt.h"
70 #include "ui/recent.h"
71 #include "ui/simple_dialog.h"
72 #include "ui/util.h"
73 #include "ui/dissect_opts.h"
74 #include "ui/commandline.h"
75 #include "ui/capture_ui_utils.h"
76 #include "ui/preference_utils.h"
77 #include "ui/software_update.h"
78 #include "ui/taps.h"
79 
80 #include "ui/qt/conversation_dialog.h"
81 #include "ui/qt/utils/color_utils.h"
82 #include "ui/qt/coloring_rules_dialog.h"
83 #include "ui/qt/endpoint_dialog.h"
84 #include "ui/qt/main_window.h"
85 #include "ui/qt/response_time_delay_dialog.h"
86 #include "ui/qt/service_response_time_dialog.h"
87 #include "ui/qt/simple_dialog.h"
88 #include "ui/qt/simple_statistics_dialog.h"
89 #include <ui/qt/widgets/splash_overlay.h>
90 #include "ui/qt/wireshark_application.h"
91 
92 #include "capture/capture-pcap-util.h"
93 
94 #include <QMessageBox>
95 #include <QScreen>
96 
97 #ifdef _WIN32
98 #  include "capture/capture-wpcap.h"
99 #  include <wsutil/file_util.h>
100 #endif /* _WIN32 */
101 
102 #ifdef HAVE_AIRPCAP
103 #  include <capture/airpcap.h>
104 #  include <capture/airpcap_loader.h>
105 //#  include "airpcap_dlg.h"
106 //#  include "airpcap_gui_utils.h"
107 #endif
108 
109 #include "epan/crypt/dot11decrypt_ws.h"
110 
111 /* Handle the addition of View menu items without request */
112 #if defined(Q_OS_MAC)
113 #include <ui/macosx/cocoa_bridge.h>
114 #endif
115 
116 #include <ui/qt/utils/qt_ui_utils.h>
117 
118 //#define DEBUG_STARTUP_TIME 1
119 
120 /* update the main window */
main_window_update(void)121 void main_window_update(void)
122 {
123     WiresharkApplication::processEvents();
124 }
125 
126 #ifdef HAVE_LIBPCAP
127 
128 /* quit the main window */
main_window_quit(void)129 void main_window_quit(void)
130 {
131     wsApp->quit();
132 }
133 
134 #endif /* HAVE_LIBPCAP */
135 
exit_application(int status)136 void exit_application(int status) {
137     if (wsApp) {
138         wsApp->quit();
139     }
140     exit(status);
141 }
142 
143 /*
144  * Report an error in command-line arguments.
145  *
146  * On Windows, Wireshark is built for the Windows subsystem, and runs
147  * without a console, so we create a console on Windows to receive the
148  * output.
149  *
150  * See create_console(), in ui/win32/console_win32.c, for an example
151  * of code to check whether we need to create a console.
152  *
153  * On UN*Xes:
154  *
155  *  If Wireshark is run from the command line, its output either goes
156  *  to the terminal or to wherever the standard error was redirected.
157  *
158  *  If Wireshark is run by executing it as a remote command, e.g. with
159  *  ssh, its output either goes to whatever socket was set up for the
160  *  remote command's standard error or to wherever the standard error
161  *  was redirected.
162  *
163  *  If Wireshark was run from the GUI, e.g. by double-clicking on its
164  *  icon or on a file that it opens, there are no guarantees as to
165  *  where the standard error went.  It could be going to /dev/null
166  *  (current macOS), or to a socket to systemd for the journal, or
167  *  to a log file in the user's home directory, or to the "console
168  *  device" ("workstation console"), or....
169  *
170  *  Part of determining that, at least for locally-run Wireshark,
171  *  is to try to open /dev/tty to determine whether the process
172  *  has a controlling terminal.  (It fails, at a minimum, for
173  *  Wireshark launched from the GUI under macOS, Ubuntu with GNOME,
174  *  and Ubuntu with KDE; in all cases, an attempt to open /dev/tty
175  *  fails with ENXIO.)  If it does have a controlling terminal,
176  *  write to the standard error, otherwise assume that the standard
177  *  error might not go anywhere that the user will be able to see.
178  *  That doesn't handle the "run by ssh" case, however; that will
179  *  not have a controlling terminal.  (This means running it by
180  *  remote execution, not by remote login.)  Perhaps there's an
181  *  environment variable to check there.
182  */
183 // xxx copied from ../gtk/main.c
184 static void
wireshark_cmdarg_err(const char * fmt,va_list ap)185 wireshark_cmdarg_err(const char *fmt, va_list ap)
186 {
187 #ifdef _WIN32
188     create_console();
189 #endif
190     fprintf(stderr, "wireshark: ");
191     vfprintf(stderr, fmt, ap);
192     fprintf(stderr, "\n");
193 }
194 
195 /*
196  * Report additional information for an error in command-line arguments.
197  * Creates a console on Windows.
198  */
199 // xxx copied from ../gtk/main.c
200 static void
wireshark_cmdarg_err_cont(const char * fmt,va_list ap)201 wireshark_cmdarg_err_cont(const char *fmt, va_list ap)
202 {
203 #ifdef _WIN32
204     create_console();
205 #endif
206     vfprintf(stderr, fmt, ap);
207     fprintf(stderr, "\n");
208 }
209 
210 // xxx based from ../gtk/main.c:get_gtk_compiled_info
211 void
get_wireshark_qt_compiled_info(GString * str)212 get_wireshark_qt_compiled_info(GString *str)
213 {
214     g_string_append(str, "with ");
215     g_string_append_printf(str,
216 #ifdef QT_VERSION
217                     "Qt %s", QT_VERSION_STR);
218 #else
219                     "Qt (version unknown)");
220 #endif
221 
222     /* Capture libraries */
223     g_string_append(str, ", ");
224     get_compiled_caplibs_version(str);
225 }
226 
227 // xxx copied from ../gtk/main.c
228 void
get_gui_compiled_info(GString * str)229 get_gui_compiled_info(GString *str)
230 {
231     epan_get_compiled_version_info(str);
232 
233     g_string_append(str, ", ");
234 #ifdef QT_MULTIMEDIA_LIB
235     g_string_append(str, "with QtMultimedia");
236 #else
237     g_string_append(str, "without QtMultimedia");
238 #endif
239 
240     g_string_append(str, ", ");
241     const char *update_info = software_update_info();
242     if (update_info) {
243         g_string_append_printf(str, "with automatic updates using %s", update_info);
244     } else {
245         g_string_append_printf(str, "without automatic updates");
246     }
247 
248 #ifdef _WIN32
249     g_string_append(str, ", ");
250 #ifdef HAVE_AIRPCAP
251     get_compiled_airpcap_version(str);
252 #else
253     g_string_append(str, "without AirPcap");
254 #endif
255 #endif /* _WIN32 */
256 
257 #ifdef HAVE_SPEEXDSP
258     g_string_append(str, ", with SpeexDSP (using system library)");
259 #else
260     g_string_append(str, ", with SpeexDSP (using bundled resampler)");
261 #endif
262 
263 #ifdef HAVE_MINIZIP
264     g_string_append(str, ", with Minizip");
265 #else
266     g_string_append(str, ", without Minizip");
267 #endif
268 }
269 
270 // xxx copied from ../gtk/main.c
271 void
get_wireshark_runtime_info(GString * str)272 get_wireshark_runtime_info(GString *str)
273 {
274     g_string_append_printf(str, ", with Qt %s", qVersion());
275 
276 #ifdef HAVE_LIBPCAP
277     /* Capture libraries */
278     g_string_append(str, ", ");
279     get_runtime_caplibs_version(str);
280 #endif
281 
282     /* stuff used by libwireshark */
283     epan_get_runtime_version_info(str);
284 
285 #ifdef HAVE_AIRPCAP
286     g_string_append(str, ", ");
287     get_runtime_airpcap_version(str);
288 #endif
289 
290     if (wsApp) {
291         // Display information
292         const char *display_mode = ColorUtils::themeIsDark() ? "dark" : "light";
293         g_string_append_printf(str, ", with %s display mode", display_mode);
294 
295         int hidpi_count = 0;
296         foreach (QScreen *screen, wsApp->screens()) {
297             if (screen->devicePixelRatio() > 1.0) {
298                 hidpi_count++;
299             }
300         }
301         if (hidpi_count == wsApp->screens().count()) {
302             g_string_append(str, ", with HiDPI");
303         } else if (hidpi_count) {
304             g_string_append(str, ", with mixed DPI");
305         } else {
306             g_string_append(str, ", without HiDPI");
307         }
308     }
309 }
310 
311 static void
qt_log_message_handler(QtMsgType type,const QMessageLogContext &,const QString & msg)312 qt_log_message_handler(QtMsgType type, const QMessageLogContext &, const QString &msg)
313 {
314     enum ws_log_level log_level = LOG_LEVEL_DEBUG;
315 
316     switch (type) {
317     case QtInfoMsg:
318         log_level = LOG_LEVEL_INFO;
319         break;
320     // We want qDebug() messages to show up at our default log level.
321     case QtDebugMsg:
322     case QtWarningMsg:
323         log_level = LOG_LEVEL_WARNING;
324         break;
325     case QtCriticalMsg:
326         log_level = LOG_LEVEL_CRITICAL;
327         break;
328     case QtFatalMsg:
329         log_level = LOG_LEVEL_ERROR;
330         break;
331     default:
332         break;
333     }
334     ws_log(LOG_DOMAIN_QTUI, log_level, "%s", qUtf8Printable(msg));
335 }
336 
337 #ifdef HAVE_LIBPCAP
338 /*  Check if there's something important to tell the user during startup.
339  *  We want to do this *after* showing the main window so that any windows
340  *  we pop up will be above the main window.
341  */
342 static void
check_and_warn_user_startup()343 check_and_warn_user_startup()
344 {
345     gchar               *cur_user, *cur_group;
346 
347     /* Tell the user not to run as root. */
348     if (running_with_special_privs() && recent.privs_warn_if_elevated) {
349         cur_user = get_cur_username();
350         cur_group = get_cur_groupname();
351         simple_message_box(ESD_TYPE_WARN, &recent.privs_warn_if_elevated,
352         "Running as user \"%s\" and group \"%s\".\n"
353         "This could be dangerous.\n\n"
354         "If you're running Wireshark this way in order to perform live capture, "
355         "you may want to be aware that there is a better way documented at\n"
356         WS_WIKI_URL("CaptureSetup/CapturePrivileges"), cur_user, cur_group);
357         g_free(cur_user);
358         g_free(cur_group);
359     }
360 }
361 #endif
362 
363 #ifdef _WIN32
364 // Try to avoid library search path collisions. QCoreApplication will
365 // search QT_INSTALL_PREFIX/plugins for platform DLLs before searching
366 // the application directory. If
367 //
368 // - You have Qt version 5.x.y installed in the default location
369 //   (C:\Qt\5.x) on your machine.
370 //
371 // and
372 //
373 // - You install Wireshark that was built on a machine with Qt version
374 //   5.x.z installed in the default location.
375 //
376 // Qt5Core.dll will load qwindows.dll from your local C:\Qt\5.x\...\plugins
377 // directory. This may not be compatible with qwindows.dll from that
378 // same path on the build machine. At any rate, loading DLLs from paths
379 // you don't control is ill-advised. We work around this by removing every
380 // path except our application directory.
381 
382 static inline void
win32_reset_library_path(void)383 win32_reset_library_path(void)
384 {
385     QString app_path = QDir(get_progfile_dir()).path();
386     foreach (QString path, QCoreApplication::libraryPaths()) {
387         QCoreApplication::removeLibraryPath(path);
388     }
389     QCoreApplication::addLibraryPath(app_path);
390 }
391 #endif
392 
393 #ifdef Q_OS_MAC
394 // Try to work around
395 //
396 //     https://gitlab.com/wireshark/wireshark/-/issues/17075
397 //
398 // aka
399 //
400 //     https://bugreports.qt.io/browse/QTBUG-87014
401 //
402 // The fix at
403 //
404 //     https://codereview.qt-project.org/c/qt/qtbase/+/322228/3/src/plugins/platforms/cocoa/qnsview_drawing.mm
405 //
406 // enables layer backing if we're running on Big Sur OR we're running on
407 // Catalina AND we were built with the Catalina SDK. Enable layer backing
408 // here by setting QT_MAC_WANTS_LAYER=1, but only if we're running on Big
409 // Sur and our version of Qt doesn't have a fix for QTBUG-87014.
410 #include <QOperatingSystemVersion>
411 static inline void
macos_enable_layer_backing(void)412 macos_enable_layer_backing(void)
413 {
414     // At the time of this writing, the QTBUG-87014 for layerEnabledByMacOS is...
415     //
416     // ...in https://github.com/qt/qtbase/blob/5.12/src/plugins/platforms/cocoa/qnsview_drawing.mm
417     // ...not in https://github.com/qt/qtbase/blob/5.12.10/src/plugins/platforms/cocoa/qnsview_drawing.mm
418     // ...in https://github.com/qt/qtbase/blob/5.15/src/plugins/platforms/cocoa/qnsview_drawing.mm
419     // ...not in https://github.com/qt/qtbase/blob/5.15.2/src/plugins/platforms/cocoa/qnsview_drawing.mm
420     // ...not in https://github.com/qt/qtbase/blob/6.0/src/plugins/platforms/cocoa/qnsview_drawing.mm
421     // ...not in https://github.com/qt/qtbase/blob/6.0.0/src/plugins/platforms/cocoa/qnsview_drawing.mm
422     //
423     // We'll assume that it will be fixed in 5.12.11, 5.15.3, and 6.0.1.
424     // Note that we only ship LTS versions of Qt with our macOS packages.
425     // Feel free to add other versions if needed.
426 #if  \
427         (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) && QT_VERSION < QT_VERSION_CHECK(5, 12, 11) \
428         || (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) &&  QT_VERSION < QT_VERSION_CHECK(5, 15, 3)) \
429         || (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) &&  QT_VERSION < QT_VERSION_CHECK(6, 0, 1)) \
430     )
431     QOperatingSystemVersion os_ver = QOperatingSystemVersion::current();
432     int major_ver = os_ver.majorVersion();
433     int minor_ver = os_ver.minorVersion();
434     if ( (major_ver == 10 && minor_ver >= 16) || major_ver >= 11 ) {
435         if (qgetenv("QT_MAC_WANTS_LAYER").isEmpty()) {
436             qputenv("QT_MAC_WANTS_LAYER", "1");
437         }
438     }
439 #endif
440 }
441 #endif
442 
443 /* And now our feature presentation... [ fade to music ] */
main(int argc,char * qt_argv[])444 int main(int argc, char *qt_argv[])
445 {
446     MainWindow *main_w;
447 
448 #ifdef _WIN32
449     LPWSTR              *wc_argv;
450     int                  wc_argc;
451 #endif
452     int                  ret_val = EXIT_SUCCESS;
453     char               **argv = qt_argv;
454 
455     char                *rf_path;
456     int                  rf_open_errno;
457 #ifdef HAVE_LIBPCAP
458     gchar               *err_str, *err_str_secondary;;
459 #else
460 #ifdef _WIN32
461 #ifdef HAVE_AIRPCAP
462     gchar               *err_str;
463 #endif
464 #endif
465 #endif
466     gchar               *err_msg = NULL;
467 
468     QString              dfilter, read_filter;
469 #ifdef HAVE_LIBPCAP
470     int                  caps_queries = 0;
471 #endif
472     /* Start time in microseconds */
473     guint64 start_time = g_get_monotonic_time();
474     static const struct report_message_routines wireshark_report_routines = {
475         vfailure_alert_box,
476         vwarning_alert_box,
477         open_failure_alert_box,
478         read_failure_alert_box,
479         write_failure_alert_box,
480         cfile_open_failure_alert_box,
481         cfile_dump_open_failure_alert_box,
482         cfile_read_failure_alert_box,
483         cfile_write_failure_alert_box,
484         cfile_close_failure_alert_box
485     };
486 
487 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
488     /*
489      * See:
490      *
491      *    issue #16908;
492      *
493      *    https://doc.qt.io/qt-5/qvector.html#maximum-size-and-out-of-memory-conditions
494      *
495      *    https://forum.qt.io/topic/114950/qvector-realloc-throwing-sigsegv-when-very-large-surface3d-is-rendered
496      *
497      * for why we're doing this; the widget we use for the packet list
498      * uses QVector, so those limitations apply to it.
499      *
500      * Apparently, this will be fixed in Qt 6:
501      *
502      *    https://github.com/qt/qtbase/commit/215ca735341b9487826023a7983382851ce8bf26
503      *
504      *    https://github.com/qt/qtbase/commit/2a6cdec718934ca2cc7f6f9c616ebe62f6912123#diff-724f419b0bb0487c2629bb16cf534c4b268ddcee89b5177189b607f940cfd83dR192
505      *
506      * Hopefully QList won't cause any performance hits relative to
507      * QVector.
508      *
509      * We pick 53 million records as a value that should avoid the problem;
510      * see the Wireshark issue for why that value was chosen.
511      */
512     cf_set_max_records(53000000);
513 #endif
514 
515 #ifdef Q_OS_MAC
516     macos_enable_layer_backing();
517 #endif
518 
519     cmdarg_err_init(wireshark_cmdarg_err, wireshark_cmdarg_err_cont);
520 
521     /* Initialize log handler early so we can have proper logging during startup. */
522     ws_log_init_with_writer("wireshark", console_log_writer, vcmdarg_err);
523     /* For backward compatibility with GLib logging and Wireshark 3.4. */
524     ws_log_console_writer_set_use_stdout(TRUE);
525 
526     qInstallMessageHandler(qt_log_message_handler);
527 
528 #ifdef _WIN32
529     restore_pipes();
530 #endif
531 
532 #ifdef DEBUG_STARTUP_TIME
533     prefs.gui_console_open = console_open_always;
534 #endif /* DEBUG_STARTUP_TIME */
535 
536 #if defined(Q_OS_MAC)
537     /* Disable automatic addition of tab menu entries in view menu */
538     CocoaBridge::cleanOSGeneratedMenuItems();
539 #endif
540 
541     /*
542      * Set the C-language locale to the native environment and set the
543      * code page to UTF-8 on Windows.
544      */
545 #ifdef _WIN32
546     setlocale(LC_ALL, ".UTF-8");
547 #else
548     setlocale(LC_ALL, "");
549 #endif
550 
551 #ifdef _WIN32
552     //
553     // On Windows, QCoreApplication has its own WinMain(), which gets the
554     // command line using GetCommandLineW(), breaks it into individual
555     // arguments using CommandLineToArgvW(), and then "helpfully"
556     // converts those UTF-16LE arguments into strings in the local code
557     // page.
558     //
559     // We don't want that, because not all file names can be represented
560     // in the local code page, so we do the same, but we convert the
561     // strings into UTF-8.
562     //
563     wc_argv = CommandLineToArgvW(GetCommandLineW(), &wc_argc);
564     if (wc_argv) {
565         argc = wc_argc;
566         argv = arg_list_utf_16to8(wc_argc, wc_argv);
567         LocalFree(wc_argv);
568     } /* XXX else bail because something is horribly, horribly wrong? */
569 
570     create_app_running_mutex();
571 #endif /* _WIN32 */
572 
573     /* Early logging command-line initialization. */
574     ws_log_parse_args(&argc, argv, vcmdarg_err, INVALID_OPTION);
575 
576     /*
577      * Get credential information for later use, and drop privileges
578      * before doing anything else.
579      * Let the user know if anything happened.
580      */
581     init_process_policies();
582     relinquish_special_privs_perm();
583 
584     /*
585      * Attempt to get the pathname of the directory containing the
586      * executable file.
587      */
588     /* init_progfile_dir_error = */ init_progfile_dir(argv[0]);
589     /* ws_log(NULL, LOG_LEVEL_DEBUG, "progfile_dir: %s", get_progfile_dir()); */
590 
591 #ifdef _WIN32
592     ws_init_dll_search_path();
593     /* Load wpcap if possible. Do this before collecting the run-time version information */
594     load_wpcap();
595 
596 #ifdef HAVE_AIRPCAP
597     /* Load the airpcap.dll.  This must also be done before collecting
598      * run-time version information. */
599     load_airpcap();
600 #if 0
601     airpcap_dll_ret_val = load_airpcap();
602 
603     switch (airpcap_dll_ret_val) {
604     case AIRPCAP_DLL_OK:
605         /* load the airpcap interfaces */
606         g_airpcap_if_list = get_airpcap_interface_list(&err, &err_str);
607 
608         if (g_airpcap_if_list == NULL || g_list_length(g_airpcap_if_list) == 0) {
609             if (err == CANT_GET_AIRPCAP_INTERFACE_LIST && err_str != NULL) {
610                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", "Failed to open Airpcap Adapters.");
611                 g_free(err_str);
612             }
613             airpcap_if_active = NULL;
614 
615         } else {
616 
617             /* select the first as default (THIS SHOULD BE CHANGED) */
618             airpcap_if_active = airpcap_get_default_if(airpcap_if_list);
619         }
620         break;
621     /*
622      * XXX - Maybe we need to warn the user if one of the following happens???
623      */
624     case AIRPCAP_DLL_OLD:
625         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s","AIRPCAP_DLL_OLD\n");
626         break;
627 
628     case AIRPCAP_DLL_ERROR:
629         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s","AIRPCAP_DLL_ERROR\n");
630         break;
631 
632     case AIRPCAP_DLL_NOT_FOUND:
633         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s","AIRPCAP_DDL_NOT_FOUND\n");
634         break;
635     }
636 #endif
637 #endif /* HAVE_AIRPCAP */
638 #endif /* _WIN32 */
639 
640     /* Get the compile-time version information string */
641     ws_init_version_info("Wireshark", get_wireshark_qt_compiled_info,
642                          get_gui_compiled_info, get_wireshark_runtime_info);
643 
644     /* Create the user profiles directory */
645     if (create_profiles_dir(&rf_path) == -1) {
646         simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
647                       "Could not create profiles directory\n\"%s\": %s.",
648                       rf_path, g_strerror(errno));
649         g_free (rf_path);
650     }
651 
652     profile_store_persconffiles(TRUE);
653     recent_init();
654 
655     /* Read the profile independent recent file.  We have to do this here so we can */
656     /* set the profile before it can be set from the command line parameter */
657     if (!recent_read_static(&rf_path, &rf_open_errno)) {
658         simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
659                       "Could not open common recent file\n\"%s\": %s.",
660                       rf_path, g_strerror(rf_open_errno));
661         g_free(rf_path);
662     }
663 
664     commandline_early_options(argc, argv);
665 
666 #ifdef _WIN32
667     win32_reset_library_path();
668 #endif
669 
670     // Handle DPI scaling on Windows. This causes problems in at least
671     // one case on X11 and we don't yet support Android.
672     // We do the equivalent on macOS by setting NSHighResolutionCapable
673     // in Info.plist.
674     // Note that this enables Windows 8.1-style Per-monitor DPI
675     // awareness but not Windows 10-style Per-monitor v2 awareness.
676     // https://doc.qt.io/qt-5/scalability.html
677     // https://doc.qt.io/qt-5/highdpi.html
678     // https://bugreports.qt.io/browse/QTBUG-53022 - The device pixel ratio is pretty much bogus on Windows.
679     // https://bugreports.qt.io/browse/QTBUG-55510 - Windows have wrong size
680 #if defined(Q_OS_WIN)
681     QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
682 #endif
683 
684     /* Create The Wireshark app */
685     WiresharkApplication ws_app(argc, qt_argv);
686 
687     /* initialize the funnel mini-api */
688     // xxx qtshark
689     //initialize_funnel_ops();
690 
691     Dot11DecryptInitContext(&dot11decrypt_ctx);
692 
693     QString cf_name;
694     unsigned int in_file_type = WTAP_TYPE_AUTO;
695 
696     err_msg = ws_init_sockets();
697     if (err_msg != NULL)
698     {
699         cmdarg_err("%s", err_msg);
700         g_free(err_msg);
701         cmdarg_err_cont("%s", please_report_bug());
702         ret_val = INIT_FAILED;
703         goto clean_exit;
704     }
705 
706     /* Read the profile dependent (static part) of the recent file. */
707     /* Only the static part of it will be read, as we don't have the gui now to fill the */
708     /* recent lists which is done in the dynamic part. */
709     /* We have to do this already here, so command line parameters can overwrite these values. */
710     if (!recent_read_profile_static(&rf_path, &rf_open_errno)) {
711         simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
712                       "Could not open recent file\n\"%s\": %s.",
713                       rf_path, g_strerror(rf_open_errno));
714         g_free(rf_path);
715     }
716     wsApp->applyCustomColorsFromRecent();
717 
718     // Initialize our language
719     read_language_prefs();
720     wsApp->loadLanguage(language);
721 
722     /* ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_DEBUG, "Translator %s", language); */
723 
724     // Init the main window (and splash)
725     main_w = new(MainWindow);
726     main_w->show();
727     // We may not need a queued connection here but it would seem to make sense
728     // to force the issue.
729     main_w->connect(&ws_app, SIGNAL(openCaptureFile(QString,QString,unsigned int)),
730             main_w, SLOT(openCaptureFile(QString,QString,unsigned int)));
731     main_w->connect(&ws_app, SIGNAL(openCaptureOptions()),
732             main_w, SLOT(on_actionCaptureOptions_triggered()));
733 
734     /* Init the "Open file" dialog directory */
735     /* (do this after the path settings are processed) */
736     if (recent.gui_fileopen_remembered_dir &&
737         test_for_directory(recent.gui_fileopen_remembered_dir) == EISDIR) {
738       wsApp->setLastOpenDir(recent.gui_fileopen_remembered_dir);
739     } else {
740       wsApp->setLastOpenDir(get_persdatafile_dir());
741     }
742 
743 #ifdef DEBUG_STARTUP_TIME
744     ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "set_console_log_handler, elapsed time %" G_GUINT64_FORMAT " us \n", g_get_monotonic_time() - start_time);
745 #endif
746 
747 #ifdef HAVE_LIBPCAP
748     /* Set the initial values in the capture options. This might be overwritten
749        by preference settings and then again by the command line parameters. */
750     capture_opts_init(&global_capture_opts);
751 #endif
752 
753     init_report_message("Wireshark", &wireshark_report_routines);
754 
755     /*
756      * Libwiretap must be initialized before libwireshark is, so that
757      * dissection-time handlers for file-type-dependent blocks can
758      * register using the file type/subtype value for the file type.
759      */
760     wtap_init(TRUE);
761 
762     splash_update(RA_DISSECTORS, NULL, NULL);
763 #ifdef DEBUG_STARTUP_TIME
764     ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "Calling epan init, elapsed time %" G_GUINT64_FORMAT " us \n", g_get_monotonic_time() - start_time);
765 #endif
766     /* Register all dissectors; we must do this before checking for the
767        "-G" flag, as the "-G" flag dumps information registered by the
768        dissectors, and we must do it before we read the preferences, in
769        case any dissectors register preferences. */
770     if (!epan_init(splash_update, NULL, TRUE)) {
771         SimpleDialog::displayQueuedMessages(main_w);
772         ret_val = INIT_FAILED;
773         goto clean_exit;
774     }
775 #ifdef DEBUG_STARTUP_TIME
776     /* epan_init resets the preferences */
777     prefs.gui_console_open = console_open_always;
778     ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "epan done, elapsed time %" G_GUINT64_FORMAT " us \n", g_get_monotonic_time() - start_time);
779 #endif
780 
781     /* Register all audio codecs. */
782     codecs_init();
783 
784     // Read the dynamic part of the recent file. This determines whether or
785     // not the recent list appears in the main window so the earlier we can
786     // call this the better.
787     if (!recent_read_dynamic(&rf_path, &rf_open_errno)) {
788         simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
789                       "Could not open recent file\n\"%s\": %s.",
790                       rf_path, g_strerror(rf_open_errno));
791         g_free(rf_path);
792     }
793     wsApp->refreshRecentCaptures();
794 
795     splash_update(RA_LISTENERS, NULL, NULL);
796 #ifdef DEBUG_STARTUP_TIME
797     ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "Register all tap listeners, elapsed time %" G_GUINT64_FORMAT " us \n", g_get_monotonic_time() - start_time);
798 #endif
799     /* Register all tap listeners; we do this before we parse the arguments,
800        as the "-z" argument can specify a registered tap. */
801 
802     register_all_tap_listeners(tap_reg_listener);
803 
804     conversation_table_set_gui_info(init_conversation_table);
805     hostlist_table_set_gui_info(init_endpoint_table);
806     srt_table_iterate_tables(register_service_response_tables, NULL);
807     rtd_table_iterate_tables(register_response_time_delay_tables, NULL);
808     stat_tap_iterate_tables(register_simple_stat_tables, NULL);
809 
810     if (ex_opt_count("read_format") > 0) {
811         in_file_type = open_info_name_to_type(ex_opt_get_next("read_format"));
812     }
813 
814 #ifdef DEBUG_STARTUP_TIME
815     ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "Calling extcap_register_preferences, elapsed time %" G_GUINT64_FORMAT " us \n", g_get_monotonic_time() - start_time);
816 #endif
817     splash_update(RA_EXTCAP, NULL, NULL);
818     extcap_register_preferences();
819     splash_update(RA_PREFERENCES, NULL, NULL);
820 #ifdef DEBUG_STARTUP_TIME
821     ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "Calling module preferences, elapsed time %" G_GUINT64_FORMAT " us \n", g_get_monotonic_time() - start_time);
822 #endif
823 
824     global_commandline_info.prefs_p = ws_app.readConfigurationFiles(false);
825 
826     /* Now get our args */
827     commandline_other_options(argc, argv, TRUE);
828 
829     /* Convert some command-line parameters to QStrings */
830     if (global_commandline_info.cf_name != NULL)
831         cf_name = QString(global_commandline_info.cf_name);
832     if (global_commandline_info.rfilter != NULL)
833         read_filter = QString(global_commandline_info.rfilter);
834     if (global_commandline_info.dfilter != NULL)
835         dfilter = QString(global_commandline_info.dfilter);
836 
837     timestamp_set_type(recent.gui_time_format);
838     timestamp_set_precision(recent.gui_time_precision);
839     timestamp_set_seconds_type (recent.gui_seconds_format);
840 
841 #ifdef HAVE_LIBPCAP
842 #ifdef DEBUG_STARTUP_TIME
843     ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "Calling fill_in_local_interfaces, elapsed time %" G_GUINT64_FORMAT " us \n", g_get_monotonic_time() - start_time);
844 #endif
845     splash_update(RA_INTERFACES, NULL, NULL);
846 
847     if (!global_commandline_info.cf_name && !prefs.capture_no_interface_load)
848         fill_in_local_interfaces(main_window_update);
849 
850     if  (global_commandline_info.list_link_layer_types)
851         caps_queries |= CAPS_QUERY_LINK_TYPES;
852      if (global_commandline_info.list_timestamp_types)
853         caps_queries |= CAPS_QUERY_TIMESTAMP_TYPES;
854 
855     if (global_commandline_info.start_capture || caps_queries) {
856         /* We're supposed to do a live capture or get a list of link-layer/timestamp
857            types for a live capture device; if the user didn't specify an
858            interface to use, pick a default. */
859         ret_val = capture_opts_default_iface_if_necessary(&global_capture_opts,
860         ((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);
861         if (ret_val != 0) {
862             goto clean_exit;
863         }
864     }
865 
866     /*
867      * If requested, list the link layer types and/or time stamp types
868      * and exit.
869      */
870     if (caps_queries) {
871         guint i;
872 
873 #ifdef _WIN32
874         create_console();
875 #endif /* _WIN32 */
876         /* Get the list of link-layer types for the capture devices. */
877         ret_val = EXIT_SUCCESS;
878         for (i = 0; i < global_capture_opts.ifaces->len; i++) {
879             interface_options *interface_opts;
880             if_capabilities_t *caps;
881             char *auth_str = NULL;
882 
883             interface_opts = &g_array_index(global_capture_opts.ifaces, interface_options, i);
884 #ifdef HAVE_PCAP_REMOTE
885             if (interface_opts->auth_type == CAPTURE_AUTH_PWD) {
886                 auth_str = g_strdup_printf("%s:%s", interface_opts->auth_username, interface_opts->auth_password);
887             }
888 #endif
889             caps = capture_get_if_capabilities(interface_opts->name, interface_opts->monitor_mode,
890                                                auth_str, &err_str, &err_str_secondary, NULL);
891             g_free(auth_str);
892             if (caps == NULL) {
893                 cmdarg_err("%s%s%s", err_str, err_str_secondary ? "\n" : "", err_str_secondary ? err_str_secondary : "");
894                 g_free(err_str);
895                 g_free(err_str_secondary);
896                 ret_val = INVALID_CAPABILITY;
897                 break;
898             }
899             ret_val = capture_opts_print_if_capabilities(caps, interface_opts,
900                                                          caps_queries);
901             free_if_capabilities(caps);
902             if (ret_val != EXIT_SUCCESS) {
903                 break;
904             }
905         }
906 #ifdef _WIN32
907         destroy_console();
908 #endif /* _WIN32 */
909         goto clean_exit;
910     }
911 
912     capture_opts_trim_snaplen(&global_capture_opts, MIN_PACKET_SIZE);
913     capture_opts_trim_ring_num_files(&global_capture_opts);
914 #endif /* HAVE_LIBPCAP */
915 
916     /* Notify all registered modules that have had any of their preferences
917        changed either from one of the preferences file or from the command
918        line that their preferences have changed. */
919 #ifdef DEBUG_STARTUP_TIME
920     ws_log(LOG_DOMAIN_MAIN, LOG_LEVEL_INFO, "Calling prefs_apply_all, elapsed time %" G_GUINT64_FORMAT " us \n", g_get_monotonic_time() - start_time);
921 #endif
922     prefs_apply_all();
923     prefs_to_capture_opts();
924     wsApp->emitAppSignal(WiresharkApplication::PreferencesChanged);
925 
926 #ifdef HAVE_LIBPCAP
927     if ((global_capture_opts.num_selected == 0) &&
928             (prefs.capture_device != NULL)) {
929         guint i;
930         interface_t *device;
931         for (i = 0; i < global_capture_opts.all_ifaces->len; i++) {
932             device = &g_array_index(global_capture_opts.all_ifaces, interface_t, i);
933             if (!device->hidden && strcmp(device->display_name, prefs.capture_device) == 0) {
934                 device->selected = TRUE;
935                 global_capture_opts.num_selected++;
936                 break;
937             }
938         }
939     }
940 #endif
941 
942     /*
943      * Enabled and disabled protocols and heuristic dissectors as per
944      * command-line options.
945      */
946     if (!setup_enabled_and_disabled_protocols()) {
947         ret_val = INVALID_OPTION;
948         goto clean_exit;
949     }
950 
951     build_column_format_array(&CaptureFile::globalCapFile()->cinfo, global_commandline_info.prefs_p->num_cols, TRUE);
952     wsApp->emitAppSignal(WiresharkApplication::ColumnsChanged); // We read "recent" widths above.
953     wsApp->emitAppSignal(WiresharkApplication::RecentPreferencesRead); // Must be emitted after PreferencesChanged.
954 
955     wsApp->setMonospaceFont(prefs.gui_qt_font_name);
956 
957     /* For update of WindowTitle (When use gui.window_title preference) */
958     main_w->setWSWindowTitle();
959 
960     if (!color_filters_init(&err_msg, color_filter_add_cb)) {
961         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_msg);
962         g_free(err_msg);
963     }
964 
965     wsApp->allSystemsGo();
966     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);
967     SimpleDialog::displayQueuedMessages(main_w);
968 
969     /* User could specify filename, or display filter, or both */
970     if (!dfilter.isEmpty())
971         main_w->filterPackets(dfilter, false);
972     if (!cf_name.isEmpty()) {
973         if (main_w->openCaptureFile(cf_name, read_filter, in_file_type)) {
974 
975             /* Open stat windows; we do so after creating the main window,
976                to avoid Qt warnings, and after successfully opening the
977                capture file, so we know we have something to compute stats
978                on, and after registering all dissectors, so that MATE will
979                have registered its field array and we can have a tap filter
980                with one of MATE's late-registered fields as part of the
981                filter. */
982             start_requested_stats();
983 
984             if (global_commandline_info.go_to_packet != 0) {
985                 /* Jump to the specified frame number, kept for backward
986                    compatibility. */
987                 cf_goto_frame(CaptureFile::globalCapFile(), global_commandline_info.go_to_packet);
988             } else if (global_commandline_info.jfilter != NULL) {
989                 dfilter_t *jump_to_filter = NULL;
990                 /* try to compile given filter */
991                 if (!dfilter_compile(global_commandline_info.jfilter, &jump_to_filter, &err_msg)) {
992                     // Similar code in MainWindow::mergeCaptureFile().
993                     QMessageBox::warning(main_w, QObject::tr("Invalid Display Filter"),
994                                          QObject::tr("The filter expression %1 isn't a valid display filter. (%2).")
995                                                  .arg(global_commandline_info.jfilter, err_msg),
996                                          QMessageBox::Ok);
997                     g_free(err_msg);
998                 } else {
999                     /* Filter ok, jump to the first packet matching the filter
1000                        conditions. Default search direction is forward, but if
1001                        option d was given, search backwards */
1002                     cf_find_packet_dfilter(CaptureFile::globalCapFile(), jump_to_filter, global_commandline_info.jump_backwards);
1003                 }
1004             }
1005         }
1006     }
1007 #ifdef HAVE_LIBPCAP
1008     else {
1009         if (global_commandline_info.start_capture) {
1010             if (global_capture_opts.save_file != NULL) {
1011                 /* Save the directory name for future file dialogs. */
1012                 /* (get_dirname overwrites filename) */
1013                 gchar *s = g_strdup(global_capture_opts.save_file);
1014                 set_last_open_dir(get_dirname(s));
1015                 g_free(s);
1016             }
1017             /* "-k" was specified; start a capture. */
1018             check_and_warn_user_startup();
1019 
1020             /* If no user interfaces were specified on the command line,
1021                copy the list of selected interfaces to the set of interfaces
1022                to use for this capture. */
1023             if (global_capture_opts.ifaces->len == 0)
1024                 collect_ifaces(&global_capture_opts);
1025             CaptureFile::globalCapFile()->window = main_w;
1026             if (capture_start(&global_capture_opts, global_commandline_info.capture_comments,
1027                               main_w->captureSession(), main_w->captureInfoData(),
1028                               main_window_update)) {
1029                 /* The capture started.  Open stat windows; we do so after creating
1030                    the main window, to avoid GTK warnings, and after successfully
1031                    opening the capture file, so we know we have something to compute
1032                    stats on, and after registering all dissectors, so that MATE will
1033                    have registered its field array and we can have a tap filter with
1034                    one of MATE's late-registered fields as part of the filter. */
1035                 start_requested_stats();
1036             }
1037         }
1038         /* if the user didn't supply a capture filter, use the one to filter out remote connections like SSH */
1039         if (!global_commandline_info.start_capture && !global_capture_opts.default_options.cfilter) {
1040             global_capture_opts.default_options.cfilter = g_strdup(get_conn_cfilter());
1041         }
1042     }
1043 #endif /* HAVE_LIBPCAP */
1044 
1045     // UAT and UI settings files used in configuration profiles which are used
1046     // in Qt dialogs are not registered during startup because they only get
1047     // loaded when the dialog is shown.  Register them here.
1048     profile_register_persconffile("io_graphs");
1049     profile_register_persconffile("import_hexdump.json");
1050 
1051     profile_store_persconffiles(FALSE);
1052 
1053     ret_val = wsApp->exec();
1054     wsApp = NULL;
1055 
1056     delete main_w;
1057     recent_cleanup();
1058     epan_cleanup();
1059 
1060     extcap_cleanup();
1061 
1062     Dot11DecryptDestroyContext(&dot11decrypt_ctx);
1063 
1064     ws_cleanup_sockets();
1065 
1066 #ifdef _WIN32
1067     /* For some unknown reason, the "atexit()" call in "create_console()"
1068        doesn't arrange that "destroy_console()" be called when we exit,
1069        so we call it here if a console was created. */
1070     destroy_console();
1071 #endif /* _WIN32 */
1072 
1073 clean_exit:
1074 #ifdef HAVE_LIBPCAP
1075     capture_opts_cleanup(&global_capture_opts);
1076 #endif
1077     col_cleanup(&CaptureFile::globalCapFile()->cinfo);
1078     codecs_cleanup();
1079     wtap_cleanup();
1080     free_progdirs();
1081     commandline_options_free();
1082     exit_application(ret_val);
1083 }
1084