diff options
Diffstat (limited to 'epan/column.c')
-rw-r--r-- | epan/column.c | 1187 |
1 files changed, 1187 insertions, 0 deletions
diff --git a/epan/column.c b/epan/column.c new file mode 100644 index 00000000..e11e1873 --- /dev/null +++ b/epan/column.c @@ -0,0 +1,1187 @@ +/* column.c + * Routines for handling column preferences + * + * 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 <stdlib.h> +#include <string.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include <epan/timestamp.h> +#include <epan/prefs.h> +#include <epan/dfilter/dfilter.h> +#include <epan/column.h> +#include <epan/column-info.h> +#include <epan/packet.h> +#include <wsutil/ws_assert.h> + +static int proto_cols = -1; +static hf_register_info *hf_cols = NULL; +static unsigned int hf_cols_cleanup = 0; + +/* Given a format number (as defined in column-utils.h), returns its equivalent + string */ +const gchar * +col_format_to_string(const gint fmt) { + static const gchar *const slist[NUM_COL_FMTS] = { + "%Yt", /* 0) COL_ABS_YMD_TIME */ + "%YDOYt", /* 1) COL_ABS_YDOY_TIME */ + "%At", /* 2) COL_ABS_TIME */ + "%B", /* 3) COL_CUMULATIVE_BYTES */ + "%Cus", /* 4) COL_CUSTOM */ + "%Tt", /* 5) COL_DELTA_TIME */ + "%Gt", /* 6) COL_DELTA_TIME_DIS */ + "%rd", /* 7) COL_RES_DST */ + "%ud", /* 8) COL_UNRES_DST */ + "%rD", /* 9) COL_RES_DST_PORT */ + "%uD", /* 10) COL_UNRES_DST_PORT */ + "%d", /* 11) COL_DEF_DST */ + "%D", /* 12) COL_DEF_DST_PORT */ + "%a", /* 13) COL_EXPERT */ + "%I", /* 14) COL_IF_DIR */ + "%F", /* 15) COL_FREQ_CHAN */ + "%hd", /* 16) COL_DEF_DL_DST */ + "%hs", /* 17) COL_DEF_DL_SRC */ + "%rhd", /* 18) COL_RES_DL_DST */ + "%uhd", /* 19) COL_UNRES_DL_DST */ + "%rhs", /* 20) COL_RES_DL_SRC*/ + "%uhs", /* 21) COL_UNRES_DL_SRC */ + "%e", /* 22) COL_RSSI */ + "%x", /* 23) COL_TX_RATE */ + "%f", /* 24) COL_DSCP_VALUE */ + "%i", /* 25) COL_INFO */ + "%rnd", /* 26) COL_RES_NET_DST */ + "%und", /* 27) COL_UNRES_NET_DST */ + "%rns", /* 28) COL_RES_NET_SRC */ + "%uns", /* 29) COL_UNRES_NET_SRC */ + "%nd", /* 30) COL_DEF_NET_DST */ + "%ns", /* 31) COL_DEF_NET_SRC */ + "%m", /* 32) COL_NUMBER */ + "%L", /* 33) COL_PACKET_LENGTH */ + "%p", /* 34) COL_PROTOCOL */ + "%Rt", /* 35) COL_REL_TIME */ + "%s", /* 36) COL_DEF_SRC */ + "%S", /* 37) COL_DEF_SRC_PORT */ + "%rs", /* 38) COL_RES_SRC */ + "%us", /* 39) COL_UNRES_SRC */ + "%rS", /* 40) COL_RES_SRC_PORT */ + "%uS", /* 41) COL_UNRES_SRC_PORT */ + "%Yut", /* 42) COL_UTC_YMD_TIME */ + "%YDOYut", /* 43) COL_UTC_YDOY_TIME */ + "%Aut", /* 44) COL_UTC_TIME */ + "%t" /* 45) COL_CLS_TIME */ + }; + + /* Note the formats in migrated_columns[] below have been used in deprecated + * columns, and avoid reusing them. + */ + if (fmt < 0 || fmt >= NUM_COL_FMTS) + return NULL; + + return(slist[fmt]); +} + +/* Given a format number (as defined in column-utils.h), returns its + description */ +const gchar * +col_format_desc(const gint fmt_num) { + + /* This should be sorted alphabetically, e.g. `sort -t, -k2` */ + /* + * This is currently used in the preferences UI, so out-of-numeric-order + * performance shouldn't be an issue. + */ + static const value_string dlist_vals[] = { + + { COL_ABS_YMD_TIME, "Absolute date, as YYYY-MM-DD, and time" }, + { COL_ABS_YDOY_TIME, "Absolute date, as YYYY/DOY, and time" }, + { COL_ABS_TIME, "Absolute time" }, + { COL_CUMULATIVE_BYTES, "Cumulative Bytes" }, + { COL_CUSTOM, "Custom" }, + { COL_DELTA_TIME_DIS, "Delta time displayed" }, + { COL_DELTA_TIME, "Delta time" }, + { COL_RES_DST, "Dest addr (resolved)" }, + { COL_UNRES_DST, "Dest addr (unresolved)" }, + { COL_RES_DST_PORT, "Dest port (resolved)" }, + { COL_UNRES_DST_PORT, "Dest port (unresolved)" }, + { COL_DEF_DST, "Destination address" }, + { COL_DEF_DST_PORT, "Destination port" }, + { COL_EXPERT, "Expert Info Severity" }, + { COL_IF_DIR, "FW-1 monitor if/direction" }, + { COL_FREQ_CHAN, "Frequency/Channel" }, + { COL_DEF_DL_DST, "Hardware dest addr" }, + { COL_DEF_DL_SRC, "Hardware src addr" }, + { COL_RES_DL_DST, "Hw dest addr (resolved)" }, + { COL_UNRES_DL_DST, "Hw dest addr (unresolved)" }, + { COL_RES_DL_SRC, "Hw src addr (resolved)" }, + { COL_UNRES_DL_SRC, "Hw src addr (unresolved)" }, + { COL_RSSI, "IEEE 802.11 RSSI" }, + { COL_TX_RATE, "IEEE 802.11 TX rate" }, + { COL_DSCP_VALUE, "IP DSCP Value" }, + { COL_INFO, "Information" }, + { COL_RES_NET_DST, "Net dest addr (resolved)" }, + { COL_UNRES_NET_DST, "Net dest addr (unresolved)" }, + { COL_RES_NET_SRC, "Net src addr (resolved)" }, + { COL_UNRES_NET_SRC, "Net src addr (unresolved)" }, + { COL_DEF_NET_DST, "Network dest addr" }, + { COL_DEF_NET_SRC, "Network src addr" }, + { COL_NUMBER, "Number" }, + { COL_PACKET_LENGTH, "Packet length (bytes)" }, + { COL_PROTOCOL, "Protocol" }, + { COL_REL_TIME, "Relative time" }, + { COL_DEF_SRC, "Source address" }, + { COL_DEF_SRC_PORT, "Source port" }, + { COL_RES_SRC, "Src addr (resolved)" }, + { COL_UNRES_SRC, "Src addr (unresolved)" }, + { COL_RES_SRC_PORT, "Src port (resolved)" }, + { COL_UNRES_SRC_PORT, "Src port (unresolved)" }, + { COL_CLS_TIME, "Time (format as specified)" }, + { COL_UTC_YMD_TIME, "UTC date, as YYYY-MM-DD, and time" }, + { COL_UTC_YDOY_TIME, "UTC date, as YYYY/DOY, and time" }, + { COL_UTC_TIME, "UTC time" }, + + { 0, NULL } + }; + + const gchar *val_str = try_val_to_str(fmt_num, dlist_vals); + ws_assert(val_str != NULL); + return val_str; +} + +/* Given a format number (as defined in column-utils.h), returns its + filter abbreviation */ +const gchar * +col_format_abbrev(const gint fmt_num) { + + static const value_string alist_vals[] = { + + { COL_ABS_YMD_TIME, COLUMN_FIELD_FILTER"abs_ymd_time" }, + { COL_ABS_YDOY_TIME, COLUMN_FIELD_FILTER"abs_ydoy_time" }, + { COL_ABS_TIME, COLUMN_FIELD_FILTER"abs_time" }, + { COL_CUMULATIVE_BYTES, COLUMN_FIELD_FILTER"cumulative_bytes" }, + { COL_CUSTOM, COLUMN_FIELD_FILTER"custom" }, + { COL_DELTA_TIME_DIS, COLUMN_FIELD_FILTER"delta_time_dis" }, + { COL_DELTA_TIME, COLUMN_FIELD_FILTER"delta_time" }, + { COL_RES_DST, COLUMN_FIELD_FILTER"res_dst" }, + { COL_UNRES_DST, COLUMN_FIELD_FILTER"unres_dst" }, + { COL_RES_DST_PORT, COLUMN_FIELD_FILTER"res_dst_port" }, + { COL_UNRES_DST_PORT, COLUMN_FIELD_FILTER"unres_dst_port" }, + { COL_DEF_DST, COLUMN_FIELD_FILTER"def_dst" }, + { COL_DEF_DST_PORT, COLUMN_FIELD_FILTER"def_dst_port" }, + { COL_EXPERT, COLUMN_FIELD_FILTER"expert" }, + { COL_IF_DIR, COLUMN_FIELD_FILTER"if_dir" }, + { COL_FREQ_CHAN, COLUMN_FIELD_FILTER"freq_chan" }, + { COL_DEF_DL_DST, COLUMN_FIELD_FILTER"def_dl_dst" }, + { COL_DEF_DL_SRC, COLUMN_FIELD_FILTER"def_dl_src" }, + { COL_RES_DL_DST, COLUMN_FIELD_FILTER"res_dl_dst" }, + { COL_UNRES_DL_DST, COLUMN_FIELD_FILTER"unres_dl_dst" }, + { COL_RES_DL_SRC, COLUMN_FIELD_FILTER"res_dl_src" }, + { COL_UNRES_DL_SRC, COLUMN_FIELD_FILTER"unres_dl_src" }, + { COL_RSSI, COLUMN_FIELD_FILTER"rssi" }, + { COL_TX_RATE, COLUMN_FIELD_FILTER"tx_rate" }, + { COL_DSCP_VALUE, COLUMN_FIELD_FILTER"dscp" }, + { COL_INFO, COLUMN_FIELD_FILTER"info" }, + { COL_RES_NET_DST, COLUMN_FIELD_FILTER"res_net_dst" }, + { COL_UNRES_NET_DST, COLUMN_FIELD_FILTER"unres_net_dst" }, + { COL_RES_NET_SRC, COLUMN_FIELD_FILTER"res_net_src" }, + { COL_UNRES_NET_SRC, COLUMN_FIELD_FILTER"unres_net_src" }, + { COL_DEF_NET_DST, COLUMN_FIELD_FILTER"def_net_dst" }, + { COL_DEF_NET_SRC, COLUMN_FIELD_FILTER"def_net_src" }, + { COL_NUMBER, COLUMN_FIELD_FILTER"number" }, + { COL_PACKET_LENGTH, COLUMN_FIELD_FILTER"packet_length" }, + { COL_PROTOCOL, COLUMN_FIELD_FILTER"protocol" }, + { COL_REL_TIME, COLUMN_FIELD_FILTER"rel_time" }, + { COL_DEF_SRC, COLUMN_FIELD_FILTER"def_src" }, + { COL_DEF_SRC_PORT, COLUMN_FIELD_FILTER"def_src_port" }, + { COL_RES_SRC, COLUMN_FIELD_FILTER"res_src" }, + { COL_UNRES_SRC, COLUMN_FIELD_FILTER"unres_src" }, + { COL_RES_SRC_PORT, COLUMN_FIELD_FILTER"res_src_port" }, + { COL_UNRES_SRC_PORT, COLUMN_FIELD_FILTER"unres_src_port" }, + { COL_CLS_TIME, COLUMN_FIELD_FILTER"cls_time" }, + { COL_UTC_YMD_TIME, COLUMN_FIELD_FILTER"utc_ymc_time" }, + { COL_UTC_YDOY_TIME, COLUMN_FIELD_FILTER"utc_ydoy_time" }, + { COL_UTC_TIME, COLUMN_FIELD_FILTER"utc_time" }, + + { 0, NULL } + }; + + const gchar *val_str = try_val_to_str(fmt_num, alist_vals); + ws_assert(val_str != NULL); + return val_str; +} +/* Array of columns that have been migrated to custom columns */ +struct deprecated_columns { + const gchar *col_fmt; + const gchar *col_expr; +}; + +static struct deprecated_columns migrated_columns[] = { + { /* COL_COS_VALUE */ "%U", "vlan.priority" }, + { /* COL_CIRCUIT_ID */ "%c", "iax2.call" }, + { /* COL_BSSGP_TLLI */ "%l", "bssgp.tlli" }, + { /* COL_HPUX_SUBSYS */ "%H", "nettl.subsys" }, + { /* COL_HPUX_DEVID */ "%P", "nettl.devid" }, + { /* COL_FR_DLCI */ "%C", "fr.dlci" }, + { /* COL_REL_CONV_TIME */ "%rct", "tcp.time_relative" }, + { /* COL_DELTA_CONV_TIME */ "%dct", "tcp.time_delta" }, + { /* COL_OXID */ "%XO", "fc.ox_id" }, + { /* COL_RXID */ "%XR", "fc.rx_id" }, + { /* COL_SRCIDX */ "%Xd", "mdshdr.srcidx" }, + { /* COL_DSTIDX */ "%Xs", "mdshdr.dstidx" }, + { /* COL_DCE_CTX */ "%z", "dcerpc.cn_ctx_id" }, + /* The columns above here have been migrated since August 2009 and all + * completely removed since January 2016. At some point we could remove + * these; how many people have a preference file that they haven't opened + * and saved since then? + */ + { /* COL_8021Q_VLAN_ID */ "%q", "vlan.id||nstrace.vlan" }, + { /* COL_VSAN */ "%V", "mdshdr.vsan||brdwlk.vsan||fc.vft.vf_id" }, + { /* COL_DCE_CALL */ "%y", "dcerpc.cn_call_id||dcerpc.dg_seqnum" }, + { /* COL_TEI */ "%E", "lapd.tei" }, +}; + +const char* +try_convert_to_column_field(const char *field) +{ + static const value_string migrated_fields[] = { + { COL_NUMBER, COLUMN_FIELD_FILTER"No." }, + { COL_CLS_TIME, COLUMN_FIELD_FILTER"Time" }, + { COL_DEF_SRC, COLUMN_FIELD_FILTER"Source" }, + { COL_DEF_DST, COLUMN_FIELD_FILTER"Destination" }, + { COL_PROTOCOL, COLUMN_FIELD_FILTER"Protocol" }, + { COL_PACKET_LENGTH, COLUMN_FIELD_FILTER"Length" }, + { COL_INFO, COLUMN_FIELD_FILTER"Info" }, + { 0, NULL }, + }; + + int idx; + + idx = str_to_val_idx(field, migrated_fields); + + if (idx >= 0) { + return col_format_abbrev(migrated_fields[idx].value); + } + + return NULL; +} + +/* + * Parse a column format, filling in the relevant fields of a fmt_data. + */ +gboolean +parse_column_format(fmt_data *cfmt, const char *fmt) +{ + const gchar *cust_format = col_format_to_string(COL_CUSTOM); + size_t cust_format_len = strlen(cust_format); + gchar **cust_format_info; + char *p; + int col_fmt; + gchar *col_custom_fields = NULL; + long col_custom_occurrence = 0; + bool col_resolved = true; + + /* + * Is this a custom column? + */ + if ((strlen(fmt) > cust_format_len) && (fmt[cust_format_len] == ':') && + strncmp(fmt, cust_format, cust_format_len) == 0) { + /* Yes. */ + col_fmt = COL_CUSTOM; + cust_format_info = g_strsplit(&fmt[cust_format_len+1], ":", 3); /* add 1 for ':' */ + col_custom_fields = g_strdup(cust_format_info[0]); + if (col_custom_fields && cust_format_info[1]) { + col_custom_occurrence = strtol(cust_format_info[1], &p, 10); + if (p == cust_format_info[1] || *p != '\0') { + /* Not a valid number. */ + g_free(col_custom_fields); + g_strfreev(cust_format_info); + return FALSE; + } + } + if (col_custom_fields && cust_format_info[1] && cust_format_info[2]) { + col_resolved = (cust_format_info[2][0] == 'U') ? false : true; + } + g_strfreev(cust_format_info); + } else { + col_fmt = get_column_format_from_str(fmt); + if (col_fmt == -1) + return FALSE; + } + + cfmt->fmt = col_fmt; + cfmt->custom_fields = col_custom_fields; + cfmt->custom_occurrence = (int)col_custom_occurrence; + cfmt->resolved = col_resolved; + return TRUE; +} + +void +try_convert_to_custom_column(char **fmt) +{ + guint haystack_idx; + + for (haystack_idx = 0; + haystack_idx < G_N_ELEMENTS(migrated_columns); + ++haystack_idx) { + + if (strcmp(migrated_columns[haystack_idx].col_fmt, *fmt) == 0) { + gchar *cust_col = ws_strdup_printf("%%Cus:%s:0", + migrated_columns[haystack_idx].col_expr); + + g_free(*fmt); + *fmt = cust_col; + } + } +} + +void +column_dump_column_formats(void) +{ + gint fmt; + + for (fmt = 0; fmt < NUM_COL_FMTS; fmt++) { + printf("%s\t%s\n", col_format_to_string(fmt), col_format_desc(fmt)); + } + + printf("\nFor example, to print Wireshark's default columns with tshark:\n\n" +#ifdef _WIN32 + "tshark.exe -o \"gui.column.format:" + "\\\"No.\\\",\\\"%%m\\\"," + "\\\"Time\\\",\\\"%%t\\\"," + "\\\"Source\\\",\\\"%%s\\\"," + "\\\"Destination\\\",\\\"%%d\\\"," + "\\\"Protocol\\\",\\\"%%p\\\"," + "\\\"Length\\\",\\\"%%L\\\"," + "\\\"Info\\\",\\\"%%i\\\"\"\n"); +#else + "tshark -o 'gui.column.format:" + "\"No.\",\"%%m\"," + "\"Time\",\"%%t\"," + "\"Source\",\"%%s\"," + "\"Destination\",\"%%d\"," + "\"Protocol\",\"%%p\"," + "\"Length\",\"%%L\"," + "\"Info\",\"%%i\"'\n"); +#endif +} + +/* Marks each array element true if it can be substituted for the given + column format */ +void +get_column_format_matches(gboolean *fmt_list, const gint format) { + + /* Get the obvious: the format itself */ + if ((format >= 0) && (format < NUM_COL_FMTS)) + fmt_list[format] = TRUE; + + /* Get any formats lower down on the chain */ + switch (format) { + case COL_DEF_SRC: + fmt_list[COL_RES_DL_SRC] = TRUE; + fmt_list[COL_RES_NET_SRC] = TRUE; + break; + case COL_RES_SRC: + fmt_list[COL_RES_DL_SRC] = TRUE; + fmt_list[COL_RES_NET_SRC] = TRUE; + break; + case COL_UNRES_SRC: + fmt_list[COL_UNRES_DL_SRC] = TRUE; + fmt_list[COL_UNRES_NET_SRC] = TRUE; + break; + case COL_DEF_DST: + fmt_list[COL_RES_DL_DST] = TRUE; + fmt_list[COL_RES_NET_DST] = TRUE; + break; + case COL_RES_DST: + fmt_list[COL_RES_DL_DST] = TRUE; + fmt_list[COL_RES_NET_DST] = TRUE; + break; + case COL_UNRES_DST: + fmt_list[COL_UNRES_DL_DST] = TRUE; + fmt_list[COL_UNRES_NET_DST] = TRUE; + break; + case COL_DEF_DL_SRC: + fmt_list[COL_RES_DL_SRC] = TRUE; + break; + case COL_DEF_DL_DST: + fmt_list[COL_RES_DL_DST] = TRUE; + break; + case COL_DEF_NET_SRC: + fmt_list[COL_RES_NET_SRC] = TRUE; + break; + case COL_DEF_NET_DST: + fmt_list[COL_RES_NET_DST] = TRUE; + break; + case COL_DEF_SRC_PORT: + fmt_list[COL_RES_SRC_PORT] = TRUE; + break; + case COL_DEF_DST_PORT: + fmt_list[COL_RES_DST_PORT] = TRUE; + break; + default: + break; + } +} + +/* + * These tables are indexed by the number of digits of precision for + * time stamps; all TS_PREC_FIXED_ types have values equal to the + * number of digits of precision, and NUM_WS_TSPREC_VALS is the + * total number of such values as there's a one-to-one correspondence + * between WS_TSPREC_ values and TS_PREC_FIXED_ values. + */ + +/* + * Strings for YYYY-MM-DD HH:MM:SS.SSSS dates and times. + * (Yes, we know, this has a Y10K problem.) + */ +static const char *ts_ymd[NUM_WS_TSPREC_VALS] = { + "0000-00-00 00:00:00", + "0000-00-00 00:00:00.0", + "0000-00-00 00:00:00.00", + "0000-00-00 00:00:00.000", + "0000-00-00 00:00:00.0000", + "0000-00-00 00:00:00.00000", + "0000-00-00 00:00:00.000000", + "0000-00-00 00:00:00.0000000", + "0000-00-00 00:00:00.00000000", + "0000-00-00 00:00:00.000000000", +}; + +/* + * Strings for YYYY/DOY HH:MM:SS.SSSS dates and times. + * (Yes, we know, this also has a Y10K problem.) + */ +static const char *ts_ydoy[NUM_WS_TSPREC_VALS] = { + "0000/000 00:00:00", + "0000/000 00:00:00.0", + "0000/000 00:00:00.00", + "0000/000 00:00:00.000", + "0000/000 00:00:00.0000", + "0000/000 00:00:00.00000", + "0000/000 00:00:00.000000", + "0000/000 00:00:00.0000000", + "0000/000 00:00:00.00000000", + "0000/000 00:00:00.000000000", +}; + +/* + * Strings for HH:MM:SS.SSSS absolute times without dates. + */ +static const char *ts_abstime[NUM_WS_TSPREC_VALS] = { + "00:00:00", + "00:00:00.0", + "00:00:00.00", + "00:00:00.000", + "00:00:00.0000", + "00:00:00.00000", + "00:00:00.000000", + "00:00:00.0000000", + "00:00:00.00000000", + "00:00:00.000000000", +}; + +/* + * Strings for SSSS.S relative and delta times. + * (Yes, this has s 10,000-seconds problem.) + */ +static const char *ts_rel_delta_time[NUM_WS_TSPREC_VALS] = { + "0000", + "0000.0", + "0000.00", + "0000.000", + "0000.0000", + "0000.00000", + "0000.000000", + "0000.0000000", + "0000.00000000", + "0000.000000000", +}; + +/* + * Strings for UN*X/POSIX Epoch times. + */ +static const char *ts_epoch_time[NUM_WS_TSPREC_VALS] = { + "0000000000000000000", + "0000000000000000000.0", + "0000000000000000000.00", + "0000000000000000000.000", + "0000000000000000000.0000", + "0000000000000000000.00000", + "0000000000000000000.000000", + "0000000000000000000.0000000", + "0000000000000000000.00000000", + "0000000000000000000.000000000", +}; + +/* Returns a string representing the longest possible value for + a timestamp column type. */ +static const char * +get_timestamp_column_longest_string(const gint type, const gint precision) +{ + + switch(type) { + case(TS_ABSOLUTE_WITH_YMD): + case(TS_UTC_WITH_YMD): + if(precision == TS_PREC_AUTO) { + /* + * Return the string for the maximum precision, so that + * our caller leaves room for that string. + */ + return ts_ymd[WS_TSPREC_MAX]; + } else if(precision >= 0 && precision < NUM_WS_TSPREC_VALS) + return ts_ymd[precision]; + else + ws_assert_not_reached(); + break; + case(TS_ABSOLUTE_WITH_YDOY): + case(TS_UTC_WITH_YDOY): + if(precision == TS_PREC_AUTO) { + /* + * Return the string for the maximum precision, so that + * our caller leaves room for that string. + */ + return ts_ydoy[WS_TSPREC_MAX]; + } else if(precision >= 0 && precision < NUM_WS_TSPREC_VALS) + return ts_ydoy[precision]; + else + ws_assert_not_reached(); + break; + case(TS_ABSOLUTE): + case(TS_UTC): + if(precision == TS_PREC_AUTO) { + /* + * Return the string for the maximum precision, so that + * our caller leaves room for that string. + */ + return ts_abstime[WS_TSPREC_MAX]; + } else if(precision >= 0 && precision < NUM_WS_TSPREC_VALS) + return ts_abstime[precision]; + else + ws_assert_not_reached(); + break; + case(TS_RELATIVE): /* fallthrough */ + case(TS_DELTA): + case(TS_DELTA_DIS): + if(precision == TS_PREC_AUTO) { + /* + * Return the string for the maximum precision, so that + * our caller leaves room for that string. + */ + return ts_rel_delta_time[WS_TSPREC_MAX]; + } else if(precision >= 0 && precision < NUM_WS_TSPREC_VALS) + return ts_rel_delta_time[precision]; + else + ws_assert_not_reached(); + break; + case(TS_EPOCH): + /* This is enough to represent 2^63 (signed 64-bit integer) + fractions */ + if(precision == TS_PREC_AUTO) { + /* + * Return the string for the maximum precision, so that + * our caller leaves room for that string. + */ + return ts_epoch_time[WS_TSPREC_MAX]; + } else if(precision >= 0 && precision < NUM_WS_TSPREC_VALS) + return ts_epoch_time[precision]; + else + ws_assert_not_reached(); + break; + case(TS_NOT_SET): + /* This should not happen. */ + return "0000.000000"; + break; + default: + ws_assert_not_reached(); + } + + /* never reached, satisfy compiler */ + return ""; +} + +/* Returns a string representing the longest possible value for a + particular column type. See also get_column_width_string() above. + + Except for the COL...SRC and COL...DST columns, these are used + only when a capture is being displayed while it's taking place; + they are arguably somewhat fragile, as changes to the code that + generates them don't cause these widths to change, but that's + probably not too big a problem, given that the sizes are + recomputed based on the actual data in the columns when the capture + is done, and given that the width for COL...SRC and COL...DST columns + is somewhat arbitrary in any case. We should probably clean + that up eventually, though. */ +static const char * +get_column_longest_string(const gint format) +{ + switch (format) { + case COL_NUMBER: + return "0000000"; + break; + case COL_CLS_TIME: + return get_timestamp_column_longest_string(timestamp_get_type(), timestamp_get_precision()); + break; + case COL_ABS_YMD_TIME: + return get_timestamp_column_longest_string(TS_ABSOLUTE_WITH_YMD, timestamp_get_precision()); + break; + case COL_ABS_YDOY_TIME: + return get_timestamp_column_longest_string(TS_ABSOLUTE_WITH_YDOY, timestamp_get_precision()); + break; + case COL_UTC_YMD_TIME: + return get_timestamp_column_longest_string(TS_UTC_WITH_YMD, timestamp_get_precision()); + break; + case COL_UTC_YDOY_TIME: + return get_timestamp_column_longest_string(TS_UTC_WITH_YDOY, timestamp_get_precision()); + break; + case COL_ABS_TIME: + return get_timestamp_column_longest_string(TS_ABSOLUTE, timestamp_get_precision()); + break; + case COL_UTC_TIME: + return get_timestamp_column_longest_string(TS_UTC, timestamp_get_precision()); + break; + case COL_REL_TIME: + return get_timestamp_column_longest_string(TS_RELATIVE, timestamp_get_precision()); + break; + case COL_DELTA_TIME: + return get_timestamp_column_longest_string(TS_DELTA, timestamp_get_precision()); + break; + case COL_DELTA_TIME_DIS: + return get_timestamp_column_longest_string(TS_DELTA_DIS, timestamp_get_precision()); + break; + case COL_DEF_SRC: + case COL_RES_SRC: + case COL_UNRES_SRC: + case COL_DEF_DL_SRC: + case COL_RES_DL_SRC: + case COL_UNRES_DL_SRC: + case COL_DEF_NET_SRC: + case COL_RES_NET_SRC: + case COL_UNRES_NET_SRC: + case COL_DEF_DST: + case COL_RES_DST: + case COL_UNRES_DST: + case COL_DEF_DL_DST: + case COL_RES_DL_DST: + case COL_UNRES_DL_DST: + case COL_DEF_NET_DST: + case COL_RES_NET_DST: + case COL_UNRES_NET_DST: + return "00000000.000000000000"; /* IPX-style */ + break; + case COL_DEF_SRC_PORT: + case COL_RES_SRC_PORT: + case COL_UNRES_SRC_PORT: + case COL_DEF_DST_PORT: + case COL_RES_DST_PORT: + case COL_UNRES_DST_PORT: + return "000000"; + break; + case COL_PROTOCOL: + return "Protocol"; /* not the longest, but the longest is too long */ + break; + case COL_PACKET_LENGTH: + return "00000"; + break; + case COL_CUMULATIVE_BYTES: + return "00000000"; + break; + case COL_IF_DIR: + return "i 00000000 I"; + break; + case COL_TX_RATE: + return "108.0"; + break; + case COL_RSSI: + return "100"; + break; + case COL_DSCP_VALUE: + return "AAA BBB"; /* not the longest, but the longest is too long */ + break; + case COL_EXPERT: + return "ERROR"; + break; + case COL_FREQ_CHAN: + return "9999 MHz [A 999]"; + break; + case COL_CUSTOM: + return "0000000000"; /* not the longest, but the longest is too long */ + break; + default: /* COL_INFO */ + return "Source port: kerberos-master Destination port: kerberos-master"; + break; + } +} + +/* Returns the longer string of the column title or the hard-coded width of + * its contents for building the packet list layout. */ +const gchar * +get_column_width_string(const gint format, const gint col) +{ + if(strlen(get_column_longest_string(format)) > + strlen(get_column_title(col))) + return get_column_longest_string(format); + else + return get_column_title(col); +} + +/* Returns the longest possible width, in characters, for a particular + column type. */ +gint +get_column_char_width(const gint format) +{ + return (gint)strlen(get_column_longest_string(format)); +} + +gint +get_column_format(const gint col) +{ + GList *clp = g_list_nth(prefs.col_list, col); + fmt_data *cfmt; + + if (!clp) /* Invalid column requested */ + return -1; + + cfmt = (fmt_data *) clp->data; + + return(cfmt->fmt); +} + +void +set_column_format(const gint col, const gint fmt) +{ + GList *clp = g_list_nth(prefs.col_list, col); + fmt_data *cfmt; + + if (!clp) /* Invalid column requested */ + return; + + cfmt = (fmt_data *) clp->data; + + cfmt->fmt = fmt; +} + +gint +get_column_format_from_str(const gchar *str) +{ + gint i; + + for (i = 0; i < NUM_COL_FMTS; i++) { + if (strcmp(str, col_format_to_string(i)) == 0) + return i; + } + return -1; /* illegal */ +} + +gchar * +get_column_title(const gint col) +{ + GList *clp = g_list_nth(prefs.col_list, col); + fmt_data *cfmt; + + if (!clp) /* Invalid column requested */ + return NULL; + + cfmt = (fmt_data *) clp->data; + + return(cfmt->title); +} + +void +set_column_title(const gint col, const gchar *title) +{ + GList *clp = g_list_nth(prefs.col_list, col); + fmt_data *cfmt; + + if (!clp) /* Invalid column requested */ + return; + + cfmt = (fmt_data *) clp->data; + + g_free (cfmt->title); + cfmt->title = g_strdup (title); +} + +gboolean +get_column_visible(const gint col) +{ + GList *clp = g_list_nth(prefs.col_list, col); + fmt_data *cfmt; + + if (!clp) /* Invalid column requested */ + return TRUE; + + cfmt = (fmt_data *) clp->data; + + return(cfmt->visible); +} + +void +set_column_visible(const gint col, gboolean visible) +{ + GList *clp = g_list_nth(prefs.col_list, col); + fmt_data *cfmt; + + if (!clp) /* Invalid column requested */ + return; + + cfmt = (fmt_data *) clp->data; + + cfmt->visible = visible; +} + +gboolean +get_column_resolved(const gint col) +{ + GList *clp = g_list_nth(prefs.col_list, col); + fmt_data *cfmt; + + if (!clp) /* Invalid column requested */ + return TRUE; + + cfmt = (fmt_data *) clp->data; + + return(cfmt->resolved); +} + +void +set_column_resolved(const gint col, gboolean resolved) +{ + GList *clp = g_list_nth(prefs.col_list, col); + fmt_data *cfmt; + + if (!clp) /* Invalid column requested */ + return; + + cfmt = (fmt_data *) clp->data; + + cfmt->resolved = resolved; +} + +const gchar * +get_column_custom_fields(const gint col) +{ + GList *clp = g_list_nth(prefs.col_list, col); + fmt_data *cfmt; + + if (!clp) /* Invalid column requested */ + return NULL; + + cfmt = (fmt_data *) clp->data; + + return(cfmt->custom_fields); +} + +void +set_column_custom_fields(const gint col, const char *custom_fields) +{ + GList *clp = g_list_nth(prefs.col_list, col); + fmt_data *cfmt; + + if (!clp) /* Invalid column requested */ + return; + + cfmt = (fmt_data *) clp->data; + + g_free (cfmt->custom_fields); + cfmt->custom_fields = g_strdup (custom_fields); +} + +gint +get_column_custom_occurrence(const gint col) +{ + GList *clp = g_list_nth(prefs.col_list, col); + fmt_data *cfmt; + + if (!clp) /* Invalid column requested */ + return 0; + + cfmt = (fmt_data *) clp->data; + + return(cfmt->custom_occurrence); +} + +void +set_column_custom_occurrence(const gint col, const gint custom_occurrence) +{ + GList *clp = g_list_nth(prefs.col_list, col); + fmt_data *cfmt; + + if (!clp) /* Invalid column requested */ + return; + + cfmt = (fmt_data *) clp->data; + + cfmt->custom_occurrence = custom_occurrence; +} + +static gchar * +get_custom_field_tooltip (gchar *custom_field, gint occurrence) +{ + header_field_info *hfi = proto_registrar_get_byname(custom_field); + if (hfi == NULL) { + /* Not a valid field */ + return ws_strdup_printf("Unknown Field: %s", custom_field); + } + + if (hfi->parent == -1) { + /* Protocol */ + return ws_strdup_printf("%s (%s)", hfi->name, hfi->abbrev); + } + + if (occurrence == 0) { + /* All occurrences */ + return ws_strdup_printf("%s\n%s (%s)", proto_get_protocol_name(hfi->parent), hfi->name, hfi->abbrev); + } + + /* One given occurrence */ + return ws_strdup_printf("%s\n%s (%s#%d)", proto_get_protocol_name(hfi->parent), hfi->name, hfi->abbrev, occurrence); +} + +gchar * +get_column_tooltip(const gint col) +{ + GList *clp = g_list_nth(prefs.col_list, col); + fmt_data *cfmt; + gchar **fields; + gboolean first = TRUE; + GString *column_tooltip; + guint i; + + if (!clp) /* Invalid column requested */ + return NULL; + + cfmt = (fmt_data *) clp->data; + + if (cfmt->fmt != COL_CUSTOM) { + /* Use format description */ + return g_strdup(col_format_desc(cfmt->fmt)); + } + + fields = g_regex_split_simple(COL_CUSTOM_PRIME_REGEX, cfmt->custom_fields, + (GRegexCompileFlags) (G_REGEX_ANCHORED | G_REGEX_RAW), + G_REGEX_MATCH_ANCHORED); + column_tooltip = g_string_new(""); + + for (i = 0; i < g_strv_length(fields); i++) { + if (fields[i] && *fields[i]) { + gchar *field_tooltip = get_custom_field_tooltip(fields[i], cfmt->custom_occurrence); + if (!first) { + g_string_append(column_tooltip, "\n\nOR\n\n"); + } + g_string_append(column_tooltip, field_tooltip); + g_free (field_tooltip); + first = FALSE; + } + } + + g_strfreev(fields); + + return g_string_free (column_tooltip, FALSE); +} + +const gchar* +get_column_text(column_info *cinfo, const gint col) +{ + ws_assert(cinfo); + ws_assert(col < cinfo->num_cols); + + if (!get_column_resolved(col) && cinfo->col_expr.col_expr_val[col]) { + /* Use the unresolved value in col_expr_val */ + return cinfo->col_expr.col_expr_val[col]; + } + + return cinfo->columns[col].col_data; +} + +void +col_finalize(column_info *cinfo) +{ + int i; + col_item_t* col_item; + + for (i = 0; i < cinfo->num_cols; i++) { + col_item = &cinfo->columns[i]; + + if (col_item->col_fmt == COL_CUSTOM) { + if(!dfilter_compile(col_item->col_custom_fields, &col_item->col_custom_dfilter, NULL)) { + /* XXX: Should we issue a warning? */ + g_free(col_item->col_custom_fields); + col_item->col_custom_fields = NULL; + col_item->col_custom_occurrence = 0; + col_item->col_custom_dfilter = NULL; + } + if (col_item->col_custom_fields) { + gchar **fields = g_regex_split(cinfo->prime_regex, col_item->col_custom_fields, + G_REGEX_MATCH_ANCHORED); + guint i_field; + + for (i_field = 0; i_field < g_strv_length(fields); i_field++) { + if (fields[i_field] && *fields[i_field]) { + header_field_info *hfinfo = proto_registrar_get_byname(fields[i_field]); + if (hfinfo) { + int *idx = g_new(int, 1); + *idx = hfinfo->id; + col_item->col_custom_fields_ids = g_slist_append(col_item->col_custom_fields_ids, idx); + } + } + } + g_strfreev(fields); + } + } else { + col_item->col_custom_fields = NULL; + col_item->col_custom_occurrence = 0; + col_item->col_custom_dfilter = NULL; + } + + col_item->fmt_matx = g_new0(gboolean, NUM_COL_FMTS); + get_column_format_matches(col_item->fmt_matx, col_item->col_fmt); + col_item->col_data = NULL; + + if (col_item->col_fmt == COL_INFO) { + col_item->col_buf = g_new(gchar, COL_MAX_INFO_LEN); + cinfo->col_expr.col_expr_val[i] = g_new(gchar, COL_MAX_INFO_LEN); + } else { + col_item->col_buf = g_new(gchar, COL_MAX_LEN); + cinfo->col_expr.col_expr_val[i] = g_new(gchar, COL_MAX_LEN); + } + + cinfo->col_expr.col_expr[i] = ""; + } + + cinfo->col_expr.col_expr[i] = NULL; + cinfo->col_expr.col_expr_val[i] = NULL; + + for (i = 0; i < cinfo->num_cols; i++) { + int j; + + for (j = 0; j < NUM_COL_FMTS; j++) { + if (!cinfo->columns[i].fmt_matx[j]) + continue; + + if (cinfo->col_first[j] == -1) + cinfo->col_first[j] = i; + + cinfo->col_last[j] = i; + } + } +} + +void +build_column_format_array(column_info *cinfo, const gint num_cols, const gboolean reset_fences) +{ + int i; + col_item_t* col_item; + + /* Build the column format array */ + col_setup(cinfo, num_cols); + + for (i = 0; i < cinfo->num_cols; i++) { + col_item = &cinfo->columns[i]; + col_item->col_fmt = get_column_format(i); + col_item->col_title = g_strdup(get_column_title(i)); + if (col_item->col_fmt == COL_CUSTOM) { + col_item->col_custom_fields = g_strdup(get_column_custom_fields(i)); + col_item->col_custom_occurrence = get_column_custom_occurrence(i); + } + col_item->hf_id = proto_registrar_get_id_byname(col_format_abbrev(col_item->col_fmt)); + + if(reset_fences) + col_item->col_fence = 0; + } + + col_finalize(cinfo); +} + +static void +column_deregister_fields(void) +{ + if (hf_cols) { + for (unsigned int i = 0; i < hf_cols_cleanup; ++i) { + proto_deregister_field(proto_cols, *(hf_cols[i].p_id)); + g_free(hf_cols[i].p_id); + } + proto_add_deregistered_data(hf_cols); + hf_cols = NULL; + hf_cols_cleanup = 0; + } +} + +void +column_register_fields(void) +{ + + int* hf_id; + GArray *hf_col_array; + hf_register_info new_hf; + fmt_data *cfmt; + gboolean *used_fmts; + 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"); + } + column_deregister_fields(); + if (prefs.col_list != NULL) { + prefs.num_cols = g_list_length(prefs.col_list); + hf_col_array = g_array_new(FALSE, TRUE, sizeof(hf_register_info)); + used_fmts = g_new0(gboolean, NUM_COL_FMTS); + /* Only register a field for each format type once, but don't register + * these at all. The first two behave oddly (because they depend on + * whether the current field and previous fields are displayed). We + * might want to do custom columns in the future, though. + */ + used_fmts[COL_DELTA_TIME_DIS] = 1; + used_fmts[COL_CUMULATIVE_BYTES] = 1; + used_fmts[COL_CUSTOM] = 1; + + for (GList *elem = g_list_first(prefs.col_list); elem != NULL; elem = elem->next) { + cfmt = (fmt_data*)elem->data; + if (!used_fmts[cfmt->fmt]) { + used_fmts[cfmt->fmt] = TRUE; + hf_id = g_new(int, 1); + *hf_id = -1; + new_hf.p_id = hf_id; + new_hf.hfinfo.name = g_strdup(col_format_desc(cfmt->fmt)); + new_hf.hfinfo.abbrev = g_strdup(col_format_abbrev(cfmt->fmt)); + new_hf.hfinfo.type = FT_STRING; + new_hf.hfinfo.display = BASE_NONE; + new_hf.hfinfo.strings = NULL; + new_hf.hfinfo.bitmask = 0; + new_hf.hfinfo.blurb = NULL; + HFILL_INIT(new_hf); + g_array_append_vals(hf_col_array, &new_hf, 1); + } + } + g_free(used_fmts); + hf_cols_cleanup = hf_col_array->len; + + proto_register_field_array(proto_cols, (hf_register_info*)hf_col_array->data, hf_col_array->len); + hf_cols = (hf_register_info*)g_array_free(hf_col_array, FALSE); + } +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 2 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=2 tabstop=8 expandtab: + * :indentSize=2:tabSize=8:noTabs=true: + */ + |