diff options
Diffstat (limited to 'epan/column-utils.c')
-rw-r--r-- | epan/column-utils.c | 2099 |
1 files changed, 2099 insertions, 0 deletions
diff --git a/epan/column-utils.c b/epan/column-utils.c new file mode 100644 index 00000000..5e5b298e --- /dev/null +++ b/epan/column-utils.c @@ -0,0 +1,2099 @@ +/* column-utils.c + * Routines for column utilities. + * + * 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" + +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <locale.h> +#include <limits.h> + +#include "column-utils.h" +#include "timestamp.h" +#include "to_str.h" +#include "packet_info.h" +#include "wsutil/pint.h" +#include "addr_resolv.h" +#include "address_types.h" +#include "ipv6.h" +#include "osi-utils.h" +#include "value_string.h" +#include "column-info.h" +#include "column.h" +#include "proto.h" + +#include <epan/strutil.h> +#include <epan/epan.h> +#include <epan/dfilter/dfilter.h> + +#include <wsutil/utf8_entities.h> +#include <wsutil/ws_assert.h> +#include <wsutil/unicode-utils.h> +#include <wsutil/time_util.h> + +#ifdef HAVE_LUA +#include <epan/wslua/wslua.h> +#endif + +#define COL_BUF_MAX_LEN (((COL_MAX_INFO_LEN) > (COL_MAX_LEN)) ? \ + (COL_MAX_INFO_LEN) : (COL_MAX_LEN)) + +/* Used for locale decimal point */ +static char *col_decimal_point; + +/* Used to indicate updated column information, e.g. a new request/response. */ +static gboolean col_data_changed_; + +static int proto_cols = -1; +static gint ett_cols = -1; + +/* Allocate all the data structures for constructing column data, given + the number of columns. */ +void +col_setup(column_info *cinfo, const gint num_cols) +{ + int i; + + col_decimal_point = localeconv()->decimal_point; + cinfo->num_cols = num_cols; + cinfo->columns = g_new(col_item_t, num_cols); + cinfo->col_first = g_new(int, NUM_COL_FMTS); + cinfo->col_last = g_new(int, NUM_COL_FMTS); + for (i = 0; i < num_cols; i++) { + cinfo->columns[i].col_custom_fields_ids = NULL; + } + cinfo->col_expr.col_expr = g_new(const gchar*, num_cols + 1); + cinfo->col_expr.col_expr_val = g_new(gchar*, num_cols + 1); + + for (i = 0; i < NUM_COL_FMTS; i++) { + cinfo->col_first[i] = -1; + cinfo->col_last[i] = -1; + } + cinfo->prime_regex = g_regex_new(COL_CUSTOM_PRIME_REGEX, + (GRegexCompileFlags) (G_REGEX_ANCHORED | G_REGEX_RAW), + G_REGEX_MATCH_ANCHORED, NULL); +} + +static void +col_custom_fields_ids_free(GSList** custom_fields_id) +{ + if (*custom_fields_id != NULL) { + g_slist_free_full(*custom_fields_id, g_free); + } + *custom_fields_id = NULL; +} + +/* Cleanup all the data structures for constructing column data; undoes + the allocations that col_setup() does. */ +void +col_cleanup(column_info *cinfo) +{ + int i; + col_item_t* col_item; + + if (!cinfo) + return; + + for (i = 0; i < cinfo->num_cols; i++) { + col_item = &cinfo->columns[i]; + g_free(col_item->fmt_matx); + g_free(col_item->col_title); + g_free(col_item->col_custom_fields); + dfilter_free(col_item->col_custom_dfilter); + /* col_item->col_data points to col_buf or static memory */ + g_free(col_item->col_buf); + g_free(cinfo->col_expr.col_expr_val[i]); + col_custom_fields_ids_free(&col_item->col_custom_fields_ids); + } + + g_free(cinfo->columns); + g_free(cinfo->col_first); + g_free(cinfo->col_last); + /* + * XXX - MSVC doesn't correctly handle the "const" qualifier; it thinks + * "const XXX **" means "pointer to const pointer to XXX", i.e. that + * it's a pointer to something that's "const"ant, not "pointer to + * pointer to const XXX", i.e. that it's a pointer to a pointer to + * something that's "const"ant. Cast its bogus complaints away. + */ + g_free((gchar **)cinfo->col_expr.col_expr); + g_free(cinfo->col_expr.col_expr_val); + if (cinfo->prime_regex) + g_regex_unref(cinfo->prime_regex); +} + +/* Initialize the data structures for constructing column data. */ +void +col_init(column_info *cinfo, const struct epan_session *epan) +{ + int i; + col_item_t* col_item; + + if (!cinfo) + return; + + for (i = 0; i < cinfo->num_cols; i++) { + col_item = &cinfo->columns[i]; + col_item->col_buf[0] = '\0'; + col_item->col_data = col_item->col_buf; + col_item->col_fence = 0; + col_item->writable = TRUE; + cinfo->col_expr.col_expr[i] = ""; + cinfo->col_expr.col_expr_val[i][0] = '\0'; + } + cinfo->writable = TRUE; + cinfo->epan = epan; +} + +gboolean +col_get_writable(column_info *cinfo, const gint col) +{ + int i; + col_item_t* col_item; + + if (cinfo == NULL) + return FALSE; + + /* "global" (not) writeability will always override + an individual column */ + if ((col == -1) || (cinfo->writable == FALSE)) + return cinfo->writable; + + if (cinfo->col_first[col] >= 0) { + for (i = cinfo->col_first[col]; i <= cinfo->col_last[col]; i++) { + col_item = &cinfo->columns[i]; + if (col_item->fmt_matx[col]) { + return col_item->writable; + } + } + } + return FALSE; +} + +void +col_set_writable(column_info *cinfo, const gint col, const gboolean writable) +{ + int i; + col_item_t* col_item; + + if (cinfo) { + if (col == -1) { + cinfo->writable = writable; + } else if (cinfo->col_first[col] >= 0) { + for (i = cinfo->col_first[col]; i <= cinfo->col_last[col]; i++) { + col_item = &cinfo->columns[i]; + if (col_item->fmt_matx[col]) { + col_item->writable = writable; + } + } + } + } +} + +/* Checks to see if a particular packet information element is needed for the packet list */ +#define CHECK_COL(cinfo, el) \ + /* We are constructing columns, and they're writable */ \ + (col_get_writable(cinfo, el) && \ + /* There is at least one column in that format */ \ + ((cinfo)->col_first[el] >= 0)) + +/* Sets the fence for a column to be at the end of the column. */ +void +col_set_fence(column_info *cinfo, const gint el) +{ + int i; + col_item_t* col_item; + + if (!CHECK_COL(cinfo, el)) + return; + + for (i = cinfo->col_first[el]; i <= cinfo->col_last[el]; i++) { + col_item = &cinfo->columns[i]; + if (col_item->fmt_matx[el]) { + col_item->col_fence = (int)strlen(col_item->col_data); + } + } +} + +/* Clear the fence for a column. */ +void +col_clear_fence(column_info *cinfo, const gint el) +{ + int i; + col_item_t* col_item; + + if (!CHECK_COL(cinfo, el)) + return; + + for (i = cinfo->col_first[el]; i <= cinfo->col_last[el]; i++) { + col_item = &cinfo->columns[i]; + if (col_item->fmt_matx[el]) { + col_item->col_fence = 0; + } + } +} + +/* Gets the text of a column */ +const gchar * +col_get_text(column_info *cinfo, const gint el) +{ + int i; + const gchar* text = NULL; + col_item_t* col_item; + + if (!(cinfo && (cinfo)->col_first[el] >= 0)) { + return NULL; + } + + for (i = cinfo->col_first[el]; i <= cinfo->col_last[el]; i++) { + col_item = &cinfo->columns[i]; + if (col_item->fmt_matx[el]) { + text = (col_item->col_data); + } + } + return text; +} + + +/* Use this to clear out a column, especially if you're going to be + appending to it later; at least on some platforms, it's more + efficient than using "col_add_str()" with a null string, and + more efficient than "col_set_str()" with a null string if you + later append to it, as the later append will cause a string + copy to be done. */ +void +col_clear(column_info *cinfo, const gint el) +{ + int i; + col_item_t* col_item; + + if (!CHECK_COL(cinfo, el)) + return; + + for (i = cinfo->col_first[el]; i <= cinfo->col_last[el]; i++) { + col_item = &cinfo->columns[i]; + if (col_item->fmt_matx[el]) { + /* + * At this point, either + * + * 1) col_data[i] is equal to col_buf[i], in which case we + * don't have to worry about copying col_data[i] to + * col_buf[i]; + * + * 2) col_data[i] isn't equal to col_buf[i], in which case + * the only thing that's been done to the column is + * "col_set_str()" calls and possibly "col_set_fence()" + * calls, in which case the fence is either unset and + * at the beginning of the string or set and at the end + * of the string - if it's at the beginning, we're just + * going to clear the column, and if it's at the end, + * we don't do anything. + */ + if (col_item->col_buf == col_item->col_data || col_item->col_fence == 0) { + /* + * The fence isn't at the end of the column, or the column wasn't + * last set with "col_set_str()", so clear the column out. + */ + col_item->col_buf[col_item->col_fence] = '\0'; + col_item->col_data = col_item->col_buf; + } + cinfo->col_expr.col_expr[i] = ""; + cinfo->col_expr.col_expr_val[i][0] = '\0'; + } + } +} + +#define COL_CHECK_APPEND(col_item, max_len) \ + if (col_item->col_data != col_item->col_buf) { \ + /* This was set with "col_set_str()"; copy the string they \ + set it to into the buffer, so we can append to it. */ \ + (void) g_strlcpy(col_item->col_buf, col_item->col_data, max_len); \ + col_item->col_data = col_item->col_buf; \ + } + +#define COL_CHECK_REF_TIME(fd, buf) \ + if (fd->ref_time) { \ + (void) g_strlcpy(buf, "*REF*", COL_MAX_LEN ); \ + return; \ + } + +/* The same as CHECK_COL(), but without the check to see if the column is writable. */ +#define HAVE_CUSTOM_COLS(cinfo) ((cinfo) && (cinfo)->col_first[COL_CUSTOM] >= 0) + +gboolean +have_custom_cols(column_info *cinfo) +{ + return HAVE_CUSTOM_COLS(cinfo); +} + +gboolean +have_field_extractors(void) +{ +#ifdef HAVE_LUA + return wslua_has_field_extractors(); +#else + return FALSE; +#endif +} + +/* search in edt tree custom fields */ +void col_custom_set_edt(epan_dissect_t *edt, column_info *cinfo) +{ + int i; + col_item_t* col_item; + + if (!HAVE_CUSTOM_COLS(cinfo)) + return; + + for (i = cinfo->col_first[COL_CUSTOM]; + i <= cinfo->col_last[COL_CUSTOM]; i++) { + col_item = &cinfo->columns[i]; + if (col_item->fmt_matx[COL_CUSTOM] && + col_item->col_custom_fields && + col_item->col_custom_fields_ids) { + col_item->col_data = col_item->col_buf; + cinfo->col_expr.col_expr[i] = epan_custom_set(edt, col_item->col_custom_fields_ids, + col_item->col_custom_occurrence, + col_item->col_buf, + cinfo->col_expr.col_expr_val[i], + COL_MAX_LEN); + } + } +} + +#if 0 +// Needed if we create _ws.col.custom +static void +col_custom_set(proto_tree *tree, column_info *cinfo) +{ + int i; + col_item_t* col_item; + + if (!HAVE_CUSTOM_COLS(cinfo)) + return; + + for (i = cinfo->col_first[COL_CUSTOM]; + i <= cinfo->col_last[COL_CUSTOM]; i++) { + col_item = &cinfo->columns[i]; + if (col_item->fmt_matx[COL_CUSTOM] && + col_item->col_custom_fields && + col_item->col_custom_fields_ids) { + col_item->col_data = col_item->col_buf; + cinfo->col_expr.col_expr[i] = proto_custom_set(tree, col_item->col_custom_fields_ids, + col_item->col_custom_occurrence, + col_item->col_buf, + cinfo->col_expr.col_expr_val[i], + COL_MAX_LEN); + } + } +} +#endif + +void +col_custom_prime_edt(epan_dissect_t *edt, column_info *cinfo) +{ + int i; + col_item_t* col_item; + + if (!HAVE_CUSTOM_COLS(cinfo)) + return; + + for (i = cinfo->col_first[COL_CUSTOM]; + i <= cinfo->col_last[COL_CUSTOM]; i++) { + col_item = &cinfo->columns[i]; + + if (col_item->fmt_matx[COL_CUSTOM] && + col_item->col_custom_dfilter) { + epan_dissect_prime_with_dfilter(edt, col_item->col_custom_dfilter); + } + } +} + +char* +col_custom_get_filter(epan_dissect_t *edt, column_info *cinfo, const gint col) +{ + col_item_t* col_item; + + ws_assert(cinfo); + ws_assert(col < cinfo->num_cols); + + col_item = &cinfo->columns[col]; + if (col_item->fmt_matx[COL_CUSTOM] && + col_item->col_custom_fields && + col_item->col_custom_fields_ids) { + + return proto_custom_get_filter(edt, col_item->col_custom_fields_ids, + col_item->col_custom_occurrence); + } + return NULL; +} + +void +col_append_lstr(column_info *cinfo, const gint el, const gchar *str1, ...) +{ + va_list ap; + size_t pos, max_len; + int i; + const gchar *str; + col_item_t* col_item; + + if (!CHECK_COL(cinfo, el)) + return; + + if (el == COL_INFO) + max_len = COL_MAX_INFO_LEN; + else + max_len = COL_MAX_LEN; + + for (i = cinfo->col_first[el]; i <= cinfo->col_last[el]; i++) { + col_item = &cinfo->columns[i]; + if (col_item->fmt_matx[el]) { + /* + * First arrange that we can append, if necessary. + */ + COL_CHECK_APPEND(col_item, max_len); + + pos = strlen(col_item->col_buf); + if (pos >= max_len) + return; + + va_start(ap, str1); + str = str1; + do { + if (G_UNLIKELY(str == NULL)) { + str = "(null)"; + } + WS_UTF_8_CHECK(str, -1); + pos = ws_label_strcpy(col_item->col_buf, max_len, pos, str, 0); + + } while (pos < max_len && (str = va_arg(ap, const char *)) != COL_ADD_LSTR_TERMINATOR); + va_end(ap); + } + } +} + +void +col_append_str_uint(column_info *cinfo, const gint col, const gchar *abbrev, guint32 val, const gchar *sep) +{ + char buf[16]; + + guint32_to_str_buf(val, buf, sizeof(buf)); + col_append_lstr(cinfo, col, sep ? sep : "", abbrev, "=", buf, COL_ADD_LSTR_TERMINATOR); +} + +static inline void +col_snprint_port(gchar *buf, size_t buf_siz, port_type typ, guint16 val) +{ + const char *str; + + if (gbl_resolv_flags.transport_name && + (str = try_serv_name_lookup(typ, val)) != NULL) { + snprintf(buf, buf_siz, "%s(%"PRIu16")", str, val); + } else { + snprintf(buf, buf_siz, "%"PRIu16, val); + } +} + +void +col_append_ports(column_info *cinfo, const gint col, port_type typ, guint16 src, guint16 dst) +{ + char buf_src[32], buf_dst[32]; + + col_snprint_port(buf_src, 32, typ, src); + col_snprint_port(buf_dst, 32, typ, dst); + col_append_lstr(cinfo, col, buf_src, " " UTF8_RIGHTWARDS_ARROW " ", buf_dst, COL_ADD_LSTR_TERMINATOR); +} + +void +col_append_frame_number(packet_info *pinfo, const gint col, const gchar *fmt_str, guint frame_num) +{ + col_append_fstr(pinfo->cinfo, col, fmt_str, frame_num); + if (!pinfo->fd->visited) { + col_data_changed_ = TRUE; + } +} + +static void +col_do_append_fstr(column_info *cinfo, const int el, const char *separator, const char *format, va_list ap) +{ + size_t len, max_len, sep_len, pos; + int i; + col_item_t* col_item; + char tmp[COL_BUF_MAX_LEN]; + + sep_len = (separator) ? strlen(separator) : 0; + + if (el == COL_INFO) + max_len = COL_MAX_INFO_LEN; + else + max_len = COL_MAX_LEN; + + for (i = cinfo->col_first[el]; i <= cinfo->col_last[el]; i++) { + col_item = &cinfo->columns[i]; + if (col_item->fmt_matx[el]) { + /* + * First arrange that we can append, if necessary. + */ + COL_CHECK_APPEND(col_item, max_len); + + len = strlen(col_item->col_buf); + + /* + * If we have a separator, append it if the column isn't empty. + */ + if (sep_len != 0 && len != 0) { + (void) ws_label_strcat(col_item->col_buf, max_len, separator, 0); + len += sep_len; + } + + if (len < max_len) { + va_list ap2; + + va_copy(ap2, ap); + pos = vsnprintf(tmp, sizeof(tmp), format, ap2); + va_end(ap2); + if (pos >= max_len) { + ws_utf8_truncate(tmp, max_len - 1); + } + WS_UTF_8_CHECK(tmp, -1); + ws_label_strcpy(col_item->col_buf, max_len, len, tmp, 0); + } + } + } +} + +/* Appends a vararg list to a packet info string. */ +void +col_append_fstr(column_info *cinfo, const gint el, const gchar *format, ...) +{ + va_list ap; + + if (!CHECK_COL(cinfo, el)) + return; + + va_start(ap, format); + col_do_append_fstr(cinfo, el, NULL, format, ap); + va_end(ap); +} + +/* Appends a vararg list to a packet info string. + * Prefixes it with the given separator if the column is not empty. + */ +void +col_append_sep_fstr(column_info *cinfo, const gint el, const gchar *separator, + const gchar *format, ...) +{ + va_list ap; + + if (!CHECK_COL(cinfo, el)) + return; + + if (separator == NULL) + separator = ", "; /* default */ + + va_start(ap, format); + col_do_append_fstr(cinfo, el, separator, format, ap); + va_end(ap); +} + +/* Prepends a vararg list to a packet info string. */ +void +col_prepend_fstr(column_info *cinfo, const gint el, const gchar *format, ...) +{ + va_list ap; + int i; + char orig_buf[COL_BUF_MAX_LEN]; + const char *orig; + size_t max_len, pos; + col_item_t* col_item; + char tmp[COL_BUF_MAX_LEN]; + + if (!CHECK_COL(cinfo, el)) + return; + + if (el == COL_INFO) + max_len = COL_MAX_INFO_LEN; + else + max_len = COL_MAX_LEN; + + for (i = cinfo->col_first[el]; i <= cinfo->col_last[el]; i++) { + col_item = &cinfo->columns[i]; + if (col_item->fmt_matx[el]) { + if (col_item->col_data != col_item->col_buf) { + /* This was set with "col_set_str()"; which is effectively const */ + orig = col_item->col_data; + } else { + (void) g_strlcpy(orig_buf, col_item->col_buf, max_len); + orig = orig_buf; + } + va_start(ap, format); + pos = vsnprintf(tmp, sizeof(tmp), format, ap); + va_end(ap); + if (pos >= max_len) { + ws_utf8_truncate(tmp, max_len - 1); + } + WS_UTF_8_CHECK(tmp, -1); + pos = ws_label_strcpy(col_item->col_buf, max_len, 0, tmp, 0); + + /* + * Move the fence, unless it's at the beginning of the string. + */ + if (col_item->col_fence > 0) + col_item->col_fence += (int) strlen(col_item->col_buf); + + /* + * Append the original data. + */ + ws_label_strcpy(col_item->col_buf, max_len, pos, orig, 0); + col_item->col_data = col_item->col_buf; + } + } +} +void +col_prepend_fence_fstr(column_info *cinfo, const gint el, const gchar *format, ...) +{ + va_list ap; + int i; + char orig_buf[COL_BUF_MAX_LEN]; + const char *orig; + size_t max_len, pos; + col_item_t* col_item; + char tmp[COL_BUF_MAX_LEN]; + + if (!CHECK_COL(cinfo, el)) + return; + + if (el == COL_INFO) + max_len = COL_MAX_INFO_LEN; + else + max_len = COL_MAX_LEN; + + for (i = cinfo->col_first[el]; i <= cinfo->col_last[el]; i++) { + col_item = &cinfo->columns[i]; + if (col_item->fmt_matx[el]) { + if (col_item->col_data != col_item->col_buf) { + /* This was set with "col_set_str()"; which is effectively const */ + orig = col_item->col_data; + } else { + (void) g_strlcpy(orig_buf, col_item->col_buf, max_len); + orig = orig_buf; + } + va_start(ap, format); + pos = vsnprintf(tmp, sizeof(tmp), format, ap); + va_end(ap); + if (pos >= max_len) { + ws_utf8_truncate(tmp, max_len - 1); + } + WS_UTF_8_CHECK(tmp, -1); + pos = ws_label_strcpy(col_item->col_buf, max_len, 0, tmp, 0); + + /* + * Move the fence if it exists, else create a new fence at the + * end of the prepended data. + */ + if (col_item->col_fence > 0) { + col_item->col_fence += (int) strlen(col_item->col_buf); + } else { + col_item->col_fence = (int) strlen(col_item->col_buf); + } + /* + * Append the original data. + */ + ws_label_strcpy(col_item->col_buf, max_len, pos, orig, 0); + col_item->col_data = col_item->col_buf; + } + } +} + +/* Use this if "str" points to something that won't stay around (and + must thus be copied). */ +void +col_add_str(column_info *cinfo, const gint el, const gchar* str) +{ + int i; + size_t max_len; + col_item_t* col_item; + + if (!CHECK_COL(cinfo, el)) + return; + + if (el == COL_INFO) + max_len = COL_MAX_INFO_LEN; + else + max_len = COL_MAX_LEN; + + for (i = cinfo->col_first[el]; i <= cinfo->col_last[el]; i++) { + col_item = &cinfo->columns[i]; + if (col_item->fmt_matx[el]) { + if (col_item->col_fence != 0) { + /* + * We will append the string after the fence. + * First arrange that we can append, if necessary. + */ + COL_CHECK_APPEND(col_item, max_len); + } else { + /* + * There's no fence, so we can just write to the string. + */ + col_item->col_data = col_item->col_buf; + } + WS_UTF_8_CHECK(str, -1); + (void) ws_label_strcpy(col_item->col_buf, max_len, col_item->col_fence, str, 0); + } + } +} + +/* Use this if "str" points to something that will stay around (and thus + needn't be copied). */ +void +col_set_str(column_info *cinfo, const gint el, const gchar* str) +{ + int i; + size_t max_len; + col_item_t* col_item; + + DISSECTOR_ASSERT(str); + + if (!CHECK_COL(cinfo, el)) + return; + + if (el == COL_INFO) + max_len = COL_MAX_INFO_LEN; + else + max_len = COL_MAX_LEN; + + for (i = cinfo->col_first[el]; i <= cinfo->col_last[el]; i++) { + col_item = &cinfo->columns[i]; + if (col_item->fmt_matx[el]) { + if (col_item->col_fence != 0) { + /* + * We will append the string after the fence. + * First arrange that we can append, if necessary. + */ + COL_CHECK_APPEND(col_item, max_len); + + (void) g_strlcpy(&col_item->col_buf[col_item->col_fence], str, max_len - col_item->col_fence); + } else { + /* + * There's no fence, so we can just set the column to point + * to the string. + */ + col_item->col_data = str; + } + } + } +} + +void +col_add_lstr(column_info *cinfo, const gint el, const gchar *str1, ...) +{ + va_list ap; + int i; + gsize pos; + gsize max_len; + const gchar *str; + col_item_t* col_item; + + if (!CHECK_COL(cinfo, el)) + return; + + if (el == COL_INFO) + max_len = COL_MAX_INFO_LEN; + else + max_len = COL_MAX_LEN; + + for (i = cinfo->col_first[el]; i <= cinfo->col_last[el]; i++) { + col_item = &cinfo->columns[i]; + if (col_item->fmt_matx[el]) { + pos = col_item->col_fence; + if (pos != 0) { + /* + * We will append the string after the fence. + * First arrange that we can append, if necessary. + */ + COL_CHECK_APPEND(col_item, max_len); + } else { + /* + * There's no fence, so we can just write to the string. + */ + col_item->col_data = col_item->col_buf; + } + + va_start(ap, str1); + str = str1; + do { + if (G_UNLIKELY(str == NULL)) { + str = "(null)"; + } + WS_UTF_8_CHECK(str, -1); + pos = ws_label_strcpy(col_item->col_buf, max_len, pos, str, 0); + + } while (pos < max_len && (str = va_arg(ap, const char *)) != COL_ADD_LSTR_TERMINATOR); + va_end(ap); + } + } +} + +/* Adds a vararg list to a packet info string. */ +void +col_add_fstr(column_info *cinfo, const gint el, const gchar *format, ...) +{ + va_list ap; + int i, pos; + int max_len; + col_item_t* col_item; + char tmp[COL_BUF_MAX_LEN]; + + if (!CHECK_COL(cinfo, el)) + return; + + if (el == COL_INFO) + max_len = COL_MAX_INFO_LEN; + else + max_len = COL_MAX_LEN; + + for (i = cinfo->col_first[el]; i <= cinfo->col_last[el]; i++) { + col_item = &cinfo->columns[i]; + if (col_item->fmt_matx[el]) { + if (col_item->col_fence != 0) { + /* + * We will append the string after the fence. + * First arrange that we can append, if necessary. + */ + COL_CHECK_APPEND(col_item, max_len); + } else { + /* + * There's no fence, so we can just write to the string. + */ + col_item->col_data = col_item->col_buf; + } + va_start(ap, format); + pos = vsnprintf(tmp, sizeof(tmp), format, ap); + va_end(ap); + if (pos >= max_len) { + ws_utf8_truncate(tmp, max_len - 1); + } + WS_UTF_8_CHECK(tmp, -1); + ws_label_strcpy(col_item->col_buf, max_len, col_item->col_fence, tmp, 0); + } + } +} + +static void +col_do_append_str(column_info *cinfo, const gint el, const gchar* separator, + const gchar* str) +{ + int i; + size_t len, max_len; + col_item_t* col_item; + + if (el == COL_INFO) + max_len = COL_MAX_INFO_LEN; + else + max_len = COL_MAX_LEN; + + for (i = cinfo->col_first[el]; i <= cinfo->col_last[el]; i++) { + col_item = &cinfo->columns[i]; + if (col_item->fmt_matx[el]) { + /* + * First arrange that we can append, if necessary. + */ + COL_CHECK_APPEND(col_item, max_len); + + len = col_item->col_buf[0]; + + /* + * If we have a separator, append it if the column isn't empty. + */ + if (separator != NULL) { + if (len != 0) { + (void) ws_label_strcat(col_item->col_buf, max_len, separator, 0); + } + } + WS_UTF_8_CHECK(str, -1); + (void) ws_label_strcat(col_item->col_buf, max_len, str, 0); + } + } +} + +void +col_append_str(column_info *cinfo, const gint el, const gchar* str) +{ + if (!CHECK_COL(cinfo, el)) + return; + + col_do_append_str(cinfo, el, NULL, str); +} + +void +col_append_sep_str(column_info *cinfo, const gint el, const gchar* separator, + const gchar* str) +{ + if (!CHECK_COL(cinfo, el)) + return; + + if (separator == NULL) + separator = ", "; /* default */ + + col_do_append_str(cinfo, el, separator, str); +} + +/* --------------------------------- */ +gboolean +col_has_time_fmt(column_info *cinfo, const gint col) +{ + col_item_t* col_item = &cinfo->columns[col]; + return ((col_item->fmt_matx[COL_CLS_TIME]) || + (col_item->fmt_matx[COL_ABS_TIME]) || + (col_item->fmt_matx[COL_ABS_YMD_TIME]) || + (col_item->fmt_matx[COL_ABS_YDOY_TIME]) || + (col_item->fmt_matx[COL_UTC_TIME]) || + (col_item->fmt_matx[COL_UTC_YMD_TIME]) || + (col_item->fmt_matx[COL_UTC_YDOY_TIME]) || + (col_item->fmt_matx[COL_REL_TIME]) || + (col_item->fmt_matx[COL_DELTA_TIME]) || + (col_item->fmt_matx[COL_DELTA_TIME_DIS])); +} + +static int +get_frame_timestamp_precision(const frame_data *fd) +{ + int tsprecision; + + tsprecision = timestamp_get_precision(); + if (tsprecision == TS_PREC_AUTO) + tsprecision = fd->tsprec; + else if (tsprecision < 0) + ws_assert_not_reached(); + + /* + * Time stamp precision values higher than the maximum + * precision we support can't be handled. Just display + * those times with the maximum precision we support. + */ + if (tsprecision > WS_TSPREC_MAX) + tsprecision = WS_TSPREC_MAX; + + return tsprecision; +} + +static int +get_default_timestamp_precision(void) +{ + int tsprecision; + + tsprecision = timestamp_get_precision(); + if (tsprecision == TS_PREC_AUTO) + tsprecision = WS_TSPREC_MAX; /* default to the maximum precision we support */ + else if (tsprecision < 0) + ws_assert_not_reached(); + + /* + * Time stamp precision values higher than the maximum + * precision we support can't be handled. Just display + * those times with the maximum precision we support. + */ + if (tsprecision > WS_TSPREC_MAX) + tsprecision = WS_TSPREC_MAX; + + return tsprecision; +} + +static void +set_abs_ymd_time(const frame_data *fd, gchar *buf, char *decimal_point, gboolean local) +{ + if (!fd->has_ts) { + buf[0] = '\0'; + return; + } + format_nstime_as_iso8601(buf, COL_MAX_LEN, &fd->abs_ts, decimal_point, local, get_frame_timestamp_precision(fd)); +} + +static void +col_set_abs_ymd_time(const frame_data *fd, column_info *cinfo, const int col) +{ + set_abs_ymd_time(fd, cinfo->columns[col].col_buf, col_decimal_point, TRUE); + cinfo->col_expr.col_expr[col] = "frame.time"; + (void) g_strlcpy(cinfo->col_expr.col_expr_val[col],cinfo->columns[col].col_buf,COL_MAX_LEN); + + cinfo->columns[col].col_data = cinfo->columns[col].col_buf; +} + +static void +col_set_utc_ymd_time(const frame_data *fd, column_info *cinfo, const int col) +{ + set_abs_ymd_time(fd, cinfo->columns[col].col_buf, col_decimal_point, FALSE); + cinfo->col_expr.col_expr[col] = "frame.time"; + (void) g_strlcpy(cinfo->col_expr.col_expr_val[col],cinfo->columns[col].col_buf,COL_MAX_LEN); + + cinfo->columns[col].col_data = cinfo->columns[col].col_buf; +} + +static void +set_abs_ydoy_time(const frame_data *fd, gchar *buf, char *decimal_point, gboolean local) +{ + struct tm tm, *tmp; + char *ptr; + size_t remaining; + int num_bytes; + int tsprecision; + + if (!fd->has_ts) { + buf[0] = '\0'; + return; + } + + if (local) + tmp = ws_localtime_r(&fd->abs_ts.secs, &tm); + else + tmp = ws_gmtime_r(&fd->abs_ts.secs, &tm); + if (tmp == NULL) { + snprintf(buf, COL_MAX_LEN, "Not representable"); + return; + } + ptr = buf; + remaining = COL_MAX_LEN; + num_bytes = snprintf(ptr, remaining,"%04d/%03d %02d:%02d:%02d", + tmp->tm_year + 1900, + tmp->tm_yday + 1, + tmp->tm_hour, + tmp->tm_min, + tmp->tm_sec); + if (num_bytes < 0) { + /* + * That got an error. + * Not much else we can do. + */ + snprintf(ptr, remaining, "snprintf() failed"); + return; + } + if ((unsigned int)num_bytes >= remaining) { + /* + * That filled up or would have overflowed the buffer. + * Nothing more we can do. + */ + return; + } + ptr += num_bytes; + remaining -= num_bytes; + + tsprecision = get_frame_timestamp_precision(fd); + if (tsprecision != 0) { + /* + * Append the fractional part. + * Get the nsecs as a 32-bit unsigned value, as it should never + * be negative, so we treat it as unsigned. + */ + format_fractional_part_nsecs(ptr, remaining, (guint32)fd->abs_ts.nsecs, decimal_point, tsprecision); + } +} + +static void +col_set_abs_ydoy_time(const frame_data *fd, column_info *cinfo, const int col) +{ + set_abs_ydoy_time(fd, cinfo->columns[col].col_buf, col_decimal_point, TRUE); + cinfo->col_expr.col_expr[col] = "frame.time"; + (void) g_strlcpy(cinfo->col_expr.col_expr_val[col],cinfo->columns[col].col_buf,COL_MAX_LEN); + + cinfo->columns[col].col_data = cinfo->columns[col].col_buf; +} + +static void +col_set_utc_ydoy_time(const frame_data *fd, column_info *cinfo, const int col) +{ + set_abs_ydoy_time(fd, cinfo->columns[col].col_buf, col_decimal_point, FALSE); + cinfo->col_expr.col_expr[col] = "frame.time"; + (void) g_strlcpy(cinfo->col_expr.col_expr_val[col],cinfo->columns[col].col_buf,COL_MAX_LEN); + + cinfo->columns[col].col_data = cinfo->columns[col].col_buf; +} + +static void +set_time_seconds(const frame_data *fd, const nstime_t *ts, gchar *buf) +{ + ws_assert(fd->has_ts); + + display_signed_time(buf, COL_MAX_LEN, ts, get_frame_timestamp_precision(fd)); +} + +static void +set_time_hour_min_sec(const frame_data *fd, const nstime_t *ts, gchar *buf, char *decimal_point) +{ + time_t secs = ts->secs; + guint32 nsecs; + gboolean negative = FALSE; + char *ptr; + size_t remaining; + int num_bytes; + int tsprecision; + + ws_assert(fd->has_ts); + + if (secs < 0) { + secs = -secs; + negative = TRUE; + } + if (ts->nsecs >= 0) { + nsecs = ts->nsecs; + } else if (G_LIKELY(ts->nsecs != INT_MIN)) { + /* + * This isn't the smallest negative number that fits in 32 + * bits, so we can compute its negative and store it in a + * 32-bit unsigned int variable. + */ + nsecs = -ts->nsecs; + negative = TRUE; + } else { + /* + * -2147483648 is the smallest number that fits in a signed + * 2's complement 32-bit variable, and its negative doesn't + * fit in 32 bits. + * + * Just cast it to a 32-bit unsigned int value to set the + * 32-bit unsigned int variable to 2147483648. + * + * Note that, on platforms where both integers and long + * integers are 32-bit, such as 32-bit UN*Xes and both + * 32-bit *and* 64-bit Windows, making the variable in + * question a long will not avoid undefined behavior. + */ + nsecs = (guint32)ts->nsecs; + negative = TRUE; + } + ptr = buf; + remaining = COL_MAX_LEN; + if (secs >= (60*60)) { + num_bytes = snprintf(ptr, remaining, "%s%dh %2dm %2d", + negative ? "- " : "", + (gint32) secs / (60 * 60), + (gint32) (secs / 60) % 60, + (gint32) secs % 60); + } else if (secs >= 60) { + num_bytes = snprintf(ptr, remaining, "%s%dm %2d", + negative ? "- " : "", + (gint32) secs / 60, + (gint32) secs % 60); + } else { + num_bytes = snprintf(ptr, remaining, "%s%d", + negative ? "- " : "", + (gint32) secs); + } + if (num_bytes < 0) { + /* + * That got an error. + * Not much else we can do. + */ + snprintf(ptr, remaining, "snprintf() failed"); + return; + } + if ((unsigned int)num_bytes >= remaining) { + /* + * That filled up or would have overflowed the buffer. + * Nothing more we can do. + */ + return; + } + ptr += num_bytes; + remaining -= num_bytes; + + tsprecision = get_frame_timestamp_precision(fd); + if (tsprecision != 0) { + /* + * Append the fractional part. + */ + num_bytes = format_fractional_part_nsecs(ptr, remaining, nsecs, decimal_point, tsprecision); + if ((unsigned int)num_bytes >= remaining) { + /* + * That filled up or would have overflowed the buffer. + * Nothing more we can do. + */ + return; + } + ptr += num_bytes; + remaining -= num_bytes; + } + + /* Append the "s" for seconds. */ + snprintf(ptr, remaining, "s"); +} + +static void +col_set_rel_time(const frame_data *fd, column_info *cinfo, const int col) +{ + nstime_t del_rel_ts; + + if (!fd->has_ts) { + cinfo->columns[col].col_buf[0] = '\0'; + return; + } + + frame_delta_abs_time(cinfo->epan, fd, fd->frame_ref_num, &del_rel_ts); + + switch (timestamp_get_seconds_type()) { + case TS_SECONDS_DEFAULT: + set_time_seconds(fd, &del_rel_ts, cinfo->columns[col].col_buf); + cinfo->col_expr.col_expr[col] = "frame.time_relative"; + (void) g_strlcpy(cinfo->col_expr.col_expr_val[col],cinfo->columns[col].col_buf,COL_MAX_LEN); + break; + case TS_SECONDS_HOUR_MIN_SEC: + set_time_hour_min_sec(fd, &del_rel_ts, cinfo->columns[col].col_buf, col_decimal_point); + cinfo->col_expr.col_expr[col] = "frame.time_relative"; + set_time_seconds(fd, &del_rel_ts, cinfo->col_expr.col_expr_val[col]); + break; + default: + ws_assert_not_reached(); + } + cinfo->columns[col].col_data = cinfo->columns[col].col_buf; +} + +static void +col_set_delta_time(const frame_data *fd, column_info *cinfo, const int col) +{ + nstime_t del_cap_ts; + + if (!fd->has_ts) { + cinfo->columns[col].col_buf[0] = '\0'; + return; + } + + frame_delta_abs_time(cinfo->epan, fd, fd->num - 1, &del_cap_ts); + + switch (timestamp_get_seconds_type()) { + case TS_SECONDS_DEFAULT: + set_time_seconds(fd, &del_cap_ts, cinfo->columns[col].col_buf); + cinfo->col_expr.col_expr[col] = "frame.time_delta"; + (void) g_strlcpy(cinfo->col_expr.col_expr_val[col],cinfo->columns[col].col_buf,COL_MAX_LEN); + break; + case TS_SECONDS_HOUR_MIN_SEC: + set_time_hour_min_sec(fd, &del_cap_ts, cinfo->columns[col].col_buf, col_decimal_point); + cinfo->col_expr.col_expr[col] = "frame.time_delta"; + set_time_seconds(fd, &del_cap_ts, cinfo->col_expr.col_expr_val[col]); + break; + default: + ws_assert_not_reached(); + } + + cinfo->columns[col].col_data = cinfo->columns[col].col_buf; +} + +static void +col_set_delta_time_dis(const frame_data *fd, column_info *cinfo, const int col) +{ + nstime_t del_dis_ts; + + if (!fd->has_ts) { + cinfo->columns[col].col_buf[0] = '\0'; + return; + } + + frame_delta_abs_time(cinfo->epan, fd, fd->prev_dis_num, &del_dis_ts); + + switch (timestamp_get_seconds_type()) { + case TS_SECONDS_DEFAULT: + set_time_seconds(fd, &del_dis_ts, cinfo->columns[col].col_buf); + cinfo->col_expr.col_expr[col] = "frame.time_delta_displayed"; + (void) g_strlcpy(cinfo->col_expr.col_expr_val[col],cinfo->columns[col].col_buf,COL_MAX_LEN); + break; + case TS_SECONDS_HOUR_MIN_SEC: + set_time_hour_min_sec(fd, &del_dis_ts, cinfo->columns[col].col_buf, col_decimal_point); + cinfo->col_expr.col_expr[col] = "frame.time_delta_displayed"; + set_time_seconds(fd, &del_dis_ts, cinfo->col_expr.col_expr_val[col]); + break; + default: + ws_assert_not_reached(); + } + + cinfo->columns[col].col_data = cinfo->columns[col].col_buf; +} + +/* + * Time, without date. + */ +static void +set_abs_time(const frame_data *fd, gchar *buf, char *decimal_point, gboolean local) +{ + struct tm tm, *tmp; + gchar *ptr; + size_t remaining; + int num_bytes; + int tsprecision; + + if (!fd->has_ts) { + *buf = '\0'; + return; + } + + ptr = buf; + remaining = COL_MAX_LEN; + + if (local) + tmp = ws_localtime_r(&fd->abs_ts.secs, &tm); + else + tmp = ws_gmtime_r(&fd->abs_ts.secs, &tm); + if (tmp == NULL) { + snprintf(ptr, remaining, "Not representable"); + return; + } + + /* Integral part. */ + num_bytes = snprintf(ptr, remaining, "%02d:%02d:%02d", + tmp->tm_hour, + tmp->tm_min, + tmp->tm_sec); + if (num_bytes < 0) { + /* + * That got an error. + * Not much else we can do. + */ + snprintf(ptr, remaining, "snprintf() failed"); + return; + } + if ((unsigned int)num_bytes >= remaining) { + /* + * That filled up or would have overflowed the buffer. + * Nothing more we can do. + */ + return; + } + ptr += num_bytes; + remaining -= num_bytes; + + tsprecision = get_frame_timestamp_precision(fd); + if (tsprecision != 0) { + /* + * Append the fractional part. + * Get the nsecs as a 32-bit unsigned value, as it should never + * be negative, so we treat it as unsigned. + */ + format_fractional_part_nsecs(ptr, remaining, (guint32)fd->abs_ts.nsecs, decimal_point, tsprecision); + } +} + +static void +col_set_abs_time(const frame_data *fd, column_info *cinfo, const int col) +{ + set_abs_time(fd, cinfo->columns[col].col_buf, col_decimal_point, TRUE); + cinfo->col_expr.col_expr[col] = "frame.time"; + (void) g_strlcpy(cinfo->col_expr.col_expr_val[col],cinfo->columns[col].col_buf,COL_MAX_LEN); + + cinfo->columns[col].col_data = cinfo->columns[col].col_buf; +} + +static void +col_set_utc_time(const frame_data *fd, column_info *cinfo, const int col) +{ + set_abs_time(fd, cinfo->columns[col].col_buf, col_decimal_point, FALSE); + cinfo->col_expr.col_expr[col] = "frame.time"; + (void) g_strlcpy(cinfo->col_expr.col_expr_val[col],cinfo->columns[col].col_buf,COL_MAX_LEN); + + cinfo->columns[col].col_data = cinfo->columns[col].col_buf; +} + +static gboolean +set_epoch_time(const frame_data *fd, gchar *buf) +{ + if (!fd->has_ts) { + buf[0] = '\0'; + return FALSE; + } + display_epoch_time(buf, COL_MAX_LEN, &fd->abs_ts, get_frame_timestamp_precision(fd)); + return TRUE; +} + +static void +col_set_epoch_time(const frame_data *fd, column_info *cinfo, const int col) +{ + if (set_epoch_time(fd, cinfo->columns[col].col_buf)) { + cinfo->col_expr.col_expr[col] = "frame.time_delta"; + (void) g_strlcpy(cinfo->col_expr.col_expr_val[col],cinfo->columns[col].col_buf,COL_MAX_LEN); + } + cinfo->columns[col].col_data = cinfo->columns[col].col_buf; +} + +void +set_fd_time(const epan_t *epan, frame_data *fd, gchar *buf) +{ + + switch (timestamp_get_type()) { + case TS_ABSOLUTE: + set_abs_time(fd, buf, col_decimal_point, TRUE); + break; + + case TS_ABSOLUTE_WITH_YMD: + set_abs_ymd_time(fd, buf, col_decimal_point, TRUE); + break; + + case TS_ABSOLUTE_WITH_YDOY: + set_abs_ydoy_time(fd, buf, col_decimal_point, TRUE); + break; + + case TS_RELATIVE: + if (fd->has_ts) { + nstime_t del_rel_ts; + + frame_delta_abs_time(epan, fd, fd->frame_ref_num, &del_rel_ts); + + switch (timestamp_get_seconds_type()) { + case TS_SECONDS_DEFAULT: + set_time_seconds(fd, &del_rel_ts, buf); + break; + case TS_SECONDS_HOUR_MIN_SEC: + set_time_seconds(fd, &del_rel_ts, buf); + break; + default: + ws_assert_not_reached(); + } + } else { + buf[0] = '\0'; + } + break; + + case TS_DELTA: + if (fd->has_ts) { + nstime_t del_cap_ts; + + frame_delta_abs_time(epan, fd, fd->num - 1, &del_cap_ts); + + switch (timestamp_get_seconds_type()) { + case TS_SECONDS_DEFAULT: + set_time_seconds(fd, &del_cap_ts, buf); + break; + case TS_SECONDS_HOUR_MIN_SEC: + set_time_hour_min_sec(fd, &del_cap_ts, buf, col_decimal_point); + break; + default: + ws_assert_not_reached(); + } + } else { + buf[0] = '\0'; + } + break; + + case TS_DELTA_DIS: + if (fd->has_ts) { + nstime_t del_dis_ts; + + frame_delta_abs_time(epan, fd, fd->prev_dis_num, &del_dis_ts); + + switch (timestamp_get_seconds_type()) { + case TS_SECONDS_DEFAULT: + set_time_seconds(fd, &del_dis_ts, buf); + break; + case TS_SECONDS_HOUR_MIN_SEC: + set_time_hour_min_sec(fd, &del_dis_ts, buf, col_decimal_point); + break; + default: + ws_assert_not_reached(); + } + } else { + buf[0] = '\0'; + } + break; + + case TS_EPOCH: + set_epoch_time(fd, buf); + break; + + case TS_UTC: + set_abs_time(fd, buf, col_decimal_point, FALSE); + break; + + case TS_UTC_WITH_YMD: + set_abs_ymd_time(fd, buf, col_decimal_point, FALSE); + break; + + case TS_UTC_WITH_YDOY: + set_abs_ydoy_time(fd, buf, col_decimal_point, FALSE); + break; + + case TS_NOT_SET: + /* code is missing for this case, but I don't know which [jmayer20051219] */ + ws_assert_not_reached(); + break; + } +} + +static void +col_set_cls_time(const frame_data *fd, column_info *cinfo, const gint col) +{ + switch (timestamp_get_type()) { + case TS_ABSOLUTE: + col_set_abs_time(fd, cinfo, col); + break; + + case TS_ABSOLUTE_WITH_YMD: + col_set_abs_ymd_time(fd, cinfo, col); + break; + + case TS_ABSOLUTE_WITH_YDOY: + col_set_abs_ydoy_time(fd, cinfo, col); + break; + + case TS_RELATIVE: + col_set_rel_time(fd, cinfo, col); + break; + + case TS_DELTA: + col_set_delta_time(fd, cinfo, col); + break; + + case TS_DELTA_DIS: + col_set_delta_time_dis(fd, cinfo, col); + break; + + case TS_EPOCH: + col_set_epoch_time(fd, cinfo, col); + break; + + case TS_UTC: + col_set_utc_time(fd, cinfo, col); + break; + + case TS_UTC_WITH_YMD: + col_set_utc_ymd_time(fd, cinfo, col); + break; + + case TS_UTC_WITH_YDOY: + col_set_utc_ydoy_time(fd, cinfo, col); + break; + + case TS_NOT_SET: + /* code is missing for this case, but I don't know which [jmayer20051219] */ + ws_assert_not_reached(); + break; + } +} + +/* Set the format of the variable time format. */ +static void +col_set_fmt_time(const frame_data *fd, column_info *cinfo, const gint fmt, const gint col) +{ + COL_CHECK_REF_TIME(fd, cinfo->columns[col].col_buf); + + switch (fmt) { + case COL_CLS_TIME: + col_set_cls_time(fd, cinfo, col); + break; + + case COL_ABS_TIME: + col_set_abs_time(fd, cinfo, col); + break; + + case COL_ABS_YMD_TIME: + col_set_abs_ymd_time(fd, cinfo, col); + break; + + case COL_ABS_YDOY_TIME: + col_set_abs_ydoy_time(fd, cinfo, col); + break; + + case COL_REL_TIME: + col_set_rel_time(fd, cinfo, col); + break; + + case COL_DELTA_TIME: + col_set_delta_time(fd, cinfo, col); + break; + + case COL_DELTA_TIME_DIS: + col_set_delta_time_dis(fd, cinfo, col); + break; + + case COL_UTC_TIME: + col_set_utc_time(fd, cinfo, col); + break; + + case COL_UTC_YMD_TIME: + col_set_utc_ymd_time(fd, cinfo, col); + break; + + case COL_UTC_YDOY_TIME: + col_set_utc_ydoy_time(fd, cinfo, col); + break; + + default: + ws_assert_not_reached(); + break; + } +} + +/* --------------------------- */ +/* Set the given (relative) time to a column element. + * + * Used by dissectors to set the time in a column + * + * @param cinfo the current packet row + * @param el the column to use, e.g. COL_INFO + * @param ts the time to set in the column + * @param fieldname the fieldname to use for creating a filter (when + * applying/preparing/copying as filter) + */ +void +col_set_time(column_info *cinfo, const gint el, const nstime_t *ts, const char *fieldname) +{ + int col; + col_item_t* col_item; + + if (!CHECK_COL(cinfo, el)) + return; + + /** @todo TODO: We don't respect fd->ref_time (no way to access 'fd') + COL_CHECK_REF_TIME(fd, buf); + */ + + for (col = cinfo->col_first[el]; col <= cinfo->col_last[el]; col++) { + col_item = &cinfo->columns[col]; + if (col_item->fmt_matx[el]) { + display_signed_time(col_item->col_buf, COL_MAX_LEN, ts, get_default_timestamp_precision()); + col_item->col_data = col_item->col_buf; + cinfo->col_expr.col_expr[col] = fieldname; + (void) g_strlcpy(cinfo->col_expr.col_expr_val[col],col_item->col_buf,COL_MAX_LEN); + } + } +} + +static void +col_set_addr(packet_info *pinfo, const int col, const address *addr, const gboolean is_src, + const gboolean fill_col_exprs, const gboolean res) +{ + const char *name; + col_item_t* col_item = &pinfo->cinfo->columns[col]; + + if (addr->type == AT_NONE) { + /* No address, nothing to do */ + return; + } + + if (res && (name = address_to_name(addr)) != NULL) + col_item->col_data = name; + else { + col_item->col_data = col_item->col_buf; + address_to_str_buf(addr, col_item->col_buf, COL_MAX_LEN); + } + + if (!fill_col_exprs) + return; + + pinfo->cinfo->col_expr.col_expr[col] = address_type_column_filter_string(addr, is_src); + /* For address types that have a filter, create a string */ + if (strlen(pinfo->cinfo->col_expr.col_expr[col]) > 0) + address_to_str_buf(addr, pinfo->cinfo->col_expr.col_expr_val[col], COL_MAX_LEN); +} + +/* ------------------------ */ +static void +col_set_port(packet_info *pinfo, const int col, const gboolean is_res, const gboolean is_src, const gboolean fill_col_exprs _U_) +{ + guint32 port; + col_item_t* col_item = &pinfo->cinfo->columns[col]; + + if (is_src) + port = pinfo->srcport; + else + port = pinfo->destport; + + /* TODO: Use fill_col_exprs */ + + switch (pinfo->ptype) { + case PT_SCTP: + if (is_res) + (void) g_strlcpy(col_item->col_buf, sctp_port_to_display(pinfo->pool, port), COL_MAX_LEN); + else + guint32_to_str_buf(port, col_item->col_buf, COL_MAX_LEN); + break; + + case PT_TCP: + guint32_to_str_buf(port, pinfo->cinfo->col_expr.col_expr_val[col], COL_MAX_LEN); + if (is_res) + (void) g_strlcpy(col_item->col_buf, tcp_port_to_display(pinfo->pool, port), COL_MAX_LEN); + else + (void) g_strlcpy(col_item->col_buf, pinfo->cinfo->col_expr.col_expr_val[col], COL_MAX_LEN); + if (is_src) + pinfo->cinfo->col_expr.col_expr[col] = "tcp.srcport"; + else + pinfo->cinfo->col_expr.col_expr[col] = "tcp.dstport"; + break; + + case PT_UDP: + guint32_to_str_buf(port, pinfo->cinfo->col_expr.col_expr_val[col], COL_MAX_LEN); + if (is_res) + (void) g_strlcpy(col_item->col_buf, udp_port_to_display(pinfo->pool, port), COL_MAX_LEN); + else + (void) g_strlcpy(col_item->col_buf, pinfo->cinfo->col_expr.col_expr_val[col], COL_MAX_LEN); + if (is_src) + pinfo->cinfo->col_expr.col_expr[col] = "udp.srcport"; + else + pinfo->cinfo->col_expr.col_expr[col] = "udp.dstport"; + break; + + case PT_DDP: + if (is_src) + pinfo->cinfo->col_expr.col_expr[col] = "ddp.src_socket"; + else + pinfo->cinfo->col_expr.col_expr[col] = "ddp.dst_socket"; + guint32_to_str_buf(port, pinfo->cinfo->col_expr.col_expr_val[col], COL_MAX_LEN); + (void) g_strlcpy(col_item->col_buf, pinfo->cinfo->col_expr.col_expr_val[col], COL_MAX_LEN); + break; + + case PT_IPX: + /* XXX - resolve IPX socket numbers */ + snprintf(col_item->col_buf, COL_MAX_LEN, "0x%04x", port); + (void) g_strlcpy(pinfo->cinfo->col_expr.col_expr_val[col], col_item->col_buf,COL_MAX_LEN); + if (is_src) + pinfo->cinfo->col_expr.col_expr[col] = "ipx.src.socket"; + else + pinfo->cinfo->col_expr.col_expr[col] = "ipx.dst.socket"; + break; + + case PT_IDP: + /* XXX - resolve IDP socket numbers */ + snprintf(col_item->col_buf, COL_MAX_LEN, "0x%04x", port); + (void) g_strlcpy(pinfo->cinfo->col_expr.col_expr_val[col], col_item->col_buf,COL_MAX_LEN); + if (is_src) + pinfo->cinfo->col_expr.col_expr[col] = "idp.src.socket"; + else + pinfo->cinfo->col_expr.col_expr[col] = "idp.dst.socket"; + break; + + case PT_USB: + /* XXX - resolve USB endpoint numbers */ + snprintf(col_item->col_buf, COL_MAX_LEN, "0x%08x", port); + (void) g_strlcpy(pinfo->cinfo->col_expr.col_expr_val[col], col_item->col_buf,COL_MAX_LEN); + if (is_src) + pinfo->cinfo->col_expr.col_expr[col] = "usb.src.endpoint"; + else + pinfo->cinfo->col_expr.col_expr[col] = "usb.dst.endpoint"; + break; + + default: + break; + } + col_item->col_data = col_item->col_buf; +} + +gboolean +col_based_on_frame_data(column_info *cinfo, const gint col) +{ + ws_assert(cinfo); + ws_assert(col < cinfo->num_cols); + + switch (cinfo->columns[col].col_fmt) { + case COL_NUMBER: + case COL_CLS_TIME: + case COL_ABS_TIME: + case COL_ABS_YMD_TIME: + case COL_ABS_YDOY_TIME: + case COL_UTC_TIME: + case COL_UTC_YMD_TIME: + case COL_UTC_YDOY_TIME: + case COL_REL_TIME: + case COL_DELTA_TIME: + case COL_DELTA_TIME_DIS: + case COL_PACKET_LENGTH: + case COL_CUMULATIVE_BYTES: + return TRUE; + + default: + return FALSE; + } +} + +void +col_fill_in_frame_data(const frame_data *fd, column_info *cinfo, const gint col, const gboolean fill_col_exprs) +{ + col_item_t* col_item = &cinfo->columns[col]; + + switch (col_item->col_fmt) { + case COL_NUMBER: + guint32_to_str_buf(fd->num, col_item->col_buf, COL_MAX_LEN); + col_item->col_data = col_item->col_buf; + break; + + case COL_CLS_TIME: + case COL_ABS_TIME: + case COL_ABS_YMD_TIME: + case COL_ABS_YDOY_TIME: + case COL_UTC_TIME: + case COL_UTC_YMD_TIME: + case COL_UTC_YDOY_TIME: + case COL_REL_TIME: + case COL_DELTA_TIME: + case COL_DELTA_TIME_DIS: + /* TODO: Pass on fill_col_exprs */ + col_set_fmt_time(fd, cinfo, col_item->col_fmt, col); + break; + + case COL_PACKET_LENGTH: + guint32_to_str_buf(fd->pkt_len, col_item->col_buf, COL_MAX_LEN); + col_item->col_data = col_item->col_buf; + break; + + case COL_CUMULATIVE_BYTES: + guint32_to_str_buf(fd->cum_bytes, col_item->col_buf, COL_MAX_LEN); + col_item->col_data = col_item->col_buf; + break; + + default: + break; + } + + if (!fill_col_exprs) + return; + + switch (col_item->col_fmt) { + case COL_NUMBER: + cinfo->col_expr.col_expr[col] = "frame.number"; + (void) g_strlcpy(cinfo->col_expr.col_expr_val[col], col_item->col_buf, COL_MAX_LEN); + break; + + case COL_CLS_TIME: + case COL_ABS_TIME: + case COL_ABS_YMD_TIME: + case COL_ABS_YDOY_TIME: + case COL_UTC_TIME: + case COL_UTC_YMD_TIME: + case COL_UTC_YDOY_TIME: + case COL_REL_TIME: + case COL_DELTA_TIME: + case COL_DELTA_TIME_DIS: + /* Already handled above */ + break; + + case COL_PACKET_LENGTH: + cinfo->col_expr.col_expr[col] = "frame.len"; + (void) g_strlcpy(cinfo->col_expr.col_expr_val[col], col_item->col_buf, COL_MAX_LEN); + break; + + case COL_CUMULATIVE_BYTES: + break; + + default: + break; + } +} + +void +col_fill_in(packet_info *pinfo, const gboolean fill_col_exprs, const gboolean fill_fd_colums) +{ + int i; + col_item_t* col_item; + + if (!pinfo->cinfo) + return; + + for (i = 0; i < pinfo->cinfo->num_cols; i++) { + col_item = &pinfo->cinfo->columns[i]; + if (col_based_on_frame_data(pinfo->cinfo, i)) { + if (fill_fd_colums) + col_fill_in_frame_data(pinfo->fd, pinfo->cinfo, i, fill_col_exprs); + } else { + switch (col_item->col_fmt) { + case COL_DEF_SRC: + case COL_RES_SRC: /* COL_DEF_SRC is currently just like COL_RES_SRC */ + col_set_addr(pinfo, i, &pinfo->src, TRUE, fill_col_exprs, TRUE); + break; + + case COL_UNRES_SRC: + col_set_addr(pinfo, i, &pinfo->src, TRUE, fill_col_exprs, FALSE); + break; + + case COL_DEF_DL_SRC: + case COL_RES_DL_SRC: + col_set_addr(pinfo, i, &pinfo->dl_src, TRUE, fill_col_exprs, TRUE); + break; + + case COL_UNRES_DL_SRC: + col_set_addr(pinfo, i, &pinfo->dl_src, TRUE, fill_col_exprs, FALSE); + break; + + case COL_DEF_NET_SRC: + case COL_RES_NET_SRC: + col_set_addr(pinfo, i, &pinfo->net_src, TRUE, fill_col_exprs, TRUE); + break; + + case COL_UNRES_NET_SRC: + col_set_addr(pinfo, i, &pinfo->net_src, TRUE, fill_col_exprs, FALSE); + break; + + case COL_DEF_DST: + case COL_RES_DST: /* COL_DEF_DST is currently just like COL_RES_DST */ + col_set_addr(pinfo, i, &pinfo->dst, FALSE, fill_col_exprs, TRUE); + break; + + case COL_UNRES_DST: + col_set_addr(pinfo, i, &pinfo->dst, FALSE, fill_col_exprs, FALSE); + break; + + case COL_DEF_DL_DST: + case COL_RES_DL_DST: + col_set_addr(pinfo, i, &pinfo->dl_dst, FALSE, fill_col_exprs, TRUE); + break; + + case COL_UNRES_DL_DST: + col_set_addr(pinfo, i, &pinfo->dl_dst, FALSE, fill_col_exprs, FALSE); + break; + + case COL_DEF_NET_DST: + case COL_RES_NET_DST: + col_set_addr(pinfo, i, &pinfo->net_dst, FALSE, fill_col_exprs, TRUE); + break; + + case COL_UNRES_NET_DST: + col_set_addr(pinfo, i, &pinfo->net_dst, FALSE, fill_col_exprs, FALSE); + break; + + case COL_DEF_SRC_PORT: + case COL_RES_SRC_PORT: /* COL_DEF_SRC_PORT is currently just like COL_RES_SRC_PORT */ + col_set_port(pinfo, i, TRUE, TRUE, fill_col_exprs); + break; + + case COL_UNRES_SRC_PORT: + col_set_port(pinfo, i, FALSE, TRUE, fill_col_exprs); + break; + + case COL_DEF_DST_PORT: + case COL_RES_DST_PORT: /* COL_DEF_DST_PORT is currently just like COL_RES_DST_PORT */ + col_set_port(pinfo, i, TRUE, FALSE, fill_col_exprs); + break; + + case COL_UNRES_DST_PORT: + col_set_port(pinfo, i, FALSE, FALSE, fill_col_exprs); + break; + + case COL_CUSTOM: + /* Formatting handled by col_custom_set_edt() / col_custom_get_filter() */ + break; + + case NUM_COL_FMTS: /* keep compiler happy - shouldn't get here */ + ws_assert_not_reached(); + break; + default: + if (col_item->col_fmt >= NUM_COL_FMTS) { + ws_assert_not_reached(); + } + /* + * Formatting handled by expert.c (COL_EXPERT), or individual + * dissectors. Fill in from the text using the internal hfid. + */ + if (fill_col_exprs) { + pinfo->cinfo->col_expr.col_expr[i] = proto_registrar_get_nth(col_item->hf_id)->abbrev; + (void) g_strlcpy(pinfo->cinfo->col_expr.col_expr_val[i], pinfo->cinfo->columns[i].col_data, (col_item->col_fmt == COL_INFO) ? COL_MAX_INFO_LEN : COL_MAX_LEN); + } + break; + } + } + } +} + +/* + * Fill in columns if we got an error reading the packet. + * We set most columns to "???", fill in columns that don't need data read + * from the file, and set the Info column to an error message. + */ +void +col_fill_in_error(column_info *cinfo, frame_data *fdata, const gboolean fill_col_exprs, const gboolean fill_fd_colums) +{ + int i; + col_item_t* col_item; + + if (!cinfo) + return; + + for (i = 0; i < cinfo->num_cols; i++) { + col_item = &cinfo->columns[i]; + if (col_based_on_frame_data(cinfo, i)) { + if (fill_fd_colums) + col_fill_in_frame_data(fdata, cinfo, i, fill_col_exprs); + } else if (col_item->col_fmt == COL_INFO) { + /* XXX - say more than this */ + col_item->col_data = "Read error"; + } else { + if (col_item->col_fmt >= NUM_COL_FMTS) { + ws_assert_not_reached(); + } + /* + * No dissection was done, and these columns are set as the + * result of the dissection, so.... + */ + col_item->col_data = "???"; + break; + } + } +} + +gboolean col_data_changed(void) { + gboolean cur_cdc = col_data_changed_; + col_data_changed_ = FALSE; + return cur_cdc; +} + +void +col_register_protocol(void) +{ + /* This gets called by proto_init() before column_register_fields() + * gets called by the preference modules actually getting registered. + */ + if (proto_cols == -1) { + proto_cols = proto_get_id_by_filter_name("_ws.col"); + } + if (proto_cols == -1) { + proto_cols = proto_register_protocol("Wireshark Columns", "Columns", "_ws.col"); + } + static gint *ett[] = { + &ett_cols + }; + proto_register_subtree_array(ett, G_N_ELEMENTS(ett)); +} + +void +col_dissect(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + proto_item *ti; + proto_tree *col_tree; + + column_info *cinfo = pinfo->cinfo; + + if (!cinfo) { + return; + } + + if (proto_field_is_referenced(tree, proto_cols)) { + // XXX: Needed if we also create _ws.col.custom + //col_custom_set(tree, cinfo); + col_fill_in(pinfo, FALSE, TRUE); + ti = proto_tree_add_item(tree, proto_cols, tvb, 0, 0, ENC_NA); + proto_item_set_hidden(ti); + col_tree = proto_item_add_subtree(ti, ett_cols); + for (int i = 0; i < cinfo->num_cols; ++i) { + if (cinfo->columns[i].hf_id != -1) { + if (cinfo->columns[i].col_fmt == COL_CUSTOM) { + ti = proto_tree_add_string_format(col_tree, cinfo->columns[i].hf_id, tvb, 0, 0, get_column_text(cinfo, i), "%s: %s", get_column_title(i), get_column_text(cinfo, i)); + } else { + ti = proto_tree_add_string(col_tree, cinfo->columns[i].hf_id, tvb, 0, 0, get_column_text(cinfo, i)); + } + proto_item_set_hidden(ti); + } + } + } +} + +/* + * Editor modelines + * + * Local Variables: + * c-basic-offset: 2 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * ex: set shiftwidth=2 tabstop=8 expandtab: + * :indentSize=2:tabSize=8:noTabs=true: + */ |