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