forked from osmocom/wireshark
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5268 lines
175 KiB
5268 lines
175 KiB
/* file.c
|
|
* File I/O routines
|
|
*
|
|
* Wireshark - Network traffic analyzer
|
|
* By Gerald Combs <gerald@wireshark.org>
|
|
* Copyright 1998 Gerald Combs
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
#include <config.h>
|
|
#define WS_LOG_DOMAIN LOG_DOMAIN_CAPTURE
|
|
|
|
#include <time.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
|
|
#include <wsutil/file_util.h>
|
|
#include <wsutil/filesystem.h>
|
|
#include <wsutil/json_dumper.h>
|
|
#include <wsutil/wslog.h>
|
|
#include <wsutil/ws_assert.h>
|
|
#include <wsutil/version_info.h>
|
|
|
|
#include <wiretap/merge.h>
|
|
|
|
#include <epan/exceptions.h>
|
|
#include <epan/epan.h>
|
|
#include <epan/column.h>
|
|
#include <epan/packet.h>
|
|
#include <epan/column-utils.h>
|
|
#include <epan/expert.h>
|
|
#include <epan/prefs.h>
|
|
#include <epan/dfilter/dfilter.h>
|
|
#include <epan/epan_dissect.h>
|
|
#include <epan/tap.h>
|
|
#include <epan/timestamp.h>
|
|
#include <epan/strutil.h>
|
|
#include <epan/addr_resolv.h>
|
|
#include <epan/color_filters.h>
|
|
#include <epan/secrets.h>
|
|
|
|
#include "cfile.h"
|
|
#include "file.h"
|
|
#include "fileset.h"
|
|
#include "frame_tvbuff.h"
|
|
|
|
#include "ui/alert_box.h"
|
|
#include "ui/simple_dialog.h"
|
|
#include "ui/main_statusbar.h"
|
|
#include "ui/progress_dlg.h"
|
|
#include "ui/urls.h"
|
|
#include "ui/ws_ui_util.h"
|
|
#include "ui/packet_list_utils.h"
|
|
|
|
/* Needed for addrinfo */
|
|
#include <sys/types.h>
|
|
|
|
#ifdef HAVE_SYS_SOCKET_H
|
|
#include <sys/socket.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_NETINET_IN_H
|
|
# include <netinet/in.h>
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
# include <winsock2.h>
|
|
# include <ws2tcpip.h>
|
|
#endif
|
|
|
|
static gboolean read_record(capture_file *cf, wtap_rec *rec, Buffer *buf,
|
|
dfilter_t *dfcode, epan_dissect_t *edt, column_info *cinfo, gint64 offset);
|
|
|
|
static void rescan_packets(capture_file *cf, const char *action, const char *action_item, gboolean redissect);
|
|
|
|
typedef enum {
|
|
MR_NOTMATCHED,
|
|
MR_MATCHED,
|
|
MR_ERROR
|
|
} match_result;
|
|
typedef match_result (*ws_match_function)(capture_file *, frame_data *,
|
|
wtap_rec *, Buffer *, void *);
|
|
static match_result match_protocol_tree(capture_file *cf, frame_data *fdata,
|
|
wtap_rec *, Buffer *, void *criterion);
|
|
static void match_subtree_text(proto_node *node, gpointer data);
|
|
static match_result match_summary_line(capture_file *cf, frame_data *fdata,
|
|
wtap_rec *, Buffer *, void *criterion);
|
|
static match_result match_narrow_and_wide(capture_file *cf, frame_data *fdata,
|
|
wtap_rec *, Buffer *, void *criterion);
|
|
static match_result match_narrow_and_wide_case(capture_file *cf, frame_data *fdata,
|
|
wtap_rec *, Buffer *, void *criterion);
|
|
static match_result match_narrow(capture_file *cf, frame_data *fdata,
|
|
wtap_rec *, Buffer *, void *criterion);
|
|
static match_result match_narrow_case(capture_file *cf, frame_data *fdata,
|
|
wtap_rec *, Buffer *, void *criterion);
|
|
static match_result match_wide(capture_file *cf, frame_data *fdata,
|
|
wtap_rec *, Buffer *, void *criterion);
|
|
static match_result match_wide_case(capture_file *cf, frame_data *fdata,
|
|
wtap_rec *, Buffer *, void *criterion);
|
|
static match_result match_binary(capture_file *cf, frame_data *fdata,
|
|
wtap_rec *, Buffer *, void *criterion);
|
|
static match_result match_regex(capture_file *cf, frame_data *fdata,
|
|
wtap_rec *, Buffer *, void *criterion);
|
|
static match_result match_dfilter(capture_file *cf, frame_data *fdata,
|
|
wtap_rec *, Buffer *, void *criterion);
|
|
static match_result match_marked(capture_file *cf, frame_data *fdata,
|
|
wtap_rec *, Buffer *, void *criterion);
|
|
static match_result match_time_reference(capture_file *cf, frame_data *fdata,
|
|
wtap_rec *, Buffer *, void *criterion);
|
|
static gboolean find_packet(capture_file *cf, ws_match_function match_function,
|
|
void *criterion, search_direction dir);
|
|
|
|
static void cf_rename_failure_alert_box(const char *filename, int err);
|
|
|
|
/* Seconds spent processing packets between pushing UI updates. */
|
|
#define PROGBAR_UPDATE_INTERVAL 0.150
|
|
|
|
/* Show the progress bar after this many seconds. */
|
|
#define PROGBAR_SHOW_DELAY 0.5
|
|
|
|
/*
|
|
* Maximum number of records we support in a file.
|
|
*
|
|
* It is, at most, the maximum value of a guint32, as we use a guint32
|
|
* for the frame number.
|
|
*
|
|
* We allow it to be set to a lower value; see issue #16908 for why
|
|
* we're doing this. Thanks, Qt!
|
|
*/
|
|
static guint32 max_records = G_MAXUINT32;
|
|
|
|
void
|
|
cf_set_max_records(guint max_records_arg)
|
|
{
|
|
max_records = max_records_arg;
|
|
}
|
|
|
|
/*
|
|
* We could probably use g_signal_...() instead of the callbacks below but that
|
|
* would require linking our CLI programs to libgobject and creating an object
|
|
* instance for the signals.
|
|
*/
|
|
typedef struct {
|
|
cf_callback_t cb_fct;
|
|
gpointer user_data;
|
|
} cf_callback_data_t;
|
|
|
|
static GList *cf_callbacks = NULL;
|
|
|
|
static void
|
|
cf_callback_invoke(int event, gpointer data)
|
|
{
|
|
cf_callback_data_t *cb;
|
|
GList *cb_item = cf_callbacks;
|
|
|
|
/* there should be at least one interested */
|
|
ws_assert(cb_item != NULL);
|
|
|
|
while (cb_item != NULL) {
|
|
cb = (cf_callback_data_t *)cb_item->data;
|
|
cb->cb_fct(event, data, cb->user_data);
|
|
cb_item = g_list_next(cb_item);
|
|
}
|
|
}
|
|
|
|
void
|
|
cf_callback_add(cf_callback_t func, gpointer user_data)
|
|
{
|
|
cf_callback_data_t *cb;
|
|
|
|
cb = g_new(cf_callback_data_t,1);
|
|
cb->cb_fct = func;
|
|
cb->user_data = user_data;
|
|
|
|
cf_callbacks = g_list_prepend(cf_callbacks, cb);
|
|
}
|
|
|
|
void
|
|
cf_callback_remove(cf_callback_t func, gpointer user_data)
|
|
{
|
|
cf_callback_data_t *cb;
|
|
GList *cb_item = cf_callbacks;
|
|
|
|
while (cb_item != NULL) {
|
|
cb = (cf_callback_data_t *)cb_item->data;
|
|
if (cb->cb_fct == func && cb->user_data == user_data) {
|
|
cf_callbacks = g_list_remove(cf_callbacks, cb);
|
|
g_free(cb);
|
|
return;
|
|
}
|
|
cb_item = g_list_next(cb_item);
|
|
}
|
|
|
|
ws_assert_not_reached();
|
|
}
|
|
|
|
gulong
|
|
cf_get_computed_elapsed(capture_file *cf)
|
|
{
|
|
return cf->computed_elapsed;
|
|
}
|
|
|
|
static void
|
|
compute_elapsed(capture_file *cf, gint64 start_time)
|
|
{
|
|
gint64 delta_time = g_get_monotonic_time() - start_time;
|
|
|
|
cf->computed_elapsed = (gulong) (delta_time / 1000); /* ms */
|
|
}
|
|
|
|
static const nstime_t *
|
|
ws_get_frame_ts(struct packet_provider_data *prov, guint32 frame_num)
|
|
{
|
|
if (prov->prev_dis && prov->prev_dis->num == frame_num)
|
|
return &prov->prev_dis->abs_ts;
|
|
|
|
if (prov->prev_cap && prov->prev_cap->num == frame_num)
|
|
return &prov->prev_cap->abs_ts;
|
|
|
|
if (prov->frames) {
|
|
frame_data *fd = frame_data_sequence_find(prov->frames, frame_num);
|
|
|
|
return (fd) ? &fd->abs_ts : NULL;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static epan_t *
|
|
ws_epan_new(capture_file *cf)
|
|
{
|
|
static const struct packet_provider_funcs funcs = {
|
|
ws_get_frame_ts,
|
|
cap_file_provider_get_interface_name,
|
|
cap_file_provider_get_interface_description,
|
|
cap_file_provider_get_modified_block
|
|
};
|
|
|
|
return epan_new(&cf->provider, &funcs);
|
|
}
|
|
|
|
cf_status_t
|
|
cf_open(capture_file *cf, const char *fname, unsigned int type, gboolean is_tempfile, int *err)
|
|
{
|
|
wtap *wth;
|
|
gchar *err_info;
|
|
|
|
wth = wtap_open_offline(fname, type, err, &err_info, TRUE);
|
|
if (wth == NULL)
|
|
goto fail;
|
|
|
|
/* The open succeeded. Close whatever capture file we had open,
|
|
and fill in the information for this file. */
|
|
cf_close(cf);
|
|
|
|
/* Initialize the record metadata. */
|
|
wtap_rec_init(&cf->rec);
|
|
|
|
/* XXX - we really want to initialize this after we've read all
|
|
the packets, so we know how much we'll ultimately need. */
|
|
ws_buffer_init(&cf->buf, 1514);
|
|
|
|
/* We're about to start reading the file. */
|
|
cf->state = FILE_READ_IN_PROGRESS;
|
|
|
|
cf->provider.wth = wth;
|
|
cf->f_datalen = 0;
|
|
|
|
/* Set the file name because we need it to set the follow stream filter.
|
|
XXX - is that still true? We need it for other reasons, though,
|
|
in any case. */
|
|
cf->filename = g_strdup(fname);
|
|
|
|
/* Indicate whether it's a permanent or temporary file. */
|
|
cf->is_tempfile = is_tempfile;
|
|
|
|
/* No user changes yet. */
|
|
cf->unsaved_changes = FALSE;
|
|
|
|
cf->computed_elapsed = 0;
|
|
|
|
cf->cd_t = wtap_file_type_subtype(cf->provider.wth);
|
|
cf->open_type = type;
|
|
cf->linktypes = g_array_sized_new(FALSE, FALSE, (guint) sizeof(int), 1);
|
|
cf->count = 0;
|
|
cf->packet_comment_count = 0;
|
|
cf->displayed_count = 0;
|
|
cf->marked_count = 0;
|
|
cf->ignored_count = 0;
|
|
cf->ref_time_count = 0;
|
|
cf->drops_known = FALSE;
|
|
cf->drops = 0;
|
|
cf->snap = wtap_snapshot_length(cf->provider.wth);
|
|
|
|
/* Allocate a frame_data_sequence for the frames in this file */
|
|
cf->provider.frames = new_frame_data_sequence();
|
|
|
|
nstime_set_zero(&cf->elapsed_time);
|
|
cf->provider.ref = NULL;
|
|
cf->provider.prev_dis = NULL;
|
|
cf->provider.prev_cap = NULL;
|
|
cf->cum_bytes = 0;
|
|
|
|
/* Create new epan session for dissection.
|
|
* (The old one was freed in cf_close().)
|
|
*/
|
|
cf->epan = ws_epan_new(cf);
|
|
|
|
packet_list_queue_draw();
|
|
cf_callback_invoke(cf_cb_file_opened, cf);
|
|
|
|
wtap_set_cb_new_ipv4(cf->provider.wth, add_ipv4_name);
|
|
wtap_set_cb_new_ipv6(cf->provider.wth, (wtap_new_ipv6_callback_t) add_ipv6_name);
|
|
wtap_set_cb_new_secrets(cf->provider.wth, secrets_wtap_callback);
|
|
|
|
return CF_OK;
|
|
|
|
fail:
|
|
cfile_open_failure_alert_box(fname, *err, err_info);
|
|
return CF_ERROR;
|
|
}
|
|
|
|
/*
|
|
* Add an encapsulation type to cf->linktypes.
|
|
*/
|
|
static void
|
|
cf_add_encapsulation_type(capture_file *cf, int encap)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < cf->linktypes->len; i++) {
|
|
if (g_array_index(cf->linktypes, gint, i) == encap)
|
|
return; /* it's already there */
|
|
}
|
|
/* It's not already there - add it. */
|
|
g_array_append_val(cf->linktypes, encap);
|
|
}
|
|
|
|
/* Reset everything to a pristine state */
|
|
void
|
|
cf_close(capture_file *cf)
|
|
{
|
|
cf->stop_flag = FALSE;
|
|
if (cf->state == FILE_CLOSED)
|
|
return; /* Nothing to do */
|
|
|
|
/* Die if we're in the middle of reading a file. */
|
|
ws_assert(cf->state != FILE_READ_IN_PROGRESS);
|
|
ws_assert(!cf->read_lock);
|
|
|
|
cf_callback_invoke(cf_cb_file_closing, cf);
|
|
|
|
/* close things, if not already closed before */
|
|
color_filters_cleanup();
|
|
|
|
if (cf->provider.wth) {
|
|
wtap_close(cf->provider.wth);
|
|
cf->provider.wth = NULL;
|
|
}
|
|
/* We have no file open... */
|
|
if (cf->filename != NULL) {
|
|
/* If it's a temporary file, remove it. */
|
|
if (cf->is_tempfile)
|
|
ws_unlink(cf->filename);
|
|
g_free(cf->filename);
|
|
cf->filename = NULL;
|
|
}
|
|
/* ...which means we have no changes to that file to save. */
|
|
cf->unsaved_changes = FALSE;
|
|
|
|
/* no open_routine type */
|
|
cf->open_type = WTAP_TYPE_AUTO;
|
|
|
|
/* Clean up the record metadata. */
|
|
wtap_rec_cleanup(&cf->rec);
|
|
|
|
/* Clear the packet list. */
|
|
packet_list_freeze();
|
|
packet_list_clear();
|
|
packet_list_thaw();
|
|
|
|
/* Free up the packet buffer. */
|
|
ws_buffer_free(&cf->buf);
|
|
|
|
dfilter_free(cf->rfcode);
|
|
cf->rfcode = NULL;
|
|
if (cf->provider.frames != NULL) {
|
|
free_frame_data_sequence(cf->provider.frames);
|
|
cf->provider.frames = NULL;
|
|
}
|
|
if (cf->provider.frames_modified_blocks) {
|
|
g_tree_destroy(cf->provider.frames_modified_blocks);
|
|
cf->provider.frames_modified_blocks = NULL;
|
|
}
|
|
cf_unselect_packet(cf); /* nothing to select */
|
|
cf->first_displayed = 0;
|
|
cf->last_displayed = 0;
|
|
|
|
/* No frames, no frame selected, no field in that frame selected. */
|
|
cf->count = 0;
|
|
cf->current_frame = NULL;
|
|
cf->finfo_selected = NULL;
|
|
|
|
/* No frame link-layer types, either. */
|
|
if (cf->linktypes != NULL) {
|
|
g_array_free(cf->linktypes, TRUE);
|
|
cf->linktypes = NULL;
|
|
}
|
|
|
|
cf->f_datalen = 0;
|
|
nstime_set_zero(&cf->elapsed_time);
|
|
|
|
reset_tap_listeners();
|
|
|
|
epan_free(cf->epan);
|
|
cf->epan = NULL;
|
|
|
|
/* We have no file open. */
|
|
cf->state = FILE_CLOSED;
|
|
|
|
cf_callback_invoke(cf_cb_file_closed, cf);
|
|
}
|
|
|
|
/*
|
|
* TRUE if the progress dialog doesn't exist and it looks like we'll
|
|
* take > 2s to load, FALSE otherwise.
|
|
*/
|
|
static inline gboolean
|
|
progress_is_slow(progdlg_t *progdlg, GTimer *prog_timer, gint64 size, gint64 pos)
|
|
{
|
|
double elapsed;
|
|
|
|
if (progdlg) return FALSE;
|
|
elapsed = g_timer_elapsed(prog_timer, NULL);
|
|
if ((elapsed / 2 > PROGBAR_SHOW_DELAY && (size / pos) > 2) /* It looks like we're going to be slow. */
|
|
|| elapsed > PROGBAR_SHOW_DELAY) { /* We are indeed slow. */
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static float
|
|
calc_progbar_val(capture_file *cf, gint64 size, gint64 file_pos, gchar *status_str, gulong status_size)
|
|
{
|
|
float progbar_val;
|
|
|
|
progbar_val = (gfloat) file_pos / (gfloat) size;
|
|
if (progbar_val > 1.0) {
|
|
|
|
/* The file probably grew while we were reading it.
|
|
* Update file size, and try again.
|
|
*/
|
|
size = wtap_file_size(cf->provider.wth, NULL);
|
|
|
|
if (size >= 0)
|
|
progbar_val = (gfloat) file_pos / (gfloat) size;
|
|
|
|
/* If it's still > 1, either "wtap_file_size()" failed (in which
|
|
* case there's not much we can do about it), or the file
|
|
* *shrank* (in which case there's not much we can do about
|
|
* it); just clip the progress value at 1.0.
|
|
*/
|
|
if (progbar_val > 1.0f)
|
|
progbar_val = 1.0f;
|
|
}
|
|
|
|
snprintf(status_str, status_size,
|
|
"%" PRId64 "KB of %" PRId64 "KB",
|
|
file_pos / 1024, size / 1024);
|
|
|
|
return progbar_val;
|
|
}
|
|
|
|
cf_read_status_t
|
|
cf_read(capture_file *cf, gboolean reloading)
|
|
{
|
|
int err = 0;
|
|
gchar *err_info = NULL;
|
|
volatile gboolean too_many_records = FALSE;
|
|
gchar *name_ptr;
|
|
progdlg_t *volatile progbar = NULL;
|
|
GTimer *prog_timer = g_timer_new();
|
|
gint64 size;
|
|
gint64 start_time;
|
|
epan_dissect_t edt;
|
|
wtap_rec rec;
|
|
Buffer buf;
|
|
dfilter_t *dfcode;
|
|
column_info *cinfo;
|
|
volatile gboolean create_proto_tree;
|
|
guint tap_flags;
|
|
gboolean compiled _U_;
|
|
volatile gboolean is_read_aborted = FALSE;
|
|
|
|
/* The update_progress_dlg call below might end up accepting a user request to
|
|
* trigger redissection/rescans which can modify/destroy the dissection
|
|
* context ("cf->epan"). That condition should be prevented by callers, but in
|
|
* case it occurs let's fail gracefully.
|
|
*/
|
|
if (cf->read_lock) {
|
|
ws_warning("Failing due to recursive cf_read(\"%s\", %d) call!",
|
|
cf->filename, reloading);
|
|
return CF_READ_ERROR;
|
|
}
|
|
cf->read_lock = TRUE;
|
|
|
|
/* Compile the current display filter.
|
|
* We assume this will not fail since cf->dfilter is only set in
|
|
* cf_filter IFF the filter was valid.
|
|
*/
|
|
compiled = dfilter_compile(cf->dfilter, &dfcode, NULL);
|
|
ws_assert(!cf->dfilter || (compiled && dfcode));
|
|
|
|
/* Get the union of the flags for all tap listeners. */
|
|
tap_flags = union_of_tap_listener_flags();
|
|
|
|
/*
|
|
* Determine whether we need to create a protocol tree.
|
|
* We do if:
|
|
*
|
|
* we're going to apply a display filter;
|
|
*
|
|
* one of the tap listeners is going to apply a filter;
|
|
*
|
|
* one of the tap listeners requires a protocol tree;
|
|
*
|
|
* a postdissector wants field values or protocols on
|
|
* the first pass.
|
|
*/
|
|
create_proto_tree =
|
|
(dfcode != NULL || have_filtering_tap_listeners() ||
|
|
(tap_flags & TL_REQUIRES_PROTO_TREE) || postdissectors_want_hfids());
|
|
|
|
reset_tap_listeners();
|
|
|
|
name_ptr = g_filename_display_basename(cf->filename);
|
|
|
|
if (reloading)
|
|
cf_callback_invoke(cf_cb_file_reload_started, cf);
|
|
else
|
|
cf_callback_invoke(cf_cb_file_read_started, cf);
|
|
|
|
/* Record the file's compression type.
|
|
XXX - do we know this at open time? */
|
|
cf->compression_type = wtap_get_compression_type(cf->provider.wth);
|
|
|
|
/* The packet list window will be empty until the file is completly loaded */
|
|
packet_list_freeze();
|
|
|
|
cf->stop_flag = FALSE;
|
|
start_time = g_get_monotonic_time();
|
|
|
|
epan_dissect_init(&edt, cf->epan, create_proto_tree, FALSE);
|
|
|
|
/* If any tap listeners require the columns, construct them. */
|
|
cinfo = (tap_flags & TL_REQUIRES_COLUMNS) ? &cf->cinfo : NULL;
|
|
|
|
/* Find the size of the file. */
|
|
size = wtap_file_size(cf->provider.wth, NULL);
|
|
|
|
g_timer_start(prog_timer);
|
|
|
|
wtap_rec_init(&rec);
|
|
ws_buffer_init(&buf, 1514);
|
|
|
|
TRY {
|
|
gint64 file_pos;
|
|
gint64 data_offset;
|
|
|
|
float progbar_val;
|
|
gchar status_str[100];
|
|
|
|
while ((wtap_read(cf->provider.wth, &rec, &buf, &err, &err_info,
|
|
&data_offset))) {
|
|
if (size >= 0) {
|
|
if (cf->count == max_records) {
|
|
/*
|
|
* Quit if we've already read the maximum number of
|
|
* records allowed.
|
|
*/
|
|
too_many_records = TRUE;
|
|
break;
|
|
}
|
|
file_pos = wtap_read_so_far(cf->provider.wth);
|
|
|
|
/* Create the progress bar if necessary. */
|
|
if (progress_is_slow(progbar, prog_timer, size, file_pos)) {
|
|
progbar_val = calc_progbar_val(cf, size, file_pos, status_str, sizeof(status_str));
|
|
progbar = delayed_create_progress_dlg(cf->window, NULL, NULL, TRUE,
|
|
&cf->stop_flag, progbar_val);
|
|
}
|
|
|
|
/*
|
|
* Update the progress bar, but do it only after
|
|
* PROGBAR_UPDATE_INTERVAL has elapsed. Calling update_progress_dlg
|
|
* and packets_bar_update will likely trigger UI paint events, which
|
|
* might take a while depending on the platform and display. Reset
|
|
* our timer *after* painting.
|
|
*/
|
|
if (progbar && g_timer_elapsed(prog_timer, NULL) > PROGBAR_UPDATE_INTERVAL) {
|
|
progbar_val = calc_progbar_val(cf, size, file_pos, status_str, sizeof(status_str));
|
|
/* update the packet bar content on the first run or frequently on very large files */
|
|
update_progress_dlg(progbar, progbar_val, status_str);
|
|
compute_elapsed(cf, start_time);
|
|
packets_bar_update();
|
|
g_timer_start(prog_timer);
|
|
}
|
|
/*
|
|
* The previous GUI triggers should not have destroyed the running
|
|
* session. If that did happen, it could blow up when read_record tries
|
|
* to use the destroyed edt.session, so detect it right here.
|
|
*/
|
|
ws_assert(edt.session == cf->epan);
|
|
}
|
|
|
|
if (cf->state == FILE_READ_ABORTED) {
|
|
/* Well, the user decided to exit Wireshark. Break out of the
|
|
loop, and let the code below (which is called even if there
|
|
aren't any packets left to read) exit. */
|
|
is_read_aborted = TRUE;
|
|
break;
|
|
}
|
|
if (cf->stop_flag) {
|
|
/* Well, the user decided to abort the read. He/She will be warned and
|
|
it might be enough for him/her to work with the already loaded
|
|
packets.
|
|
This is especially true for very large capture files, where you don't
|
|
want to wait loading the whole file (which may last minutes or even
|
|
hours even on fast machines) just to see that it was the wrong file. */
|
|
break;
|
|
}
|
|
read_record(cf, &rec, &buf, dfcode, &edt, cinfo, data_offset);
|
|
wtap_rec_reset(&rec);
|
|
}
|
|
}
|
|
CATCH(OutOfMemoryError) {
|
|
simple_message_box(ESD_TYPE_ERROR, NULL,
|
|
"More information and workarounds can be found at\n"
|
|
WS_WIKI_URL("KnownBugs/OutOfMemory"),
|
|
"Sorry, but Wireshark has run out of memory and has to terminate now.");
|
|
#if 0
|
|
/* Could we close the current capture and free up memory from that? */
|
|
#else
|
|
/* we have to terminate, as we cannot recover from the memory error */
|
|
exit(1);
|
|
#endif
|
|
}
|
|
ENDTRY;
|
|
|
|
/* We're done reading sequentially through the file. */
|
|
cf->state = FILE_READ_DONE;
|
|
|
|
/* Destroy the progress bar if it was created. */
|
|
if (progbar != NULL)
|
|
destroy_progress_dlg(progbar);
|
|
g_timer_destroy(prog_timer);
|
|
|
|
/* Free the display name */
|
|
g_free(name_ptr);
|
|
|
|
/* Cleanup and release all dfilter resources */
|
|
dfilter_free(dfcode);
|
|
|
|
epan_dissect_cleanup(&edt);
|
|
wtap_rec_cleanup(&rec);
|
|
ws_buffer_free(&buf);
|
|
|
|
/* Close the sequential I/O side, to free up memory it requires. */
|
|
wtap_sequential_close(cf->provider.wth);
|
|
|
|
/* Allow the protocol dissectors to free up memory that they
|
|
* don't need after the sequential run-through of the packets. */
|
|
postseq_cleanup_all_protocols();
|
|
|
|
/* compute the time it took to load the file */
|
|
compute_elapsed(cf, start_time);
|
|
|
|
/* Set the file encapsulation type now; we don't know what it is until
|
|
we've looked at all the packets, as we don't know until then whether
|
|
there's more than one type (and thus whether it's
|
|
WTAP_ENCAP_PER_PACKET). */
|
|
cf->lnk_t = wtap_file_encap(cf->provider.wth);
|
|
|
|
cf->current_frame = frame_data_sequence_find(cf->provider.frames, cf->first_displayed);
|
|
|
|
packet_list_thaw();
|
|
|
|
/* It is safe again to execute redissections or sort. */
|
|
ws_assert(cf->read_lock);
|
|
cf->read_lock = FALSE;
|
|
|
|
if (reloading)
|
|
cf_callback_invoke(cf_cb_file_reload_finished, cf);
|
|
else
|
|
cf_callback_invoke(cf_cb_file_read_finished, cf);
|
|
|
|
/* If we have any displayed packets to select, select the first of those
|
|
packets by making the first row the selected row. */
|
|
if (cf->first_displayed != 0) {
|
|
packet_list_select_row_from_data(NULL);
|
|
}
|
|
|
|
if (is_read_aborted) {
|
|
/*
|
|
* Well, the user decided to exit Wireshark while reading this *offline*
|
|
* capture file (Live captures are handled by something like
|
|
* cf_continue_tail). Clean up accordingly.
|
|
*/
|
|
cf_close(cf);
|
|
cf->redissection_queued = RESCAN_NONE;
|
|
return CF_READ_ABORTED;
|
|
}
|
|
|
|
if (cf->redissection_queued != RESCAN_NONE) {
|
|
/* Redissection was queued up. Clear the request and perform it now. */
|
|
gboolean redissect = cf->redissection_queued == RESCAN_REDISSECT;
|
|
rescan_packets(cf, NULL, NULL, redissect);
|
|
}
|
|
|
|
if (cf->stop_flag) {
|
|
simple_message_box(ESD_TYPE_WARN, NULL,
|
|
"The remaining packets in the file were discarded.\n"
|
|
"\n"
|
|
"As a lot of packets from the original file will be missing,\n"
|
|
"remember to be careful when saving the current content to a file.\n",
|
|
"File loading was cancelled.");
|
|
return CF_READ_ERROR;
|
|
}
|
|
|
|
if (err != 0) {
|
|
/* Put up a message box noting that the read failed somewhere along
|
|
the line. Don't throw out the stuff we managed to read, though,
|
|
if any. */
|
|
cfile_read_failure_alert_box(NULL, err, err_info);
|
|
return CF_READ_ERROR;
|
|
} else if (too_many_records) {
|
|
simple_message_box(ESD_TYPE_WARN, NULL,
|
|
"The remaining packets in the file were discarded.\n"
|
|
"\n"
|
|
"As a lot of packets from the original file will be missing,\n"
|
|
"remember to be careful when saving the current content to a file.\n"
|
|
"\n"
|
|
"The command-line utility editcap can be used to split "
|
|
"the file into multiple smaller files",
|
|
"The file contains more records than the maximum "
|
|
"supported number of records, %u.", max_records);
|
|
return CF_READ_ERROR;
|
|
} else
|
|
return CF_READ_OK;
|
|
}
|
|
|
|
#ifdef HAVE_LIBPCAP
|
|
cf_read_status_t
|
|
cf_continue_tail(capture_file *cf, volatile int to_read, wtap_rec *rec,
|
|
Buffer *buf, int *err)
|
|
{
|
|
gchar *err_info;
|
|
volatile int newly_displayed_packets = 0;
|
|
dfilter_t *dfcode;
|
|
epan_dissect_t edt;
|
|
gboolean create_proto_tree;
|
|
guint tap_flags;
|
|
gboolean compiled _U_;
|
|
|
|
/* Compile the current display filter.
|
|
* We assume this will not fail since cf->dfilter is only set in
|
|
* cf_filter IFF the filter was valid.
|
|
*/
|
|
compiled = dfilter_compile(cf->dfilter, &dfcode, NULL);
|
|
ws_assert(!cf->dfilter || (compiled && dfcode));
|
|
|
|
/* Get the union of the flags for all tap listeners. */
|
|
tap_flags = union_of_tap_listener_flags();
|
|
|
|
/*
|
|
* Determine whether we need to create a protocol tree.
|
|
* We do if:
|
|
*
|
|
* we're going to apply a display filter;
|
|
*
|
|
* one of the tap listeners is going to apply a filter;
|
|
*
|
|
* one of the tap listeners requires a protocol tree;
|
|
*
|
|
* a postdissector wants field values or protocols on
|
|
* the first pass.
|
|
*/
|
|
create_proto_tree =
|
|
(dfcode != NULL || have_filtering_tap_listeners() ||
|
|
(tap_flags & TL_REQUIRES_PROTO_TREE) || postdissectors_want_hfids());
|
|
|
|
*err = 0;
|
|
|
|
/* Don't freeze/thaw the list when doing live capture */
|
|
/*packet_list_freeze();*/
|
|
|
|
epan_dissect_init(&edt, cf->epan, create_proto_tree, FALSE);
|
|
|
|
TRY {
|
|
gint64 data_offset = 0;
|
|
column_info *cinfo;
|
|
|
|
/* If any tap listeners require the columns, construct them. */
|
|
cinfo = (tap_flags & TL_REQUIRES_COLUMNS) ? &cf->cinfo : NULL;
|
|
|
|
while (to_read != 0) {
|
|
wtap_cleareof(cf->provider.wth);
|
|
if (!wtap_read(cf->provider.wth, rec, buf, err, &err_info,
|
|
&data_offset)) {
|
|
break;
|
|
}
|
|
if (cf->state == FILE_READ_ABORTED) {
|
|
/* Well, the user decided to exit Wireshark. Break out of the
|
|
loop, and let the code below (which is called even if there
|
|
aren't any packets left to read) exit. */
|
|
break;
|
|
}
|
|
if (read_record(cf, rec, buf, dfcode, &edt, cinfo, data_offset)) {
|
|
newly_displayed_packets++;
|
|
}
|
|
to_read--;
|
|
}
|
|
wtap_rec_reset(rec);
|
|
}
|
|
CATCH(OutOfMemoryError) {
|
|
simple_message_box(ESD_TYPE_ERROR, NULL,
|
|
"More information and workarounds can be found at\n"
|
|
WS_WIKI_URL("KnownBugs/OutOfMemory"),
|
|
"Sorry, but Wireshark has run out of memory and has to terminate now.");
|
|
#if 0
|
|
/* Could we close the current capture and free up memory from that? */
|
|
return CF_READ_ABORTED;
|
|
#else
|
|
/* we have to terminate, as we cannot recover from the memory error */
|
|
exit(1);
|
|
#endif
|
|
}
|
|
ENDTRY;
|
|
|
|
/* Update the file encapsulation; it might have changed based on the
|
|
packets we've read. */
|
|
cf->lnk_t = wtap_file_encap(cf->provider.wth);
|
|
|
|
/* Cleanup and release all dfilter resources */
|
|
dfilter_free(dfcode);
|
|
|
|
epan_dissect_cleanup(&edt);
|
|
|
|
/* Don't freeze/thaw the list when doing live capture */
|
|
/*packet_list_thaw();*/
|
|
/* With the new packet list the first packet
|
|
* isn't automatically selected.
|
|
*/
|
|
if (!cf->current_frame && !packet_list_multi_select_active())
|
|
packet_list_select_row_from_data(NULL);
|
|
|
|
if (cf->state == FILE_READ_ABORTED) {
|
|
/* Well, the user decided to exit Wireshark. Return CF_READ_ABORTED
|
|
so that our caller can kill off the capture child process;
|
|
this will cause an EOF on the pipe from the child, so
|
|
"cf_finish_tail()" will be called, and it will clean up
|
|
and exit. */
|
|
return CF_READ_ABORTED;
|
|
} else if (*err != 0) {
|
|
/* We got an error reading the capture file.
|
|
XXX - pop up a dialog box instead? */
|
|
if (err_info != NULL) {
|
|
ws_warning("Error \"%s\" while reading \"%s\" (\"%s\")",
|
|
wtap_strerror(*err), cf->filename, err_info);
|
|
g_free(err_info);
|
|
} else {
|
|
ws_warning("Error \"%s\" while reading \"%s\"",
|
|
wtap_strerror(*err), cf->filename);
|
|
}
|
|
return CF_READ_ERROR;
|
|
} else
|
|
return CF_READ_OK;
|
|
}
|
|
|
|
void
|
|
cf_fake_continue_tail(capture_file *cf)
|
|
{
|
|
cf->state = FILE_READ_DONE;
|
|
}
|
|
|
|
cf_read_status_t
|
|
cf_finish_tail(capture_file *cf, wtap_rec *rec, Buffer *buf, int *err)
|
|
{
|
|
gchar *err_info;
|
|
gint64 data_offset;
|
|
dfilter_t *dfcode;
|
|
column_info *cinfo;
|
|
epan_dissect_t edt;
|
|
gboolean create_proto_tree;
|
|
guint tap_flags;
|
|
gboolean compiled _U_;
|
|
|
|
/* Compile the current display filter.
|
|
* We assume this will not fail since cf->dfilter is only set in
|
|
* cf_filter IFF the filter was valid.
|
|
*/
|
|
compiled = dfilter_compile(cf->dfilter, &dfcode, NULL);
|
|
ws_assert(!cf->dfilter || (compiled && dfcode));
|
|
|
|
/* Get the union of the flags for all tap listeners. */
|
|
tap_flags = union_of_tap_listener_flags();
|
|
|
|
/* If any tap listeners require the columns, construct them. */
|
|
cinfo = (tap_flags & TL_REQUIRES_COLUMNS) ? &cf->cinfo : NULL;
|
|
|
|
/*
|
|
* Determine whether we need to create a protocol tree.
|
|
* We do if:
|
|
*
|
|
* we're going to apply a display filter;
|
|
*
|
|
* one of the tap listeners is going to apply a filter;
|
|
*
|
|
* one of the tap listeners requires a protocol tree;
|
|
*
|
|
* a postdissector wants field values or protocols on
|
|
* the first pass.
|
|
*/
|
|
create_proto_tree =
|
|
(dfcode != NULL || have_filtering_tap_listeners() ||
|
|
(tap_flags & TL_REQUIRES_PROTO_TREE) || postdissectors_want_hfids());
|
|
|
|
if (cf->provider.wth == NULL) {
|
|
cf_close(cf);
|
|
return CF_READ_ERROR;
|
|
}
|
|
|
|
/* Don't freeze/thaw the list when doing live capture */
|
|
/*packet_list_freeze();*/
|
|
|
|
epan_dissect_init(&edt, cf->epan, create_proto_tree, FALSE);
|
|
|
|
while ((wtap_read(cf->provider.wth, rec, buf, err, &err_info, &data_offset))) {
|
|
if (cf->state == FILE_READ_ABORTED) {
|
|
/* Well, the user decided to abort the read. Break out of the
|
|
loop, and let the code below (which is called even if there
|
|
aren't any packets left to read) exit. */
|
|
break;
|
|
}
|
|
read_record(cf, rec, buf, dfcode, &edt, cinfo, data_offset);
|
|
wtap_rec_reset(rec);
|
|
}
|
|
|
|
/* Cleanup and release all dfilter resources */
|
|
dfilter_free(dfcode);
|
|
|
|
epan_dissect_cleanup(&edt);
|
|
|
|
/* Don't freeze/thaw the list when doing live capture */
|
|
/*packet_list_thaw();*/
|
|
|
|
if (cf->state == FILE_READ_ABORTED) {
|
|
/* Well, the user decided to abort the read. We're only called
|
|
when the child capture process closes the pipe to us (meaning
|
|
it's probably exited), so we can just close the capture
|
|
file; we return CF_READ_ABORTED so our caller can do whatever
|
|
is appropriate when that happens. */
|
|
cf_close(cf);
|
|
return CF_READ_ABORTED;
|
|
}
|
|
|
|
/* We're done reading sequentially through the file. */
|
|
cf->state = FILE_READ_DONE;
|
|
|
|
/* We're done reading sequentially through the file; close the
|
|
sequential I/O side, to free up memory it requires. */
|
|
wtap_sequential_close(cf->provider.wth);
|
|
|
|
/* Allow the protocol dissectors to free up memory that they
|
|
* don't need after the sequential run-through of the packets. */
|
|
postseq_cleanup_all_protocols();
|
|
|
|
/* Update the file encapsulation; it might have changed based on the
|
|
packets we've read. */
|
|
cf->lnk_t = wtap_file_encap(cf->provider.wth);
|
|
|
|
/* Update the details in the file-set dialog, as the capture file
|
|
* has likely grown since we first stat-ed it */
|
|
fileset_update_file(cf->filename);
|
|
|
|
if (*err != 0) {
|
|
/* We got an error reading the capture file.
|
|
XXX - pop up a dialog box? */
|
|
if (err_info != NULL) {
|
|
ws_warning("Error \"%s\" while reading \"%s\" (\"%s\")",
|
|
wtap_strerror(*err), cf->filename, err_info);
|
|
g_free(err_info);
|
|
} else {
|
|
ws_warning("Error \"%s\" while reading \"%s\"",
|
|
wtap_strerror(*err), cf->filename);
|
|
}
|
|
return CF_READ_ERROR;
|
|
} else {
|
|
return CF_READ_OK;
|
|
}
|
|
}
|
|
#endif /* HAVE_LIBPCAP */
|
|
|
|
gchar *
|
|
cf_get_display_name(capture_file *cf)
|
|
{
|
|
gchar *displayname;
|
|
|
|
/* Return a name to use in displays */
|
|
if (!cf->is_tempfile) {
|
|
/* Get the last component of the file name, and use that. */
|
|
if (cf->filename) {
|
|
displayname = g_filename_display_basename(cf->filename);
|
|
} else {
|
|
displayname=g_strdup("(No file)");
|
|
}
|
|
} else {
|
|
/* The file we read is a temporary file from a live capture or
|
|
a merge operation; we don't mention its name, but, if it's
|
|
from a capture, give the source of the capture. */
|
|
if (cf->source) {
|
|
displayname = g_strdup(cf->source);
|
|
} else {
|
|
displayname = g_strdup("(Untitled)");
|
|
}
|
|
}
|
|
return displayname;
|
|
}
|
|
|
|
gchar *
|
|
cf_get_basename(capture_file *cf)
|
|
{
|
|
gchar *displayname;
|
|
|
|
/* Return a name to use in the GUI for the basename for files to
|
|
which we save statistics */
|
|
if (!cf->is_tempfile) {
|
|
/* Get the last component of the file name, and use that. */
|
|
if (cf->filename) {
|
|
displayname = g_filename_display_basename(cf->filename);
|
|
|
|
/* If the file name ends with any extension that corresponds
|
|
to a file type we support - including compressed versions
|
|
of those files - strip it off. */
|
|
size_t displayname_len = strlen(displayname);
|
|
GSList *extensions = wtap_get_all_file_extensions_list();
|
|
GSList *suffix;
|
|
for (suffix = extensions; suffix != NULL; suffix = g_slist_next(suffix)) {
|
|
/* Does the file name end with that extension? */
|
|
const char *extension = (char *)suffix->data;
|
|
size_t extension_len = strlen(extension);
|
|
if (displayname_len > extension_len &&
|
|
displayname[displayname_len - extension_len - 1] == '.' &&
|
|
strcmp(&displayname[displayname_len - extension_len], extension) == 0) {
|
|
/* Yes. Strip the extension off, and return the result. */
|
|
displayname[displayname_len - extension_len - 1] = '\0';
|
|
break;
|
|
}
|
|
}
|
|
wtap_free_extensions_list(extensions);
|
|
} else {
|
|
displayname=g_strdup("");
|
|
}
|
|
} else {
|
|
/* The file we read is a temporary file from a live capture or
|
|
a merge operation; we don't mention its name, but, if it's
|
|
from a capture, give the source of the capture. */
|
|
if (cf->source) {
|
|
displayname = g_strdup(cf->source);
|
|
} else {
|
|
displayname = g_strdup("");
|
|
}
|
|
}
|
|
return displayname;
|
|
}
|
|
|
|
void
|
|
cf_set_tempfile_source(capture_file *cf, gchar *source)
|
|
{
|
|
if (cf->source) {
|
|
g_free(cf->source);
|
|
}
|
|
|
|
if (source) {
|
|
cf->source = g_strdup(source);
|
|
} else {
|
|
cf->source = g_strdup("");
|
|
}
|
|
}
|
|
|
|
const gchar *
|
|
cf_get_tempfile_source(capture_file *cf)
|
|
{
|
|
if (!cf->source) {
|
|
return "";
|
|
}
|
|
|
|
return cf->source;
|
|
}
|
|
|
|
/* XXX - use a macro instead? */
|
|
int
|
|
cf_get_packet_count(capture_file *cf)
|
|
{
|
|
return cf->count;
|
|
}
|
|
|
|
/* XXX - use a macro instead? */
|
|
gboolean
|
|
cf_is_tempfile(capture_file *cf)
|
|
{
|
|
return cf->is_tempfile;
|
|
}
|
|
|
|
void
|
|
cf_set_tempfile(capture_file *cf, gboolean is_tempfile)
|
|
{
|
|
cf->is_tempfile = is_tempfile;
|
|
}
|
|
|
|
|
|
/* XXX - use a macro instead? */
|
|
void
|
|
cf_set_drops_known(capture_file *cf, gboolean drops_known)
|
|
{
|
|
cf->drops_known = drops_known;
|
|
}
|
|
|
|
/* XXX - use a macro instead? */
|
|
void
|
|
cf_set_drops(capture_file *cf, guint32 drops)
|
|
{
|
|
cf->drops = drops;
|
|
}
|
|
|
|
/* XXX - use a macro instead? */
|
|
gboolean
|
|
cf_get_drops_known(capture_file *cf)
|
|
{
|
|
return cf->drops_known;
|
|
}
|
|
|
|
/* XXX - use a macro instead? */
|
|
guint32
|
|
cf_get_drops(capture_file *cf)
|
|
{
|
|
return cf->drops;
|
|
}
|
|
|
|
void
|
|
cf_set_rfcode(capture_file *cf, dfilter_t *rfcode)
|
|
{
|
|
cf->rfcode = rfcode;
|
|
}
|
|
|
|
static void
|
|
add_packet_to_packet_list(frame_data *fdata, capture_file *cf,
|
|
epan_dissect_t *edt, dfilter_t *dfcode, column_info *cinfo,
|
|
wtap_rec *rec, Buffer *buf, gboolean add_to_packet_list)
|
|
{
|
|
frame_data_set_before_dissect(fdata, &cf->elapsed_time,
|
|
&cf->provider.ref, cf->provider.prev_dis);
|
|
cf->provider.prev_cap = fdata;
|
|
|
|
if (dfcode != NULL) {
|
|
epan_dissect_prime_with_dfilter(edt, dfcode);
|
|
}
|
|
#if 0
|
|
/* Prepare coloring rules, this ensures that display filter rules containing
|
|
* frame.color_rule references are still processed.
|
|
* TODO: actually detect that situation or maybe apply other optimizations? */
|
|
if (edt->tree && color_filters_used()) {
|
|
color_filters_prime_edt(edt);
|
|
fdata->need_colorize = 1;
|
|
}
|
|
#endif
|
|
|
|
if (!fdata->visited) {
|
|
/* This is the first pass, so prime the epan_dissect_t with the
|
|
hfids postdissectors want on the first pass. */
|
|
prime_epan_dissect_with_postdissector_wanted_hfids(edt);
|
|
}
|
|
|
|
/* Dissect the frame. */
|
|
epan_dissect_run_with_taps(edt, cf->cd_t, rec,
|
|
frame_tvbuff_new_buffer(&cf->provider, fdata, buf),
|
|
fdata, cinfo);
|
|
|
|
/* If we don't have a display filter, set "passed_dfilter" to 1. */
|
|
if (dfcode != NULL) {
|
|
fdata->passed_dfilter = dfilter_apply_edt(dfcode, edt) ? 1 : 0;
|
|
|
|
if (fdata->passed_dfilter && edt->pi.fd->dependent_frames) {
|
|
/* This frame passed the display filter but it may depend on other
|
|
* (potentially not displayed) frames. Find those frames and mark them
|
|
* as depended upon.
|
|
*/
|
|
g_hash_table_foreach(edt->pi.fd->dependent_frames, find_and_mark_frame_depended_upon, cf->provider.frames);
|
|
}
|
|
} else
|
|
fdata->passed_dfilter = 1;
|
|
|
|
if (fdata->passed_dfilter || fdata->ref_time)
|
|
cf->displayed_count++;
|
|
|
|
if (add_to_packet_list) {
|
|
/* We fill the needed columns from new_packet_list */
|
|
packet_list_append(cinfo, fdata);
|
|
}
|
|
|
|
if (fdata->passed_dfilter || fdata->ref_time)
|
|
{
|
|
frame_data_set_after_dissect(fdata, &cf->cum_bytes);
|
|
cf->provider.prev_dis = fdata;
|
|
|
|
/* If we haven't yet seen the first frame, this is it. */
|
|
if (cf->first_displayed == 0)
|
|
cf->first_displayed = fdata->num;
|
|
|
|
/* This is the last frame we've seen so far. */
|
|
cf->last_displayed = fdata->num;
|
|
}
|
|
|
|
epan_dissect_reset(edt);
|
|
}
|
|
|
|
/*
|
|
* Read in a new record.
|
|
* Returns TRUE if the packet was added to the packet (record) list,
|
|
* FALSE otherwise.
|
|
*/
|
|
static gboolean
|
|
read_record(capture_file *cf, wtap_rec *rec, Buffer *buf, dfilter_t *dfcode,
|
|
epan_dissect_t *edt, column_info *cinfo, gint64 offset)
|
|
{
|
|
frame_data fdlocal;
|
|
frame_data *fdata;
|
|
gboolean passed = TRUE;
|
|
gboolean added = FALSE;
|
|
|
|
/* Add this packet's link-layer encapsulation type to cf->linktypes, if
|
|
it's not already there.
|
|
XXX - yes, this is O(N), so if every packet had a different
|
|
link-layer encapsulation type, it'd be O(N^2) to read the file, but
|
|
there are probably going to be a small number of encapsulation types
|
|
in a file. */
|
|
if (rec->rec_type == REC_TYPE_PACKET) {
|
|
cf_add_encapsulation_type(cf, rec->rec_header.packet_header.pkt_encap);
|
|
}
|
|
|
|
/* The frame number of this packet, if we add it to the set of frames,
|
|
would be one more than the count of frames in the file so far. */
|
|
frame_data_init(&fdlocal, cf->count + 1, rec, offset, cf->cum_bytes);
|
|
|
|
if (cf->rfcode) {
|
|
epan_dissect_t rf_edt;
|
|
|
|
epan_dissect_init(&rf_edt, cf->epan, TRUE, FALSE);
|
|
epan_dissect_prime_with_dfilter(&rf_edt, cf->rfcode);
|
|
epan_dissect_run(&rf_edt, cf->cd_t, rec,
|
|
frame_tvbuff_new_buffer(&cf->provider, &fdlocal, buf),
|
|
&fdlocal, NULL);
|
|
passed = dfilter_apply_edt(cf->rfcode, &rf_edt);
|
|
epan_dissect_cleanup(&rf_edt);
|
|
}
|
|
|
|
if (passed) {
|
|
added = TRUE;
|
|
|
|
/* This does a shallow copy of fdlocal, which is good enough. */
|
|
fdata = frame_data_sequence_add(cf->provider.frames, &fdlocal);
|
|
|
|
cf->count++;
|
|
if (rec->block != NULL)
|
|
cf->packet_comment_count += wtap_block_count_option(rec->block, OPT_COMMENT);
|
|
cf->f_datalen = offset + fdlocal.cap_len;
|
|
|
|
/* When a redissection is in progress (or queued), do not process packets.
|
|
* This will be done once all (new) packets have been scanned. */
|
|
if (!cf->redissecting && cf->redissection_queued == RESCAN_NONE) {
|
|
add_packet_to_packet_list(fdata, cf, edt, dfcode, cinfo, rec, buf, TRUE);
|
|
}
|
|
}
|
|
|
|
return added;
|
|
}
|
|
|
|
|
|
typedef struct _callback_data_t {
|
|
gpointer pd_window;
|
|
gint64 f_len;
|
|
progdlg_t *progbar;
|
|
GTimer *prog_timer;
|
|
gboolean stop_flag;
|
|
} callback_data_t;
|
|
|
|
|
|
static gboolean
|
|
merge_callback(merge_event event, int num _U_,
|
|
const merge_in_file_t in_files[], const guint in_file_count,
|
|
void *data)
|
|
{
|
|
guint i;
|
|
callback_data_t *cb_data = (callback_data_t*) data;
|
|
|
|
ws_assert(cb_data != NULL);
|
|
|
|
switch (event) {
|
|
|
|
case MERGE_EVENT_INPUT_FILES_OPENED:
|
|
/* do nothing */
|
|
break;
|
|
|
|
case MERGE_EVENT_FRAME_TYPE_SELECTED:
|
|
/* do nothing */
|
|
break;
|
|
|
|
case MERGE_EVENT_READY_TO_MERGE:
|
|
/* Get the sum of the sizes of all the files. */
|
|
for (i = 0; i < in_file_count; i++)
|
|
cb_data->f_len += in_files[i].size;
|
|
|
|
cb_data->prog_timer = g_timer_new();
|
|
g_timer_start(cb_data->prog_timer);
|
|
break;
|
|
|
|
case MERGE_EVENT_RECORD_WAS_READ:
|
|
{
|
|
/* Create the progress bar if necessary.
|
|
We check on every iteration of the loop, so that it takes no
|
|
longer than the standard time to create it (otherwise, for a
|
|
large file, we might take considerably longer than that standard
|
|
time in order to get to the next progress bar step). */
|
|
if (cb_data->progbar == NULL) {
|
|
cb_data->progbar = delayed_create_progress_dlg(cb_data->pd_window, NULL, NULL,
|
|
FALSE, &cb_data->stop_flag, 0.0f);
|
|
}
|
|
|
|
/*
|
|
* Update the progress bar, but do it only after
|
|
* PROGBAR_UPDATE_INTERVAL has elapsed. Calling update_progress_dlg
|
|
* and packets_bar_update will likely trigger UI paint events, which
|
|
* might take a while depending on the platform and display. Reset
|
|
* our timer *after* painting.
|
|
*/
|
|
if (g_timer_elapsed(cb_data->prog_timer, NULL) > PROGBAR_UPDATE_INTERVAL) {
|
|
float progbar_val;
|
|
gint64 file_pos = 0;
|
|
/* Get the sum of the seek positions in all of the files. */
|
|
for (i = 0; i < in_file_count; i++)
|
|
file_pos += wtap_read_so_far(in_files[i].wth);
|
|
|
|
progbar_val = (gfloat) file_pos / (gfloat) cb_data->f_len;
|
|
if (progbar_val > 1.0f) {
|
|
/* Some file probably grew while we were reading it.
|
|
That "shouldn't happen", so we'll just clip the progress
|
|
value at 1.0. */
|
|
progbar_val = 1.0f;
|
|
}
|
|
|
|
if (cb_data->progbar != NULL) {
|
|
gchar status_str[100];
|
|
snprintf(status_str, sizeof(status_str),
|
|
"%" PRId64 "KB of %" PRId64 "KB",
|
|
file_pos / 1024, cb_data->f_len / 1024);
|
|
update_progress_dlg(cb_data->progbar, progbar_val, status_str);
|
|
}
|
|
g_timer_start(cb_data->prog_timer);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MERGE_EVENT_DONE:
|
|
/* We're done merging the files; destroy the progress bar if it was created. */
|
|
if (cb_data->progbar != NULL)
|
|
destroy_progress_dlg(cb_data->progbar);
|
|
g_timer_destroy(cb_data->prog_timer);
|
|
break;
|
|
}
|
|
|
|
return cb_data->stop_flag;
|
|
}
|
|
|
|
|
|
|
|
cf_status_t
|
|
cf_merge_files_to_tempfile(gpointer pd_window, const char *temp_dir, char **out_filenamep,
|
|
int in_file_count, const char *const *in_filenames,
|
|
int file_type, gboolean do_append)
|
|
{
|
|
int err = 0;
|
|
gchar *err_info = NULL;
|
|
guint err_fileno;
|
|
guint32 err_framenum;
|
|
merge_result status;
|
|
merge_progress_callback_t cb;
|
|
callback_data_t *cb_data = g_new0(callback_data_t, 1);
|
|
|
|
/* prepare our callback routine */
|
|
cb_data->pd_window = pd_window;
|
|
cb.callback_func = merge_callback;
|
|
cb.data = cb_data;
|
|
|
|
cf_callback_invoke(cf_cb_file_merge_started, NULL);
|
|
|
|
/* merge the files */
|
|
status = merge_files_to_tempfile(temp_dir, out_filenamep, "wireshark", file_type,
|
|
in_filenames,
|
|
in_file_count, do_append,
|
|
IDB_MERGE_MODE_ALL_SAME, 0 /* snaplen */,
|
|
"Wireshark", &cb, &err, &err_info,
|
|
&err_fileno, &err_framenum);
|
|
|
|
g_free(cb.data);
|
|
|
|
switch (status) {
|
|
case MERGE_OK:
|
|
break;
|
|
|
|
case MERGE_USER_ABORTED:
|
|
/* this isn't really an error, though we will return CF_ERROR later */
|
|
break;
|
|
|
|
case MERGE_ERR_CANT_OPEN_INFILE:
|
|
cfile_open_failure_alert_box(in_filenames[err_fileno], err, err_info);
|
|
break;
|
|
|
|
case MERGE_ERR_CANT_OPEN_OUTFILE:
|
|
cfile_dump_open_failure_alert_box(*out_filenamep, err, err_info,
|
|
file_type);
|
|
break;
|
|
|
|
case MERGE_ERR_CANT_READ_INFILE:
|
|
cfile_read_failure_alert_box(in_filenames[err_fileno], err, err_info);
|
|
break;
|
|
|
|
case MERGE_ERR_BAD_PHDR_INTERFACE_ID:
|
|
simple_error_message_box("Record %u of \"%s\" has an interface ID that does not match any IDB in its file.",
|
|
err_framenum, in_filenames[err_fileno]);
|
|
break;
|
|
|
|
case MERGE_ERR_CANT_WRITE_OUTFILE:
|
|
cfile_write_failure_alert_box(in_filenames[err_fileno],
|
|
*out_filenamep, err, err_info,
|
|
err_framenum, file_type);
|
|
break;
|
|
|
|
case MERGE_ERR_CANT_CLOSE_OUTFILE:
|
|
cfile_close_failure_alert_box(*out_filenamep, err, err_info);
|
|
break;
|
|
|
|
default:
|
|
simple_error_message_box("Unknown merge_files error %d", status);
|
|
break;
|
|
}
|
|
|
|
cf_callback_invoke(cf_cb_file_merge_finished, NULL);
|
|
|
|
if (status != MERGE_OK) {
|
|
/* Callers aren't expected to treat an error or an explicit abort
|
|
differently - we put up error dialogs ourselves, so they don't
|
|
have to. */
|
|
return CF_ERROR;
|
|
} else
|
|
return CF_OK;
|
|
}
|
|
|
|
cf_status_t
|
|
cf_filter_packets(capture_file *cf, gchar *dftext, gboolean force)
|
|
{
|
|
const char *filter_new = dftext ? dftext : "";
|
|
const char *filter_old = cf->dfilter ? cf->dfilter : "";
|
|
dfilter_t *dfcode;
|
|
df_error_t *df_err;
|
|
|
|
/* if new filter equals old one, do nothing unless told to do so */
|
|
if (!force && strcmp(filter_new, filter_old) == 0) {
|
|
return CF_OK;
|
|
}
|
|
|
|
dfcode=NULL;
|
|
|
|
if (dftext == NULL) {
|
|
/* The new filter is an empty filter (i.e., display all packets).
|
|
* so leave dfcode==NULL
|
|
*/
|
|
} else {
|
|
/*
|
|
* We have a filter; make a copy of it (as we'll be saving it),
|
|
* and try to compile it.
|
|
*/
|
|
dftext = g_strdup(dftext);
|
|
if (!dfilter_compile(dftext, &dfcode, &df_err)) {
|
|
/* The attempt failed; report an error. */
|
|
simple_message_box(ESD_TYPE_ERROR, NULL,
|
|
"See the help for a description of the display filter syntax.",
|
|
"\"%s\" isn't a valid display filter: %s",
|
|
dftext, df_err->msg);
|
|
dfilter_error_free(df_err);
|
|
g_free(dftext);
|
|
return CF_ERROR;
|
|
}
|
|
|
|
/* Was it empty? */
|
|
if (dfcode == NULL) {
|
|
/* Yes - free the filter text, and set it to null. */
|
|
g_free(dftext);
|
|
dftext = NULL;
|
|
}
|
|
}
|
|
|
|
/* We have a valid filter. Replace the current filter. */
|
|
g_free(cf->dfilter);
|
|
cf->dfilter = dftext;
|
|
|
|
|
|
/* Now rescan the packet list, applying the new filter, but not
|
|
* throwing away information constructed on a previous pass.
|
|
* If a dissection is already in progress, queue it.
|
|
*/
|
|
if (cf->redissection_queued == RESCAN_NONE) {
|
|
if (cf->read_lock) {
|
|
cf->redissection_queued = RESCAN_SCAN;
|
|
} else if (cf->state != FILE_CLOSED) {
|
|
if (dftext == NULL) {
|
|
rescan_packets(cf, "Resetting", "filter", FALSE);
|
|
} else {
|
|
rescan_packets(cf, "Filtering", dftext, FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Cleanup and release all dfilter resources */
|
|
dfilter_free(dfcode);
|
|
|
|
return CF_OK;
|
|
}
|
|
|
|
void
|
|
cf_redissect_packets(capture_file *cf)
|
|
{
|
|
if (cf->read_lock || cf->redissection_queued == RESCAN_SCAN) {
|
|
/* Dissection in progress, signal redissection rather than rescanning. That
|
|
* would destroy the current (in-progress) dissection in "cf_read" which
|
|
* will cause issues when "cf_read" tries to add packets to the list.
|
|
* If a previous rescan was requested, "upgrade" it to a full redissection.
|
|
*/
|
|
cf->redissection_queued = RESCAN_REDISSECT;
|
|
}
|
|
if (cf->redissection_queued != RESCAN_NONE) {
|
|
/* Redissection is (already) queued, wait for "cf_read" to finish. */
|
|
return;
|
|
}
|
|
|
|
if (cf->state != FILE_CLOSED) {
|
|
/* Restart dissection in case no cf_read is pending. */
|
|
rescan_packets(cf, "Reprocessing", "all packets", TRUE);
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
cf_read_record(capture_file *cf, const frame_data *fdata,
|
|
wtap_rec *rec, Buffer *buf)
|
|
{
|
|
int err;
|
|
gchar *err_info;
|
|
|
|
if (!wtap_seek_read(cf->provider.wth, fdata->file_off, rec, buf, &err, &err_info)) {
|
|
cfile_read_failure_alert_box(cf->filename, err, err_info);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
cf_read_record_no_alert(capture_file *cf, const frame_data *fdata,
|
|
wtap_rec *rec, Buffer *buf)
|
|
{
|
|
int err;
|
|
gchar *err_info;
|
|
|
|
if (!wtap_seek_read(cf->provider.wth, fdata->file_off, rec, buf, &err, &err_info)) {
|
|
g_free(err_info);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
cf_read_current_record(capture_file *cf)
|
|
{
|
|
return cf_read_record(cf, cf->current_frame, &cf->rec, &cf->buf);
|
|
}
|
|
|
|
/* Rescan the list of packets, reconstructing the CList.
|
|
|
|
"action" describes why we're doing this; it's used in the progress
|
|
dialog box.
|
|
|
|
"action_item" describes what we're doing; it's used in the progress
|
|
dialog box.
|
|
|
|
"redissect" is TRUE if we need to make the dissectors reconstruct
|
|
any state information they have (because a preference that affects
|
|
some dissector has changed, meaning some dissector might construct
|
|
its state differently from the way it was constructed the last time). */
|
|
static void
|
|
rescan_packets(capture_file *cf, const char *action, const char *action_item, gboolean redissect)
|
|
{
|
|
/* Rescan packets new packet list */
|
|
guint32 framenum;
|
|
frame_data *fdata;
|
|
wtap_rec rec;
|
|
Buffer buf;
|
|
progdlg_t *progbar = NULL;
|
|
GTimer *prog_timer = g_timer_new();
|
|
int count;
|
|
frame_data *selected_frame, *preceding_frame, *following_frame, *prev_frame;
|
|
int selected_frame_num, preceding_frame_num, following_frame_num, prev_frame_num;
|
|
gboolean selected_frame_seen;
|
|
float progbar_val;
|
|
gint64 start_time;
|
|
gchar status_str[100];
|
|
epan_dissect_t edt;
|
|
dfilter_t *dfcode;
|
|
column_info *cinfo;
|
|
gboolean create_proto_tree;
|
|
guint tap_flags;
|
|
gboolean add_to_packet_list = FALSE;
|
|
gboolean compiled _U_;
|
|
guint32 frames_count;
|
|
gboolean queued_rescan_type = RESCAN_NONE;
|
|
|
|
/* Rescan in progress, clear pending actions. */
|
|
cf->redissection_queued = RESCAN_NONE;
|
|
ws_assert(!cf->read_lock);
|
|
cf->read_lock = TRUE;
|
|
|
|
wtap_rec_init(&rec);
|
|
ws_buffer_init(&buf, 1514);
|
|
|
|
/* Compile the current display filter.
|
|
* We assume this will not fail since cf->dfilter is only set in
|
|
* cf_filter IFF the filter was valid.
|
|
*/
|
|
compiled = dfilter_compile(cf->dfilter, &dfcode, NULL);
|
|
ws_assert(!cf->dfilter || (compiled && dfcode));
|
|
|
|
/* Update references in display filter (if any) for the protocol
|
|
* tree corresponding to the currently selected frame in the GUI. */
|
|
if (dfcode && cf->edt != NULL && cf->edt->tree != NULL)
|
|
dfilter_load_field_references(dfcode, cf->edt->tree);
|
|
|
|
if (dfcode != NULL) {
|
|
dfilter_log_full(LOG_DOMAIN_DFILTER, LOG_LEVEL_NOISY, NULL, -1, NULL,
|
|
dfcode, "Rescanning packets with display filter");
|
|
}
|
|
|
|
/* Get the union of the flags for all tap listeners. */
|
|
tap_flags = union_of_tap_listener_flags();
|
|
|
|
/* If any tap listeners require the columns, construct them. */
|
|
cinfo = (tap_flags & TL_REQUIRES_COLUMNS) ? &cf->cinfo : NULL;
|
|
|
|
/*
|
|
* Determine whether we need to create a protocol tree.
|
|
* We do if:
|
|
*
|
|
* we're going to apply a display filter;
|
|
*
|
|
* one of the tap listeners is going to apply a filter;
|
|
*
|
|
* one of the tap listeners requires a protocol tree;
|
|
*
|
|
* we're redissecting and a postdissector wants field
|
|
* values or protocols on the first pass.
|
|
*/
|
|
create_proto_tree =
|
|
(dfcode != NULL || have_filtering_tap_listeners() ||
|
|
(tap_flags & TL_REQUIRES_PROTO_TREE) ||
|
|
(redissect && postdissectors_want_hfids()));
|
|
|
|
reset_tap_listeners();
|
|
/* Which frame, if any, is the currently selected frame?
|
|
XXX - should the selected frame or the focus frame be the "current"
|
|
frame, that frame being the one from which "Find Frame" searches
|
|
start? */
|
|
selected_frame = cf->current_frame;
|
|
|
|
/* Mark frame num as not found */
|
|
selected_frame_num = -1;
|
|
|
|
/* Freeze the packet list while we redo it, so we don't get any
|
|
screen updates while it happens. */
|
|
packet_list_freeze();
|
|
|
|
if (redissect) {
|
|
/* We need to re-initialize all the state information that protocols
|
|
keep, because some preference that controls a dissector has changed,
|
|
which might cause the state information to be constructed differently
|
|
by that dissector. */
|
|
|
|
/* We might receive new packets while redissecting, and we don't
|
|
want to dissect those before their time. */
|
|
cf->redissecting = TRUE;
|
|
|
|
/* 'reset' dissection session */
|
|
epan_free(cf->epan);
|
|
if (cf->edt && cf->edt->pi.fd) {
|
|
/* All pointers in "per frame proto data" for the currently selected
|
|
packet are allocated in wmem_file_scope() and deallocated in epan_free().
|
|
Free them here to avoid unintended usage in packet_list_clear(). */
|
|
frame_data_destroy(cf->edt->pi.fd);
|
|
}
|
|
cf->epan = ws_epan_new(cf);
|
|
cf->cinfo.epan = cf->epan;
|
|
|
|
/* A new Lua tap listener may be registered in lua_prime_all_fields()
|
|
called via epan_new() / init_dissection() when reloading Lua plugins. */
|
|
if (!create_proto_tree && have_filtering_tap_listeners()) {
|
|
create_proto_tree = TRUE;
|
|
}
|
|
|
|
/* We need to redissect the packets so we have to discard our old
|
|
* packet list store. */
|
|
packet_list_clear();
|
|
add_to_packet_list = TRUE;
|
|
}
|
|
|
|
/* We don't yet know which will be the first and last frames displayed. */
|
|
cf->first_displayed = 0;
|
|
cf->last_displayed = 0;
|
|
|
|
/* We currently don't display any packets */
|
|
cf->displayed_count = 0;
|
|
|
|
/* Iterate through the list of frames. Call a routine for each frame
|
|
to check whether it should be displayed and, if so, add it to
|
|
the display list. */
|
|
cf->provider.ref = NULL;
|
|
cf->provider.prev_dis = NULL;
|
|
cf->provider.prev_cap = NULL;
|
|
cf->cum_bytes = 0;
|
|
|
|
cf_callback_invoke(cf_cb_file_rescan_started, cf);
|
|
|
|
g_timer_start(prog_timer);
|
|
/* Count of packets at which we've looked. */
|
|
count = 0;
|
|
/* Progress so far. */
|
|
progbar_val = 0.0f;
|
|
|
|
cf->stop_flag = FALSE;
|
|
start_time = g_get_monotonic_time();
|
|
|
|
/* no previous row yet */
|
|
prev_frame_num = -1;
|
|
prev_frame = NULL;
|
|
|
|
preceding_frame_num = -1;
|
|
preceding_frame = NULL;
|
|
following_frame_num = -1;
|
|
following_frame = NULL;
|
|
|
|
selected_frame_seen = FALSE;
|
|
|
|
frames_count = cf->count;
|
|
|
|
epan_dissect_init(&edt, cf->epan, create_proto_tree, FALSE);
|
|
|
|
if (redissect) {
|
|
/*
|
|
* Decryption secrets and name resolution blocks are read while
|
|
* sequentially processing records and then passed to the dissector.
|
|
* During redissection, the previous information is lost (see epan_free
|
|
* above), but they are not read again from the file as only packet
|
|
* records are re-read. Therefore reset the wtap secrets and name
|
|
* resolution callbacks such that wtap resupplies the callbacks with
|
|
* previously read information.
|
|
*/
|
|
wtap_set_cb_new_ipv4(cf->provider.wth, add_ipv4_name);
|
|
wtap_set_cb_new_ipv6(cf->provider.wth, (wtap_new_ipv6_callback_t) add_ipv6_name);
|
|
wtap_set_cb_new_secrets(cf->provider.wth, secrets_wtap_callback);
|
|
}
|
|
|
|
for (framenum = 1; framenum <= frames_count; framenum++) {
|
|
fdata = frame_data_sequence_find(cf->provider.frames, framenum);
|
|
|
|
/* Create the progress bar if necessary.
|
|
We check on every iteration of the loop, so that it takes no
|
|
longer than the standard time to create it (otherwise, for a
|
|
large file, we might take considerably longer than that standard
|
|
time in order to get to the next progress bar step). */
|
|
if (progbar == NULL)
|
|
progbar = delayed_create_progress_dlg(cf->window, action, action_item, TRUE,
|
|
&cf->stop_flag,
|
|
progbar_val);
|
|
|
|
/*
|
|
* Update the progress bar, but do it only after PROGBAR_UPDATE_INTERVAL
|
|
* has elapsed. Calling update_progress_dlg and packets_bar_update will
|
|
* likely trigger UI paint events, which might take a while depending on
|
|
* the platform and display. Reset our timer *after* painting.
|
|
*/
|
|
if (g_timer_elapsed(prog_timer, NULL) > PROGBAR_UPDATE_INTERVAL) {
|
|
/* let's not divide by zero. I should never be started
|
|
* with count == 0, so let's assert that
|
|
*/
|
|
ws_assert(cf->count > 0);
|
|
progbar_val = (gfloat) count / frames_count;
|
|
|
|
if (progbar != NULL) {
|
|
snprintf(status_str, sizeof(status_str),
|
|
"%4u of %u frames", count, frames_count);
|
|
update_progress_dlg(progbar, progbar_val, status_str);
|
|
}
|
|
|
|
g_timer_start(prog_timer);
|
|
}
|
|
|
|
queued_rescan_type = cf->redissection_queued;
|
|
if (queued_rescan_type != RESCAN_NONE) {
|
|
/* A redissection was requested while an existing redissection was
|
|
* pending. */
|
|
break;
|
|
}
|
|
|
|
if (cf->stop_flag) {
|
|
/* Well, the user decided to abort the filtering. Just stop.
|
|
|
|
XXX - go back to the previous filter? Users probably just
|
|
want not to wait for a filtering operation to finish;
|
|
unless we cancel by having no filter, reverting to the
|
|
previous filter will probably be even more expensive than
|
|
continuing the filtering, as it involves going back to the
|
|
beginning and filtering, and even with no filter we currently
|
|
have to re-generate the entire clist, which is also expensive.
|
|
|
|
I'm not sure what Network Monitor does, but it doesn't appear
|
|
to give you an unfiltered display if you cancel. */
|
|
break;
|
|
}
|
|
|
|
count++;
|
|
|
|
if (redissect) {
|
|
/* Since all state for the frame was destroyed, mark the frame
|
|
* as not visited, free the GSList referring to the state
|
|
* data (the per-frame data itself was freed by
|
|
* "init_dissection()"), and null out the GSList pointer. */
|
|
frame_data_reset(fdata);
|
|
frames_count = cf->count;
|
|
}
|
|
|
|
/* Frame dependencies from the previous dissection/filtering are no longer valid. */
|
|
fdata->dependent_of_displayed = 0;
|
|
|
|
if (!cf_read_record(cf, fdata, &rec, &buf))
|
|
break; /* error reading the frame */
|
|
|
|
/* If the previous frame is displayed, and we haven't yet seen the
|
|
selected frame, remember that frame - it's the closest one we've
|
|
yet seen before the selected frame. */
|
|
if (prev_frame_num != -1 && !selected_frame_seen && prev_frame->passed_dfilter) {
|
|
preceding_frame_num = prev_frame_num;
|
|
preceding_frame = prev_frame;
|
|
}
|
|
|
|
add_packet_to_packet_list(fdata, cf, &edt, dfcode,
|
|
cinfo, &rec, &buf,
|
|
add_to_packet_list);
|
|
|
|
/* If this frame is displayed, and this is the first frame we've
|
|
seen displayed after the selected frame, remember this frame -
|
|
it's the closest one we've yet seen at or after the selected
|
|
frame. */
|
|
if (fdata->passed_dfilter && selected_frame_seen && following_frame_num == -1) {
|
|
following_frame_num = fdata->num;
|
|
following_frame = fdata;
|
|
}
|
|
if (fdata == selected_frame) {
|
|
selected_frame_seen = TRUE;
|
|
if (fdata->passed_dfilter)
|
|
selected_frame_num = fdata->num;
|
|
}
|
|
|
|
/* Remember this frame - it'll be the previous frame
|
|
on the next pass through the loop. */
|
|
prev_frame_num = fdata->num;
|
|
prev_frame = fdata;
|
|
wtap_rec_reset(&rec);
|
|
}
|
|
|
|
epan_dissect_cleanup(&edt);
|
|
wtap_rec_cleanup(&rec);
|
|
ws_buffer_free(&buf);
|
|
|
|
/* We are done redissecting the packet list. */
|
|
cf->redissecting = FALSE;
|
|
|
|
if (redissect) {
|
|
frames_count = cf->count;
|
|
/* Clear out what remains of the visited flags and per-frame data
|
|
pointers.
|
|
|
|
XXX - that may cause various forms of bogosity when dissecting
|
|
these frames, as they won't have been seen by this sequential
|
|
pass, but the only alternative I see is to keep scanning them
|
|
even though the user requested that the scan stop, and that
|
|
would leave the user stuck with an Wireshark grinding on
|
|
until it finishes. Should we just stick them with that? */
|
|
for (; framenum <= frames_count; framenum++) {
|
|
fdata = frame_data_sequence_find(cf->provider.frames, framenum);
|
|
frame_data_reset(fdata);
|
|
}
|
|
}
|
|
|
|
/* We're done filtering the packets; destroy the progress bar if it
|
|
was created. */
|
|
if (progbar != NULL)
|
|
destroy_progress_dlg(progbar);
|
|
g_timer_destroy(prog_timer);
|
|
|
|
/* Unfreeze the packet list. */
|
|
if (!add_to_packet_list)
|
|
packet_list_recreate_visible_rows();
|
|
|
|
/* Compute the time it took to filter the file */
|
|
compute_elapsed(cf, start_time);
|
|
|
|
packet_list_thaw();
|
|
|
|
/* It is safe again to execute redissections or sort. */
|
|
ws_assert(cf->read_lock);
|
|
cf->read_lock = FALSE;
|
|
|
|
cf_callback_invoke(cf_cb_file_rescan_finished, cf);
|
|
|
|
if (selected_frame_num == -1) {
|
|
/* The selected frame didn't pass the filter. */
|
|
if (selected_frame == NULL) {
|
|
/* That's because there *was* no selected frame. Make the first
|
|
displayed frame the current frame. */
|
|
selected_frame_num = 0;
|
|
} else {
|
|
/* Find the nearest displayed frame to the selected frame (whether
|
|
it's before or after that frame) and make that the current frame.
|
|
If the next and previous displayed frames are equidistant from the
|
|
selected frame, choose the next one. */
|
|
ws_assert(following_frame == NULL ||
|
|
following_frame->num >= selected_frame->num);
|
|
ws_assert(preceding_frame == NULL ||
|
|
preceding_frame->num <= selected_frame->num);
|
|
if (following_frame == NULL) {
|
|
/* No frame after the selected frame passed the filter, so we
|
|
have to select the last displayed frame before the selected
|
|
frame. */
|
|
selected_frame_num = preceding_frame_num;
|
|
selected_frame = preceding_frame;
|
|
} else if (preceding_frame == NULL) {
|
|
/* No frame before the selected frame passed the filter, so we
|
|
have to select the first displayed frame after the selected
|
|
frame. */
|
|
selected_frame_num = following_frame_num;
|
|
selected_frame = following_frame;
|
|
} else {
|
|
/* Frames before and after the selected frame passed the filter, so
|
|
we'll select the previous frame */
|
|
selected_frame_num = preceding_frame_num;
|
|
selected_frame = preceding_frame;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (selected_frame_num == -1) {
|
|
/* There are no frames displayed at all. */
|
|
cf_unselect_packet(cf);
|
|
} else {
|
|
/* Either the frame that was selected passed the filter, or we've
|
|
found the nearest displayed frame to that frame. Select it, make
|
|
it the focus row, and make it visible. */
|
|
/* Set to invalid to force update of packet list and packet details */
|
|
if (selected_frame_num == 0) {
|
|
packet_list_select_row_from_data(NULL);
|
|
}else{
|
|
if (!packet_list_select_row_from_data(selected_frame)) {
|
|
/* We didn't find a row corresponding to this frame.
|
|
This means that the frame isn't being displayed currently,
|
|
so we can't select it. */
|
|
simple_message_box(ESD_TYPE_INFO, NULL,
|
|
"The capture file is probably not fully dissected.",
|
|
"End of capture exceeded.");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Cleanup and release all dfilter resources */
|
|
dfilter_free(dfcode);
|
|
|
|
/* If another rescan (due to dfilter change) or redissection (due to profile
|
|
* change) was requested, the rescan above is aborted and restarted here. */
|
|
if (queued_rescan_type != RESCAN_NONE) {
|
|
redissect = redissect || queued_rescan_type == RESCAN_REDISSECT;
|
|
rescan_packets(cf, "Reprocessing", "all packets", redissect);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Scan through all frame data and recalculate the ref time
|
|
* without rereading the file.
|
|
* XXX - do we need a progres bar or is this fast enough?
|
|
*/
|
|
void
|
|
cf_reftime_packets(capture_file* cf)
|
|
{
|
|
guint32 framenum;
|
|
frame_data *fdata;
|
|
nstime_t rel_ts;
|
|
|
|
cf->provider.ref = NULL;
|
|
cf->provider.prev_dis = NULL;
|
|
cf->cum_bytes = 0;
|
|
|
|
for (framenum = 1; framenum <= cf->count; framenum++) {
|
|
fdata = frame_data_sequence_find(cf->provider.frames, framenum);
|
|
|
|
/* just add some value here until we know if it is being displayed or not */
|
|
fdata->cum_bytes = cf->cum_bytes + fdata->pkt_len;
|
|
|
|
/*
|
|
*Timestamps
|
|
*/
|
|
|
|
/* If we don't have the time stamp of the first packet in the
|
|
capture, it's because this is the first packet. Save the time
|
|
stamp of this packet as the time stamp of the first packet. */
|
|
if (cf->provider.ref == NULL)
|
|
cf->provider.ref = fdata;
|
|
/* if this frames is marked as a reference time frame, reset
|
|
firstsec and firstusec to this frame */
|
|
if (fdata->ref_time)
|
|
cf->provider.ref = fdata;
|
|
|
|
/* If we don't have the time stamp of the previous displayed packet,
|
|
it's because this is the first displayed packet. Save the time
|
|
stamp of this packet as the time stamp of the previous displayed
|
|
packet. */
|
|
if (cf->provider.prev_dis == NULL) {
|
|
cf->provider.prev_dis = fdata;
|
|
}
|
|
|
|
/* Get the time elapsed between the first packet and this packet. */
|
|
fdata->frame_ref_num = (fdata != cf->provider.ref) ? cf->provider.ref->num : 0;
|
|
nstime_delta(&rel_ts, &fdata->abs_ts, &cf->provider.ref->abs_ts);
|
|
|
|
/* If it's greater than the current elapsed time, set the elapsed time
|
|
to it (we check for "greater than" so as not to be confused by
|
|
time moving backwards). */
|
|
if ((gint32)cf->elapsed_time.secs < rel_ts.secs
|
|
|| ((gint32)cf->elapsed_time.secs == rel_ts.secs && (gint32)cf->elapsed_time.nsecs < rel_ts.nsecs)) {
|
|
cf->elapsed_time = rel_ts;
|
|
}
|
|
|
|
/* If this frame is displayed, get the time elapsed between the
|
|
previous displayed packet and this packet. */
|
|
if ( fdata->passed_dfilter ) {
|
|
fdata->prev_dis_num = cf->provider.prev_dis->num;
|
|
cf->provider.prev_dis = fdata;
|
|
}
|
|
|
|
/*
|
|
* Byte counts
|
|
*/
|
|
if ( (fdata->passed_dfilter) || (fdata->ref_time) ) {
|
|
/* This frame either passed the display filter list or is marked as
|
|
a time reference frame. All time reference frames are displayed
|
|
even if they don't pass the display filter */
|
|
if (fdata->ref_time) {
|
|
/* if this was a TIME REF frame we should reset the cum_bytes field */
|
|
cf->cum_bytes = fdata->pkt_len;
|
|
fdata->cum_bytes = cf->cum_bytes;
|
|
} else {
|
|
/* increase cum_bytes with this packets length */
|
|
cf->cum_bytes += fdata->pkt_len;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
typedef enum {
|
|
PSP_FINISHED,
|
|
PSP_STOPPED,
|
|
PSP_FAILED
|
|
} psp_return_t;
|
|
|
|
static psp_return_t
|
|
process_specified_records(capture_file *cf, packet_range_t *range,
|
|
const char *string1, const char *string2, gboolean terminate_is_stop,
|
|
gboolean (*callback)(capture_file *, frame_data *,
|
|
wtap_rec *, Buffer *, void *),
|
|
void *callback_args,
|
|
gboolean show_progress_bar)
|
|
{
|
|
guint32 framenum;
|
|
frame_data *fdata;
|
|
wtap_rec rec;
|
|
Buffer buf;
|
|
psp_return_t ret = PSP_FINISHED;
|
|
|
|
progdlg_t *progbar = NULL;
|
|
GTimer *prog_timer = g_timer_new();
|
|
int progbar_count;
|
|
float progbar_val;
|
|
gchar progbar_status_str[100];
|
|
range_process_e process_this;
|
|
|
|
wtap_rec_init(&rec);
|
|
ws_buffer_init(&buf, 1514);
|
|
|
|
g_timer_start(prog_timer);
|
|
/* Count of packets at which we've looked. */
|
|
progbar_count = 0;
|
|
/* Progress so far. */
|
|
progbar_val = 0.0f;
|
|
|
|
if (cf->read_lock) {
|
|
ws_warning("Failing due to nested process_specified_records(\"%s\") call!", cf->filename);
|
|
return PSP_FAILED;
|
|
}
|
|
cf->read_lock = TRUE;
|
|
|
|
cf->stop_flag = FALSE;
|
|
|
|
if (range != NULL)
|
|
packet_range_process_init(range);
|
|
|
|
/* Iterate through all the packets, printing the packets that
|
|
were selected by the current display filter. */
|
|
for (framenum = 1; framenum <= cf->count; framenum++) {
|
|
fdata = frame_data_sequence_find(cf->provider.frames, framenum);
|
|
|
|
/* Create the progress bar if necessary.
|
|
We check on every iteration of the loop, so that it takes no
|
|
longer than the standard time to create it (otherwise, for a
|
|
large file, we might take considerably longer than that standard
|
|
time in order to get to the next progress bar step). */
|
|
if (show_progress_bar && progbar == NULL)
|
|
progbar = delayed_create_progress_dlg(cf->window, string1, string2,
|
|
terminate_is_stop,
|
|
&cf->stop_flag,
|
|
progbar_val);
|
|
|
|
/*
|
|
* Update the progress bar, but do it only after PROGBAR_UPDATE_INTERVAL
|
|
* has elapsed. Calling update_progress_dlg and packets_bar_update will
|
|
* likely trigger UI paint events, which might take a while depending on
|
|
* the platform and display. Reset our timer *after* painting.
|
|
*/
|
|
if (progbar && g_timer_elapsed(prog_timer, NULL) > PROGBAR_UPDATE_INTERVAL) {
|
|
/* let's not divide by zero. I should never be started
|
|
* with count == 0, so let's assert that
|
|
*/
|
|
ws_assert(cf->count > 0);
|
|
progbar_val = (gfloat) progbar_count / cf->count;
|
|
|
|
snprintf(progbar_status_str, sizeof(progbar_status_str),
|
|
"%4u of %u packets", progbar_count, cf->count);
|
|
update_progress_dlg(progbar, progbar_val, progbar_status_str);
|
|
|
|
g_timer_start(prog_timer);
|
|
}
|
|
|
|
if (cf->stop_flag) {
|
|
/* Well, the user decided to abort the operation. Just stop,
|
|
and arrange to return PSP_STOPPED to our caller, so they know
|
|
it was stopped explicitly. */
|
|
ret = PSP_STOPPED;
|
|
break;
|
|
}
|
|
|
|
progbar_count++;
|
|
|
|
if (range != NULL) {
|
|
/* do we have to process this packet? */
|
|
process_this = packet_range_process_packet(range, fdata);
|
|
if (process_this == range_process_next) {
|
|
/* this packet uninteresting, continue with next one */
|
|
continue;
|
|
} else if (process_this == range_processing_finished) {
|
|
/* all interesting packets processed, stop the loop */
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Get the packet */
|
|
if (!cf_read_record(cf, fdata, &rec, &buf)) {
|
|
/* Attempt to get the packet failed. */
|
|
ret = PSP_FAILED;
|
|
break;
|
|
}
|
|
/* Process the packet */
|
|
if (!callback(cf, fdata, &rec, &buf, callback_args)) {
|
|
/* Callback failed. We assume it reported the error appropriately. */
|
|
ret = PSP_FAILED;
|
|
break;
|
|
}
|
|
wtap_rec_reset(&rec);
|
|
}
|
|
|
|
/* We're done printing the packets; destroy the progress bar if
|
|
it was created. */
|
|
if (progbar != NULL)
|
|
destroy_progress_dlg(progbar);
|
|
g_timer_destroy(prog_timer);
|
|
|
|
ws_assert(cf->read_lock);
|
|
cf->read_lock = FALSE;
|
|
|
|
wtap_rec_cleanup(&rec);
|
|
ws_buffer_free(&buf);
|
|
|
|
return ret;
|
|
}
|
|
|
|
typedef struct {
|
|
epan_dissect_t edt;
|
|
column_info *cinfo;
|
|
} retap_callback_args_t;
|
|
|
|
static gboolean
|
|
retap_packet(capture_file *cf, frame_data *fdata, wtap_rec *rec, Buffer *buf,
|
|
void *argsp)
|
|
{
|
|
retap_callback_args_t *args = (retap_callback_args_t *)argsp;
|
|
|
|
epan_dissect_run_with_taps(&args->edt, cf->cd_t, rec,
|
|
frame_tvbuff_new_buffer(&cf->provider, fdata, buf),
|
|
fdata, args->cinfo);
|
|
epan_dissect_reset(&args->edt);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
cf_read_status_t
|
|
cf_retap_packets(capture_file *cf)
|
|
{
|
|
packet_range_t range;
|
|
retap_callback_args_t callback_args;
|
|
gboolean create_proto_tree;
|
|
guint tap_flags;
|
|
psp_return_t ret;
|
|
|
|
/* Presumably the user closed the capture file. */
|
|
if (cf == NULL) {
|
|
return CF_READ_ABORTED;
|
|
}
|
|
|
|
cf_callback_invoke(cf_cb_file_retap_started, cf);
|
|
|
|
/* Get the union of the flags for all tap listeners. */
|
|
tap_flags = union_of_tap_listener_flags();
|
|
|
|
/* If any tap listeners require the columns, construct them. */
|
|
callback_args.cinfo = (tap_flags & TL_REQUIRES_COLUMNS) ? &cf->cinfo : NULL;
|
|
|
|
/*
|
|
* Determine whether we need to create a protocol tree.
|
|
* We do if:
|
|
*
|
|
* one of the tap listeners is going to apply a filter;
|
|
*
|
|
* one of the tap listeners requires a protocol tree.
|
|
*/
|
|
create_proto_tree =
|
|
(have_filtering_tap_listeners() || (tap_flags & TL_REQUIRES_PROTO_TREE));
|
|
|
|
/* Reset the tap listeners. */
|
|
reset_tap_listeners();
|
|
|
|
epan_dissect_init(&callback_args.edt, cf->epan, create_proto_tree, FALSE);
|
|
|
|
/* Iterate through the list of packets, dissecting all packets and
|
|
re-running the taps. */
|
|
packet_range_init(&range, cf);
|
|
packet_range_process_init(&range);
|
|
|
|
ret = process_specified_records(cf, &range, "Recalculating statistics on",
|
|
"all packets", TRUE, retap_packet,
|
|
&callback_args, TRUE);
|
|
|
|
packet_range_cleanup(&range);
|
|
epan_dissect_cleanup(&callback_args.edt);
|
|
|
|
cf_callback_invoke(cf_cb_file_retap_finished, cf);
|
|
|
|
switch (ret) {
|
|
case PSP_FINISHED:
|
|
/* Completed successfully. */
|
|
return CF_READ_OK;
|
|
|
|
case PSP_STOPPED:
|
|
/* Well, the user decided to abort the refiltering.
|
|
Return CF_READ_ABORTED so our caller knows they did that. */
|
|
return CF_READ_ABORTED;
|
|
|
|
case PSP_FAILED:
|
|
/* Error while retapping. */
|
|
return CF_READ_ERROR;
|
|
}
|
|
|
|
ws_assert_not_reached();
|
|
return CF_READ_OK;
|
|
}
|
|
|
|
typedef struct {
|
|
print_args_t *print_args;
|
|
gboolean print_header_line;
|
|
char *header_line_buf;
|
|
int header_line_buf_len;
|
|
gboolean print_formfeed;
|
|
gboolean print_separator;
|
|
char *line_buf;
|
|
int line_buf_len;
|
|
gint *col_widths;
|
|
int num_visible_cols;
|
|
gint *visible_cols;
|
|
epan_dissect_t edt;
|
|
} print_callback_args_t;
|
|
|
|
static gboolean
|
|
print_packet(capture_file *cf, frame_data *fdata, wtap_rec *rec, Buffer *buf,
|
|
void *argsp)
|
|
{
|
|
print_callback_args_t *args = (print_callback_args_t *)argsp;
|
|
int i;
|
|
char *cp;
|
|
int line_len;
|
|
int column_len;
|
|
int cp_off;
|
|
char bookmark_name[9+10+1]; /* "__frameNNNNNNNNNN__\0" */
|
|
char bookmark_title[6+10+1]; /* "Frame NNNNNNNNNN__\0" */
|
|
col_item_t* col_item;
|
|
const gchar* col_text;
|
|
|
|
/* Fill in the column information if we're printing the summary
|
|
information. */
|
|
if (args->print_args->print_summary) {
|
|
col_custom_prime_edt(&args->edt, &cf->cinfo);
|
|
epan_dissect_run(&args->edt, cf->cd_t, rec,
|
|
frame_tvbuff_new_buffer(&cf->provider, fdata, buf),
|
|
fdata, &cf->cinfo);
|
|
epan_dissect_fill_in_columns(&args->edt, FALSE, TRUE);
|
|
} else
|
|
epan_dissect_run(&args->edt, cf->cd_t, rec,
|
|
frame_tvbuff_new_buffer(&cf->provider, fdata, buf),
|
|
fdata, NULL);
|
|
|
|
if (args->print_formfeed) {
|
|
if (!new_page(args->print_args->stream))
|
|
goto fail;
|
|
|
|
/*
|
|
* Print another header line if we print a packet summary on the
|
|
* new page.
|
|
*/
|
|
if (args->print_args->print_col_headings)
|
|
args->print_header_line = TRUE;
|
|
} else {
|
|
if (args->print_separator) {
|
|
if (!print_line(args->print_args->stream, 0, ""))
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We generate bookmarks, if the output format supports them.
|
|
* The name is "__frameN__".
|
|
*/
|
|
snprintf(bookmark_name, sizeof bookmark_name, "__frame%u__", fdata->num);
|
|
|
|
if (args->print_args->print_summary) {
|
|
if (!args->print_args->print_col_headings)
|
|
args->print_header_line = FALSE;
|
|
if (args->print_header_line) {
|
|
if (!print_line(args->print_args->stream, 0, args->header_line_buf))
|
|
goto fail;
|
|
args->print_header_line = FALSE; /* we might not need to print any more */
|
|
}
|
|
cp = &args->line_buf[0];
|
|
line_len = 0;
|
|
for (i = 0; i < args->num_visible_cols; i++) {
|
|
col_item = &cf->cinfo.columns[args->visible_cols[i]];
|
|
col_text = get_column_text(&cf->cinfo, args->visible_cols[i]);
|
|
/* Find the length of the string for this column. */
|
|
column_len = (int) strlen(col_text);
|
|
if (args->col_widths[i] > column_len)
|
|
column_len = args->col_widths[i];
|
|
|
|
/* Make sure there's room in the line buffer for the column; if not,
|
|
double its length. */
|
|
line_len += column_len + 1; /* "+1" for space */
|
|
if (line_len > args->line_buf_len) {
|
|
cp_off = (int) (cp - args->line_buf);
|
|
args->line_buf_len = 2 * line_len;
|
|
args->line_buf = (char *)g_realloc(args->line_buf, args->line_buf_len + 1);
|
|
cp = args->line_buf + cp_off;
|
|
}
|
|
|
|
/* Right-justify the packet number column. */
|
|
if (col_item->col_fmt == COL_NUMBER)
|
|
snprintf(cp, column_len+1, "%*s", args->col_widths[i], col_text);
|
|
else
|
|
snprintf(cp, column_len+1, "%-*s", args->col_widths[i], col_text);
|
|
cp += column_len;
|
|
if (i != args->num_visible_cols - 1)
|
|
*cp++ = ' ';
|
|
}
|
|
*cp = '\0';
|
|
|
|
/*
|
|
* Generate a bookmark, using the summary line as the title.
|
|
*/
|
|
if (!print_bookmark(args->print_args->stream, bookmark_name,
|
|
args->line_buf))
|
|
goto fail;
|
|
|
|
if (!print_line(args->print_args->stream, 0, args->line_buf))
|
|
goto fail;
|
|
} else {
|
|
/*
|
|
* Generate a bookmark, using "Frame N" as the title, as we're not
|
|
* printing the summary line.
|
|
*/
|
|
snprintf(bookmark_title, sizeof bookmark_title, "Frame %u", fdata->num);
|
|
if (!print_bookmark(args->print_args->stream, bookmark_name,
|
|
bookmark_title))
|
|
goto fail;
|
|
} /* if (print_summary) */
|
|
|
|
if (args->print_args->print_dissections != print_dissections_none) {
|
|
if (args->print_args->print_summary) {
|
|
/* Separate the summary line from the tree with a blank line. */
|
|
if (!print_line(args->print_args->stream, 0, ""))
|
|
goto fail;
|
|
}
|
|
|
|
/* Print the information in that tree. */
|
|
if (!proto_tree_print(args->print_args->print_dissections,
|
|
args->print_args->print_hex, &args->edt, NULL,
|
|
args->print_args->stream))
|
|
goto fail;
|
|
|
|
/* Print a blank line if we print anything after this (aka more than one packet). */
|
|
args->print_separator = TRUE;
|
|
|
|
/* Print a header line if we print any more packet summaries */
|
|
if (args->print_args->print_col_headings)
|
|
args->print_header_line = TRUE;
|
|
}
|
|
|
|
if (args->print_args->print_hex) {
|
|
if (args->print_args->print_summary || (args->print_args->print_dissections != print_dissections_none)) {
|
|
if (!print_line(args->print_args->stream, 0, ""))
|
|
goto fail;
|
|
}
|
|
/* Print the full packet data as hex. */
|
|
if (!print_hex_data(args->print_args->stream, &args->edt, args->print_args->hexdump_options))
|
|
goto fail;
|
|
|
|
/* Print a blank line if we print anything after this (aka more than one packet). */
|
|
args->print_separator = TRUE;
|
|
|
|
/* Print a header line if we print any more packet summaries */
|
|
if (args->print_args->print_col_headings)
|
|
args->print_header_line = TRUE;
|
|
} /* if (args->print_args->print_dissections != print_dissections_none) */
|
|
|
|
epan_dissect_reset(&args->edt);
|
|
|
|
/* do we want to have a formfeed between each packet from now on? */
|
|
if (args->print_args->print_formfeed) {
|
|
args->print_formfeed = TRUE;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
fail:
|
|
epan_dissect_reset(&args->edt);
|
|
return FALSE;
|
|
}
|
|
|
|
cf_print_status_t
|
|
cf_print_packets(capture_file *cf, print_args_t *print_args,
|
|
gboolean show_progress_bar)
|
|
{
|
|
print_callback_args_t callback_args;
|
|
gint data_width;
|
|
char *cp;
|
|
int i, cp_off, column_len, line_len;
|
|
int num_visible_col = 0, last_visible_col = 0, visible_col_count;
|
|
psp_return_t ret;
|
|
GList *clp;
|
|
fmt_data *cfmt;
|
|
gboolean proto_tree_needed;
|
|
|
|
callback_args.print_args = print_args;
|
|
callback_args.print_header_line = print_args->print_col_headings;
|
|
callback_args.header_line_buf = NULL;
|
|
callback_args.header_line_buf_len = 256;
|
|
callback_args.print_formfeed = FALSE;
|
|
callback_args.print_separator = FALSE;
|
|
callback_args.line_buf = NULL;
|
|
callback_args.line_buf_len = 256;
|
|
callback_args.col_widths = NULL;
|
|
callback_args.num_visible_cols = 0;
|
|
callback_args.visible_cols = NULL;
|
|
|
|
if (!print_preamble(print_args->stream, cf->filename, get_ws_vcs_version_info())) {
|
|
destroy_print_stream(print_args->stream);
|
|
return CF_PRINT_WRITE_ERROR;
|
|
}
|
|
|
|
if (print_args->print_summary) {
|
|
/* We're printing packet summaries. Allocate the header line buffer
|
|
and get the column widths. */
|
|
callback_args.header_line_buf = (char *)g_malloc(callback_args.header_line_buf_len + 1);
|
|
|
|
/* Find the number of visible columns and the last visible column */
|
|
for (i = 0; i < prefs.num_cols; i++) {
|
|
|
|
clp = g_list_nth(prefs.col_list, i);
|
|
if (clp == NULL) /* Sanity check, Invalid column requested */
|
|
continue;
|
|
|
|
cfmt = (fmt_data *) clp->data;
|
|
if (cfmt->visible) {
|
|
num_visible_col++;
|
|
last_visible_col = i;
|
|
}
|
|
}
|
|
|
|
/* if num_visible_col is 0, we are done */
|
|
if (num_visible_col == 0) {
|
|
g_free(callback_args.header_line_buf);
|
|
return CF_PRINT_OK;
|
|
}
|
|
|
|
/* Find the widths for each of the columns - maximum of the
|
|
width of the title and the width of the data - and construct
|
|
a buffer with a line containing the column titles. */
|
|
callback_args.num_visible_cols = num_visible_col;
|
|
callback_args.col_widths = g_new(gint, num_visible_col);
|
|
callback_args.visible_cols = g_new(gint, num_visible_col);
|
|
cp = &callback_args.header_line_buf[0];
|
|
line_len = 0;
|
|
visible_col_count = 0;
|
|
for (i = 0; i < cf->cinfo.num_cols; i++) {
|
|
|
|
clp = g_list_nth(prefs.col_list, i);
|
|
if (clp == NULL) /* Sanity check, Invalid column requested */
|
|
continue;
|
|
|
|
cfmt = (fmt_data *) clp->data;
|
|
if (cfmt->visible == FALSE)
|
|
continue;
|
|
|
|
/* Save the order of visible columns */
|
|
callback_args.visible_cols[visible_col_count] = i;
|
|
|
|
/* Don't pad the last column. */
|
|
if (i == last_visible_col)
|
|
callback_args.col_widths[visible_col_count] = 0;
|
|
else {
|
|
callback_args.col_widths[visible_col_count] = (gint) strlen(cf->cinfo.columns[i].col_title);
|
|
data_width = get_column_char_width(get_column_format(i));
|
|
if (data_width > callback_args.col_widths[visible_col_count])
|
|
callback_args.col_widths[visible_col_count] = data_width;
|
|
}
|
|
|
|
/* Find the length of the string for this column. */
|
|
column_len = (int) strlen(cf->cinfo.columns[i].col_title);
|
|
if (callback_args.col_widths[visible_col_count] > column_len)
|
|
column_len = callback_args.col_widths[visible_col_count];
|
|
|
|
/* Make sure there's room in the line buffer for the column; if not,
|
|
double its length. */
|
|
line_len += column_len + 1; /* "+1" for space */
|
|
if (line_len > callback_args.header_line_buf_len) {
|
|
cp_off = (int) (cp - callback_args.header_line_buf);
|
|
callback_args.header_line_buf_len = 2 * line_len;
|
|
callback_args.header_line_buf = (char *)g_realloc(callback_args.header_line_buf,
|
|
callback_args.header_line_buf_len + 1);
|
|
cp = callback_args.header_line_buf + cp_off;
|
|
}
|
|
|
|
/* Right-justify the packet number column. */
|
|
/* if (cf->cinfo.col_fmt[i] == COL_NUMBER)
|
|
snprintf(cp, column_len+1, "%*s", callback_args.col_widths[visible_col_count], cf->cinfo.columns[i].col_title);
|
|
else*/
|
|
snprintf(cp, column_len+1, "%-*s", callback_args.col_widths[visible_col_count], cf->cinfo.columns[i].col_title);
|
|
cp += column_len;
|
|
if (i != cf->cinfo.num_cols - 1)
|
|
*cp++ = ' ';
|
|
|
|
visible_col_count++;
|
|
}
|
|
*cp = '\0';
|
|
|
|
/* Now start out the main line buffer with the same length as the
|
|
header line buffer. */
|
|
callback_args.line_buf_len = callback_args.header_line_buf_len;
|
|
callback_args.line_buf = (char *)g_malloc(callback_args.line_buf_len + 1);
|
|
} /* if (print_summary) */
|
|
|
|
/* Create the protocol tree, and make it visible, if we're printing
|
|
the dissection or the hex data.
|
|
XXX - do we need it if we're just printing the hex data? */
|
|
proto_tree_needed =
|
|
callback_args.print_args->print_dissections != print_dissections_none ||
|
|
callback_args.print_args->print_hex ||
|
|
have_custom_cols(&cf->cinfo) || have_field_extractors();
|
|
epan_dissect_init(&callback_args.edt, cf->epan, proto_tree_needed, proto_tree_needed);
|
|
|
|
/* Iterate through the list of packets, printing the packets we were
|
|
told to print. */
|
|
ret = process_specified_records(cf, &print_args->range, "Printing",
|
|
"selected packets", TRUE, print_packet,
|
|
&callback_args, show_progress_bar);
|
|
epan_dissect_cleanup(&callback_args.edt);
|
|
g_free(callback_args.header_line_buf);
|
|
g_free(callback_args.line_buf);
|
|
g_free(callback_args.col_widths);
|
|
g_free(callback_args.visible_cols);
|
|
|
|
switch (ret) {
|
|
|
|
case PSP_FINISHED:
|
|
/* Completed successfully. */
|
|
break;
|
|
|
|
case PSP_STOPPED:
|
|
/* Well, the user decided to abort the printing.
|
|
|
|
XXX - note that what got generated before they did that
|
|
will get printed if we're piping to a print program; we'd
|
|
have to write to a file and then hand that to the print
|
|
program to make it actually not print anything. */
|
|
break;
|
|
|
|
case PSP_FAILED:
|
|
/* Error while printing.
|
|
|
|
XXX - note that what got generated before they did that
|
|
will get printed if we're piping to a print program; we'd
|
|
have to write to a file and then hand that to the print
|
|
program to make it actually not print anything. */
|
|
destroy_print_stream(print_args->stream);
|
|
return CF_PRINT_WRITE_ERROR;
|
|
}
|
|
|
|
if (!print_finale(print_args->stream)) {
|
|
destroy_print_stream(print_args->stream);
|
|
return CF_PRINT_WRITE_ERROR;
|
|
}
|
|
|
|
if (!destroy_print_stream(print_args->stream))
|
|
return CF_PRINT_WRITE_ERROR;
|
|
|
|
return CF_PRINT_OK;
|
|
}
|
|
|
|
typedef struct {
|
|
FILE *fh;
|
|
epan_dissect_t edt;
|
|
print_args_t *print_args;
|
|
json_dumper jdumper;
|
|
} write_packet_callback_args_t;
|
|
|
|
static gboolean
|
|
write_pdml_packet(capture_file *cf, frame_data *fdata, wtap_rec *rec,
|
|
Buffer *buf, void *argsp)
|
|
{
|
|
write_packet_callback_args_t *args = (write_packet_callback_args_t *)argsp;
|
|
|
|
/* Create the protocol tree, but don't fill in the column information. */
|
|
epan_dissect_run(&args->edt, cf->cd_t, rec,
|
|
frame_tvbuff_new_buffer(&cf->provider, fdata, buf),
|
|
fdata, NULL);
|
|
|
|
/* Write out the information in that tree. */
|
|
write_pdml_proto_tree(NULL, NULL, &args->edt, &cf->cinfo, args->fh, FALSE);
|
|
|
|
epan_dissect_reset(&args->edt);
|
|
|
|
return !ferror(args->fh);
|
|
}
|
|
|
|
cf_print_status_t
|
|
cf_write_pdml_packets(capture_file *cf, print_args_t *print_args)
|
|
{
|
|
write_packet_callback_args_t callback_args;
|
|
FILE *fh;
|
|
psp_return_t ret;
|
|
|
|
fh = ws_fopen(print_args->file, "w");
|
|
if (fh == NULL)
|
|
return CF_PRINT_OPEN_ERROR; /* attempt to open destination failed */
|
|
|
|
write_pdml_preamble(fh, cf->filename);
|
|
if (ferror(fh)) {
|
|
fclose(fh);
|
|
return CF_PRINT_WRITE_ERROR;
|
|
}
|
|
|
|
callback_args.fh = fh;
|
|
callback_args.print_args = print_args;
|
|
epan_dissect_init(&callback_args.edt, cf->epan, TRUE, TRUE);
|
|
|
|
/* Iterate through the list of packets, printing the packets we were
|
|
told to print. */
|
|
ret = process_specified_records(cf, &print_args->range, "Writing PDML",
|
|
"selected packets", TRUE,
|
|
write_pdml_packet, &callback_args, TRUE);
|
|
|
|
epan_dissect_cleanup(&callback_args.edt);
|
|
|
|
switch (ret) {
|
|
|
|
case PSP_FINISHED:
|
|
/* Completed successfully. */
|
|
break;
|
|
|
|
case PSP_STOPPED:
|
|
/* Well, the user decided to abort the printing. */
|
|
break;
|
|
|
|
case PSP_FAILED:
|
|
/* Error while printing. */
|
|
fclose(fh);
|
|
return CF_PRINT_WRITE_ERROR;
|
|
}
|
|
|
|
write_pdml_finale(fh);
|
|
if (ferror(fh)) {
|
|
fclose(fh);
|
|
return CF_PRINT_WRITE_ERROR;
|
|
}
|
|
|
|
/* XXX - check for an error */
|
|
fclose(fh);
|
|
|
|
return CF_PRINT_OK;
|
|
}
|
|
|
|
static gboolean
|
|
write_psml_packet(capture_file *cf, frame_data *fdata, wtap_rec *rec,
|
|
Buffer *buf, void *argsp)
|
|
{
|
|
write_packet_callback_args_t *args = (write_packet_callback_args_t *)argsp;
|
|
|
|
/* Fill in the column information */
|
|
col_custom_prime_edt(&args->edt, &cf->cinfo);
|
|
epan_dissect_run(&args->edt, cf->cd_t, rec,
|
|
frame_tvbuff_new_buffer(&cf->provider, fdata, buf),
|
|
fdata, &cf->cinfo);
|
|
epan_dissect_fill_in_columns(&args->edt, FALSE, TRUE);
|
|
|
|
/* Write out the column information. */
|
|
write_psml_columns(&args->edt, args->fh, FALSE);
|
|
|
|
epan_dissect_reset(&args->edt);
|
|
|
|
return !ferror(args->fh);
|
|
}
|
|
|
|
cf_print_status_t
|
|
cf_write_psml_packets(capture_file *cf, print_args_t *print_args)
|
|
{
|
|
write_packet_callback_args_t callback_args;
|
|
FILE *fh;
|
|
psp_return_t ret;
|
|
|
|
gboolean proto_tree_needed;
|
|
|
|
fh = ws_fopen(print_args->file, "w");
|
|
if (fh == NULL)
|
|
return CF_PRINT_OPEN_ERROR; /* attempt to open destination failed */
|
|
|
|
write_psml_preamble(&cf->cinfo, fh);
|
|
if (ferror(fh)) {
|
|
fclose(fh);
|
|
return CF_PRINT_WRITE_ERROR;
|
|
}
|
|
|
|
callback_args.fh = fh;
|
|
callback_args.print_args = print_args;
|
|
|
|
/* Fill in the column information, only create the protocol tree
|
|
if having custom columns or field extractors. */
|
|
proto_tree_needed = have_custom_cols(&cf->cinfo) || have_field_extractors();
|
|
epan_dissect_init(&callback_args.edt, cf->epan, proto_tree_needed, proto_tree_needed);
|
|
|
|
/* Iterate through the list of packets, printing the packets we were
|
|
told to print. */
|
|
ret = process_specified_records(cf, &print_args->range, "Writing PSML",
|
|
"selected packets", TRUE,
|
|
write_psml_packet, &callback_args, TRUE);
|
|
|
|
epan_dissect_cleanup(&callback_args.edt);
|
|
|
|
switch (ret) {
|
|
|
|
case PSP_FINISHED:
|
|
/* Completed successfully. */
|
|
break;
|
|
|
|
case PSP_STOPPED:
|
|
/* Well, the user decided to abort the printing. */
|
|
break;
|
|
|
|
case PSP_FAILED:
|
|
/* Error while printing. */
|
|
fclose(fh);
|
|
return CF_PRINT_WRITE_ERROR;
|
|
}
|
|
|
|
write_psml_finale(fh);
|
|
if (ferror(fh)) {
|
|
fclose(fh);
|
|
return CF_PRINT_WRITE_ERROR;
|
|
}
|
|
|
|
/* XXX - check for an error */
|
|
fclose(fh);
|
|
|
|
return CF_PRINT_OK;
|
|
}
|
|
|
|
static gboolean
|
|
write_csv_packet(capture_file *cf, frame_data *fdata, wtap_rec *rec< |