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/epan_dissect.h>
54 #include <epan/uat-int.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(NULL, NULL, TRUE)) {
171 ret = EPAN_INIT_FAIL;
177 /* Load libwireshark settings from the current profile. */
178 prefs_p = epan_load_settings();
180 read_filter_list(CFILTER_LIST);
182 if (!color_filters_init(&err_msg, NULL)) {
183 fprintf(stderr, "%s\n", err_msg);
187 cap_file_init(&cfile);
189 /* Notify all registered modules that have had any of their preferences
190 changed either from one of the preferences file or from the command
191 line that their preferences have changed. */
194 /* Build the column format array */
195 build_column_format_array(&cfile.cinfo, prefs_p->num_cols, TRUE);
197 #ifdef HAVE_MAXMINDDB
198 /* mmdbresolve is started from mmdb_resolve_start(), which is called from epan_load_settings via: read_prefs -> (...) uat_load_all -> maxmind_db_post_update_cb.
199 * Need to stop it, otherwise all sharkd will have same mmdbresolve process, including pipe descriptors to read and write. */
200 uat_clear(uat_get_table_by_name("MaxMind Database Paths"));
205 col_cleanup(&cfile.cinfo);
213 static const nstime_t *
214 sharkd_get_frame_ts(struct packet_provider_data *prov, guint32 frame_num)
216 if (prov->ref && prov->ref->num == frame_num)
217 return &prov->ref->abs_ts;
219 if (prov->prev_dis && prov->prev_dis->num == frame_num)
220 return &prov->prev_dis->abs_ts;
222 if (prov->prev_cap && prov->prev_cap->num == frame_num)
223 return &prov->prev_cap->abs_ts;
226 frame_data *fd = frame_data_sequence_find(prov->frames, frame_num);
228 return (fd) ? &fd->abs_ts : NULL;
235 sharkd_epan_new(capture_file *cf)
237 static const struct packet_provider_funcs funcs = {
239 cap_file_provider_get_interface_name,
240 cap_file_provider_get_interface_description,
241 cap_file_provider_get_user_comment
244 return epan_new(&cf->provider, &funcs);
248 process_packet(capture_file *cf, epan_dissect_t *edt,
249 gint64 offset, wtap_rec *rec, const guchar *pd)
254 /* If we're not running a display filter and we're not printing any
255 packet information, we don't need to do a dissection. This means
256 that all packets can be marked as 'passed'. */
259 /* The frame number of this packet, if we add it to the set of frames,
260 would be one more than the count of frames in the file so far. */
261 frame_data_init(&fdlocal, cf->count + 1, rec, offset, cum_bytes);
263 /* If we're going to print packet information, or we're going to
264 run a read filter, or display filter, or we're going to process taps, set up to
265 do a dissection and do so. */
267 if (gbl_resolv_flags.mac_name || gbl_resolv_flags.network_name ||
268 gbl_resolv_flags.transport_name)
269 /* Grab any resolved addresses */
270 host_name_lookup_process();
272 /* If we're running a read filter, prime the epan_dissect_t with that
275 epan_dissect_prime_with_dfilter(edt, cf->rfcode);
278 epan_dissect_prime_with_dfilter(edt, cf->dfcode);
280 /* This is the first and only pass, so prime the epan_dissect_t
281 with the hfids postdissectors want on the first pass. */
282 prime_epan_dissect_with_postdissector_wanted_hfids(edt);
284 frame_data_set_before_dissect(&fdlocal, &cf->elapsed_time,
285 &cf->provider.ref, cf->provider.prev_dis);
286 if (cf->provider.ref == &fdlocal) {
288 cf->provider.ref = &ref_frame;
291 epan_dissect_run(edt, cf->cd_t, rec,
292 frame_tvbuff_new(&cf->provider, &fdlocal, pd),
295 /* Run the read filter if we have one. */
297 passed = dfilter_apply_edt(cf->rfcode, edt);
301 frame_data_set_after_dissect(&fdlocal, &cum_bytes);
302 cf->provider.prev_cap = cf->provider.prev_dis = frame_data_sequence_add(cf->provider.frames, &fdlocal);
304 /* If we're not doing dissection then there won't be any dependent frames.
305 * More importantly, edt.pi.dependent_frames won't be initialized because
306 * epan hasn't been initialized.
307 * if we *are* doing dissection, then mark the dependent frames, but only
308 * if a display filter was given and it matches this packet.
310 if (edt && cf->dfcode) {
311 if (dfilter_apply_edt(cf->dfcode, edt)) {
312 g_slist_foreach(edt->pi.dependent_frames, find_and_mark_frame_depended_upon, cf->provider.frames);
318 /* if we don't add it to the frame_data_sequence, clean it up right now
320 frame_data_destroy(&fdlocal);
324 epan_dissect_reset(edt);
331 load_cap_file(capture_file *cf, int max_packet_count, gint64 max_byte_count)
334 gchar *err_info = NULL;
336 epan_dissect_t *edt = NULL;
339 /* Allocate a frame_data_sequence for all the frames. */
340 cf->provider.frames = new_frame_data_sequence();
343 gboolean create_proto_tree;
346 * Determine whether we need to create a protocol tree.
349 * we're going to apply a read filter;
351 * we're going to apply a display filter;
353 * a postdissector wants field values or protocols
357 (cf->rfcode != NULL || cf->dfcode != NULL || postdissectors_want_hfids());
359 /* We're not going to display the protocol tree on this pass,
360 so it's not going to be "visible". */
361 edt = epan_dissect_new(cf->epan, create_proto_tree, FALSE);
364 while (wtap_read(cf->provider.wth, &err, &err_info, &data_offset)) {
365 if (process_packet(cf, edt, data_offset, wtap_get_rec(cf->provider.wth),
366 wtap_get_buf_ptr(cf->provider.wth))) {
367 /* Stop reading if we have the maximum number of packets;
368 * When the -c option has not been used, max_packet_count
369 * starts at 0, which practically means, never stop reading.
370 * (unless we roll over max_packet_count ?)
372 if ( (--max_packet_count == 0) || (max_byte_count != 0 && data_offset >= max_byte_count)) {
373 err = 0; /* This is not an error */
380 epan_dissect_free(edt);
384 /* Close the sequential I/O side, to free up memory it requires. */
385 wtap_sequential_close(cf->provider.wth);
387 /* Allow the protocol dissectors to free up memory that they
388 * don't need after the sequential run-through of the packets. */
389 postseq_cleanup_all_protocols();
391 cf->provider.prev_dis = NULL;
392 cf->provider.prev_cap = NULL;
396 cfile_read_failure_message("sharkd", cf->filename, err, err_info);
403 cf_open(capture_file *cf, const char *fname, unsigned int type, gboolean is_tempfile, int *err)
408 wth = wtap_open_offline(fname, type, err, &err_info, TRUE);
412 /* The open succeeded. Fill in the information for this file. */
414 cf->provider.wth = wth;
415 cf->f_datalen = 0; /* not used, but set it anyway */
417 /* Set the file name because we need it to set the follow stream filter.
418 XXX - is that still true? We need it for other reasons, though,
420 cf->filename = g_strdup(fname);
422 /* Indicate whether it's a permanent or temporary file. */
423 cf->is_tempfile = is_tempfile;
425 /* No user changes yet. */
426 cf->unsaved_changes = FALSE;
428 cf->cd_t = wtap_file_type_subtype(cf->provider.wth);
429 cf->open_type = type;
431 cf->drops_known = FALSE;
433 cf->snap = wtap_snapshot_length(cf->provider.wth);
434 nstime_set_zero(&cf->elapsed_time);
435 cf->provider.ref = NULL;
436 cf->provider.prev_dis = NULL;
437 cf->provider.prev_cap = NULL;
439 /* Create new epan session for dissection. */
441 cf->epan = sharkd_epan_new(cf);
443 cf->state = FILE_READ_IN_PROGRESS;
445 wtap_set_cb_new_ipv4(cf->provider.wth, add_ipv4_name);
446 wtap_set_cb_new_ipv6(cf->provider.wth, (wtap_new_ipv6_callback_t) add_ipv6_name);
451 cfile_open_failure_message("sharkd", fname, *err, err_info);
456 * General errors and warnings are reported with an console message
460 failure_warning_message(const char *msg_format, va_list ap)
462 fprintf(stderr, "sharkd: ");
463 vfprintf(stderr, msg_format, ap);
464 fprintf(stderr, "\n");
468 * Open/create errors are reported with an console message in sharkd.
471 open_failure_message(const char *filename, int err, gboolean for_writing)
473 fprintf(stderr, "sharkd: ");
474 fprintf(stderr, file_open_error_message(err, for_writing), filename);
475 fprintf(stderr, "\n");
479 * Read errors are reported with an console message in sharkd.
482 read_failure_message(const char *filename, int err)
484 cmdarg_err("An error occurred while reading from the file \"%s\": %s.",
485 filename, g_strerror(err));
489 * Write errors are reported with an console message in sharkd.
492 write_failure_message(const char *filename, int err)
494 cmdarg_err("An error occurred while writing to the file \"%s\": %s.",
495 filename, g_strerror(err));
499 * Report additional information for an error in command-line arguments.
502 failure_message_cont(const char *msg_format, va_list ap)
504 vfprintf(stderr, msg_format, ap);
505 fprintf(stderr, "\n");
509 sharkd_cf_open(const char *fname, unsigned int type, gboolean is_tempfile, int *err)
511 return cf_open(&cfile, fname, type, is_tempfile, err);
515 sharkd_load_cap_file(void)
517 return load_cap_file(&cfile, 0, 0);
521 sharkd_get_frame(guint32 framenum)
523 return frame_data_sequence_find(cfile.provider.frames, framenum);
527 sharkd_dissect_request(guint32 framenum, guint32 frame_ref_num, guint32 prev_dis_num, sharkd_dissect_func_t cb, guint32 dissect_flags, void *data)
530 column_info *cinfo = (dissect_flags & SHARKD_DISSECT_FLAG_COLUMNS) ? &cfile.cinfo : NULL;
532 gboolean create_proto_tree;
533 wtap_rec rec; /* Record metadata */
534 Buffer buf; /* Record data */
537 char *err_info = NULL;
539 fdata = sharkd_get_frame(framenum);
544 ws_buffer_init(&buf, 1500);
546 if (!wtap_seek_read(cfile.provider.wth, fdata->file_off, &rec, &buf, &err, &err_info)) {
547 wtap_rec_cleanup(&rec);
548 ws_buffer_free(&buf);
549 return -1; /* error reading the record */
552 create_proto_tree = ((dissect_flags & SHARKD_DISSECT_FLAG_PROTO_TREE) ||
553 ((dissect_flags & SHARKD_DISSECT_FLAG_COLOR) && color_filters_used()) ||
554 (cinfo && have_custom_cols(cinfo)));
555 epan_dissect_init(&edt, cfile.epan, create_proto_tree, (dissect_flags & SHARKD_DISSECT_FLAG_PROTO_TREE));
557 if (dissect_flags & SHARKD_DISSECT_FLAG_COLOR) {
558 color_filters_prime_edt(&edt);
559 fdata->flags.need_colorize = 1;
563 col_custom_prime_edt(&edt, cinfo);
566 * XXX - need to catch an OutOfMemoryError exception and
567 * attempt to recover from it.
569 fdata->flags.ref_time = (framenum == frame_ref_num);
570 fdata->frame_ref_num = frame_ref_num;
571 fdata->prev_dis_num = prev_dis_num;
572 epan_dissect_run(&edt, cfile.cd_t, &rec,
573 frame_tvbuff_new_buffer(&cfile.provider, fdata, &buf),
577 /* "Stringify" non frame_data vals */
578 epan_dissect_fill_in_columns(&edt, FALSE, TRUE/* fill_fd_columns */);
581 cb(&edt, (dissect_flags & SHARKD_DISSECT_FLAG_PROTO_TREE) ? edt.tree : NULL,
582 cinfo, (dissect_flags & SHARKD_DISSECT_FLAG_BYTES) ? edt.pi.data_src : NULL,
585 epan_dissect_cleanup(&edt);
586 wtap_rec_cleanup(&rec);
587 ws_buffer_free(&buf);
591 /* based on packet_list_dissect_and_cache_record */
593 sharkd_dissect_columns(frame_data *fdata, guint32 frame_ref_num, guint32 prev_dis_num, column_info *cinfo, gboolean dissect_color)
596 gboolean create_proto_tree;
597 wtap_rec rec; /* Record metadata */
598 Buffer buf; /* Record data */
601 char *err_info = NULL;
604 ws_buffer_init(&buf, 1500);
606 if (!wtap_seek_read(cfile.provider.wth, fdata->file_off, &rec, &buf, &err, &err_info)) {
607 col_fill_in_error(cinfo, fdata, FALSE, FALSE /* fill_fd_columns */);
608 wtap_rec_cleanup(&rec);
609 ws_buffer_free(&buf);
610 return -1; /* error reading the record */
613 create_proto_tree = (dissect_color && color_filters_used()) || (cinfo && have_custom_cols(cinfo));
615 epan_dissect_init(&edt, cfile.epan, create_proto_tree, FALSE /* proto_tree_visible */);
618 color_filters_prime_edt(&edt);
619 fdata->flags.need_colorize = 1;
623 col_custom_prime_edt(&edt, cinfo);
626 * XXX - need to catch an OutOfMemoryError exception and
627 * attempt to recover from it.
629 fdata->flags.ref_time = (fdata->num == frame_ref_num);
630 fdata->frame_ref_num = frame_ref_num;
631 fdata->prev_dis_num = prev_dis_num;
632 epan_dissect_run(&edt, cfile.cd_t, &rec,
633 frame_tvbuff_new_buffer(&cfile.provider, fdata, &buf),
637 /* "Stringify" non frame_data vals */
638 epan_dissect_fill_in_columns(&edt, FALSE, TRUE/* fill_fd_columns */);
641 epan_dissect_cleanup(&edt);
642 wtap_rec_cleanup(&rec);
643 ws_buffer_free(&buf);
655 char *err_info = NULL;
658 gboolean create_proto_tree;
662 /* Get the union of the flags for all tap listeners. */
663 tap_flags = union_of_tap_listener_flags();
665 /* If any tap listeners require the columns, construct them. */
666 cinfo = (tap_flags & TL_REQUIRES_COLUMNS) ? &cfile.cinfo : NULL;
669 * Determine whether we need to create a protocol tree.
672 * one of the tap listeners is going to apply a filter;
674 * one of the tap listeners requires a protocol tree.
677 (have_filtering_tap_listeners() || (tap_flags & TL_REQUIRES_PROTO_TREE));
680 ws_buffer_init(&buf, 1500);
681 epan_dissect_init(&edt, cfile.epan, create_proto_tree, FALSE);
683 reset_tap_listeners();
685 for (framenum = 1; framenum <= cfile.count; framenum++) {
686 fdata = sharkd_get_frame(framenum);
688 if (!wtap_seek_read(cfile.provider.wth, fdata->file_off, &rec, &buf, &err, &err_info))
691 fdata->flags.ref_time = FALSE;
692 fdata->frame_ref_num = (framenum != 1) ? 1 : 0;
693 fdata->prev_dis_num = framenum - 1;
694 epan_dissect_run_with_taps(&edt, cfile.cd_t, &rec,
695 frame_tvbuff_new(&cfile.provider, fdata, ws_buffer_start_ptr(&buf)),
697 epan_dissect_reset(&edt);
700 wtap_rec_cleanup(&rec);
701 ws_buffer_free(&buf);
702 epan_dissect_cleanup(&edt);
704 draw_tap_listeners(TRUE);
710 sharkd_filter(const char *dftext, guint8 **result)
712 dfilter_t *dfcode = NULL;
714 guint32 framenum, prev_dis_num = 0;
715 guint32 frames_count;
719 char *err_info = NULL;
726 if (!dfilter_compile(dftext, &dfcode, &err_info)) {
731 /* if dfilter_compile() success, but (dfcode == NULL) all frames are matching */
732 if (dfcode == NULL) {
737 frames_count = cfile.count;
740 ws_buffer_init(&buf, 1500);
741 epan_dissect_init(&edt, cfile.epan, TRUE, FALSE);
744 result_bits = (guint8 *) g_malloc(2 + (frames_count / 8));
746 for (framenum = 1; framenum <= frames_count; framenum++) {
747 frame_data *fdata = sharkd_get_frame(framenum);
749 if ((framenum & 7) == 0) {
750 result_bits[(framenum / 8) - 1] = passed_bits;
754 if (!wtap_seek_read(cfile.provider.wth, fdata->file_off, &rec, &buf, &err, &err_info))
757 /* frame_data_set_before_dissect */
758 epan_dissect_prime_with_dfilter(&edt, dfcode);
760 fdata->flags.ref_time = FALSE;
761 fdata->frame_ref_num = (framenum != 1) ? 1 : 0;
762 fdata->prev_dis_num = prev_dis_num;
763 epan_dissect_run(&edt, cfile.cd_t, &rec,
764 frame_tvbuff_new_buffer(&cfile.provider, fdata, &buf),
767 if (dfilter_apply_edt(dfcode, &edt)) {
768 passed_bits |= (1 << (framenum % 8));
769 prev_dis_num = framenum;
772 /* if passed or ref -> frame_data_set_after_dissect */
774 epan_dissect_reset(&edt);
777 if ((framenum & 7) == 0)
779 result_bits[framenum / 8] = passed_bits;
781 wtap_rec_cleanup(&rec);
782 ws_buffer_free(&buf);
783 epan_dissect_cleanup(&edt);
785 dfilter_free(dfcode);
787 *result = result_bits;
793 sharkd_get_user_comment(const frame_data *fd)
795 return cap_file_provider_get_user_comment(&cfile.provider, fd);
799 sharkd_set_user_comment(frame_data *fd, const gchar *new_comment)
801 cap_file_provider_set_user_comment(&cfile.provider, fd, new_comment);
806 const char *sharkd_version(void)
808 /* based on get_ws_vcs_version_info(), but shorter */
817 * Editor modelines - https://www.wireshark.org/tools/modelines.html
822 * indent-tabs-mode: nil
825 * vi: set shiftwidth=2 tabstop=8 expandtab:
826 * :indentSize=2:tabSize=8:noTabs=true: