Use wtap_blocks for packet comments

Mostly functioning proof of concept for #14329. This work is intended to
allow Wireshark to support multiple packet comments per packet.

Uses and expands upon the `wtap_block` API in `wiretap/wtap_opttypes.h`.
It attaches a `wtap_block` structure to `wtap_rec` in place of its
current `opt_comment` and `packet_verdict` members to hold OPT_COMMENT
and OPT_PKT_VERDICT option values.
pespin/rlcmac
David Perry 2021-04-29 07:23:21 -04:00 committed by Guy Harris
parent 20f38c06ea
commit 73087d6fb4
41 changed files with 1566 additions and 553 deletions

View File

@ -59,7 +59,7 @@ struct packet_provider_data {
frame_data *prev_dis;
frame_data *prev_cap;
frame_data_sequence *frames; /* Sequence of frames, if we're keeping that information */
GTree *frames_user_comments; /* BST with user comments for frames (key = frame_data) */
GTree *frames_edited_blocks; /* BST with changed blocks for frames (key = frame_data) */
};
typedef struct _capture_file {
@ -131,8 +131,8 @@ extern void cap_file_init(capture_file *cf);
const char *cap_file_provider_get_interface_name(struct packet_provider_data *prov, guint32 interface_id);
const char *cap_file_provider_get_interface_description(struct packet_provider_data *prov, guint32 interface_id);
const char *cap_file_provider_get_user_comment(struct packet_provider_data *prov, const frame_data *fd);
void cap_file_provider_set_user_comment(struct packet_provider_data *prov, frame_data *fd, const char *new_comment);
wtap_block_t cap_file_provider_get_user_block(struct packet_provider_data *prov, const frame_data *fd);
void cap_file_provider_set_user_block(struct packet_provider_data *prov, frame_data *fd, const wtap_block_t new_block);
#ifdef __cplusplus
}

View File

@ -559,7 +559,7 @@ libwireshark.so.0 libwireshark0 #MINVER#
epan_get_interface_description@Base 2.3.0
epan_get_interface_name@Base 1.99.2
epan_get_runtime_version_info@Base 1.9.1
epan_get_user_comment@Base 1.99.2
epan_get_user_block@Base 3.5.0
epan_get_version@Base 1.9.1
epan_get_version_number@Base 2.5.0
epan_init@Base 2.9.0

View File

@ -23,39 +23,52 @@ libwiretap.so.0 libwiretap0 #MINVER#
wtap_add_generated_idb@Base 3.3.0
wtap_addrinfo_list_empty@Base 2.5.0
wtap_block_add_custom_option@Base 3.5.0
wtap_block_add_bytes_option@Base 3.5.0
wtap_block_add_bytes_option_borrow@Base 3.5.0
wtap_block_add_if_filter_option@Base 3.5.0
wtap_block_add_ipv4_option@Base 2.1.2
wtap_block_add_ipv6_option@Base 2.1.2
wtap_block_add_string_option@Base 2.1.2
wtap_block_add_string_option_format@Base 2.1.2
wtap_block_add_uint32_option@Base 3.5.0
wtap_block_add_uint64_option@Base 2.1.2
wtap_block_add_uint8_option@Base 2.1.2
wtap_block_array_free@Base 2.1.2
wtap_block_copy@Base 2.1.2
wtap_block_count_option@Base 3.5.0
wtap_block_create@Base 2.1.2
wtap_block_foreach_option@Base 2.1.2
wtap_block_free@Base 2.1.2
wtap_block_get_bytes_option_value@Base 3.5.0
wtap_block_get_if_filter_option_value@Base 3.5.0
wtap_block_get_ipv4_option_value@Base 2.1.2
wtap_block_get_ipv6_option_value@Base 2.1.2
wtap_block_get_mandatory_data@Base 2.1.2
wtap_block_get_nth_bytes_option_value@Base 3.5.0
wtap_block_get_nth_string_option_value@Base 2.1.2
wtap_block_get_options_size_padded@Base 3.5.0
wtap_block_get_string_option_value@Base 2.1.2
wtap_block_get_type@Base 3.5.0
wtap_block_get_uint32_option_value@Base 3.5.0
wtap_block_get_uint64_option_value@Base 2.1.2
wtap_block_get_uint8_option_value@Base 2.1.2
wtap_block_make_copy@Base 3.3.2
wtap_block_option_get_value_size@Base 3.5.0
wtap_block_ref@Base 3.5.0
wtap_block_remove_nth_option_instance@Base 2.2.0
wtap_block_remove_option@Base 2.2.0
wtap_block_set_bytes_option_value@Base 3.5.0
wtap_block_set_if_filter_option_value@Base 3.5.0
wtap_block_set_ipv4_option_value@Base 2.1.2
wtap_block_set_ipv6_option_value@Base 2.1.2
wtap_block_set_nth_bytes_option_value@Base 3.5.0
wtap_block_set_nth_string_option_value@Base 2.1.2
wtap_block_set_nth_string_option_value_format@Base 3.5.0
wtap_block_set_string_option_value@Base 2.1.2
wtap_block_set_string_option_value_format@Base 2.1.2
wtap_block_set_uint32_option_value@Base 3.5.0
wtap_block_set_uint64_option_value@Base 2.1.2
wtap_block_set_uint8_option_value@Base 2.1.2
wtap_block_unref@Base 3.5.0
wtap_cleanup@Base 2.3.0
wtap_cleareof@Base 1.9.1
wtap_close@Base 1.9.1

View File

@ -1260,7 +1260,6 @@ main(int argc, char *argv[])
case LONGOPT_CAPTURE_COMMENT:
{
/* pcapng supports multiple comments, so support them here too.
* Wireshark only sees the first capture comment though.
*/
if (!capture_comments) {
capture_comments = g_ptr_array_new_with_free_func(g_free);
@ -2186,13 +2185,13 @@ main(int argc, char *argv[])
/* Copy and change rather than modify returned rec */
temp_rec = *rec;
/* The comment is not modified by dumper, cast away. */
temp_rec.opt_comment = (char *)comment;
temp_rec.has_comment_changed = TRUE;
wtap_block_add_string_option(rec->block, OPT_COMMENT, (char *)comment, strlen((char *)comment));
temp_rec.has_block_changed = TRUE;
rec = &temp_rec;
} else {
/* Copy and change rather than modify returned rec */
temp_rec = *rec;
temp_rec.has_comment_changed = FALSE;
temp_rec.has_block_changed = FALSE;
rec = &temp_rec;
}
}
@ -2278,7 +2277,7 @@ clean_exit:
if (idbs_seen != NULL) {
for (guint b = 0; b < idbs_seen->len; b++) {
wtap_block_t if_data = g_array_index(idbs_seen, wtap_block_t, b);
wtap_block_free(if_data);
wtap_block_unref(if_data);
}
g_array_free(idbs_seen, TRUE);
}

View File

@ -177,6 +177,15 @@ static dissector_table_t wtap_fts_rec_dissector_table;
#define OPT_VERDICT_TYPE_TC 1
#define OPT_VERDICT_TYPE_XDP 2
/* Structure for passing as userdata to wtap_block_foreach_option */
typedef struct fr_foreach_s {
proto_item *item;
proto_tree *tree;
tvbuff_t *tvb;
packet_info *pinfo;
guint n_changes;
} fr_foreach_t;
static const char *
get_verdict_type_string(guint8 type)
{
@ -191,29 +200,6 @@ get_verdict_type_string(guint8 type)
return "Unknown";
}
static void
format_verdict_summary(wtap_rec *rec, char *buffer, size_t n)
{
buffer[0] = 0;
for(guint i = 0; i < rec->packet_verdict->len; i++) {
char *format = i ? ", %s (%u)" : "%s (%u)";
size_t offset = strlen(buffer);
GBytes *verdict = (GBytes *) g_ptr_array_index(rec->packet_verdict, i);
const guint8 *data;
if (verdict == NULL)
continue;
if (offset >= n)
return;
data = (const guint8 *) g_bytes_get_data(verdict, NULL);
snprintf(buffer + offset, n - offset,
format, get_verdict_type_string(data[0]), data[0]);
}
}
static void
ensure_tree_item(proto_tree *tree, guint count)
{
@ -276,10 +262,80 @@ call_frame_end_routine(gpointer routine)
(*func)();
}
static gboolean
frame_add_comment(wtap_block_t block _U_, guint option_id, wtap_opttype_e option_type _U_, wtap_optval_t *option, void *user_data)
{
fr_foreach_t *fr_user_data = (fr_foreach_t *)user_data;
proto_item *comment_item;
if (option_id == OPT_COMMENT) {
comment_item = proto_tree_add_string_format(fr_user_data->tree, hf_comments_text,
fr_user_data->tvb, 0, 0,
option->stringval,
"%s", option->stringval);
expert_add_info_format(fr_user_data->pinfo, comment_item, &ei_comments_text,
"%s", option->stringval);
}
fr_user_data->n_changes++;
return TRUE;
}
static gboolean
frame_add_verdict(wtap_block_t block _U_, guint option_id, wtap_opttype_e option_type _U_, wtap_optval_t *option, void *user_data)
{
fr_foreach_t *fr_user_data = (fr_foreach_t *)user_data;
if (option_id == OPT_PKT_VERDICT) {
GBytes *verdict = option->byteval;
const guint8 *verdict_data;
gsize len;
char *format = fr_user_data->n_changes ? ", %s (%u)" : "%s (%u)";
if (verdict == NULL)
return TRUE;
verdict_data = (const guint8 *) g_bytes_get_data(verdict, &len);
if (len == 0)
return TRUE;
proto_item_append_text(fr_user_data->item, format,
get_verdict_type_string(verdict_data[0]), verdict_data[0]);
len -= 1;
switch(verdict_data[0]) {
case OPT_VERDICT_TYPE_HW:
proto_tree_add_bytes_with_length(fr_user_data->tree, hf_frame_verdict_hardware,
fr_user_data->tvb, 0, 0, verdict_data + 1, (gint) len);
break;
case OPT_VERDICT_TYPE_TC:
if (len == 8) {
gint64 val;
memcpy(&val, verdict_data + 1, sizeof(val));
proto_tree_add_int64(fr_user_data->tree, hf_frame_verdict_tc, fr_user_data->tvb, 0, 0, val);
}
break;
case OPT_VERDICT_TYPE_XDP:
if (len == 8) {
gint64 val;
memcpy(&val, verdict_data + 1, sizeof(val));
proto_tree_add_int64(fr_user_data->tree, hf_frame_verdict_xdp, fr_user_data->tvb, 0, 0, val);
}
break;
default:
proto_tree_add_bytes_with_length(fr_user_data->tree, hf_frame_verdict_unknown,
fr_user_data->tvb, 0, 0, verdict_data, (gint) len + 1);
break;
}
}
fr_user_data->n_changes++;
return TRUE;
}
static int
dissect_frame(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data)
{
proto_item *volatile ti = NULL, *comment_item;
proto_item *volatile ti = NULL;
guint cap_len = 0, frame_len = 0;
proto_tree *volatile tree;
proto_tree *comments_tree;
@ -289,6 +345,7 @@ dissect_frame(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void*
frame_data_t *fr_data = (frame_data_t*)data;
const color_filter_t *color_filter;
dissector_handle_t dissector_handle;
fr_foreach_t fr_user_data;
tree=parent_tree;
@ -397,17 +454,15 @@ dissect_frame(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void*
DISSECTOR_ASSERT_NOT_REACHED();
break;
}
if (fr_data->pkt_comment) {
if (wtap_block_count_option(fr_data->pkt_block, OPT_COMMENT) > 0) {
item = proto_tree_add_item(tree, proto_pkt_comment, tvb, 0, 0, ENC_NA);
comments_tree = proto_item_add_subtree(item, ett_comments);
comment_item = proto_tree_add_string_format(comments_tree, hf_comments_text, tvb, 0, 0,
fr_data->pkt_comment, "%s",
fr_data->pkt_comment);
expert_add_info_format(pinfo, comment_item, &ei_comments_text,
"%s", fr_data->pkt_comment);
fr_user_data.item = item;
fr_user_data.tree = comments_tree;
fr_user_data.pinfo = pinfo;
fr_user_data.tvb = tvb;
fr_user_data.n_changes = 0;
wtap_block_foreach_option(fr_data->pkt_block, frame_add_comment, (void *)&fr_user_data);
}
/* if FRAME is not referenced from any filters we don't need to worry about
@ -593,56 +648,18 @@ dissect_frame(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void*
proto_tree_add_uint64(fh_tree, hf_frame_packet_id, tvb, 0, 0,
pinfo->rec->rec_header.packet_header.packet_id);
if (pinfo->rec->presence_flags & WTAP_HAS_VERDICT &&
pinfo->rec->packet_verdict != NULL) {
char line_buffer[128];
if (wtap_block_count_option(fr_data->pkt_block, OPT_PKT_VERDICT) > 0) {
proto_tree *verdict_tree;
proto_item *verdict_item;
format_verdict_summary(pinfo->rec, line_buffer, sizeof(line_buffer));
verdict_item = proto_tree_add_string(fh_tree, hf_frame_verdict, tvb, 0, 0, line_buffer);
verdict_item = proto_tree_add_string(fh_tree, hf_frame_verdict, tvb, 0, 0, "");
verdict_tree = proto_item_add_subtree(verdict_item, ett_verdict);
for(guint i = 0; i < pinfo->rec->packet_verdict->len; i++) {
GBytes *verdict = (GBytes *) g_ptr_array_index(pinfo->rec->packet_verdict, i);
const guint8 *verdict_data;
gsize len;
if (verdict == NULL)
continue;
verdict_data = (const guint8 *) g_bytes_get_data(verdict, &len);
if (len == 0)
continue;
len -= 1;
switch(verdict_data[0]) {
case OPT_VERDICT_TYPE_HW:
proto_tree_add_bytes_with_length(verdict_tree, hf_frame_verdict_hardware,
tvb, 0, 0, verdict_data + 1, (gint) len);
break;
case OPT_VERDICT_TYPE_TC:
if (len == 8) {
gint64 val;
memcpy(&val, verdict_data + 1, sizeof(val));
proto_tree_add_int64(verdict_tree, hf_frame_verdict_tc, tvb, 0, 0, val);
}
break;
case OPT_VERDICT_TYPE_XDP:
if (len == 8) {
gint64 val;
memcpy(&val, verdict_data + 1, sizeof(val));
proto_tree_add_int64(verdict_tree, hf_frame_verdict_xdp, tvb, 0, 0, val);
}
break;
default:
proto_tree_add_bytes_with_length(verdict_tree, hf_frame_verdict_unknown,
tvb, 0, 0, verdict_data, (gint) len + 1);
break;
}
}
fr_user_data.item = verdict_item;
fr_user_data.tree = verdict_tree;
fr_user_data.pinfo = pinfo;
fr_user_data.tvb = tvb;
fr_user_data.n_changes = 0;
wtap_block_foreach_option(pinfo->rec->block, frame_add_verdict, (void *)&fr_user_data);
}
if (pinfo->rec->rec_type == REC_TYPE_PACKET)

View File

@ -448,11 +448,11 @@ epan_new(struct packet_provider_data *prov,
return session;
}
const char *
epan_get_user_comment(const epan_t *session, const frame_data *fd)
wtap_block_t
epan_get_user_block(const epan_t *session, const frame_data *fd)
{
if (session->funcs.get_user_comment)
return session->funcs.get_user_comment(session->prov, fd);
if (session->funcs.get_user_block)
return session->funcs.get_user_block(session->prov, fd);
return NULL;
}
@ -558,6 +558,8 @@ epan_dissect_reset(epan_dissect_t *edt)
ws_assert(edt);
wtap_block_unref(edt->pi.rec->block);
g_slist_free(edt->pi.proto_data);
g_slist_free(edt->pi.dependent_frames);
@ -611,6 +613,8 @@ epan_dissect_run(epan_dissect_t *edt, int file_type_subtype,
/* free all memory allocated */
wmem_leave_packet_scope();
wtap_block_unref(rec->block);
rec->block = NULL;
}
void
@ -625,6 +629,8 @@ epan_dissect_run_with_taps(epan_dissect_t *edt, int file_type_subtype,
/* free all memory allocated */
wmem_leave_packet_scope();
wtap_block_unref(rec->block);
rec->block = NULL;
}
void
@ -639,6 +645,8 @@ epan_dissect_file_run(epan_dissect_t *edt, wtap_rec *rec,
/* free all memory allocated */
wmem_leave_packet_scope();
wtap_block_unref(rec->block);
rec->block = NULL;
}
void
@ -652,6 +660,8 @@ epan_dissect_file_run_with_taps(epan_dissect_t *edt, wtap_rec *rec,
/* free all memory allocated */
wmem_leave_packet_scope();
wtap_block_unref(rec->block);
rec->block = NULL;
}
void

View File

@ -16,6 +16,7 @@
#include <epan/prefs.h>
#include <epan/frame_data.h>
#include <epan/register.h>
#include <wiretap/wtap_opttypes.h>
#include "ws_symbol_export.h"
#ifdef __cplusplus
@ -49,7 +50,7 @@ struct packet_provider_funcs {
const nstime_t *(*get_frame_ts)(struct packet_provider_data *prov, guint32 frame_num);
const char *(*get_interface_name)(struct packet_provider_data *prov, guint32 interface_id);
const char *(*get_interface_description)(struct packet_provider_data *prov, guint32 interface_id);
const char *(*get_user_comment)(struct packet_provider_data *prov, const frame_data *fd);
wtap_block_t (*get_user_block)(struct packet_provider_data *prov, const frame_data *fd);
};
/**
@ -153,7 +154,7 @@ typedef struct epan_session epan_t;
WS_DLL_PUBLIC epan_t *epan_new(struct packet_provider_data *prov,
const struct packet_provider_funcs *funcs);
WS_DLL_PUBLIC const char *epan_get_user_comment(const epan_t *session, const frame_data *fd);
WS_DLL_PUBLIC wtap_block_t epan_get_user_block(const epan_t *session, const frame_data *fd);
WS_DLL_PUBLIC const char *epan_get_interface_name(const epan_t *session, guint32 interface_id);

View File

@ -218,8 +218,8 @@ frame_data_init(frame_data *fdata, guint32 num, const wtap_rec *rec,
ws_assert(rec->tsprec <= 0xF);
fdata->tsprec = (unsigned int)rec->tsprec;
fdata->abs_ts = rec->ts;
fdata->has_phdr_comment = (rec->opt_comment != NULL);
fdata->has_user_comment = 0;
fdata->has_phdr_block = (rec->block != NULL);
fdata->has_user_block = 0;
fdata->need_colorize = 0;
fdata->color_filter = NULL;
fdata->shift_offset.secs = 0;

View File

@ -84,8 +84,8 @@ typedef struct _frame_data {
unsigned int ref_time : 1; /**< 1 = marked as a reference time frame, 0 = normal */
unsigned int ignored : 1; /**< 1 = ignore this frame, 0 = normal */
unsigned int has_ts : 1; /**< 1 = has time stamp, 0 = no time stamp */
unsigned int has_phdr_comment : 1; /** 1 = there's comment for this packet */
unsigned int has_user_comment : 1; /** 1 = user set (also deleted) comment for this packet */
unsigned int has_phdr_block : 1; /** 1 = there's a block (possibly with options) for this packet */
unsigned int has_user_block : 1; /** 1 = user changed block (or its options) for this packet */
unsigned int need_colorize : 1; /**< 1 = need to (re-)calculate packet color */
unsigned int tsprec : 4; /**< Time stamp precision -2^tsprec gives up to femtoseconds */
nstime_t abs_ts; /**< Absolute timestamp */

View File

@ -583,13 +583,16 @@ dissect_record(epan_dissect_t *edt, int file_type_subtype,
frame_delta_abs_time(edt->session, fd, fd->frame_ref_num, &edt->pi.rel_ts);
/* pkt comment use first user, later from rec */
if (fd->has_user_comment)
frame_dissector_data.pkt_comment = epan_get_user_comment(edt->session, fd);
else if (fd->has_phdr_comment)
frame_dissector_data.pkt_comment = rec->opt_comment;
else
frame_dissector_data.pkt_comment = NULL;
/* pkt block use first user, later from rec */
if (fd->has_user_block) {
frame_dissector_data.pkt_block = epan_get_user_block(edt->session, fd);
}
else if (fd->has_phdr_block) {
frame_dissector_data.pkt_block = rec->block;
}
else {
frame_dissector_data.pkt_block = NULL;
}
frame_dissector_data.file_type_subtype = file_type_subtype;
frame_dissector_data.color_edt = edt; /* Used strictly for "coloring rules" */
@ -612,6 +615,8 @@ dissect_record(epan_dissect_t *edt, int file_type_subtype,
record_type);
}
ENDTRY;
wtap_block_unref(rec->block);
rec->block = NULL;
fd->visited = 1;
}
@ -652,13 +657,16 @@ dissect_file(epan_dissect_t *edt, wtap_rec *rec,
TRY {
/* pkt comment use first user, later from rec */
if (fd->has_user_comment)
file_dissector_data.pkt_comment = epan_get_user_comment(edt->session, fd);
else if (fd->has_phdr_comment)
file_dissector_data.pkt_comment = rec->opt_comment;
else
file_dissector_data.pkt_comment = NULL;
/* pkt block use first user, later from rec */
if (fd->has_user_block) {
file_dissector_data.pkt_block = epan_get_user_block(edt->session, fd);
}
else if (fd->has_phdr_block) {
file_dissector_data.pkt_block = rec->block;
}
else {
file_dissector_data.pkt_block = NULL;
}
file_dissector_data.color_edt = edt; /* Used strictly for "coloring rules" */
@ -680,6 +688,8 @@ dissect_file(epan_dissect_t *edt, wtap_rec *rec,
"[Malformed Record: Packet Length]");
}
ENDTRY;
wtap_block_unref(rec->block);
rec->block = NULL;
fd->visited = 1;
}

View File

@ -11,6 +11,7 @@
#ifndef __PACKET_H__
#define __PACKET_H__
#include <wiretap/wtap_opttypes.h>
#include "proto.h"
#include "tvbuff.h"
#include "epan.h"
@ -747,7 +748,7 @@ WS_DLL_PUBLIC void mark_frame_as_depended_upon(packet_info *pinfo, guint32 frame
typedef struct frame_data_s
{
int file_type_subtype;
const gchar *pkt_comment; /**< NULL if not available */
wtap_block_t pkt_block; /**< NULL if not available */
struct epan_dissect *color_edt; /** Used strictly for "coloring rules" */
} frame_data_t;
@ -755,7 +756,7 @@ typedef struct frame_data_s
/* Structure passed to the file dissector */
typedef struct file_data_s
{
const gchar *pkt_comment; /**< NULL if not available */
wtap_block_t pkt_block; /**< NULL if not available */
struct epan_dissect *color_edt; /** Used strictly for "coloring rules" */
} file_data_t;

View File

@ -14,6 +14,7 @@
#include "config.h"
#include <wiretap/wtap_opttypes.h>
#include <epan/wmem/wmem.h>
/* WSLUA_MODULE Dumper Saving Capture Files
@ -384,8 +385,8 @@ WSLUA_METHOD Dumper_dump(lua_State* L) {
rec.rec_header.packet_header.pseudo_header = *ph->wph;
}
/* TODO: Can we get access to pinfo->pkt_comment here somehow? We
* should be copying it to pkthdr.opt_comment if we can. */
/* TODO: Can we get access to pinfo->rec->block here somehow? We
* should be copying it to pkthdr.pkt_block if we can. */
if (! wtap_dump(d, &rec, ba->data, &err, &err_info)) {
switch (err) {
@ -539,15 +540,15 @@ WSLUA_METHOD Dumper_dump_current(lua_State* L) {
rec.rec_header.packet_header.pseudo_header = *lua_pinfo->pseudo_header;
/*
* wtap_dump does not modify rec.opt_comment, so it should be possible to
* pass epan_get_user_comment() or lua_pinfo->rec->opt_comment directly.
* wtap_dump does not modify rec.block, so it should be possible to
* pass epan_get_user_block() or lua_pinfo->rec->block directly.
* Temporarily duplicating the memory should not hurt though.
*/
if (lua_pinfo->fd->has_user_comment) {
rec.opt_comment = wmem_strdup(wmem_packet_scope(), epan_get_user_comment(lua_pinfo->epan, lua_pinfo->fd));
rec.has_comment_changed = TRUE;
} else if (lua_pinfo->fd->has_phdr_comment) {
rec.opt_comment = wmem_strdup(wmem_packet_scope(), lua_pinfo->rec->opt_comment);
if (lua_pinfo->fd->has_user_block) {
rec.block = epan_get_user_block(lua_pinfo->epan, lua_pinfo->fd);
rec.has_block_changed = TRUE;
} else if (lua_pinfo->fd->has_phdr_block) {
rec.block = lua_pinfo->rec->block;
}
data = (const guchar *)tvb_memdup(wmem_packet_scope(),tvb,0,rec.rec_header.packet_header.caplen);

View File

@ -19,6 +19,7 @@
#include "config.h"
#include "wslua.h"
#include <wiretap/wtap_opttypes.h>
#include <wiretap/wtap-int.h>
/* this is way overkill for this one member, but in case we need to add

View File

@ -258,8 +258,8 @@ wslua_filehandler_read(wtap *wth, wtap_rec *rec, Buffer *buf,
*err = errno = 0;
}
g_free(rec->opt_comment);
rec->opt_comment = NULL;
wtap_block_unref(rec->block);
rec->block = NULL;
fp = push_File(L, wth->fh);
fc = push_CaptureInfo(L, wth, FALSE);
@ -316,8 +316,8 @@ wslua_filehandler_seek_read(wtap *wth, gint64 seek_off,
*err = errno = 0;
}
g_free(rec->opt_comment);
rec->opt_comment = NULL;
wtap_block_unref(rec->block);
rec->block = NULL;
fp = push_File(L, wth->random_fh);
fc = push_CaptureInfo(L, wth, FALSE);

View File

@ -14,6 +14,7 @@
*/
#include "wslua_file_common.h"
#include <lua.h>
/* WSLUA_CONTINUE_MODULE File */
@ -55,8 +56,8 @@ WSLUA_METAMETHOD FrameInfo__tostring(lua_State* L) {
lua_pushstring(L,"FrameInfo pointer is NULL!");
} else {
if (fi->rec)
lua_pushfstring(L, "FrameInfo: rec_type=%u, presence_flags=%d, caplen=%d, len=%d, pkt_encap=%d, opt_comment='%s'",
fi->rec->rec_type, fi->rec->presence_flags, fi->rec->rec_header.packet_header.caplen, fi->rec->rec_header.packet_header.len, fi->rec->rec_header.packet_header.pkt_encap, fi->rec->opt_comment);
lua_pushfstring(L, "FrameInfo: rec_type=%u, presence_flags=%d, caplen=%d, len=%d, pkt_encap=%d, block='%p'",
fi->rec->rec_type, fi->rec->presence_flags, fi->rec->rec_header.packet_header.caplen, fi->rec->rec_header.packet_header.len, fi->rec->rec_header.packet_header.pkt_encap, fi->rec->block);
else
lua_pushstring(L, "FrameInfo rec pointer is NULL!");
}
@ -103,6 +104,82 @@ static int FrameInfo__gc(lua_State* L) {
return 0;
}
/* WSLUA_ATTRIBUTE FrameInfo_comment RW table of comments in this frame. */
static int FrameInfo_get_comment (lua_State* L) {
FrameInfo fi = checkFrameInfo(L,1);
#define FRAMEINFO_COMMENTS_TABLE 2
gchar *comment = NULL;
wtap_block_t block = NULL;
guint i = 0;
guint n_comments = 0;
block = fi->rec->block;
// XXX - how to get the user-edited block, if any?
n_comments = wtap_block_count_option(block, OPT_COMMENT);
lua_createtable(L, n_comments, 0);
for (i = 0; i < n_comments; i++) {
comment = NULL;
lua_pushnumber(L, i+1);
if (WTAP_OPTTYPE_SUCCESS ==
wtap_block_get_nth_string_option_value(block, OPT_COMMENT, i, &comment)) {
lua_pushstring(L, comment);
}
else {
lua_pushnil(L);
}
lua_settable(L, FRAMEINFO_COMMENTS_TABLE);
}
return 1;
}
static int FrameInfo_set_comment (lua_State* L) {
FrameInfo fi = checkFrameInfo(L,1);
#define FRAMEINFO_COMMENTS_NEWTABLE 2
#define FRAMEINFO_COMMENTS_NEWCOMMENT 2
size_t len = 0;
gchar *comment = NULL;
wtap_block_t block = NULL;
guint i = 0;
guint n_comments = 0;
if(fi->rec->block != NULL) {
block = fi->rec->block;
}
else {
block = wtap_block_create(WTAP_BLOCK_PACKET);
fi->rec->block = block;
}
/* Strip off old comments */
n_comments = wtap_block_count_option(block, OPT_COMMENT);
for (i = 0; i < n_comments; i++) {
wtap_block_remove_nth_option_instance(block, OPT_COMMENT, 0);
}
/* Add new comment(s) */
if (lua_istable(L, FRAMEINFO_COMMENTS_NEWTABLE)) {
for (lua_pushnil(L); lua_next(L, FRAMEINFO_COMMENTS_NEWTABLE); ) {
if (lua_isstring(L,-1)) {
comment = (gchar *)luaL_checklstring(L,-1,&len);
wtap_block_add_string_option(block, OPT_COMMENT, comment, len);
} else if (! lua_isnil(L,-1) ) {
return luaL_error(L,"only strings should be in the table");
}
lua_pop(L, 1);
}
}
else if (lua_isstring(L, FRAMEINFO_COMMENTS_NEWCOMMENT)) {
comment = (gchar *)luaL_checklstring(L,FRAMEINFO_COMMENTS_NEWCOMMENT,&len);
wtap_block_add_string_option(block, OPT_COMMENT, comment, len);
}
else {
return luaL_error(L,"comment must be either a string or an array of strings");
}
return 0;
}
/* WSLUA_ATTRIBUTE FrameInfo_time RW The packet timestamp as an NSTime object.
Note: Set the `FileHandler.time_precision` to the appropriate `wtap_file_tsprec` value as well.
@ -207,12 +284,6 @@ WSLUA_ATTRIBUTE_NAMED_NUMBER_SETTER(FrameInfo,original_length,rec->rec_header.pa
WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(FrameInfo,encap,rec->rec_header.packet_header.pkt_encap);
WSLUA_ATTRIBUTE_NAMED_NUMBER_SETTER(FrameInfo,encap,rec->rec_header.packet_header.pkt_encap,int);
// rec->opt_comment will be freed by wtap_sequential_close -> wtap_rec_cleanup.
/* WSLUA_ATTRIBUTE FrameInfo_comment RW A string comment for the packet, if the
`wtap_presence_flags.COMMENTS` was set in the presence flags; nil if there is no comment. */
WSLUA_ATTRIBUTE_NAMED_STRING_GETTER(FrameInfo,comment,rec->opt_comment);
WSLUA_ATTRIBUTE_NAMED_STRING_SETTER(FrameInfo,comment,rec->opt_comment,TRUE);
/* This table is ultimately registered as a sub-table of the class' metatable,
* and if __index/__newindex is invoked then it calls the appropriate function
* from this table for getting/setting the members.
@ -269,8 +340,8 @@ WSLUA_METAMETHOD FrameInfoConst__tostring(lua_State* L) {
lua_pushstring(L,"FrameInfo pointer is NULL!");
} else {
if (fi->rec && !fi->expired)
lua_pushfstring(L, "FrameInfo: rec_type=%u, presence_flags=%d, caplen=%d, len=%d, pkt_encap=%d, opt_comment='%s'",
fi->rec->rec_type, fi->rec->presence_flags, fi->rec->rec_header.packet_header.caplen, fi->rec->rec_header.packet_header.len, fi->rec->rec_header.packet_header.pkt_encap, fi->rec->opt_comment);
lua_pushfstring(L, "FrameInfo: rec_type=%u, presence_flags=%d, caplen=%d, len=%d, pkt_encap=%d, block='%p'",
fi->rec->rec_type, fi->rec->presence_flags, fi->rec->rec_header.packet_header.caplen, fi->rec->rec_header.packet_header.len, fi->rec->rec_header.packet_header.pkt_encap, fi->rec->block);
else
lua_pushfstring(L, "FrameInfo has %s", fi->rec?"expired":"null rec pointer");
}
@ -315,6 +386,36 @@ static int FrameInfoConst__gc(lua_State* L) {
return 0;
}
/* WSLUA_ATTRIBUTE FrameInfoConst_comment RO The first string comment for the packet, if any;
nil if there is no comment. */
static int FrameInfoConst_get_comment (lua_State* L) {
FrameInfoConst fi = checkFrameInfoConst(L,1);
#define FRAMEINFOCONST_COMMENTS_TABLE 2
gchar *comment = NULL;
wtap_block_t block = NULL;
guint i = 0;
guint n_comments = 0;
block = fi->rec->block;
// XXX - how to get the user-edited block, if any?
n_comments = wtap_block_count_option(block, OPT_COMMENT);
lua_createtable(L, n_comments, 0);
for (i = 0; i < n_comments; i++) {
comment = NULL;
lua_pushnumber(L, i+1);
if (WTAP_OPTTYPE_SUCCESS ==
wtap_block_get_nth_string_option_value(block, OPT_COMMENT, i, &comment)) {
lua_pushstring(L, comment);
}
else {
lua_pushnil(L);
}
lua_settable(L, FRAMEINFOCONST_COMMENTS_TABLE);
}
return 1;
}
/* WSLUA_ATTRIBUTE FrameInfoConst_time RO The packet timestamp as an NSTime object. */
static int FrameInfoConst_get_time (lua_State* L) {
FrameInfoConst fi = checkFrameInfoConst(L,1);
@ -358,9 +459,6 @@ WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(FrameInfoConst,original_length,rec->rec_head
See `wtap_encaps` in `init.lua` for possible packet encapsulation types to use as the value for this field. */
WSLUA_ATTRIBUTE_NAMED_NUMBER_GETTER(FrameInfoConst,encap,rec->rec_header.packet_header.pkt_encap);
/* WSLUA_ATTRIBUTE FrameInfoConst_comment RO A comment for the packet; nil if there is none. */
WSLUA_ATTRIBUTE_NAMED_STRING_GETTER(FrameInfoConst,comment,rec->opt_comment);
WSLUA_ATTRIBUTES FrameInfoConst_attributes[] = {
WSLUA_ATTRIBUTE_ROREG(FrameInfoConst,rec_type),
WSLUA_ATTRIBUTE_ROREG(FrameInfoConst,flags),

View File

@ -503,8 +503,7 @@ static gboolean extcap_dumper_dump(struct extcap_dumper extcap_dumper,
rec.ts.secs = seconds;
rec.ts.nsecs = (int) nanoseconds;
rec.opt_comment = 0;
rec.opt_comment = NULL;
rec.block = NULL;
rec.rec_header.packet_header.drop_count = 0;
rec.rec_header.packet_header.pack_flags = 0;

107
file.c
View File

@ -252,7 +252,7 @@ ws_epan_new(capture_file *cf)
ws_get_frame_ts,
cap_file_provider_get_interface_name,
cap_file_provider_get_interface_description,
cap_file_provider_get_user_comment
cap_file_provider_get_user_block
};
return epan_new(&cf->provider, &funcs);
@ -407,9 +407,9 @@ cf_close(capture_file *cf)
free_frame_data_sequence(cf->provider.frames);
cf->provider.frames = NULL;
}
if (cf->provider.frames_user_comments) {
g_tree_destroy(cf->provider.frames_user_comments);
cf->provider.frames_user_comments = NULL;
if (cf->provider.frames_edited_blocks) {
g_tree_destroy(cf->provider.frames_edited_blocks);
cf->provider.frames_edited_blocks = NULL;
}
cf_unselect_packet(cf); /* nothing to select */
cf->first_displayed = 0;
@ -1285,8 +1285,8 @@ read_record(capture_file *cf, wtap_rec *rec, Buffer *buf, dfilter_t *dfcode,
fdata = frame_data_sequence_add(cf->provider.frames, &fdlocal);
cf->count++;
if (rec->opt_comment != NULL)
cf->packet_comment_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.
@ -3995,23 +3995,23 @@ cf_update_section_comment(capture_file *cf, gchar *comment)
}
/*
* Get the comment on a packet (record).
* If the comment has been edited, it returns the result of the edit,
* otherwise it returns the comment from the file.
* Get the packet block for a packet (record).
* If the block has been edited, it returns the result of the edit,
* otherwise it returns the block from the file.
* NB. Caller must wtap_block_unref() the result when done.
*/
char *
cf_get_packet_comment(capture_file *cf, const frame_data *fd)
wtap_block_t
cf_get_packet_block(capture_file *cf, const frame_data *fd)
{
char *comment;
/* fetch user block */
if (fd->has_user_block)
return wtap_block_ref(cap_file_provider_get_user_block(&cf->provider, fd));
/* fetch user comment */
if (fd->has_user_comment)
return g_strdup(cap_file_provider_get_user_comment(&cf->provider, fd));
/* fetch phdr comment */
if (fd->has_phdr_comment) {
/* fetch phdr block */
if (fd->has_phdr_block) {
wtap_rec rec; /* Record metadata */
Buffer buf; /* Record data */
wtap_block_t block;
wtap_rec_init(&rec);
ws_buffer_init(&buf, 1514);
@ -4019,41 +4019,50 @@ cf_get_packet_comment(capture_file *cf, const frame_data *fd)
if (!cf_read_record(cf, fd, &rec, &buf))
{ /* XXX, what we can do here? */ }
/* rec.opt_comment is owned by the record, copy it before it is gone. */
comment = g_strdup(rec.opt_comment);
/* rec.block is owned by the record, steal it before it is gone. */
block = wtap_block_ref(rec.block);
wtap_rec_cleanup(&rec);
ws_buffer_free(&buf);
return comment;
return block;
}
return NULL;
}
/*
* Update(replace) the comment on a capture from a frame
* Update(replace) the block on a capture from a frame
*/
gboolean
cf_set_user_packet_comment(capture_file *cf, frame_data *fd, const gchar *new_comment)
cf_set_user_packet_block(capture_file *cf, frame_data *fd, const wtap_block_t new_block)
{
char *pkt_comment = cf_get_packet_comment(cf, fd);
wtap_block_t pkt_block = cf_get_packet_block(cf, fd);
/* Check if the comment has changed */
if (!g_strcmp0(pkt_comment, new_comment)) {
g_free(pkt_comment);
return FALSE;
/* It's possible to edit the user block "in place" by doing a call to
* cf_get_packet_block() that returns an already created user block,
* editing that, and calling this function.
* If the caller did that, then the block pointers will be equal.
*/
if (pkt_block == new_block) {
/* No need to save anything here, the caller changes went right
* onto the block.
* Unfortunately we don't have a way to know how many comments were in the block
* before the caller edited it.
*/
}
g_free(pkt_comment);
else {
if (pkt_block)
cf->packet_comment_count -= wtap_block_count_option(pkt_block, OPT_COMMENT);
if (pkt_comment)
cf->packet_comment_count--;
if (new_block)
cf->packet_comment_count += wtap_block_count_option(new_block, OPT_COMMENT);
if (new_comment)
cf->packet_comment_count++;
cap_file_provider_set_user_block(&cf->provider, fd, new_block);
cap_file_provider_set_user_comment(&cf->provider, fd, new_comment);
expert_update_comment_count(cf->packet_comment_count);
}
expert_update_comment_count(cf->packet_comment_count);
/* OK, we have unsaved changes. */
/* Either way, we have unsaved changes. */
wtap_block_unref(pkt_block);
cf->unsaved_changes = TRUE;
return TRUE;
}
@ -4131,19 +4140,19 @@ save_record(capture_file *cf, frame_data *fdata, wtap_rec *rec,
wtap_rec new_rec;
int err;
gchar *err_info;
const char *pkt_comment;
wtap_block_t pkt_block;
/* Copy the record information from what was read in from the file. */
new_rec = *rec;
/* Make changes based on anything that the user has done but that
hasn't been saved yet. */
if (fdata->has_user_comment)
pkt_comment = cap_file_provider_get_user_comment(&cf->provider, fdata);
if (fdata->has_user_block)
pkt_block = cap_file_provider_get_user_block(&cf->provider, fdata);
else
pkt_comment = rec->opt_comment;
new_rec.opt_comment = g_strdup(pkt_comment);
new_rec.has_comment_changed = fdata->has_user_comment ? TRUE : FALSE;
pkt_block = rec->block;
new_rec.block = pkt_block;
new_rec.has_block_changed = fdata->has_user_block ? TRUE : FALSE;
/* XXX - what if times have been shifted? */
/* and save the packet */
@ -4153,7 +4162,6 @@ save_record(capture_file *cf, frame_data *fdata, wtap_rec *rec,
return FALSE;
}
g_free(new_rec.opt_comment);
return TRUE;
}
@ -4760,13 +4768,14 @@ cf_save_records(capture_file *cf, const char *fname, guint save_format,
for (framenum = 1; framenum <= cf->count; framenum++) {
fdata = frame_data_sequence_find(cf->provider.frames, framenum);
fdata->has_phdr_comment = FALSE;
fdata->has_user_comment = FALSE;
// XXX: This also ignores non-comment options like verdict
fdata->has_phdr_block = FALSE;
fdata->has_user_block = FALSE;
}
if (cf->provider.frames_user_comments) {
g_tree_destroy(cf->provider.frames_user_comments);
cf->provider.frames_user_comments = NULL;
if (cf->provider.frames_edited_blocks) {
g_tree_destroy(cf->provider.frames_edited_blocks);
cf->provider.frames_edited_blocks = NULL;
}
cf->packet_comment_count = 0;

16
file.h
View File

@ -689,24 +689,24 @@ cf_merge_files_to_tempfile(gpointer pd_window, char **out_filenamep,
void cf_update_section_comment(capture_file *cf, gchar *comment);
/*
* Get the comment on a packet (record).
* If the comment has been edited, it returns the result of the edit,
* otherwise it returns the comment from the file.
* Get the packet block for a packet (record).
* If the block has been edited, it returns the result of the edit,
* otherwise it returns the block from the file.
*
* @param cf the capture file
* @param fd the frame_data structure for the frame
* @returns A comment (use g_free to free) or NULL if there is none.
* @returns A block (use wtap_block_unref to free) or NULL if there is none.
*/
char *cf_get_packet_comment(capture_file *cf, const frame_data *fd);
wtap_block_t cf_get_packet_block(capture_file *cf, const frame_data *fd);
/**
* Update(replace) the comment on a capture from a frame
* Update(replace) the block on a capture from a frame
*
* @param cf the capture file
* @param fd the frame_data structure for the frame
* @param new_comment the string replacing the old comment
* @param new_block the block replacing the old block
*/
gboolean cf_set_user_packet_comment(capture_file *cf, frame_data *fd, const gchar *new_comment);
gboolean cf_set_user_packet_block(capture_file *cf, frame_data *fd, wtap_block_t new_block);
/**
* What types of comments does this file have?

View File

@ -69,24 +69,24 @@ cap_file_provider_get_interface_description(struct packet_provider_data *prov, g
return NULL;
}
const char *
cap_file_provider_get_user_comment(struct packet_provider_data *prov, const frame_data *fd)
wtap_block_t
cap_file_provider_get_user_block(struct packet_provider_data *prov, const frame_data *fd)
{
if (prov->frames_user_comments)
return (const char *)g_tree_lookup(prov->frames_user_comments, fd);
if (prov->frames_edited_blocks)
return (wtap_block_t)g_tree_lookup(prov->frames_edited_blocks, fd);
/* ws_warning? */
return NULL;
}
void
cap_file_provider_set_user_comment(struct packet_provider_data *prov, frame_data *fd, const char *new_comment)
cap_file_provider_set_user_block(struct packet_provider_data *prov, frame_data *fd, wtap_block_t new_block)
{
if (!prov->frames_user_comments)
prov->frames_user_comments = g_tree_new_full(frame_cmp, NULL, NULL, g_free);
if (!prov->frames_edited_blocks)
prov->frames_edited_blocks = g_tree_new_full(frame_cmp, NULL, NULL, (GDestroyNotify)wtap_block_unref);
/* insert new packet comment */
g_tree_replace(prov->frames_user_comments, fd, g_strdup(new_comment));
/* insert new packet block */
g_tree_replace(prov->frames_edited_blocks, fd, (gpointer)new_block);
fd->has_user_comment = TRUE;
fd->has_user_block = TRUE;
}

View File

@ -36,6 +36,7 @@ frame_read(struct tvb_frame *frame_tvb, wtap_rec *rec, Buffer *buf)
{
int err;
gchar *err_info;
gboolean ok = TRUE;
/* XXX, what if phdr->caplen isn't equal to
* frame_tvb->tvb.length + frame_tvb->offset?
@ -45,11 +46,11 @@ frame_read(struct tvb_frame *frame_tvb, wtap_rec *rec, Buffer *buf)
switch (err) {
case WTAP_ERR_BAD_FILE:
g_free(err_info);
ok = FALSE;
break;
}
return FALSE;
}
return TRUE;
return ok;
}
static GPtrArray *buffer_cache = NULL;

View File

@ -48,6 +48,7 @@
#include "ui/filter_files.h"
#include "ui/tap_export_pdu.h"
#include "ui/failure_message.h"
#include "wtap.h"
#include <epan/epan_dissect.h>
#include <epan/tap.h>
#include <epan/uat-int.h>
@ -240,7 +241,7 @@ sharkd_epan_new(capture_file *cf)
sharkd_get_frame_ts,
cap_file_provider_get_interface_name,
cap_file_provider_get_interface_description,
cap_file_provider_get_user_comment
cap_file_provider_get_user_block
};
return epan_new(&cf->provider, &funcs);
@ -767,16 +768,55 @@ sharkd_filter(const char *dftext, guint8 **result)
return framenum;
}
const char *
sharkd_get_user_comment(const frame_data *fd)
/*
* Get the user block if available, nothing otherwise.
* Must be cloned if changes desired.
*/
wtap_block_t
sharkd_get_user_block(const frame_data *fd)
{
return cap_file_provider_get_user_comment(&cfile.provider, fd);
return cap_file_provider_get_user_block(&cfile.provider, fd);
}
/*
* Gets the user block if available, otherwise the packet's default block,
* or a new packet block.
* User must wtap_block_unref() it when done.
*/
wtap_block_t
sharkd_get_packet_block(const frame_data *fd)
{
if (fd->has_user_block)
return wtap_block_ref(cap_file_provider_get_user_block(&cfile.provider, fd));
if (fd->has_phdr_block)
{
wtap_rec rec; /* Record metadata */
Buffer buf; /* Record data */
wtap_block_t block;
int err;
gchar *err_info;
wtap_rec_init(&rec);
ws_buffer_init(&buf, 1514);
if (!wtap_seek_read(cfile.provider.wth, fd->file_off, &rec, &buf, &err, &err_info))
{ /* XXX, what we can do here? */ }
/* rec.block is owned by the record, steal it before it is gone. */
block = wtap_block_ref(rec.block);
wtap_rec_cleanup(&rec);
ws_buffer_free(&buf);
return block;
}
else
return wtap_block_create(WTAP_BLOCK_PACKET);
}
int
sharkd_set_user_comment(frame_data *fd, const gchar *new_comment)
sharkd_set_user_block(frame_data *fd, wtap_block_t new_block)
{
cap_file_provider_set_user_comment(&cfile.provider, fd, new_comment);
cap_file_provider_set_user_block(&cfile.provider, fd, new_block);
return 0;
}

View File

@ -13,6 +13,7 @@
#define __SHARKD_H
#include <file.h>
#include <wiretap/wtap_opttypes.h>
#define SHARKD_DISSECT_FLAG_NULL 0x00u
#define SHARKD_DISSECT_FLAG_BYTES 0x01u
@ -35,8 +36,10 @@ int sharkd_filter(const char *dftext, guint8 **result);
frame_data *sharkd_get_frame(guint32 framenum);
int sharkd_dissect_columns(frame_data *fdata, guint32 frame_ref_num, guint32 prev_dis_num, column_info *cinfo, gboolean dissect_color);
int sharkd_dissect_request(guint32 framenum, guint32 frame_ref_num, guint32 prev_dis_num, sharkd_dissect_func_t cb, guint32 dissect_flags, void *data);
const char *sharkd_get_user_comment(const frame_data *fd);
int sharkd_set_user_comment(frame_data *fd, const gchar *new_comment);
wtap_block_t sharkd_get_user_block(const frame_data *fd);
wtap_block_t sharkd_get_packet_block(const frame_data *fd);
int sharkd_set_user_block(frame_data *fd, wtap_block_t new_block);
const char *sharkd_version(void);
/* sharkd_daemon.c */
int sharkd_init(int argc, char **argv);

View File

@ -9,6 +9,7 @@
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "wtap_opttypes.h"
#include <config.h>
#include <stdio.h>
@ -1443,9 +1444,9 @@ sharkd_session_process_frames(const char *buf, const jsmntok_t *tokens, int coun
sharkd_json_value_anyf("num", "%u", framenum);
if (fdata->has_user_comment || fdata->has_phdr_comment)
if (fdata->has_user_block || fdata->has_phdr_block)
{
if (!fdata->has_user_comment || sharkd_get_user_comment(fdata) != NULL)
if (!fdata->has_user_block || sharkd_get_user_block(fdata) != NULL)
sharkd_json_value_anyf("ct", "true");
}
@ -3347,20 +3348,34 @@ sharkd_session_process_frame_cb(epan_dissect_t *edt, proto_tree *tree, struct ep
{
packet_i