3 * Daemon variant of Wireshark
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
23 #include <epan/exceptions.h>
24 #include <epan/epan.h>
26 #include <wsutil/clopts_common.h>
27 #include <wsutil/cmdarg_err.h>
28 #include <wsutil/crash_info.h>
29 #include <wsutil/filesystem.h>
30 #include <wsutil/file_util.h>
31 #include <wsutil/privileges.h>
32 #include <wsutil/report_message.h>
33 #include <version_info.h>
34 #include <wiretap/wtap_opttypes.h>
35 #include <wiretap/pcapng.h>
37 #include <epan/decode_as.h>
38 #include <epan/timestamp.h>
39 #include <epan/packet.h>
40 #include "frame_tvbuff.h"
41 #include <epan/disabled_protos.h>
42 #include <epan/prefs.h>
43 #include <epan/column.h>
44 #include <epan/print.h>
45 #include <epan/addr_resolv.h>
47 #include "ui/ws_ui_util.h"
48 #include "ui/decode_as_utils.h"
49 #include "ui/filter_files.h"
50 #include "ui/tap_export_pdu.h"
51 #include "ui/failure_message.h"
52 #include "epan/register.h"
53 #include <epan/epan_dissect.h>
56 #include <codecs/codecs.h>
60 #include <wsutil/str_util.h>
61 #include <wsutil/utf8_entities.h>
64 #include <wsutil/plugins.h>
70 #define EPAN_INIT_FAIL 2
74 static guint32 cum_bytes;
75 static frame_data ref_frame;
77 static void failure_warning_message(const char *msg_format, va_list ap);
78 static void open_failure_message(const char *filename, int err,
79 gboolean for_writing);
80 static void read_failure_message(const char *filename, int err);
81 static void write_failure_message(const char *filename, int err);
82 static void failure_message_cont(const char *msg_format, va_list ap);
85 print_current_user(void) {
86 gchar *cur_user, *cur_group;
88 if (started_with_special_privs()) {
89 cur_user = get_cur_username();
90 cur_group = get_cur_groupname();
91 fprintf(stderr, "Running as user \"%s\" and group \"%s\".",
95 if (running_with_special_privs()) {
96 fprintf(stderr, " This could be dangerous.");
98 fprintf(stderr, "\n");
103 main(int argc, char *argv[])
105 GString *comp_info_str;
106 GString *runtime_info_str;
107 char *init_progfile_dir_error;
109 char *err_msg = NULL;
111 int ret = EXIT_SUCCESS;
113 cmdarg_err_init(failure_warning_message, failure_message_cont);
116 * Get credential information for later use, and drop privileges
117 * before doing anything else.
118 * Let the user know if anything happened.
120 init_process_policies();
121 relinquish_special_privs_perm();
122 print_current_user();
125 * Attempt to get the pathname of the executable file.
127 init_progfile_dir_error = init_progfile_dir(argv[0]);
128 if (init_progfile_dir_error != NULL) {
129 fprintf(stderr, "sharkd: Can't get pathname of sharkd program: %s.\n",
130 init_progfile_dir_error);
133 /* Get the compile-time version information string */
134 comp_info_str = get_compiled_version_info(NULL, epan_get_compiled_version_info);
136 /* Get the run-time version information string */
137 runtime_info_str = get_runtime_version_info(epan_get_runtime_version_info);
139 /* Add it to the information to be reported on a crash. */
140 ws_add_crash_info("Sharkd (Wireshark) %s\n"
145 get_ws_vcs_version_info(), comp_info_str->str, runtime_info_str->str);
146 g_string_free(comp_info_str, TRUE);
147 g_string_free(runtime_info_str, TRUE);
149 if (sharkd_init(argc, argv) < 0)
151 printf("cannot initialize sharkd\n");
156 init_report_message(failure_warning_message, failure_warning_message,
157 open_failure_message, read_failure_message,
158 write_failure_message);
160 timestamp_set_type(TS_RELATIVE);
161 timestamp_set_precision(TS_PREC_AUTO);
162 timestamp_set_seconds_type(TS_SECONDS_DEFAULT);
166 /* Register all dissectors; we must do this before checking for the
167 "-G" flag, as the "-G" flag dumps information registered by the
168 dissectors, and we must do it before we read the preferences, in
169 case any dissectors register preferences. */
170 if (!epan_init(register_all_protocols, register_all_protocol_handoffs, NULL,
172 ret = EPAN_INIT_FAIL;
178 /* Load libwireshark settings from the current profile. */
179 prefs_p = epan_load_settings();
181 read_filter_list(CFILTER_LIST);
183 if (!color_filters_init(&err_msg, NULL)) {
184 fprintf(stderr, "%s\n", err_msg);
188 cap_file_init(&cfile);
190 /* Notify all registered modules that have had any of their preferences
191 changed either from one of the preferences file or from the command
192 line that their preferences have changed. */
195 /* Build the column format array */
196 build_column_format_array(&cfile.cinfo, prefs_p->num_cols, TRUE);
200 col_cleanup(&cfile.cinfo);
208 static const nstime_t *
209 sharkd_get_frame_ts(struct packet_provider_data *prov, guint32 frame_num)
211 if (prov->ref && prov->ref->num == frame_num)
212 return &prov->ref->abs_ts;
214 if (prov->prev_dis && prov->prev_dis->num == frame_num)
215 return &prov->prev_dis->abs_ts;
217 if (prov->prev_cap && prov->prev_cap->num == frame_num)
218 return &prov->prev_cap->abs_ts;
221 frame_data *fd = frame_data_sequence_find(prov->frames, frame_num);
223 return (fd) ? &fd->abs_ts : NULL;
230 sharkd_epan_new(capture_file *cf)
232 static const struct packet_provider_funcs funcs = {
234 cap_file_provider_get_interface_name,
235 cap_file_provider_get_interface_description,
236 cap_file_provider_get_user_comment
239 return epan_new(&cf->provider, &funcs);
243 process_packet(capture_file *cf, epan_dissect_t *edt,
244 gint64 offset, wtap_rec *rec, const guchar *pd)
249 /* If we're not running a display filter and we're not printing any
250 packet information, we don't need to do a dissection. This means
251 that all packets can be marked as 'passed'. */
254 /* The frame number of this packet, if we add it to the set of frames,
255 would be one more than the count of frames in the file so far. */
256 frame_data_init(&fdlocal, cf->count + 1, rec, offset, cum_bytes);
258 /* If we're going to print packet information, or we're going to
259 run a read filter, or display filter, or we're going to process taps, set up to
260 do a dissection and do so. */
262 if (gbl_resolv_flags.mac_name || gbl_resolv_flags.network_name ||
263 gbl_resolv_flags.transport_name)
264 /* Grab any resolved addresses */
265 host_name_lookup_process();
267 /* If we're running a read filter, prime the epan_dissect_t with that
270 epan_dissect_prime_with_dfilter(edt, cf->rfcode);
273 epan_dissect_prime_with_dfilter(edt, cf->dfcode);
275 /* This is the first and only pass, so prime the epan_dissect_t
276 with the hfids postdissectors want on the first pass. */
277 prime_epan_dissect_with_postdissector_wanted_hfids(edt);
279 frame_data_set_before_dissect(&fdlocal, &cf->elapsed_time,
280 &cf->provider.ref, cf->provider.prev_dis);
281 if (cf->provider.ref == &fdlocal) {
283 cf->provider.ref = &ref_frame;
286 epan_dissect_run(edt, cf->cd_t, rec,
287 frame_tvbuff_new(&cf->provider, &fdlocal, pd),
290 /* Run the read filter if we have one. */
292 passed = dfilter_apply_edt(cf->rfcode, edt);
296 frame_data_set_after_dissect(&fdlocal, &cum_bytes);
297 cf->provider.prev_cap = cf->provider.prev_dis = frame_data_sequence_add(cf->provider.frames, &fdlocal);
299 /* If we're not doing dissection then there won't be any dependent frames.
300 * More importantly, edt.pi.dependent_frames won't be initialized because
301 * epan hasn't been initialized.
302 * if we *are* doing dissection, then mark the dependent frames, but only
303 * if a display filter was given and it matches this packet.
305 if (edt && cf->dfcode) {
306 if (dfilter_apply_edt(cf->dfcode, edt)) {
307 g_slist_foreach(edt->pi.dependent_frames, find_and_mark_frame_depended_upon, cf->provider.frames);
313 /* if we don't add it to the frame_data_sequence, clean it up right now
315 frame_data_destroy(&fdlocal);
319 epan_dissect_reset(edt);
326 load_cap_file(capture_file *cf, int max_packet_count, gint64 max_byte_count)
329 gchar *err_info = NULL;
331 epan_dissect_t *edt = NULL;
334 /* Allocate a frame_data_sequence for all the frames. */
335 cf->provider.frames = new_frame_data_sequence();
338 gboolean create_proto_tree;
341 * Determine whether we need to create a protocol tree.
344 * we're going to apply a read filter;
346 * we're going to apply a display filter;
348 * a postdissector wants field values or protocols
352 (cf->rfcode != NULL || cf->dfcode != NULL || postdissectors_want_hfids());
354 /* We're not going to display the protocol tree on this pass,
355 so it's not going to be "visible". */
356 edt = epan_dissect_new(cf->epan, create_proto_tree, FALSE);
359 while (wtap_read(cf->provider.wth, &err, &err_info, &data_offset)) {
360 if (process_packet(cf, edt, data_offset, wtap_get_rec(cf->provider.wth),
361 wtap_get_buf_ptr(cf->provider.wth))) {
362 /* Stop reading if we have the maximum number of packets;
363 * When the -c option has not been used, max_packet_count
364 * starts at 0, which practically means, never stop reading.
365 * (unless we roll over max_packet_count ?)
367 if ( (--max_packet_count == 0) || (max_byte_count != 0 && data_offset >= max_byte_count)) {
368 err = 0; /* This is not an error */
375 epan_dissect_free(edt);
379 /* Close the sequential I/O side, to free up memory it requires. */
380 wtap_sequential_close(cf->provider.wth);
382 /* Allow the protocol dissectors to free up memory that they
383 * don't need after the sequential run-through of the packets. */
384 postseq_cleanup_all_protocols();
386 cf->provider.prev_dis = NULL;
387 cf->provider.prev_cap = NULL;
391 cfile_read_failure_message("sharkd", cf->filename, err, err_info);
398 cf_open(capture_file *cf, const char *fname, unsigned int type, gboolean is_tempfile, int *err)
403 wth = wtap_open_offline(fname, type, err, &err_info, TRUE);
407 /* The open succeeded. Fill in the information for this file. */
409 /* Create new epan session for dissection. */
411 cf->epan = sharkd_epan_new(cf);
413 cf->provider.wth = wth;
414 cf->f_datalen = 0; /* not used, but set it anyway */
416 /* Set the file name because we need it to set the follow stream filter.
417 XXX - is that still true? We need it for other reasons, though,
419 cf->filename = g_strdup(fname);
421 /* Indicate whether it's a permanent or temporary file. */
422 cf->is_tempfile = is_tempfile;
424 /* No user changes yet. */
425 cf->unsaved_changes = FALSE;
427 cf->cd_t = wtap_file_type_subtype(cf->provider.wth);
428 cf->open_type = type;
430 cf->drops_known = FALSE;
432 cf->snap = wtap_snapshot_length(cf->provider.wth);
433 nstime_set_zero(&cf->elapsed_time);
434 cf->provider.ref = NULL;
435 cf->provider.prev_dis = NULL;
436 cf->provider.prev_cap = NULL;
438 cf->state = FILE_READ_IN_PROGRESS;
440 wtap_set_cb_new_ipv4(cf->provider.wth, add_ipv4_name);
441 wtap_set_cb_new_ipv6(cf->provider.wth, (wtap_new_ipv6_callback_t) add_ipv6_name);
446 cfile_open_failure_message("sharkd", fname, *err, err_info);
451 * General errors and warnings are reported with an console message
455 failure_warning_message(const char *msg_format, va_list ap)
457 fprintf(stderr, "sharkd: ");
458 vfprintf(stderr, msg_format, ap);
459 fprintf(stderr, "\n");
463 * Open/create errors are reported with an console message in sharkd.
466 open_failure_message(const char *filename, int err, gboolean for_writing)
468 fprintf(stderr, "sharkd: ");
469 fprintf(stderr, file_open_error_message(err, for_writing), filename);
470 fprintf(stderr, "\n");
474 * Read errors are reported with an console message in sharkd.
477 read_failure_message(const char *filename, int err)
479 cmdarg_err("An error occurred while reading from the file \"%s\": %s.",
480 filename, g_strerror(err));
484 * Write errors are reported with an console message in sharkd.
487 write_failure_message(const char *filename, int err)
489 cmdarg_err("An error occurred while writing to the file \"%s\": %s.",
490 filename, g_strerror(err));
494 * Report additional information for an error in command-line arguments.
497 failure_message_cont(const char *msg_format, va_list ap)
499 vfprintf(stderr, msg_format, ap);
500 fprintf(stderr, "\n");
504 sharkd_cf_open(const char *fname, unsigned int type, gboolean is_tempfile, int *err)
506 return cf_open(&cfile, fname, type, is_tempfile, err);
510 sharkd_load_cap_file(void)
512 return load_cap_file(&cfile, 0, 0);
516 sharkd_get_frame(guint32 framenum)
518 return frame_data_sequence_find(cfile.provider.frames, framenum);
522 sharkd_dissect_request(guint32 framenum, guint32 frame_ref_num, guint32 prev_dis_num, sharkd_dissect_func_t cb, int dissect_bytes, int dissect_columns, int dissect_tree, void *data)
525 column_info *cinfo = (dissect_columns) ? &cfile.cinfo : NULL;
527 gboolean create_proto_tree;
528 wtap_rec rec; /* Record metadata */
529 Buffer buf; /* Record data */
532 char *err_info = NULL;
534 fdata = sharkd_get_frame(framenum);
539 ws_buffer_init(&buf, 1500);
541 if (!wtap_seek_read(cfile.provider.wth, fdata->file_off, &rec, &buf, &err, &err_info)) {
542 ws_buffer_free(&buf);
543 return -1; /* error reading the record */
546 create_proto_tree = (dissect_tree) || (cinfo && have_custom_cols(cinfo));
547 epan_dissect_init(&edt, cfile.epan, create_proto_tree, dissect_tree);
550 col_custom_prime_edt(&edt, cinfo);
553 * XXX - need to catch an OutOfMemoryError exception and
554 * attempt to recover from it.
556 fdata->flags.ref_time = (framenum == frame_ref_num);
557 fdata->frame_ref_num = frame_ref_num;
558 fdata->prev_dis_num = prev_dis_num;
559 epan_dissect_run(&edt, cfile.cd_t, &rec,
560 frame_tvbuff_new_buffer(&cfile.provider, fdata, &buf),
564 /* "Stringify" non frame_data vals */
565 epan_dissect_fill_in_columns(&edt, FALSE, TRUE/* fill_fd_columns */);
568 cb(&edt, dissect_tree ? edt.tree : NULL, cinfo, dissect_bytes ? edt.pi.data_src : NULL, data);
570 epan_dissect_cleanup(&edt);
571 wtap_rec_cleanup(&rec);
572 ws_buffer_free(&buf);
576 /* based on packet_list_dissect_and_cache_record */
578 sharkd_dissect_columns(frame_data *fdata, guint32 frame_ref_num, guint32 prev_dis_num, column_info *cinfo, gboolean dissect_color)
581 gboolean create_proto_tree;
582 wtap_rec rec; /* Record metadata */
583 Buffer buf; /* Record data */
586 char *err_info = NULL;
589 ws_buffer_init(&buf, 1500);
591 if (!wtap_seek_read(cfile.provider.wth, fdata->file_off, &rec, &buf, &err, &err_info)) {
592 col_fill_in_error(cinfo, fdata, FALSE, FALSE /* fill_fd_columns */);
593 ws_buffer_free(&buf);
594 return -1; /* error reading the record */
597 create_proto_tree = (dissect_color && color_filters_used()) || (cinfo && have_custom_cols(cinfo));
599 epan_dissect_init(&edt, cfile.epan, create_proto_tree, FALSE /* proto_tree_visible */);
602 color_filters_prime_edt(&edt);
603 fdata->flags.need_colorize = 1;
607 col_custom_prime_edt(&edt, cinfo);
610 * XXX - need to catch an OutOfMemoryError exception and
611 * attempt to recover from it.
613 fdata->flags.ref_time = (fdata->num == frame_ref_num);
614 fdata->frame_ref_num = frame_ref_num;
615 fdata->prev_dis_num = prev_dis_num;
616 epan_dissect_run(&edt, cfile.cd_t, &rec,
617 frame_tvbuff_new_buffer(&cfile.provider, fdata, &buf),
621 /* "Stringify" non frame_data vals */
622 epan_dissect_fill_in_columns(&edt, FALSE, TRUE/* fill_fd_columns */);
625 epan_dissect_cleanup(&edt);
626 wtap_rec_cleanup(&rec);
627 ws_buffer_free(&buf);
639 char *err_info = NULL;
642 gboolean create_proto_tree;
646 /* Get the union of the flags for all tap listeners. */
647 tap_flags = union_of_tap_listener_flags();
649 /* If any tap listeners require the columns, construct them. */
650 cinfo = (tap_flags & TL_REQUIRES_COLUMNS) ? &cfile.cinfo : NULL;
653 * Determine whether we need to create a protocol tree.
656 * one of the tap listeners is going to apply a filter;
658 * one of the tap listeners requires a protocol tree.
661 (have_filtering_tap_listeners() || (tap_flags & TL_REQUIRES_PROTO_TREE));
664 ws_buffer_init(&buf, 1500);
665 epan_dissect_init(&edt, cfile.epan, create_proto_tree, FALSE);
667 reset_tap_listeners();
669 for (framenum = 1; framenum <= cfile.count; framenum++) {
670 fdata = sharkd_get_frame(framenum);
672 if (!wtap_seek_read(cfile.provider.wth, fdata->file_off, &rec, &buf, &err, &err_info))
675 fdata->flags.ref_time = FALSE;
676 fdata->frame_ref_num = (framenum != 1) ? 1 : 0;
677 fdata->prev_dis_num = framenum - 1;
678 epan_dissect_run_with_taps(&edt, cfile.cd_t, &rec,
679 frame_tvbuff_new(&cfile.provider, fdata, ws_buffer_start_ptr(&buf)),
681 epan_dissect_reset(&edt);
684 wtap_rec_cleanup(&rec);
685 ws_buffer_free(&buf);
686 epan_dissect_cleanup(&edt);
688 draw_tap_listeners(TRUE);
694 sharkd_filter(const char *dftext, guint8 **result)
696 dfilter_t *dfcode = NULL;
698 guint32 framenum, prev_dis_num = 0;
699 guint32 frames_count;
703 char *err_info = NULL;
710 if (!dfilter_compile(dftext, &dfcode, &err_info)) {
715 frames_count = cfile.count;
718 ws_buffer_init(&buf, 1500);
719 epan_dissect_init(&edt, cfile.epan, TRUE, FALSE);
722 result_bits = (guint8 *) g_malloc(2 + (frames_count / 8));
724 for (framenum = 1; framenum <= frames_count; framenum++) {
725 frame_data *fdata = sharkd_get_frame(framenum);
727 if ((framenum & 7) == 0) {
728 result_bits[(framenum / 8) - 1] = passed_bits;
732 if (!wtap_seek_read(cfile.provider.wth, fdata->file_off, &rec, &buf, &err, &err_info))
735 /* frame_data_set_before_dissect */
736 epan_dissect_prime_with_dfilter(&edt, dfcode);
738 fdata->flags.ref_time = FALSE;
739 fdata->frame_ref_num = (framenum != 1) ? 1 : 0;
740 fdata->prev_dis_num = prev_dis_num;
741 epan_dissect_run(&edt, cfile.cd_t, &rec,
742 frame_tvbuff_new_buffer(&cfile.provider, fdata, &buf),
745 if (dfilter_apply_edt(dfcode, &edt)) {
746 passed_bits |= (1 << (framenum % 8));
747 prev_dis_num = framenum;
750 /* if passed or ref -> frame_data_set_after_dissect */
752 epan_dissect_reset(&edt);
755 if ((framenum & 7) == 0)
757 result_bits[framenum / 8] = passed_bits;
759 wtap_rec_cleanup(&rec);
760 ws_buffer_free(&buf);
761 epan_dissect_cleanup(&edt);
763 dfilter_free(dfcode);
765 *result = result_bits;
771 sharkd_get_user_comment(const frame_data *fd)
773 return cap_file_provider_get_user_comment(&cfile.provider, fd);
777 sharkd_set_user_comment(frame_data *fd, const gchar *new_comment)
779 cap_file_provider_set_user_comment(&cfile.provider, fd, new_comment);
784 const char *sharkd_version(void)
786 /* based on get_ws_vcs_version_info(), but shorter */
795 * Editor modelines - https://www.wireshark.org/tools/modelines.html
800 * indent-tabs-mode: nil
803 * vi: set shiftwidth=2 tabstop=8 expandtab:
804 * :indentSize=2:tabSize=8:noTabs=true: