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 <ui/clopts_common.h>
27 #include <ui/cmdarg_err.h>
28 #include <wsutil/filesystem.h>
29 #include <wsutil/file_util.h>
30 #include <wsutil/privileges.h>
31 #include <wsutil/report_message.h>
32 #include <version_info.h>
33 #include <wiretap/wtap_opttypes.h>
34 #include <wiretap/pcapng.h>
36 #include <epan/decode_as.h>
37 #include <epan/timestamp.h>
38 #include <epan/packet.h>
39 #include "frame_tvbuff.h"
40 #include <epan/disabled_protos.h>
41 #include <epan/prefs.h>
42 #include <epan/column.h>
43 #include <epan/print.h>
44 #include <epan/addr_resolv.h>
46 #include "ui/ws_ui_util.h"
47 #include "ui/decode_as_utils.h"
48 #include "ui/filter_files.h"
49 #include "ui/tap_export_pdu.h"
50 #include "ui/failure_message.h"
51 #include <epan/epan_dissect.h>
53 #include <epan/uat-int.h>
54 #include <epan/secrets.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 char *init_progfile_dir_error;
107 char *err_msg = NULL;
109 int ret = EXIT_SUCCESS;
111 cmdarg_err_init(failure_warning_message, failure_message_cont);
114 * Get credential information for later use, and drop privileges
115 * before doing anything else.
116 * Let the user know if anything happened.
118 init_process_policies();
119 relinquish_special_privs_perm();
120 print_current_user();
123 * Attempt to get the pathname of the executable file.
125 init_progfile_dir_error = init_progfile_dir(argv[0]);
126 if (init_progfile_dir_error != NULL) {
127 fprintf(stderr, "sharkd: Can't get pathname of sharkd program: %s.\n",
128 init_progfile_dir_error);
131 /* Initialize the version information. */
132 ws_init_version_info("Sharkd (Wireshark)", NULL,
133 epan_get_compiled_version_info,
134 epan_get_runtime_version_info);
136 if (sharkd_init(argc, argv) < 0)
138 printf("cannot initialize sharkd\n");
143 init_report_message(failure_warning_message, failure_warning_message,
144 open_failure_message, read_failure_message,
145 write_failure_message);
147 timestamp_set_type(TS_RELATIVE);
148 timestamp_set_precision(TS_PREC_AUTO);
149 timestamp_set_seconds_type(TS_SECONDS_DEFAULT);
153 /* Register all dissectors; we must do this before checking for the
154 "-G" flag, as the "-G" flag dumps information registered by the
155 dissectors, and we must do it before we read the preferences, in
156 case any dissectors register preferences. */
157 if (!epan_init(NULL, NULL, TRUE)) {
158 ret = EPAN_INIT_FAIL;
164 /* Load libwireshark settings from the current profile. */
165 prefs_p = epan_load_settings();
167 read_filter_list(CFILTER_LIST);
169 if (!color_filters_init(&err_msg, NULL)) {
170 fprintf(stderr, "%s\n", err_msg);
174 cap_file_init(&cfile);
176 /* Notify all registered modules that have had any of their preferences
177 changed either from one of the preferences file or from the command
178 line that their preferences have changed. */
181 /* Build the column format array */
182 build_column_format_array(&cfile.cinfo, prefs_p->num_cols, TRUE);
184 #ifdef HAVE_MAXMINDDB
185 /* 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.
186 * Need to stop it, otherwise all sharkd will have same mmdbresolve process, including pipe descriptors to read and write. */
187 uat_clear(uat_get_table_by_name("MaxMind Database Paths"));
192 col_cleanup(&cfile.cinfo);
200 static const nstime_t *
201 sharkd_get_frame_ts(struct packet_provider_data *prov, guint32 frame_num)
203 if (prov->ref && prov->ref->num == frame_num)
204 return &prov->ref->abs_ts;
206 if (prov->prev_dis && prov->prev_dis->num == frame_num)
207 return &prov->prev_dis->abs_ts;
209 if (prov->prev_cap && prov->prev_cap->num == frame_num)
210 return &prov->prev_cap->abs_ts;
213 frame_data *fd = frame_data_sequence_find(prov->frames, frame_num);
215 return (fd) ? &fd->abs_ts : NULL;
222 sharkd_epan_new(capture_file *cf)
224 static const struct packet_provider_funcs funcs = {
226 cap_file_provider_get_interface_name,
227 cap_file_provider_get_interface_description,
228 cap_file_provider_get_user_comment
231 return epan_new(&cf->provider, &funcs);
235 process_packet(capture_file *cf, epan_dissect_t *edt,
236 gint64 offset, wtap_rec *rec, const guchar *pd)
241 /* If we're not running a display filter and we're not printing any
242 packet information, we don't need to do a dissection. This means
243 that all packets can be marked as 'passed'. */
246 /* The frame number of this packet, if we add it to the set of frames,
247 would be one more than the count of frames in the file so far. */
248 frame_data_init(&fdlocal, cf->count + 1, rec, offset, cum_bytes);
250 /* If we're going to print packet information, or we're going to
251 run a read filter, or display filter, or we're going to process taps, set up to
252 do a dissection and do so. */
254 if (gbl_resolv_flags.mac_name || gbl_resolv_flags.network_name ||
255 gbl_resolv_flags.transport_name)
256 /* Grab any resolved addresses */
257 host_name_lookup_process();
259 /* If we're running a read filter, prime the epan_dissect_t with that
262 epan_dissect_prime_with_dfilter(edt, cf->rfcode);
265 epan_dissect_prime_with_dfilter(edt, cf->dfcode);
267 /* This is the first and only pass, so prime the epan_dissect_t
268 with the hfids postdissectors want on the first pass. */
269 prime_epan_dissect_with_postdissector_wanted_hfids(edt);
271 frame_data_set_before_dissect(&fdlocal, &cf->elapsed_time,
272 &cf->provider.ref, cf->provider.prev_dis);
273 if (cf->provider.ref == &fdlocal) {
275 cf->provider.ref = &ref_frame;
278 epan_dissect_run(edt, cf->cd_t, rec,
279 frame_tvbuff_new(&cf->provider, &fdlocal, pd),
282 /* Run the read filter if we have one. */
284 passed = dfilter_apply_edt(cf->rfcode, edt);
288 frame_data_set_after_dissect(&fdlocal, &cum_bytes);
289 cf->provider.prev_cap = cf->provider.prev_dis = frame_data_sequence_add(cf->provider.frames, &fdlocal);
291 /* If we're not doing dissection then there won't be any dependent frames.
292 * More importantly, edt.pi.dependent_frames won't be initialized because
293 * epan hasn't been initialized.
294 * if we *are* doing dissection, then mark the dependent frames, but only
295 * if a display filter was given and it matches this packet.
297 if (edt && cf->dfcode) {
298 if (dfilter_apply_edt(cf->dfcode, edt)) {
299 g_slist_foreach(edt->pi.dependent_frames, find_and_mark_frame_depended_upon, cf->provider.frames);
305 /* if we don't add it to the frame_data_sequence, clean it up right now
307 frame_data_destroy(&fdlocal);
311 epan_dissect_reset(edt);
318 load_cap_file(capture_file *cf, int max_packet_count, gint64 max_byte_count)
321 gchar *err_info = NULL;
323 epan_dissect_t *edt = NULL;
326 /* Allocate a frame_data_sequence for all the frames. */
327 cf->provider.frames = new_frame_data_sequence();
330 gboolean create_proto_tree;
333 * Determine whether we need to create a protocol tree.
336 * we're going to apply a read filter;
338 * we're going to apply a display filter;
340 * a postdissector wants field values or protocols
344 (cf->rfcode != NULL || cf->dfcode != NULL || postdissectors_want_hfids());
346 /* We're not going to display the protocol tree on this pass,
347 so it's not going to be "visible". */
348 edt = epan_dissect_new(cf->epan, create_proto_tree, FALSE);
351 while (wtap_read(cf->provider.wth, &err, &err_info, &data_offset)) {
352 if (process_packet(cf, edt, data_offset, wtap_get_rec(cf->provider.wth),
353 wtap_get_buf_ptr(cf->provider.wth))) {
354 /* Stop reading if we have the maximum number of packets;
355 * When the -c option has not been used, max_packet_count
356 * starts at 0, which practically means, never stop reading.
357 * (unless we roll over max_packet_count ?)
359 if ( (--max_packet_count == 0) || (max_byte_count != 0 && data_offset >= max_byte_count)) {
360 err = 0; /* This is not an error */
367 epan_dissect_free(edt);
371 /* Close the sequential I/O side, to free up memory it requires. */
372 wtap_sequential_close(cf->provider.wth);
374 /* Allow the protocol dissectors to free up memory that they
375 * don't need after the sequential run-through of the packets. */
376 postseq_cleanup_all_protocols();
378 cf->provider.prev_dis = NULL;
379 cf->provider.prev_cap = NULL;
383 cfile_read_failure_message("sharkd", cf->filename, err, err_info);
390 cf_open(capture_file *cf, const char *fname, unsigned int type, gboolean is_tempfile, int *err)
395 wth = wtap_open_offline(fname, type, err, &err_info, TRUE);
399 /* The open succeeded. Fill in the information for this file. */
401 cf->provider.wth = wth;
402 cf->f_datalen = 0; /* not used, but set it anyway */
404 /* Set the file name because we need it to set the follow stream filter.
405 XXX - is that still true? We need it for other reasons, though,
407 cf->filename = g_strdup(fname);
409 /* Indicate whether it's a permanent or temporary file. */
410 cf->is_tempfile = is_tempfile;
412 /* No user changes yet. */
413 cf->unsaved_changes = FALSE;
415 cf->cd_t = wtap_file_type_subtype(cf->provider.wth);
416 cf->open_type = type;
418 cf->drops_known = FALSE;
420 cf->snap = wtap_snapshot_length(cf->provider.wth);
421 nstime_set_zero(&cf->elapsed_time);
422 cf->provider.ref = NULL;
423 cf->provider.prev_dis = NULL;
424 cf->provider.prev_cap = NULL;
426 /* Create new epan session for dissection. */
428 cf->epan = sharkd_epan_new(cf);
430 cf->state = FILE_READ_IN_PROGRESS;
432 wtap_set_cb_new_ipv4(cf->provider.wth, add_ipv4_name);
433 wtap_set_cb_new_ipv6(cf->provider.wth, (wtap_new_ipv6_callback_t) add_ipv6_name);
434 wtap_set_cb_new_secrets(cf->provider.wth, secrets_wtap_callback);
439 cfile_open_failure_message("sharkd", fname, *err, err_info);
444 * General errors and warnings are reported with an console message
448 failure_warning_message(const char *msg_format, va_list ap)
450 fprintf(stderr, "sharkd: ");
451 vfprintf(stderr, msg_format, ap);
452 fprintf(stderr, "\n");
456 * Open/create errors are reported with an console message in sharkd.
459 open_failure_message(const char *filename, int err, gboolean for_writing)
461 fprintf(stderr, "sharkd: ");
462 fprintf(stderr, file_open_error_message(err, for_writing), filename);
463 fprintf(stderr, "\n");
467 * Read errors are reported with an console message in sharkd.
470 read_failure_message(const char *filename, int err)
472 cmdarg_err("An error occurred while reading from the file \"%s\": %s.",
473 filename, g_strerror(err));
477 * Write errors are reported with an console message in sharkd.
480 write_failure_message(const char *filename, int err)
482 cmdarg_err("An error occurred while writing to the file \"%s\": %s.",
483 filename, g_strerror(err));
487 * Report additional information for an error in command-line arguments.
490 failure_message_cont(const char *msg_format, va_list ap)
492 vfprintf(stderr, msg_format, ap);
493 fprintf(stderr, "\n");
497 sharkd_cf_open(const char *fname, unsigned int type, gboolean is_tempfile, int *err)
499 return cf_open(&cfile, fname, type, is_tempfile, err);
503 sharkd_load_cap_file(void)
505 return load_cap_file(&cfile, 0, 0);
509 sharkd_get_frame(guint32 framenum)
511 return frame_data_sequence_find(cfile.provider.frames, framenum);
515 sharkd_dissect_request(guint32 framenum, guint32 frame_ref_num, guint32 prev_dis_num, sharkd_dissect_func_t cb, guint32 dissect_flags, void *data)
518 column_info *cinfo = (dissect_flags & SHARKD_DISSECT_FLAG_COLUMNS) ? &cfile.cinfo : NULL;
520 gboolean create_proto_tree;
521 wtap_rec rec; /* Record metadata */
522 Buffer buf; /* Record data */
525 char *err_info = NULL;
527 fdata = sharkd_get_frame(framenum);
532 ws_buffer_init(&buf, 1500);
534 if (!wtap_seek_read(cfile.provider.wth, fdata->file_off, &rec, &buf, &err, &err_info)) {
535 wtap_rec_cleanup(&rec);
536 ws_buffer_free(&buf);
537 return -1; /* error reading the record */
540 create_proto_tree = ((dissect_flags & SHARKD_DISSECT_FLAG_PROTO_TREE) ||
541 ((dissect_flags & SHARKD_DISSECT_FLAG_COLOR) && color_filters_used()) ||
542 (cinfo && have_custom_cols(cinfo)));
543 epan_dissect_init(&edt, cfile.epan, create_proto_tree, (dissect_flags & SHARKD_DISSECT_FLAG_PROTO_TREE));
545 if (dissect_flags & SHARKD_DISSECT_FLAG_COLOR) {
546 color_filters_prime_edt(&edt);
547 fdata->need_colorize = 1;
551 col_custom_prime_edt(&edt, cinfo);
554 * XXX - need to catch an OutOfMemoryError exception and
555 * attempt to recover from it.
557 fdata->ref_time = (framenum == frame_ref_num);
558 fdata->frame_ref_num = frame_ref_num;
559 fdata->prev_dis_num = prev_dis_num;
560 epan_dissect_run(&edt, cfile.cd_t, &rec,
561 frame_tvbuff_new_buffer(&cfile.provider, fdata, &buf),
565 /* "Stringify" non frame_data vals */
566 epan_dissect_fill_in_columns(&edt, FALSE, TRUE/* fill_fd_columns */);
569 cb(&edt, (dissect_flags & SHARKD_DISSECT_FLAG_PROTO_TREE) ? edt.tree : NULL,
570 cinfo, (dissect_flags & SHARKD_DISSECT_FLAG_BYTES) ? edt.pi.data_src : NULL,
573 epan_dissect_cleanup(&edt);
574 wtap_rec_cleanup(&rec);
575 ws_buffer_free(&buf);
579 /* based on packet_list_dissect_and_cache_record */
581 sharkd_dissect_columns(frame_data *fdata, guint32 frame_ref_num, guint32 prev_dis_num, column_info *cinfo, gboolean dissect_color)
584 gboolean create_proto_tree;
585 wtap_rec rec; /* Record metadata */
586 Buffer buf; /* Record data */
589 char *err_info = NULL;
592 ws_buffer_init(&buf, 1500);
594 if (!wtap_seek_read(cfile.provider.wth, fdata->file_off, &rec, &buf, &err, &err_info)) {
595 col_fill_in_error(cinfo, fdata, FALSE, FALSE /* fill_fd_columns */);
596 wtap_rec_cleanup(&rec);
597 ws_buffer_free(&buf);
598 return -1; /* error reading the record */
601 create_proto_tree = (dissect_color && color_filters_used()) || (cinfo && have_custom_cols(cinfo));
603 epan_dissect_init(&edt, cfile.epan, create_proto_tree, FALSE /* proto_tree_visible */);
606 color_filters_prime_edt(&edt);
607 fdata->need_colorize = 1;
611 col_custom_prime_edt(&edt, cinfo);
614 * XXX - need to catch an OutOfMemoryError exception and
615 * attempt to recover from it.
617 fdata->ref_time = (fdata->num == frame_ref_num);
618 fdata->frame_ref_num = frame_ref_num;
619 fdata->prev_dis_num = prev_dis_num;
620 epan_dissect_run(&edt, cfile.cd_t, &rec,
621 frame_tvbuff_new_buffer(&cfile.provider, fdata, &buf),
625 /* "Stringify" non frame_data vals */
626 epan_dissect_fill_in_columns(&edt, FALSE, TRUE/* fill_fd_columns */);
629 epan_dissect_cleanup(&edt);
630 wtap_rec_cleanup(&rec);
631 ws_buffer_free(&buf);
643 char *err_info = NULL;
646 gboolean create_proto_tree;
650 /* Get the union of the flags for all tap listeners. */
651 tap_flags = union_of_tap_listener_flags();
653 /* If any tap listeners require the columns, construct them. */
654 cinfo = (tap_flags & TL_REQUIRES_COLUMNS) ? &cfile.cinfo : NULL;
657 * Determine whether we need to create a protocol tree.
660 * one of the tap listeners is going to apply a filter;
662 * one of the tap listeners requires a protocol tree.
665 (have_filtering_tap_listeners() || (tap_flags & TL_REQUIRES_PROTO_TREE));
668 ws_buffer_init(&buf, 1500);
669 epan_dissect_init(&edt, cfile.epan, create_proto_tree, FALSE);
671 reset_tap_listeners();
673 for (framenum = 1; framenum <= cfile.count; framenum++) {
674 fdata = sharkd_get_frame(framenum);
676 if (!wtap_seek_read(cfile.provider.wth, fdata->file_off, &rec, &buf, &err, &err_info))
679 fdata->ref_time = FALSE;
680 fdata->frame_ref_num = (framenum != 1) ? 1 : 0;
681 fdata->prev_dis_num = framenum - 1;
682 epan_dissect_run_with_taps(&edt, cfile.cd_t, &rec,
683 frame_tvbuff_new(&cfile.provider, fdata, ws_buffer_start_ptr(&buf)),
685 epan_dissect_reset(&edt);
688 wtap_rec_cleanup(&rec);
689 ws_buffer_free(&buf);
690 epan_dissect_cleanup(&edt);
692 draw_tap_listeners(TRUE);
698 sharkd_filter(const char *dftext, guint8 **result)
700 dfilter_t *dfcode = NULL;
702 guint32 framenum, prev_dis_num = 0;
703 guint32 frames_count;
707 char *err_info = NULL;
714 if (!dfilter_compile(dftext, &dfcode, &err_info)) {
719 /* if dfilter_compile() success, but (dfcode == NULL) all frames are matching */
720 if (dfcode == NULL) {
725 frames_count = cfile.count;
728 ws_buffer_init(&buf, 1500);
729 epan_dissect_init(&edt, cfile.epan, TRUE, FALSE);
732 result_bits = (guint8 *) g_malloc(2 + (frames_count / 8));
734 for (framenum = 1; framenum <= frames_count; framenum++) {
735 frame_data *fdata = sharkd_get_frame(framenum);
737 if ((framenum & 7) == 0) {
738 result_bits[(framenum / 8) - 1] = passed_bits;
742 if (!wtap_seek_read(cfile.provider.wth, fdata->file_off, &rec, &buf, &err, &err_info))
745 /* frame_data_set_before_dissect */
746 epan_dissect_prime_with_dfilter(&edt, dfcode);
748 fdata->ref_time = FALSE;
749 fdata->frame_ref_num = (framenum != 1) ? 1 : 0;
750 fdata->prev_dis_num = prev_dis_num;
751 epan_dissect_run(&edt, cfile.cd_t, &rec,
752 frame_tvbuff_new_buffer(&cfile.provider, fdata, &buf),
755 if (dfilter_apply_edt(dfcode, &edt)) {
756 passed_bits |= (1 << (framenum % 8));
757 prev_dis_num = framenum;
760 /* if passed or ref -> frame_data_set_after_dissect */
762 epan_dissect_reset(&edt);
765 if ((framenum & 7) == 0)
767 result_bits[framenum / 8] = passed_bits;
769 wtap_rec_cleanup(&rec);
770 ws_buffer_free(&buf);
771 epan_dissect_cleanup(&edt);
773 dfilter_free(dfcode);
775 *result = result_bits;
781 sharkd_get_user_comment(const frame_data *fd)
783 return cap_file_provider_get_user_comment(&cfile.provider, fd);
787 sharkd_set_user_comment(frame_data *fd, const gchar *new_comment)
789 cap_file_provider_set_user_comment(&cfile.provider, fd, new_comment);
794 const char *sharkd_version(void)
796 /* based on get_ws_vcs_version_info(), but shorter */
805 * Editor modelines - https://www.wireshark.org/tools/modelines.html
810 * indent-tabs-mode: nil
813 * vi: set shiftwidth=2 tabstop=8 expandtab:
814 * :indentSize=2:tabSize=8:noTabs=true: