X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=tshark.c;h=687b6c14d553e4bda781c6bcb93ce8dd23e31aed;hb=fdfa22979374b9790667232f33347a08c297aba4;hp=137a7dcead2918e23323a293458c90f9886ee335;hpb=037c64aa34d4196da5a39dfb59af16abceb1247b;p=metze%2Fwireshark%2Fwip.git diff --git a/tshark.c b/tshark.c index 137a7dcead..687b6c14d5 100644 --- a/tshark.c +++ b/tshark.c @@ -7,19 +7,7 @@ * By Gerald Combs * Copyright 1998 Gerald Combs * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * SPDX-License-Identifier: GPL-2.0+ */ #include @@ -36,6 +24,10 @@ #include +#ifdef _WIN32 +# include +#endif + #ifndef _WIN32 #include #endif @@ -51,7 +43,6 @@ #include #include -#include #include #include @@ -60,8 +51,8 @@ #include #include #include -#include -#include +#include +#include #include #include @@ -81,21 +72,23 @@ #ifdef HAVE_LIBPCAP #include "ui/capture_ui_utils.h" #endif +#include "ui/taps.h" #include "ui/util.h" -#include "ui/ui_util.h" +#include "ui/ws_ui_util.h" #include "ui/decode_as_utils.h" +#include "ui/filter_files.h" #include "ui/cli/tshark-tap.h" #include "ui/cli/tap-exportobject.h" #include "ui/tap_export_pdu.h" #include "ui/dissect_opts.h" +#include "ui/failure_message.h" #if defined(HAVE_LIBSMI) #include "epan/oids.h" #endif #if defined(HAVE_GEOIP) #include "epan/geoip_db.h" #endif -#include "register.h" -#include "filter_files.h" +#include "epan/register.h" #include #include #include @@ -134,6 +127,25 @@ #include #endif +/* Exit codes */ +#define INVALID_OPTION 1 +#define INVALID_INTERFACE 2 +#define INVALID_FILE 2 +#define INVALID_FILTER 2 +#define INVALID_EXPORT 2 +#define INVALID_CAPABILITY 2 +#define INVALID_TAP 2 +#define INVALID_DATA_LINK 2 +#define INVALID_TIMESTAMP_TYPE 2 +#define INVALID_CAPTURE 2 +#define INIT_FAILED 2 + +/* + * values 128..65535 are capture+dissect options, 65536 is used by + * ui/commandline.c, so start tshark-specific options 1000 after this + */ +#define LONGOPT_COLOR (65536+1000) +#define LONGOPT_NO_DUPLICATE_KEYS (65536+1001) #if 0 #define tshark_debug(...) g_warning(__VA_ARGS__) @@ -141,15 +153,16 @@ #define tshark_debug(...) #endif +capture_file cfile; + static guint32 cum_bytes; -static const frame_data *ref; static frame_data ref_frame; -static frame_data *prev_dis; static frame_data prev_dis_frame; -static frame_data *prev_cap; static frame_data prev_cap_frame; static gboolean perform_two_pass_analysis; +static guint32 epan_auto_reset_count = 0; +static gboolean epan_auto_reset = FALSE; /* * The way the packet decode is to be written. @@ -158,7 +171,8 @@ typedef enum { WRITE_TEXT, /* summary or detail text */ WRITE_XML, /* PDML or PSML */ WRITE_FIELDS, /* User defined list of fields */ - WRITE_JSON, /* JSON */ + WRITE_JSON, /* JSON */ + WRITE_JSON_RAW, /* JSON only raw hex */ WRITE_EK /* JSON bulk insert to Elasticsearch */ /* Add CSV and the like here */ } output_action_e; @@ -166,21 +180,29 @@ typedef enum { static output_action_e output_action; static gboolean do_dissection; /* TRUE if we have to dissect each packet */ static gboolean print_packet_info; /* TRUE if we're to print packet information */ -static gint print_summary = -1; /* TRUE if we're to print packet summary information */ +static gboolean print_summary; /* TRUE if we're to print packet summary information */ static gboolean print_details; /* TRUE if we're to print packet details information */ static gboolean print_hex; /* TRUE if we're to print hex/ascci information */ static gboolean line_buffered; static gboolean really_quiet = FALSE; +static gchar* delimiter_char = " "; +static gboolean dissect_color = FALSE; static print_format_e print_format = PR_FMT_TEXT; -static print_stream_t *print_stream; +static print_stream_t *print_stream = NULL; static output_fields_t* output_fields = NULL; static gchar **protocolfilter = NULL; +static pf_flags protocolfilter_flags = PF_NONE; + +static gboolean no_duplicate_keys = FALSE; +static proto_node_children_grouper_func node_children_grouper = proto_node_group_children_by_unique; /* The line separator used between packets, changeable via the -S option */ static const char *separator = ""; +static gboolean prefs_loaded = FALSE; + #ifdef HAVE_LIBPCAP /* * TRUE if we're to print packet counts to keep track of captured packets. @@ -213,27 +235,23 @@ static char *output_file_name; #endif /* HAVE_LIBPCAP */ -static int load_cap_file(capture_file *, char *, int, gboolean, int, gint64); -static gboolean process_packet(capture_file *cf, epan_dissect_t *edt, gint64 offset, - struct wtap_pkthdr *whdr, const guchar *pd, - guint tap_flags); -static void show_capture_file_io_error(const char *, int, gboolean); +static void reset_epan_mem(capture_file *cf, epan_dissect_t *edt, gboolean tree, gboolean visual); +static gboolean process_cap_file(capture_file *, char *, int, gboolean, int, gint64); +static gboolean process_packet_single_pass(capture_file *cf, + epan_dissect_t *edt, gint64 offset, struct wtap_pkthdr *whdr, + const guchar *pd, guint tap_flags); static void show_print_file_io_error(int err); static gboolean write_preamble(capture_file *cf); static gboolean print_packet(capture_file *cf, epan_dissect_t *edt); static gboolean write_finale(void); -static const char *cf_open_error_message(int err, gchar *err_info, - gboolean for_writing, int file_type); +static void failure_warning_message(const char *msg_format, va_list ap); static void open_failure_message(const char *filename, int err, gboolean for_writing); -static void failure_message(const char *msg_format, va_list ap); static void read_failure_message(const char *filename, int err); static void write_failure_message(const char *filename, int err); static void failure_message_cont(const char *msg_format, va_list ap); -capture_file cfile; - static GHashTable *output_only_tables = NULL; struct string_elem { @@ -310,7 +328,11 @@ print_usage(FILE *output) fprintf(output, "Capture interface:\n"); fprintf(output, " -i name or idx of interface (def: first non-loopback)\n"); fprintf(output, " -f packet filter in libpcap filter syntax\n"); - fprintf(output, " -s packet snapshot length (def: 65535)\n"); +#ifdef HAVE_PCAP_CREATE + fprintf(output, " -s packet snapshot length (def: appropriate maximum)\n"); +#else + fprintf(output, " -s packet snapshot length (def: %u)\n", WTAP_MAX_PACKET_SIZE_STANDARD); +#endif fprintf(output, " -p don't capture in promiscuous mode\n"); #ifdef HAVE_PCAP_CREATE fprintf(output, " -I capture in monitor mode, if available\n"); @@ -319,8 +341,10 @@ print_usage(FILE *output) fprintf(output, " -B size of kernel buffer (def: %dMB)\n", DEFAULT_CAPTURE_BUFFER_SIZE); #endif fprintf(output, " -y link layer type (def: first appropriate)\n"); + fprintf(output, " --time-stamp-type timestamp method for interface\n"); fprintf(output, " -D print list of interfaces and exit\n"); fprintf(output, " -L print list of link-layer types of iface and exit\n"); + fprintf(output, " --list-time-stamp-types print list of timestamp types for iface and exit\n"); fprintf(output, "\n"); fprintf(output, "Capture stop conditions:\n"); fprintf(output, " -c stop after n packets (def: infinite)\n"); @@ -330,6 +354,7 @@ print_usage(FILE *output) /*fprintf(output, "\n");*/ fprintf(output, "Capture output:\n"); fprintf(output, " -b ... duration:NUM - switch to next file after NUM secs\n"); + fprintf(output, " interval:NUM - create time intervals of NUM secs\n"); fprintf(output, " filesize:NUM - switch to next file after NUM KB\n"); fprintf(output, " files:NUM - ringbuffer: replace after NUM files\n"); #endif /* HAVE_LIBPCAP */ @@ -344,7 +369,9 @@ print_usage(FILE *output) fprintf(output, "\n"); fprintf(output, "Processing:\n"); fprintf(output, " -2 perform a two-pass analysis\n"); + fprintf(output, " -M perform session auto reset\n"); fprintf(output, " -R packet Read filter in Wireshark display filter syntax\n"); + fprintf(output, " (requires -2)\n"); fprintf(output, " -Y packet displaY filter in Wireshark display filter\n"); fprintf(output, " syntax\n"); fprintf(output, " -n disable all name resolutions (def: all enabled)\n"); @@ -354,6 +381,8 @@ print_usage(FILE *output) fprintf(output, " Example: tcp.port==8888,http\n"); fprintf(output, " -H read a list of entries from a hosts file, which will\n"); fprintf(output, " then be written to a capture file. (Implies -W n)\n"); + fprintf(output, " --enable-protocol \n"); + fprintf(output, " enable dissection of proto_name\n"); fprintf(output, " --disable-protocol \n"); fprintf(output, " disable dissection of proto_name\n"); fprintf(output, " --enable-heuristic \n"); @@ -374,10 +403,13 @@ print_usage(FILE *output) fprintf(output, " -P print packet summary even when writing to a file\n"); fprintf(output, " -S the line separator to print between packets\n"); fprintf(output, " -x add output of hex and ASCII dump (Packet Bytes)\n"); - fprintf(output, " -T pdml|ps|psml|json|ek|text|fields\n"); + fprintf(output, " -T pdml|ps|psml|json|jsonraw|ek|tabs|text|fields|?\n"); fprintf(output, " format of text output (def: text)\n"); - fprintf(output, " -j protocols layers filter if -T ek|pdml|json selected,\n"); - fprintf(output, " (e.g. \"http tcp ip\",\n"); + fprintf(output, " -j protocols layers filter if -T ek|pdml|json selected\n"); + fprintf(output, " (e.g. \"ip ip.flags text\", filter does not expand child\n"); + fprintf(output, " nodes, unless child is specified also in the filter)\n"); + fprintf(output, " -J top level protocol filter if -T ek|pdml|json selected\n"); + fprintf(output, " (e.g. \"http tcp\", filter which expands all child nodes)\n"); fprintf(output, " -e field to print if -Tfields selected (e.g. tcp.port,\n"); fprintf(output, " _ws.col.Info)\n"); fprintf(output, " this option can be repeated to print multiple fields\n"); @@ -389,7 +421,7 @@ print_usage(FILE *output) fprintf(output, " aggregator=,|/s| select comma, space, printable character as\n"); fprintf(output, " aggregator\n"); fprintf(output, " quote=d|s|n select double, single, no quotes for values\n"); - fprintf(output, " -t a|ad|d|dd|e|r|u|ud output format of time stamps (def: r: rel. to first)\n"); + fprintf(output, " -t a|ad|d|dd|e|r|u|ud|? output format of time stamps (def: r: rel. to first)\n"); fprintf(output, " -u s|hms output format of seconds (def: s: seconds)\n"); fprintf(output, " -l flush standard output after each packet\n"); fprintf(output, " -q be more quiet on stdout (e.g. when using statistics)\n"); @@ -405,6 +437,13 @@ print_usage(FILE *output) fprintf(output, " output file (only for pcapng)\n"); fprintf(output, " --export-objects , save exported objects for a protocol to\n"); fprintf(output, " a directory named \"destdir\"\n"); + fprintf(output, " --color color output text similarly to the Wireshark GUI,\n"); + fprintf(output, " requires a terminal with 24-bit color support\n"); + fprintf(output, " Also supplies color attributes to pdml and psml formats\n"); + fprintf(output, " (Note that attributes are nonstandard)\n"); + fprintf(output, " --no-duplicate-keys If -T json is specified, merge duplicate keys in an object\n"); + fprintf(output, " into a single key with as value a json array containing all\n"); + fprintf(output, " values"); fprintf(output, "\n"); fprintf(output, "Miscellaneous:\n"); @@ -414,13 +453,12 @@ print_usage(FILE *output) fprintf(output, " -K keytab file to use for kerberos decryption\n"); fprintf(output, " -G [report] dump one of several available reports and exit\n"); fprintf(output, " default report=\"fields\"\n"); - fprintf(output, " use \"-G ?\" for more help\n"); + fprintf(output, " use \"-G help\" for more help\n"); #ifdef __linux__ fprintf(output, "\n"); fprintf(output, "WARNING: dumpcap will enable kernel BPF JIT compiler if available.\n"); fprintf(output, "You might want to reset it\n"); fprintf(output, "By doing \"echo 0 > /proc/sys/net/core/bpf_jit_enable\"\n"); - fprintf(output, "\n"); #endif } @@ -462,7 +500,7 @@ tshark_log_handler (const gchar *log_domain, GLogLevelFlags log_level, { /* ignore log message, if log_level isn't interesting based upon the console log preferences. - If the preferences haven't been loaded loaded yet, display the + If the preferences haven't been loaded yet, display the message anyway. The default console_log_level preference value is such that only @@ -473,8 +511,7 @@ tshark_log_handler (const gchar *log_domain, GLogLevelFlags log_level, ERROR and CRITICAL level messages so the current code is a behavioral change. The current behavior is the same as in Wireshark. */ - if ((log_level & G_LOG_LEVEL_MASK & prefs.console_log_level) == 0 && - prefs.console_log_level != 0) { + if (prefs_loaded && (log_level & G_LOG_LEVEL_MASK & prefs.console_log_level) == 0) { return; } @@ -482,22 +519,6 @@ tshark_log_handler (const gchar *log_domain, GLogLevelFlags log_level, } -static char * -output_file_description(const char *fname) -{ - char *save_file_string; - - /* Get a string that describes what we're writing to */ - if (strcmp(fname, "-") == 0) { - /* We're writing to the standard output */ - save_file_string = g_strdup("standard output"); - } else { - /* We're writing to a file with the name in save_file */ - save_file_string = g_strdup_printf("file \"%s\"", fname); - } - return save_file_string; -} - static void print_current_user(void) { gchar *cur_user, *cur_group; @@ -575,16 +596,31 @@ about_folders(void) constpath = get_progfile_dir(); printf("%-21s\t%s\n", "Program:", constpath); -#if defined(HAVE_PLUGINS) || defined(HAVE_LUA) +#ifdef HAVE_PLUGINS /* pers plugins */ - path = get_plugins_pers_dir(); + printf("%-21s\t%s\n", "Personal Plugins:", get_plugins_pers_dir_with_version()); - printf("%-21s\t%s\n", "Personal Plugins:", path); + /* global plugins */ + printf("%-21s\t%s\n", "Global Plugins:", get_plugins_dir_with_version()); +#endif - g_free(path); +#ifdef HAVE_LUA + /* pers lua plugins */ + printf("%-21s\t%s\n", "Personal Lua Plugins:", get_plugins_pers_dir()); - /* global plugins */ - printf("%-21s\t%s\n", "Global Plugins:", get_plugin_dir()); + /* global lua plugins */ + printf("%-21s\t%s\n", "Global Lua Plugins:", get_plugins_dir()); +#endif + +#ifdef HAVE_EXTCAP + /* Extcap */ + constpath = get_extcap_dir(); + + resultArray = g_strsplit(constpath, G_SEARCHPATH_SEPARATOR_S, 10); + for(i = 0; resultArray[i]; i++) + printf("%-21s\t%s\n", "Extcap path:", g_strstrip(resultArray[i])); + + g_strfreev(resultArray); #endif #ifdef HAVE_GEOIP @@ -612,17 +648,6 @@ about_folders(void) g_free(path); #endif -#ifdef HAVE_EXTCAP - /* Extcap */ - constpath = get_extcap_dir(); - - resultArray = g_strsplit(constpath, G_SEARCHPATH_SEPARATOR_S, 10); - for(i = 0; resultArray[i]; i++) - printf("%-21s\t%s\n", "Extcap path:", g_strstrip(resultArray[i])); - - g_strfreev(resultArray); -#endif - } int @@ -637,29 +662,25 @@ main(int argc, char *argv[]) {"version", no_argument, NULL, 'v'}, LONGOPT_CAPTURE_COMMON LONGOPT_DISSECT_COMMON + {"print", no_argument, NULL, 'P'}, {"export-objects", required_argument, NULL, LONGOPT_EXPORT_OBJECTS}, + {"color", no_argument, NULL, LONGOPT_COLOR}, + {"no-duplicate-keys", no_argument, NULL, LONGOPT_NO_DUPLICATE_KEYS}, {0, 0, 0, 0 } }; gboolean arg_error = FALSE; #ifdef _WIN32 + int result; WSADATA wsaData; #endif /* _WIN32 */ - char *gpf_path, *pf_path; - char *gdp_path, *dp_path; - char *cf_path; - int gpf_open_errno, gpf_read_errno; - int pf_open_errno, pf_read_errno; - int gdp_open_errno, gdp_read_errno; - int dp_open_errno, dp_read_errno; - int cf_open_errno; int err; - volatile int exit_status = 0; + volatile gboolean success; + volatile int exit_status = EXIT_SUCCESS; #ifdef HAVE_LIBPCAP - gboolean list_link_layer_types = FALSE; + int caps_queries = 0; gboolean start_capture = FALSE; - int status; GList *if_list; gchar *err_str; #else @@ -686,6 +707,7 @@ main(int argc, char *argv[]) int log_flags; gchar *output_only = NULL; gchar *volatile pdu_export_arg = NULL; + const char *volatile exp_pdu_filename = NULL; exp_pdu_t exp_pdu_tap_data; /* @@ -702,12 +724,12 @@ main(int argc, char *argv[]) * * Glibc and Solaris libc document that a leading + disables permutation * of options, regardless of whether POSIXLY_CORRECT is set or not; *BSD - * and OS X don't document it, but do so anyway. + * and macOS don't document it, but do so anyway. * * We do *not* use a leading - because the behavior of a leading - is * platform-dependent. */ -#define OPTSTRING "+2" OPTSTRING_CAPTURE_COMMON OPTSTRING_DISSECT_COMMON "C:e:E:F:gG:hH:j:lo:O:PqQr:R:S:T:U:vVw:W:xX:Y:z:" +#define OPTSTRING "+2" OPTSTRING_CAPTURE_COMMON OPTSTRING_DISSECT_COMMON "M:C:e:E:F:gG:hH:j:J:lo:O:PqQr:R:S:T:U:vVw:W:xX:Y:z:" static const char optstring[] = OPTSTRING; @@ -716,7 +738,7 @@ main(int argc, char *argv[]) /* Set the C-language locale to the native environment. */ setlocale(LC_ALL, ""); - cmdarg_err_init(failure_message, failure_message_cont); + cmdarg_err_init(failure_warning_message, failure_message_cont); #ifdef _WIN32 arg_list_utf_16to8(argc, argv); @@ -753,6 +775,7 @@ main(int argc, char *argv[]) #ifdef _WIN32 ws_init_dll_search_path(); +#ifdef HAVE_LIBPCAP /* Load wpcap if possible. Do this before collecting the run-time version information */ load_wpcap(); @@ -761,7 +784,8 @@ main(int argc, char *argv[]) fprintf(stderr, "The NPF driver isn't running. You may have trouble " "capturing or\nlisting interfaces.\n"); } -#endif +#endif /* HAVE_LIBPCAP */ +#endif /* _WIN32 */ /* Get the compile-time version information string */ comp_info_str = get_compiled_version_info(get_tshark_compiled_version_info, @@ -804,7 +828,8 @@ main(int argc, char *argv[]) set_profile_name (optarg); } else { cmdarg_err("Configuration Profile \"%s\" does not exist", optarg); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } break; case 'P': /* Print packet summary info even when writing to a file */ @@ -833,15 +858,6 @@ main(int argc, char *argv[]) } } - /* - * Print packet summary information is the default, unless either -V or -x - * were specified and -P was not. Note that this is new behavior, which - * allows for the possibility of printing only hex/ascii output without - * necessarily requiring that either the summary or details be printed too. - */ - if (print_summary == -1) - print_summary = (print_details || print_hex) ? FALSE : TRUE; - /** Send All g_log messages to our own handler **/ log_flags = @@ -869,8 +885,9 @@ main(int argc, char *argv[]) tshark_log_handler, NULL /* user_data */); #endif - init_report_err(failure_message, open_failure_message, read_failure_message, - write_failure_message); + init_report_message(failure_warning_message, failure_warning_message, + open_failure_message, read_failure_message, + write_failure_message); #ifdef HAVE_LIBPCAP capture_opts_init(&global_capture_opts); @@ -883,25 +900,15 @@ main(int argc, char *argv[]) wtap_init(); -#ifdef HAVE_PLUGINS - /* Register all the plugin types we have. */ - epan_register_plugin_types(); /* Types known to libwireshark */ - - /* Scan for plugins. This does *not* call their registration routines; - that's done later. */ - scan_plugins(REPORT_LOAD_FAILURE); - - /* Register all libwiretap plugin modules. */ - register_all_wiretap_modules(); -#endif - /* Register all dissectors; we must do this before checking for the "-G" flag, as the "-G" flag dumps information registered by the dissectors, and we must do it before we read the preferences, in case any dissectors register preferences. */ if (!epan_init(register_all_protocols, register_all_protocol_handoffs, NULL, - NULL)) - return 2; + NULL)) { + exit_status = INIT_FAILED; + goto clean_exit; + } /* Register all tap listeners; we do this before we parse the arguments, as the "-z" argument can specify a registered tap. */ @@ -915,7 +922,10 @@ main(int argc, char *argv[]) #ifdef HAVE_EXTCAP extcap_register_preferences(); #endif - register_all_tap_listeners(); + /* Register all tap listeners. */ + for (tap_reg_t *t = tap_reg_listener; t->cb_func != NULL; t++) { + t->cb_func(); + } conversation_table_set_gui_info(init_iousers); hostlist_table_set_gui_info(init_hostlists); srt_table_iterate_tables(register_srt_tables, NULL); @@ -939,8 +949,7 @@ main(int argc, char *argv[]) if (strcmp(argv[2], "column-formats") == 0) column_dump_column_formats(); else if (strcmp(argv[2], "currentprefs") == 0) { - read_prefs(&gpf_open_errno, &gpf_read_errno, &gpf_path, - &pf_open_errno, &pf_read_errno, &pf_path); + epan_load_settings(); write_prefs(NULL); } else if (strcmp(argv[2], "decodes") == 0) @@ -951,7 +960,8 @@ main(int argc, char *argv[]) dissector_dump_dissector_tables(); else if (strcmp(argv[2], "fieldcount") == 0) { /* return value for the test suite */ - return proto_registrar_dump_fieldcount(); + exit_status = proto_registrar_dump_fieldcount(); + goto clean_exit; } else if (strcmp(argv[2], "fields") == 0) proto_registrar_dump_fields(); else if (strcmp(argv[2], "folders") == 0) @@ -972,95 +982,45 @@ main(int argc, char *argv[]) proto_registrar_dump_protocols(); else if (strcmp(argv[2], "values") == 0) proto_registrar_dump_values(); + else if (strcmp(argv[2], "help") == 0) + glossary_option_help(); + /* These are supported only for backwards compatibility and may or may not work + * for a given user in a given directory on a given operating system with a given + * command-line interpreter. + */ else if (strcmp(argv[2], "?") == 0) glossary_option_help(); else if (strcmp(argv[2], "-?") == 0) glossary_option_help(); else { - cmdarg_err("Invalid \"%s\" option for -G flag, enter -G ? for more help.", argv[2]); - return 1; + cmdarg_err("Invalid \"%s\" option for -G flag, enter -G help for more help.", argv[2]); + exit_status = INVALID_OPTION; + goto clean_exit; } } - return 0; + exit_status = EXIT_SUCCESS; + goto clean_exit; } - /* load the decode as entries of this profile */ - load_decode_as_entries(); - - tshark_debug("tshark reading preferences"); - - prefs_p = read_prefs(&gpf_open_errno, &gpf_read_errno, &gpf_path, - &pf_open_errno, &pf_read_errno, &pf_path); - if (gpf_path != NULL) { - if (gpf_open_errno != 0) { - cmdarg_err("Can't open global preferences file \"%s\": %s.", - pf_path, g_strerror(gpf_open_errno)); - } - if (gpf_read_errno != 0) { - cmdarg_err("I/O error reading global preferences file \"%s\": %s.", - pf_path, g_strerror(gpf_read_errno)); - } - } - if (pf_path != NULL) { - if (pf_open_errno != 0) { - cmdarg_err("Can't open your preferences file \"%s\": %s.", pf_path, - g_strerror(pf_open_errno)); - } - if (pf_read_errno != 0) { - cmdarg_err("I/O error reading your preferences file \"%s\": %s.", - pf_path, g_strerror(pf_read_errno)); - } - g_free(pf_path); - pf_path = NULL; - } + tshark_debug("tshark reading settings"); - read_filter_list(CFILTER_LIST, &cf_path, &cf_open_errno); - if (cf_path != NULL) { - cmdarg_err("Could not open your capture filter file\n\"%s\": %s.", - cf_path, g_strerror(cf_open_errno)); - g_free(cf_path); - } + /* Load libwireshark settings from the current profile. */ + prefs_p = epan_load_settings(); + prefs_loaded = TRUE; - /* Read the disabled protocols file. */ - read_disabled_protos_list(&gdp_path, &gdp_open_errno, &gdp_read_errno, - &dp_path, &dp_open_errno, &dp_read_errno); - read_disabled_heur_dissector_list(&gdp_path, &gdp_open_errno, &gdp_read_errno, - &dp_path, &dp_open_errno, &dp_read_errno); - if (gdp_path != NULL) { - if (gdp_open_errno != 0) { - cmdarg_err("Could not open global disabled protocols file\n\"%s\": %s.", - gdp_path, g_strerror(gdp_open_errno)); - } - if (gdp_read_errno != 0) { - cmdarg_err("I/O error reading global disabled protocols file\n\"%s\": %s.", - gdp_path, g_strerror(gdp_read_errno)); - } - g_free(gdp_path); - } - if (dp_path != NULL) { - if (dp_open_errno != 0) { - cmdarg_err( - "Could not open your disabled protocols file\n\"%s\": %s.", dp_path, - g_strerror(dp_open_errno)); - } - if (dp_read_errno != 0) { - cmdarg_err( - "I/O error reading your disabled protocols file\n\"%s\": %s.", dp_path, - g_strerror(dp_read_errno)); - } - g_free(dp_path); - } + read_filter_list(CFILTER_LIST); cap_file_init(&cfile); /* Print format defaults to this. */ print_format = PR_FMT_TEXT; + delimiter_char = " "; output_fields = output_fields_new(); /* * To reset the options parser, set optreset to 1 on platforms that - * have optreset (documented in *BSD and OS X, apparently present but + * have optreset (documented in *BSD and macOS, apparently present but * not documented in Solaris - the Illumos repository seems to * suggest that the first Solaris getopt_long(), at least as of 2004, * was based on the NetBSD one, it had optreset) and set optind to 1, @@ -1084,14 +1044,27 @@ main(int argc, char *argv[]) while ((opt = getopt_long(argc, argv, optstring, long_options, NULL)) != -1) { switch (opt) { case '2': /* Perform two pass analysis */ + if(epan_auto_reset){ + cmdarg_err("-2 does not support auto session reset."); + arg_error=TRUE; + } perform_two_pass_analysis = TRUE; break; + case 'M': + if(perform_two_pass_analysis){ + cmdarg_err("-M does not support two pass analysis."); + arg_error=TRUE; + } + epan_auto_reset_count = get_positive_int(optarg, "epan reset count"); + epan_auto_reset = TRUE; + break; case 'a': /* autostop criteria */ case 'b': /* Ringbuffer option */ case 'c': /* Capture x packets */ case 'f': /* capture filter */ case 'g': /* enable group read access on file(s) */ case 'i': /* Use interface x */ + case LONGOPT_SET_TSTAMP_TYPE: /* Set capture timestamp type */ case 'p': /* Don't capture in promiscuous mode */ #ifdef HAVE_PCAP_REMOTE case 'A': /* Authentication */ @@ -1107,9 +1080,9 @@ main(int argc, char *argv[]) case 'B': /* Buffer size */ #endif #ifdef HAVE_LIBPCAP - status = capture_opts_add_opt(&global_capture_opts, opt, optarg, &start_capture); - if (status != 0) { - return status; + exit_status = capture_opts_add_opt(&global_capture_opts, opt, optarg, &start_capture); + if (exit_status != 0) { + goto clean_exit; } #else if (opt == 'w') { @@ -1137,11 +1110,13 @@ main(int argc, char *argv[]) cmdarg_err("%s", err_str); g_free(err_str); } - return 2; + exit_status = INVALID_INTERFACE; + goto clean_exit; } capture_opts_print_interfaces(if_list); free_interface_list(if_list); - return 0; + exit_status = EXIT_SUCCESS; + goto clean_exit; #else capture_option_specified = TRUE; arg_error = TRUE; @@ -1156,7 +1131,8 @@ main(int argc, char *argv[]) if (!output_fields_set_option(output_fields, optarg)) { cmdarg_err("\"%s\" is not a valid field output option=value pair.", optarg); output_fields_list_options(stderr); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } break; case 'F': @@ -1164,12 +1140,17 @@ main(int argc, char *argv[]) if (out_file_type < 0) { cmdarg_err("\"%s\" isn't a valid capture file type", optarg); list_capture_types(); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } break; case 'j': protocolfilter = wmem_strsplit(wmem_epan_scope(), optarg, " ", -1); break; + case 'J': + protocolfilter_flags = PF_INCLUDE_CHILDREN; + protocolfilter = wmem_strsplit(wmem_epan_scope(), optarg, " ", -1); + break; case 'W': /* Select extra information to save in our capture file */ /* This is patterned after the -N flag which may not be the best idea. */ if (strchr(optarg, 'n')) { @@ -1177,14 +1158,16 @@ main(int argc, char *argv[]) } else { cmdarg_err("Invalid -W argument \"%s\"; it must be one of:", optarg); cmdarg_err_cont("\t'n' write network address resolution information (pcapng only)"); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } break; case 'H': /* Read address to name mappings from a hosts file */ if (! add_hosts_file(optarg)) { cmdarg_err("Can't read host entries from \"%s\"", optarg); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } out_file_name_res = TRUE; break; @@ -1195,49 +1178,77 @@ main(int argc, char *argv[]) "See https://www.wireshark.org for more information.\n", get_ws_vcs_version_info()); print_usage(stdout); - return 0; + exit_status = EXIT_SUCCESS; + goto clean_exit; break; case 'l': /* "Line-buffer" standard output */ - /* This isn't line-buffering, strictly speaking, it's just - flushing the standard output after the information for - each packet is printed; however, that should be good - enough for all the purposes to which "-l" is put (and - is probably actually better for "-V", as it does fewer - writes). - - See the comment in "process_packet()" for an explanation of - why we do that, and why we don't just use "setvbuf()" to - make the standard output line-buffered (short version: in - Windows, "line-buffered" is the same as "fully-buffered", - and the output buffer is only flushed when it fills up). */ + /* The ANSI C standard does not appear to *require* that a line-buffered + stream be flushed to the host environment whenever a newline is + written, it just says that, on such a stream, characters "are + intended to be transmitted to or from the host environment as a + block when a new-line character is encountered". + + The Visual C++ 6.0 C implementation doesn't do what is intended; + even if you set a stream to be line-buffered, it still doesn't + flush the buffer at the end of every line. + + The whole reason for the "-l" flag in either tcpdump or TShark + is to allow the output of a live capture to be piped to a program + or script and to have that script see the information for the + packet as soon as it's printed, rather than having to wait until + a standard I/O buffer fills up. + + So, if the "-l" flag is specified, we flush the standard output + at the end of a packet. This will do the right thing if we're + printing packet summary lines, and, as we print the entire protocol + tree for a single packet without waiting for anything to happen, + it should be as good as line-buffered mode if we're printing + protocol trees - arguably even better, as it may do fewer + writes. */ line_buffered = TRUE; break; case 'L': /* Print list of link-layer types and exit */ #ifdef HAVE_LIBPCAP - list_link_layer_types = TRUE; + caps_queries |= CAPS_QUERY_LINK_TYPES; +#else + capture_option_specified = TRUE; + arg_error = TRUE; +#endif + break; + case LONGOPT_LIST_TSTAMP_TYPES: /* List possible timestamp types */ +#ifdef HAVE_LIBPCAP + caps_queries |= CAPS_QUERY_TIMESTAMP_TYPES; #else capture_option_specified = TRUE; arg_error = TRUE; #endif break; case 'o': /* Override preference from command line */ - switch (prefs_set_pref(optarg)) { + { + char *errmsg = NULL; + + switch (prefs_set_pref(optarg, &errmsg)) { case PREFS_SET_OK: break; case PREFS_SET_SYNTAX_ERR: - cmdarg_err("Invalid -o flag \"%s\"", optarg); - return 1; + cmdarg_err("Invalid -o flag \"%s\"%s%s", optarg, + errmsg ? ": " : "", errmsg ? errmsg : ""); + g_free(errmsg); + exit_status = INVALID_OPTION; + goto clean_exit; break; case PREFS_SET_NO_SUCH_PREF: case PREFS_SET_OBSOLETE: cmdarg_err("-o flag \"%s\" specifies unknown preference", optarg); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; break; } break; + } case 'q': /* Quiet */ quiet = TRUE; break; @@ -1262,6 +1273,10 @@ main(int argc, char *argv[]) if (strcmp(optarg, "text") == 0) { output_action = WRITE_TEXT; print_format = PR_FMT_TEXT; + } else if (strcmp(optarg, "tabs") == 0) { + output_action = WRITE_TEXT; + print_format = PR_FMT_TEXT; + delimiter_char = "\t"; } else if (strcmp(optarg, "ps") == 0) { output_action = WRITE_TEXT; print_format = PR_FMT_PS; @@ -1283,34 +1298,45 @@ main(int argc, char *argv[]) print_summary = FALSE; /* Don't allow summary */ } else if (strcmp(optarg, "ek") == 0) { output_action = WRITE_EK; + if (!print_summary) + print_details = TRUE; + } else if (strcmp(optarg, "jsonraw") == 0) { + output_action = WRITE_JSON_RAW; print_details = TRUE; /* Need details */ print_summary = FALSE; /* Don't allow summary */ } else { cmdarg_err("Invalid -T parameter \"%s\"; it must be one of:", optarg); /* x */ - cmdarg_err_cont("\t\"fields\" The values of fields specified with the -e option, in a form\n" - "\t specified by the -E option.\n" - "\t\"pdml\" Packet Details Markup Language, an XML-based format for the\n" - "\t details of a decoded packet. This information is equivalent to\n" - "\t the packet details printed with the -V flag.\n" - "\t\"ps\" PostScript for a human-readable one-line summary of each of\n" - "\t the packets, or a multi-line view of the details of each of\n" - "\t the packets, depending on whether the -V flag was specified.\n" - "\t\"psml\" Packet Summary Markup Language, an XML-based format for the\n" - "\t summary information of a decoded packet. This information is\n" - "\t equivalent to the information shown in the one-line summary\n" - "\t printed by default.\n" - "\t\"json\" Packet Summary, an JSON-based format for the details\n" - "\t summary information of a decoded packet. This information is \n" - "\t equivalent to the packet details printed with the -V flag.\n" - "\t\"ek\" Packet Summary, an EK JSON-based format for the bulk insert \n" - "\t into elastic search cluster. This information is \n" - "\t equivalent to the packet details printed with the -V flag.\n" - "\t\"text\" Text of a human-readable one-line summary of each of the\n" - "\t packets, or a multi-line view of the details of each of the\n" - "\t packets, depending on whether the -V flag was specified.\n" - "\t This is the default."); - return 1; + cmdarg_err_cont("\t\"fields\" The values of fields specified with the -e option, in a form\n" + "\t specified by the -E option.\n" + "\t\"pdml\" Packet Details Markup Language, an XML-based format for the\n" + "\t details of a decoded packet. This information is equivalent to\n" + "\t the packet details printed with the -V flag.\n" + "\t\"ps\" PostScript for a human-readable one-line summary of each of\n" + "\t the packets, or a multi-line view of the details of each of\n" + "\t the packets, depending on whether the -V flag was specified.\n" + "\t\"psml\" Packet Summary Markup Language, an XML-based format for the\n" + "\t summary information of a decoded packet. This information is\n" + "\t equivalent to the information shown in the one-line summary\n" + "\t printed by default.\n" + "\t\"json\" Packet Summary, an JSON-based format for the details\n" + "\t summary information of a decoded packet. This information is \n" + "\t equivalent to the packet details printed with the -V flag.\n" + "\t\"jsonraw\" Packet Details, a JSON-based format for machine parsing\n" + "\t including only raw hex decoded fields (same as -T json -x but\n" + "\t without text decoding, only raw fields included). \n" + "\t\"ek\" Packet Details, an EK JSON-based format for the bulk insert \n" + "\t into elastic search cluster. This information is \n" + "\t equivalent to the packet details printed with the -V flag.\n" + "\t\"text\" Text of a human-readable one-line summary of each of the\n" + "\t packets, or a multi-line view of the details of each of the\n" + "\t packets, depending on whether the -V flag was specified.\n" + "\t This is the default.\n" + "\t\"tabs\" Similar to the text report except that each column of the\n" + "\t human-readable one-line summary is delimited with an ASCII\n" + "\t horizontal tab character."); + exit_status = INVALID_OPTION; + goto clean_exit; } break; case 'U': /* Export PDUs to file */ @@ -1318,11 +1344,12 @@ main(int argc, char *argv[]) GSList *export_pdu_tap_name_list = NULL; if (!*optarg) { - cmdarg_err("Tap name is required! Valid names are:"); + cmdarg_err("A tap name is required. Valid names are:"); for (export_pdu_tap_name_list = get_export_pdu_tap_list(); export_pdu_tap_name_list; export_pdu_tap_name_list = g_slist_next(export_pdu_tap_name_list)) { cmdarg_err("%s\n", (const char*)(export_pdu_tap_name_list->data)); } - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } pdu_export_arg = g_strdup(optarg); break; @@ -1343,7 +1370,8 @@ main(int argc, char *argv[]) #ifdef HAVE_EXTCAP extcap_cleanup(); #endif - return 0; + exit_status = EXIT_SUCCESS; + goto clean_exit; case 'O': /* Only output these protocols */ /* already processed; just ignore it now */ break; @@ -1368,12 +1396,14 @@ main(int argc, char *argv[]) if (strcmp("help", optarg) == 0) { fprintf(stderr, "tshark: The available statistics for the \"-z\" option are:\n"); list_stat_cmd_args(); - return 0; + exit_status = EXIT_SUCCESS; + goto clean_exit; } if (!process_stat_cmd_arg(optarg)) { cmdarg_err("Invalid -z argument \"%s\"; it must be one of:", optarg); list_stat_cmd_args(); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } break; case 'd': /* Decode as rule */ @@ -1385,17 +1415,30 @@ main(int argc, char *argv[]) case LONGOPT_DISABLE_PROTOCOL: /* disable dissection of protocol */ case LONGOPT_ENABLE_HEURISTIC: /* enable heuristic dissection of protocol */ case LONGOPT_DISABLE_HEURISTIC: /* disable heuristic dissection of protocol */ - if (!dissect_opts_handle_opt(opt, optarg)) - return 1; + case LONGOPT_ENABLE_PROTOCOL: /* enable dissection of protocol (that is disabled by default) */ + if (!dissect_opts_handle_opt(opt, optarg)) { + exit_status = INVALID_OPTION; + goto clean_exit; + } break; case LONGOPT_EXPORT_OBJECTS: /* --export-objects */ if (strcmp("help", optarg) == 0) { fprintf(stderr, "tshark: The available export object types for the \"--export-objects\" option are:\n"); eo_list_object_types(); - return 0; + exit_status = EXIT_SUCCESS; + goto clean_exit; } - if (!eo_tap_opt_add(optarg)) - return 1; + if (!eo_tap_opt_add(optarg)) { + exit_status = INVALID_OPTION; + goto clean_exit; + } + break; + case LONGOPT_COLOR: /* print in color where appropriate */ + dissect_color = TRUE; + break; + case LONGOPT_NO_DUPLICATE_KEYS: + no_duplicate_keys = TRUE; + node_children_grouper = proto_node_group_children_by_json_key; break; default: case '?': /* Bad flag - print usage message */ @@ -1406,21 +1449,46 @@ main(int argc, char *argv[]) default: print_usage(stderr); } - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; break; } } + /* + * Print packet summary information is the default if neither -V or -x + * were specified. Note that this is new behavior, which allows for the + * possibility of printing only hex/ascii output without necessarily + * requiring that either the summary or details be printed too. + */ + if (!print_summary && !print_details && !print_hex) + print_summary = TRUE; + + if (no_duplicate_keys && output_action != WRITE_JSON && output_action != WRITE_JSON_RAW) { + cmdarg_err("--no-duplicate-keys can only be used with \"-T json\" and \"-T jsonraw\""); + exit_status = INVALID_OPTION; + goto clean_exit; + } + /* If we specified output fields, but not the output field type... */ if ((WRITE_FIELDS != output_action && WRITE_XML != output_action && WRITE_JSON != output_action && WRITE_EK != output_action) && 0 != output_fields_num_fields(output_fields)) { cmdarg_err("Output fields were specified with \"-e\", " "but \"-Tek, -Tfields, -Tjson or -Tpdml\" was not specified."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } else if (WRITE_FIELDS == output_action && 0 == output_fields_num_fields(output_fields)) { cmdarg_err("\"-Tfields\" was specified, but no fields were " "specified with \"-e\"."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; + } + + if (dissect_color) { + if (!color_filters_init(&err_msg, NULL)) { + fprintf(stderr, "%s\n", err_msg); + g_free(err_msg); + } } /* If no capture filter or display filter has been specified, and there are @@ -1432,7 +1500,8 @@ main(int argc, char *argv[]) if (dfilter != NULL) { cmdarg_err("Display filters were specified both with \"-d\" " "and with additional command-line arguments."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } dfilter = get_args_as_string(argc, argv, optind); } else { @@ -1442,19 +1511,19 @@ main(int argc, char *argv[]) if (global_capture_opts.default_options.cfilter) { cmdarg_err("A default capture filter was specified both with \"-f\"" " and with additional command-line arguments."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } for (i = 0; i < global_capture_opts.ifaces->len; i++) { - interface_options interface_opts; - interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, i); - if (interface_opts.cfilter == NULL) { - interface_opts.cfilter = get_args_as_string(argc, argv, optind); - global_capture_opts.ifaces = g_array_remove_index(global_capture_opts.ifaces, i); - g_array_insert_val(global_capture_opts.ifaces, i, interface_opts); + interface_options *interface_opts; + interface_opts = &g_array_index(global_capture_opts.ifaces, interface_options, i); + if (interface_opts->cfilter == NULL) { + interface_opts->cfilter = get_args_as_string(argc, argv, optind); } else { cmdarg_err("A capture filter was specified both with \"-f\"" " and with additional command-line arguments."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } } global_capture_opts.default_options.cfilter = get_args_as_string(argc, argv, optind); @@ -1479,7 +1548,8 @@ main(int argc, char *argv[]) if (strcmp(global_capture_opts.save_file, "-") == 0 && print_packet_info) { cmdarg_err("You can't write both raw packet data and dissected packets" " to the standard output."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } } #else @@ -1495,13 +1565,15 @@ main(int argc, char *argv[]) #endif if (arg_error) { print_usage(stderr); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } if (print_hex) { - if (output_action != WRITE_TEXT && output_action != WRITE_JSON && output_action != WRITE_EK) { - cmdarg_err("Raw packet hex data can only be printed as text, PostScript, JSON or EK JSON"); - return 1; + if (output_action != WRITE_TEXT && output_action != WRITE_JSON && output_action != WRITE_JSON_RAW && output_action != WRITE_EK) { + cmdarg_err("Raw packet hex data can only be printed as text, PostScript, JSON, JSONRAW or EK JSON"); + exit_status = INVALID_OPTION; + goto clean_exit; } } @@ -1510,7 +1582,8 @@ main(int argc, char *argv[]) if (!print_details) { cmdarg_err("-O requires -V"); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } output_only_tables = g_hash_table_new (g_str_hash, g_str_equal); @@ -1521,22 +1594,26 @@ main(int argc, char *argv[]) if (rfilter != NULL && !perform_two_pass_analysis) { cmdarg_err("-R without -2 is deprecated. For single-pass filtering use -Y."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } #ifdef HAVE_LIBPCAP - if (list_link_layer_types) { - /* We're supposed to list the link-layer types for an interface; + if (caps_queries) { + /* We're supposed to list the link-layer/timestamp types for an interface; did the user also specify a capture file to be read? */ if (cf_name) { /* Yes - that's bogus. */ - cmdarg_err("You can't specify -L and a capture file to be read."); - return 1; + cmdarg_err("You can't specify %s and a capture file to be read.", + caps_queries & CAPS_QUERY_LINK_TYPES ? "-L" : "--list-time-stamp-types"); + exit_status = INVALID_OPTION; + goto clean_exit; } /* No - did they specify a ring buffer option? */ if (global_capture_opts.multi_files_on) { cmdarg_err("Ring buffer requested, but a capture isn't being done."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } } else { if (cf_name) { @@ -1551,33 +1628,45 @@ main(int argc, char *argv[]) if (global_capture_opts.default_options.cfilter) { cmdarg_err("Only read filters, not capture filters, " "can be specified when reading a capture file."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } if (global_capture_opts.multi_files_on) { cmdarg_err("Multiple capture files requested, but " "a capture isn't being done."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } if (global_capture_opts.has_file_duration) { + cmdarg_err("Switching capture files after a time period was specified, but " + "a capture isn't being done."); + exit_status = INVALID_OPTION; + goto clean_exit; + } + if (global_capture_opts.has_file_interval) { cmdarg_err("Switching capture files after a time interval was specified, but " "a capture isn't being done."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } if (global_capture_opts.has_ring_num_files) { cmdarg_err("A ring buffer of capture files was specified, but " "a capture isn't being done."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } if (global_capture_opts.has_autostop_files) { cmdarg_err("A maximum number of capture files was specified, but " "a capture isn't being done."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } if (global_capture_opts.capture_comment) { cmdarg_err("A capture comment was specified, but " "a capture isn't being done.\nThere's no support for adding " "a capture comment to an existing capture file."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } /* Note: TShark now allows the restriction of a _read_ file by packet count @@ -1587,7 +1676,8 @@ main(int argc, char *argv[]) if (global_capture_opts.has_autostop_duration) { cmdarg_err("A maximum capture time was specified, but " "a capture isn't being done."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } } else { /* @@ -1598,7 +1688,8 @@ main(int argc, char *argv[]) * to buffer packets until we've read all of them, but a live capture * has no useful/meaningful definition of "all" */ cmdarg_err("Live captures do not support two-pass analysis."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } if (global_capture_opts.saving_to_file) { @@ -1608,12 +1699,14 @@ main(int argc, char *argv[]) if (out_file_type != WTAP_FILE_TYPE_SUBTYPE_PCAP && out_file_type != WTAP_FILE_TYPE_SUBTYPE_PCAPNG) { cmdarg_err("Live captures can only be saved in pcap or pcapng format."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } if (global_capture_opts.capture_comment && out_file_type != WTAP_FILE_TYPE_SUBTYPE_PCAPNG) { cmdarg_err("A capture comment can only be written to a pcapng file."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } if (global_capture_opts.multi_files_on) { /* Multiple-file mode doesn't work under certain conditions: @@ -1623,29 +1716,35 @@ main(int argc, char *argv[]) if (strcmp(global_capture_opts.save_file, "-") == 0) { cmdarg_err("Multiple capture files requested, but " "the capture is being written to the standard output."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } if (global_capture_opts.output_to_pipe) { cmdarg_err("Multiple capture files requested, but " "the capture file is a pipe."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } if (!global_capture_opts.has_autostop_filesize && - !global_capture_opts.has_file_duration) { + !global_capture_opts.has_file_duration && + !global_capture_opts.has_file_interval) { cmdarg_err("Multiple capture files requested, but " - "no maximum capture file size or duration was specified."); - return 1; + "no maximum capture file size, duration or interval was specified."); + exit_status = INVALID_OPTION; + goto clean_exit; } } /* Currently, we don't support read or display filters when capturing and saving the packets. */ if (rfilter != NULL) { cmdarg_err("Read filters aren't supported when capturing and saving the captured packets."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } if (dfilter != NULL) { cmdarg_err("Display filters aren't supported when capturing and saving the captured packets."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } global_capture_opts.use_pcapng = (out_file_type == WTAP_FILE_TYPE_SUBTYPE_PCAPNG) ? TRUE : FALSE; } else { @@ -1655,17 +1754,20 @@ main(int argc, char *argv[]) if (global_capture_opts.has_autostop_filesize) { cmdarg_err("Maximum capture file size specified, but " "capture isn't being saved to a file."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } if (global_capture_opts.multi_files_on) { cmdarg_err("Multiple capture files requested, but " "the capture isn't being saved to a file."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } if (global_capture_opts.capture_comment) { cmdarg_err("A capture comment was specified, but " "the capture isn't being saved to a file."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } } } @@ -1674,7 +1776,12 @@ main(int argc, char *argv[]) #ifdef _WIN32 /* Start windows sockets */ - WSAStartup( MAKEWORD( 1, 1 ), &wsaData ); + result = WSAStartup( MAKEWORD( 1, 1 ), &wsaData ); + if (result != 0) + { + exit_status = INIT_FAILED; + goto clean_exit; + } #endif /* _WIN32 */ /* Notify all registered modules that have had any of their preferences @@ -1703,7 +1810,8 @@ main(int argc, char *argv[]) cmdarg_err_cont("\t%s", (gchar *)it->data); } g_slist_free(invalid_fields); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } } #ifdef HAVE_LIBPCAP @@ -1713,11 +1821,13 @@ main(int argc, char *argv[]) global_capture_opts.output_to_pipe) { if (tap_listeners_require_dissection()) { cmdarg_err("Taps aren't supported when saving to a pipe."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } if (print_packet_info) { cmdarg_err("Printing dissected packets isn't supported when saving to a pipe."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } } #endif @@ -1728,40 +1838,20 @@ main(int argc, char *argv[]) if (in_file_type == WTAP_TYPE_AUTO) { cmdarg_err("\"%s\" isn't a valid read file format type", name? name : ""); list_read_capture_types(); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } } timestamp_set_type(global_dissect_options.time_format); - /* disabled protocols as per configuration file */ - if (gdp_path == NULL && dp_path == NULL) { - set_disabled_protos_list(); - set_disabled_heur_dissector_list(); - } - - if(global_dissect_options.disable_protocol_slist) { - GSList *proto_disable; - for (proto_disable = global_dissect_options.disable_protocol_slist; proto_disable != NULL; proto_disable = g_slist_next(proto_disable)) - { - proto_disable_proto_by_name((char*)proto_disable->data); - } - } - - if(global_dissect_options.enable_heur_slist) { - GSList *heur_enable; - for (heur_enable = global_dissect_options.enable_heur_slist; heur_enable != NULL; heur_enable = g_slist_next(heur_enable)) - { - proto_enable_heuristic_by_name((char*)heur_enable->data, TRUE); - } - } - - if(global_dissect_options.disable_heur_slist) { - GSList *heur_disable; - for (heur_disable = global_dissect_options.disable_heur_slist; heur_disable != NULL; heur_disable = g_slist_next(heur_disable)) - { - proto_enable_heuristic_by_name((char*)heur_disable->data, FALSE); - } + /* + * Enabled and disabled protocols and heuristic dissectors as per + * command-line options. + */ + if (!setup_enabled_and_disabled_protocols()) { + exit_status = INVALID_OPTION; + goto clean_exit; } /* Build the column format array */ @@ -1796,7 +1886,8 @@ main(int argc, char *argv[]) } } #endif - return 2; + exit_status = INVALID_INTERFACE; + goto clean_exit; } } cfile.rfcode = rfcode; @@ -1825,7 +1916,8 @@ main(int argc, char *argv[]) } } #endif - return 2; + exit_status = INVALID_FILTER; + goto clean_exit; } } cfile.dfcode = dfcode; @@ -1853,15 +1945,16 @@ main(int argc, char *argv[]) /* PDU export requested. Take the ownership of the '-w' file, apply tap * filters and start tapping. */ if (pdu_export_arg) { - const char *exp_pdu_filename; const char *exp_pdu_tap_name = pdu_export_arg; const char *exp_pdu_filter = dfilter; /* may be NULL to disable filter */ char *exp_pdu_error; int exp_fd; + char *comment; if (!cf_name) { cmdarg_err("PDUs export requires a capture file (specify with -r)."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } /* Take ownership of the '-w' output file. */ #ifdef HAVE_LIBPCAP @@ -1873,7 +1966,8 @@ main(int argc, char *argv[]) #endif if (exp_pdu_filename == NULL) { cmdarg_err("PDUs export requires an output file (-w)."); - return 1; + exit_status = INVALID_OPTION; + goto clean_exit; } exp_pdu_error = exp_pdu_pre_open(exp_pdu_tap_name, exp_pdu_filter, @@ -1881,21 +1975,26 @@ main(int argc, char *argv[]) if (exp_pdu_error) { cmdarg_err("Cannot register tap: %s", exp_pdu_error); g_free(exp_pdu_error); - return 2; + exit_status = INVALID_TAP; + goto clean_exit; } exp_fd = ws_open(exp_pdu_filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); if (exp_fd == -1) { cmdarg_err("%s: %s", exp_pdu_filename, file_open_error_message(errno, TRUE)); - return 2; + exit_status = INVALID_FILE; + goto clean_exit; } /* Activate the export PDU tap */ - err = exp_pdu_open(&exp_pdu_tap_data, exp_fd, - g_strdup_printf("Dump of PDUs from %s", cf_name)); + comment = g_strdup_printf("Dump of PDUs from %s", cf_name); + err = exp_pdu_open(&exp_pdu_tap_data, exp_fd, comment); if (err != 0) { - cmdarg_err("Failed to start the PDU export: %s", g_strerror(err)); - return 2; + cfile_dump_open_failure_message("TShark", exp_pdu_filename, err, + WTAP_FILE_TYPE_SUBTYPE_PCAPNG); + g_free(comment); + exit_status = INVALID_EXPORT; + goto clean_exit; } } @@ -1911,7 +2010,7 @@ main(int argc, char *argv[]) we're using any taps that need dissection. */ do_dissection = print_packet_info || rfcode || dfcode || pdu_export_arg || - tap_listeners_require_dissection(); + tap_listeners_require_dissection() || dissect_color; tshark_debug("tshark: do_dissection = %s", do_dissection ? "TRUE" : "FALSE"); if (cf_name) { @@ -1924,18 +2023,19 @@ main(int argc, char *argv[]) #ifdef HAVE_EXTCAP extcap_cleanup(); #endif - return 2; + exit_status = INVALID_FILE; + goto clean_exit; } /* Process the packets in the file */ - tshark_debug("tshark: invoking load_cap_file() to process the packets"); + tshark_debug("tshark: invoking process_cap_file() to process the packets"); TRY { #ifdef HAVE_LIBPCAP - err = load_cap_file(&cfile, global_capture_opts.save_file, out_file_type, out_file_name_res, + success = process_cap_file(&cfile, global_capture_opts.save_file, out_file_type, out_file_name_res, global_capture_opts.has_autostop_packets ? global_capture_opts.autostop_packets : 0, global_capture_opts.has_autostop_filesize ? global_capture_opts.autostop_filesize : 0); #else - err = load_cap_file(&cfile, output_file_name, out_file_type, out_file_name_res, 0, 0); + success = process_cap_file(&cfile, output_file_name, out_file_type, out_file_name_res, 0, 0); #endif } CATCH(OutOfMemoryError) { @@ -1946,10 +2046,11 @@ main(int argc, char *argv[]) "\n" "More information and workarounds can be found at\n" "https://wiki.wireshark.org/KnownBugs/OutOfMemory\n"); - err = ENOMEM; + success = FALSE; } ENDTRY; - if (err != 0) { + + if (!success) { /* We still dump out the results of taps, etc., as we might have read some packets; however, we exit with an error status. */ exit_status = 2; @@ -1958,7 +2059,7 @@ main(int argc, char *argv[]) if (pdu_export_arg) { err = exp_pdu_close(&exp_pdu_tap_data); if (err) { - cmdarg_err("%s", wtap_strerror(err)); + cfile_close_failure_message(exp_pdu_filename, err); exit_status = 2; } g_free(pdu_export_arg); @@ -1972,40 +2073,52 @@ main(int argc, char *argv[]) /* if no interface was specified, pick a default */ exit_status = capture_opts_default_iface_if_necessary(&global_capture_opts, ((prefs_p->capture_device) && (*prefs_p->capture_device != '\0')) ? get_if_name(prefs_p->capture_device) : NULL); - if (exit_status != 0) - return exit_status; + if (exit_status != 0) { + goto clean_exit; + } /* if requested, list the link layer types and exit */ - if (list_link_layer_types) { + if (caps_queries) { guint i; /* Get the list of link-layer types for the capture devices. */ for (i = 0; i < global_capture_opts.ifaces->len; i++) { - interface_options interface_opts; + interface_options *interface_opts; if_capabilities_t *caps; char *auth_str = NULL; + int if_caps_queries = caps_queries; - interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, i); + interface_opts = &g_array_index(global_capture_opts.ifaces, interface_options, i); #ifdef HAVE_PCAP_REMOTE - if (interface_opts.auth_type == CAPTURE_AUTH_PWD) { - auth_str = g_strdup_printf("%s:%s", interface_opts.auth_username, interface_opts.auth_password); + if (interface_opts->auth_type == CAPTURE_AUTH_PWD) { + auth_str = g_strdup_printf("%s:%s", interface_opts->auth_username, interface_opts->auth_password); } #endif - caps = capture_get_if_capabilities(interface_opts.name, interface_opts.monitor_mode, auth_str, &err_str, NULL); + caps = capture_get_if_capabilities(interface_opts->name, interface_opts->monitor_mode, auth_str, &err_str, NULL); g_free(auth_str); if (caps == NULL) { cmdarg_err("%s", err_str); g_free(err_str); - return 2; + exit_status = INVALID_CAPABILITY; + goto clean_exit; + } + if ((if_caps_queries & CAPS_QUERY_LINK_TYPES) && caps->data_link_types == NULL) { + cmdarg_err("The capture device \"%s\" has no data link types.", interface_opts->name); + exit_status = INVALID_DATA_LINK; + goto clean_exit; } - if (caps->data_link_types == NULL) { - cmdarg_err("The capture device \"%s\" has no data link types.", interface_opts.name); - return 2; + if ((if_caps_queries & CAPS_QUERY_TIMESTAMP_TYPES) && caps->timestamp_types == NULL) { + cmdarg_err("The capture device \"%s\" has no timestamp types.", interface_opts->name); + exit_status = INVALID_TIMESTAMP_TYPE; + goto clean_exit; } - capture_opts_print_if_capabilities(caps, interface_opts.name, interface_opts.monitor_mode); + if (interface_opts->monitor_mode) + if_caps_queries |= CAPS_MONITOR_MODE; + capture_opts_print_if_capabilities(caps, interface_opts->name, if_caps_queries); free_if_capabilities(caps); } - return 0; + exit_status = EXIT_SUCCESS; + goto clean_exit; } /* @@ -2043,7 +2156,8 @@ main(int argc, char *argv[]) if (print_packet_info) { if (!write_preamble(&cfile)) { show_print_file_io_error(errno); - return 2; + exit_status = INVALID_FILE; + goto clean_exit; } } @@ -2063,22 +2177,22 @@ main(int argc, char *argv[]) if (print_packet_info) { if (!write_finale()) { - err = errno; - show_print_file_io_error(err); + show_print_file_io_error(errno); } } #else /* No - complain. */ cmdarg_err("This version of TShark was not built with support for capturing packets."); - return 2; + exit_status = INVALID_CAPTURE; + goto clean_exit; #endif } g_free(cf_name); - if (cfile.frames != NULL) { - free_frame_data_sequence(cfile.frames); - cfile.frames = NULL; + if (cfile.provider.frames != NULL) { + free_frame_data_sequence(cfile.provider.frames); + cfile.provider.frames = NULL; } draw_tap_listeners(TRUE); @@ -2092,6 +2206,16 @@ main(int argc, char *argv[]) output_fields_free(output_fields); output_fields = NULL; +clean_exit: + destroy_print_stream(print_stream); +#ifdef HAVE_LIBPCAP + capture_opts_cleanup(&global_capture_opts); +#endif + col_cleanup(&cfile.cinfo); + free_filter_lists(); + wtap_cleanup(); + free_progdirs(); + cf_close(&cfile); return exit_status; } @@ -2207,21 +2331,19 @@ pipe_input_set_handler(gint source, gpointer user_data, ws_process_id *child_pro } static const nstime_t * -tshark_get_frame_ts(void *data, guint32 frame_num) +tshark_get_frame_ts(struct packet_provider_data *prov, guint32 frame_num) { - capture_file *cf = (capture_file *) data; - - if (ref && ref->num == frame_num) - return &ref->abs_ts; + if (prov->ref && prov->ref->num == frame_num) + return &prov->ref->abs_ts; - if (prev_dis && prev_dis->num == frame_num) - return &prev_dis->abs_ts; + if (prov->prev_dis && prov->prev_dis->num == frame_num) + return &prov->prev_dis->abs_ts; - if (prev_cap && prev_cap->num == frame_num) - return &prev_cap->abs_ts; + if (prov->prev_cap && prov->prev_cap->num == frame_num) + return &prov->prev_cap->abs_ts; - if (cf->frames) { - frame_data *fd = frame_data_sequence_find(cf->frames, frame_num); + if (prov->frames) { + frame_data *fd = frame_data_sequence_find(prov->frames, frame_num); return (fd) ? &fd->abs_ts : NULL; } @@ -2232,14 +2354,14 @@ tshark_get_frame_ts(void *data, guint32 frame_num) static epan_t * tshark_epan_new(capture_file *cf) { - epan_t *epan = epan_new(); - - epan->data = cf; - epan->get_frame_ts = tshark_get_frame_ts; - epan->get_interface_name = cap_file_get_interface_name; - epan->get_user_comment = NULL; + static const struct packet_provider_funcs funcs = { + tshark_get_frame_ts, + cap_file_provider_get_interface_name, + cap_file_provider_get_interface_description, + NULL, + }; - return epan; + return epan_new(&cf->provider, &funcs); } #ifdef HAVE_LIBPCAP @@ -2300,12 +2422,10 @@ capture(void) /* Let the user know which interfaces were chosen. */ for (i = 0; i < global_capture_opts.ifaces->len; i++) { - interface_options interface_opts; + interface_options *interface_opts; - interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, i); - interface_opts.descr = get_interface_descriptive_name(interface_opts.name); - global_capture_opts.ifaces = g_array_remove_index(global_capture_opts.ifaces, i); - g_array_insert_val(global_capture_opts.ifaces, i, interface_opts); + interface_opts = &g_array_index(global_capture_opts.ifaces, interface_options, i); + interface_opts->descr = get_interface_descriptive_name(interface_opts->name); } str = get_iface_list_string(&global_capture_opts, IFLIST_QUOTE_IF_DESCRIPTION); if (really_quiet == FALSE) @@ -2395,12 +2515,12 @@ capture_input_cfilter_error_message(capture_session *cap_session, guint i, char { capture_options *capture_opts = cap_session->capture_opts; dfilter_t *rfcode = NULL; - interface_options interface_opts; + interface_options *interface_opts; g_assert(i < capture_opts->ifaces->len); - interface_opts = g_array_index(capture_opts->ifaces, interface_options, i); + interface_opts = &g_array_index(capture_opts->ifaces, interface_options, i); - if (dfilter_compile(interface_opts.cfilter, &rfcode, NULL) && rfcode != NULL) { + if (dfilter_compile(interface_opts->cfilter, &rfcode, NULL) && rfcode != NULL) { cmdarg_err( "Invalid capture filter \"%s\" for interface '%s'.\n" "\n" @@ -2411,7 +2531,7 @@ capture_input_cfilter_error_message(capture_session *cap_session, guint i, char "so you can't use most display filter expressions as capture filters.\n" "\n" "See the User's Guide for a description of the capture filter syntax.", - interface_opts.cfilter, interface_opts.descr, error_message); + interface_opts->cfilter, interface_opts->descr, error_message); dfilter_free(rfcode); } else { cmdarg_err( @@ -2419,7 +2539,7 @@ capture_input_cfilter_error_message(capture_session *cap_session, guint i, char "\n" "That string isn't a valid capture filter (%s).\n" "See the User's Guide for a description of the capture filter syntax.", - interface_opts.cfilter, interface_opts.descr, error_message); + interface_opts->cfilter, interface_opts->descr, error_message); } } @@ -2445,9 +2565,9 @@ capture_input_new_file(capture_session *cap_session, gchar *new_file) /* we start a new capture file, close the old one (if we had one before) */ if (cf->state != FILE_CLOSED) { - if (cf->wth != NULL) { - wtap_close(cf->wth); - cf->wth = NULL; + if (cf->provider.wth != NULL) { + wtap_close(cf->provider.wth); + cf->provider.wth = NULL; } cf->state = FILE_CLOSED; } @@ -2519,11 +2639,30 @@ capture_input_new_packets(capture_session *cap_session, int to_read) gboolean create_proto_tree; epan_dissect_t *edt; - if (cf->rfcode || cf->dfcode || print_details || filtering_tap_listeners || - (tap_flags & TL_REQUIRES_PROTO_TREE) || have_custom_cols(&cf->cinfo)) - create_proto_tree = TRUE; - else - create_proto_tree = FALSE; + /* + * Determine whether we need to create a protocol tree. + * We do if: + * + * we're going to apply a read filter; + * + * we're going to apply a display filter; + * + * we're going to print the protocol tree; + * + * one of the tap listeners is going to apply a filter; + * + * one of the tap listeners requires a protocol tree; + * + * a postdissector wants field values or protocols + * on the first pass; + * + * we have custom columns (which require field values, which + * currently requires that we build a protocol tree). + */ + create_proto_tree = + (cf->rfcode || cf->dfcode || print_details || filtering_tap_listeners || + (tap_flags & TL_REQUIRES_PROTO_TREE) || postdissectors_want_hfids() || + have_custom_cols(&cf->cinfo) || dissect_color); /* The protocol tree will be "visible", i.e., printed, only if we're printing packet details, which is true if we're printing stuff @@ -2531,18 +2670,19 @@ capture_input_new_packets(capture_session *cap_session, int to_read) ("packet_details" is true). */ edt = epan_dissect_new(cf->epan, create_proto_tree, print_packet_info && print_details); - while (to_read-- && cf->wth) { - wtap_cleareof(cf->wth); - ret = wtap_read(cf->wth, &err, &err_info, &data_offset); + while (to_read-- && cf->provider.wth) { + wtap_cleareof(cf->provider.wth); + ret = wtap_read(cf->provider.wth, &err, &err_info, &data_offset); + reset_epan_mem(cf, edt, create_proto_tree, print_packet_info && print_details); if (ret == FALSE) { /* read from file failed, tell the capture child to stop */ sync_pipe_stop(cap_session); - wtap_close(cf->wth); - cf->wth = NULL; + wtap_close(cf->provider.wth); + cf->provider.wth = NULL; } else { - ret = process_packet(cf, edt, data_offset, wtap_phdr(cf->wth), - wtap_buf_ptr(cf->wth), - tap_flags); + ret = process_packet_single_pass(cf, edt, data_offset, + wtap_phdr(cf->provider.wth), + wtap_buf_ptr(cf->provider.wth), tap_flags); } if (ret != FALSE) { /* packet successfully read and gone through the "Read Filter" */ @@ -2646,8 +2786,8 @@ capture_input_closed(capture_session *cap_session, gchar *msg) report_counts(); - if (cf != NULL && cf->wth != NULL) { - wtap_close(cf->wth); + if (cf != NULL && cf->provider.wth != NULL) { + wtap_close(cf->provider.wth); if (cf->is_tempfile) { ws_unlink(cf->filename); } @@ -2713,8 +2853,8 @@ capture_cleanup(int signum _U_) static gboolean process_packet_first_pass(capture_file *cf, epan_dissect_t *edt, - gint64 offset, struct wtap_pkthdr *whdr, - const guchar *pd) + gint64 offset, struct wtap_pkthdr *whdr, + const guchar *pd) { frame_data fdlocal; guint32 framenum; @@ -2731,9 +2871,11 @@ process_packet_first_pass(capture_file *cf, epan_dissect_t *edt, frame_data_init(&fdlocal, framenum, whdr, offset, cum_bytes); - /* If we're going to print packet information, or we're going to - run a read filter, or display filter, or we're going to process taps, set up to - do a dissection and do so. */ + /* If we're going to run a read filter or a display filter, set up to + do a dissection and do so. (This is the first pass of two passes + over the packets, so we will not be printing any information + from the dissection or running taps on the packet; if we're doing + any of that, we'll do it in the second pass.) */ if (edt) { if (gbl_resolv_flags.mac_name || gbl_resolv_flags.network_name || gbl_resolv_flags.transport_name) @@ -2743,19 +2885,25 @@ process_packet_first_pass(capture_file *cf, epan_dissect_t *edt, /* If we're running a read filter, prime the epan_dissect_t with that filter. */ if (cf->rfcode) - epan_dissect_prime_dfilter(edt, cf->rfcode); + epan_dissect_prime_with_dfilter(edt, cf->rfcode); if (cf->dfcode) - epan_dissect_prime_dfilter(edt, cf->dfcode); + epan_dissect_prime_with_dfilter(edt, cf->dfcode); + + /* This is the first pass, so prime the epan_dissect_t with the + hfids postdissectors want on the first pass. */ + prime_epan_dissect_with_postdissector_wanted_hfids(edt); frame_data_set_before_dissect(&fdlocal, &cf->elapsed_time, - &ref, prev_dis); - if (ref == &fdlocal) { + &cf->provider.ref, cf->provider.prev_dis); + if (cf->provider.ref == &fdlocal) { ref_frame = fdlocal; - ref = &ref_frame; + cf->provider.ref = &ref_frame; } - epan_dissect_run(edt, cf->cd_t, whdr, frame_tvbuff_new(&fdlocal, pd), &fdlocal, NULL); + epan_dissect_run(edt, cf->cd_t, whdr, + frame_tvbuff_new(&cf->provider, &fdlocal, pd), + &fdlocal, NULL); /* Run the read filter if we have one. */ if (cf->rfcode) @@ -2764,7 +2912,7 @@ process_packet_first_pass(capture_file *cf, epan_dissect_t *edt, if (passed) { frame_data_set_after_dissect(&fdlocal, &cum_bytes); - prev_cap = prev_dis = frame_data_sequence_add(cf->frames, &fdlocal); + cf->provider.prev_cap = cf->provider.prev_dis = frame_data_sequence_add(cf->provider.frames, &fdlocal); /* If we're not doing dissection then there won't be any dependent frames. * More importantly, edt.pi.dependent_frames won't be initialized because @@ -2774,7 +2922,7 @@ process_packet_first_pass(capture_file *cf, epan_dissect_t *edt, */ if (edt && cf->dfcode) { if (dfilter_apply_edt(cf->dfcode, edt)) { - g_slist_foreach(edt->pi.dependent_frames, find_and_mark_frame_depended_upon, cf->frames); + g_slist_foreach(edt->pi.dependent_frames, find_and_mark_frame_depended_upon, cf->provider.frames); } } @@ -2792,9 +2940,9 @@ process_packet_first_pass(capture_file *cf, epan_dissect_t *edt, } static gboolean -process_packet_second_pass(capture_file *cf, epan_dissect_t *edt, frame_data *fdata, - struct wtap_pkthdr *phdr, Buffer *buf, - guint tap_flags) +process_packet_second_pass(capture_file *cf, epan_dissect_t *edt, + frame_data *fdata, struct wtap_pkthdr *phdr, + Buffer *buf, guint tap_flags) { column_info *cinfo; gboolean passed; @@ -2806,7 +2954,9 @@ process_packet_second_pass(capture_file *cf, epan_dissect_t *edt, frame_data *fd /* If we're going to print packet information, or we're going to run a read filter, or we're going to process taps, set up to - do a dissection and do so. */ + do a dissection and do so. (This is the second pass of two + passes over the packets; that's the pass where we print + packet information or run taps.) */ if (edt) { if (gbl_resolv_flags.mac_name || gbl_resolv_flags.network_name || gbl_resolv_flags.transport_name) @@ -2816,7 +2966,7 @@ process_packet_second_pass(capture_file *cf, epan_dissect_t *edt, frame_data *fd /* If we're running a display filter, prime the epan_dissect_t with that filter. */ if (cf->dfcode) - epan_dissect_prime_dfilter(edt, cf->dfcode); + epan_dissect_prime_with_dfilter(edt, cf->dfcode); col_custom_prime_edt(edt, &cf->cinfo); @@ -2832,13 +2982,20 @@ process_packet_second_pass(capture_file *cf, epan_dissect_t *edt, frame_data *fd cinfo = NULL; frame_data_set_before_dissect(fdata, &cf->elapsed_time, - &ref, prev_dis); - if (ref == fdata) { + &cf->provider.ref, cf->provider.prev_dis); + if (cf->provider.ref == fdata) { ref_frame = *fdata; - ref = &ref_frame; + cf->provider.ref = &ref_frame; + } + + if (dissect_color) { + color_filters_prime_edt(edt); + fdata->flags.need_colorize = 1; } - epan_dissect_run_with_taps(edt, cf->cd_t, phdr, frame_tvbuff_new_buffer(fdata, buf), fdata, cinfo); + epan_dissect_run_with_taps(edt, cf->cd_t, phdr, + frame_tvbuff_new_buffer(&cf->provider, fdata, buf), + fdata, cinfo); /* Run the read/display filter if we have one. */ if (cf->dfcode) @@ -2853,26 +3010,9 @@ process_packet_second_pass(capture_file *cf, epan_dissect_t *edt, frame_data *fd this packet. */ print_packet(cf, edt); - /* The ANSI C standard does not appear to *require* that a line-buffered - stream be flushed to the host environment whenever a newline is - written, it just says that, on such a stream, characters "are - intended to be transmitted to or from the host environment as a - block when a new-line character is encountered". - - The Visual C++ 6.0 C implementation doesn't do what is intended; - even if you set a stream to be line-buffered, it still doesn't - flush the buffer at the end of every line. - - So, if the "-l" flag was specified, we flush the standard output - at the end of a packet. This will do the right thing if we're - printing packet summary lines, and, as we print the entire protocol - tree for a single packet without waiting for anything to happen, - it should be as good as line-buffered mode if we're printing - protocol trees. (The whole reason for the "-l" flag in either - tcpdump or TShark is to allow the output of a live capture to - be piped to a program or script and to have that script see the - information for the packet as soon as it's printed, rather than - having to wait until a standard I/O buffer fills up. */ + /* If we're doing "line-buffering", flush the standard output + after every packet. See the comment above, for the "-l" + option, for an explanation of why we do that. */ if (line_buffered) fflush(stdout); @@ -2881,9 +3021,9 @@ process_packet_second_pass(capture_file *cf, epan_dissect_t *edt, frame_data *fd exit(2); } } - prev_dis = fdata; + cf->provider.prev_dis = fdata; } - prev_cap = fdata; + cf->provider.prev_cap = fdata; if (edt) { epan_dissect_reset(edt); @@ -2891,18 +3031,18 @@ process_packet_second_pass(capture_file *cf, epan_dissect_t *edt, frame_data *fd return passed || fdata->flags.dependent_of_displayed; } -static int -load_cap_file(capture_file *cf, char *save_file, int out_file_type, +static gboolean +process_cap_file(capture_file *cf, char *save_file, int out_file_type, gboolean out_file_name_res, int max_packet_count, gint64 max_byte_count) { + gboolean success = TRUE; gint linktype; int snapshot_length; wtap_dumper *pdh; guint32 framenum; - int err; - gchar *err_info = NULL; + int err = 0, err_pass1 = 0; + gchar *err_info = NULL, *err_info_pass1 = NULL; gint64 data_offset; - char *save_file_string = NULL; gboolean filtering_tap_listeners; guint tap_flags; GArray *shb_hdrs = NULL; @@ -2915,30 +3055,27 @@ load_cap_file(capture_file *cf, char *save_file, int out_file_type, wtap_phdr_init(&phdr); - idb_inf = wtap_file_get_idb_info(cf->wth); + idb_inf = wtap_file_get_idb_info(cf->provider.wth); #ifdef PCAP_NG_DEFAULT if (idb_inf->interface_data->len > 1) { linktype = WTAP_ENCAP_PER_PACKET; } else { - linktype = wtap_file_encap(cf->wth); + linktype = wtap_file_encap(cf->provider.wth); } #else - linktype = wtap_file_encap(cf->wth); + linktype = wtap_file_encap(cf->provider.wth); #endif if (save_file != NULL) { - /* Get a string that describes what we're writing to */ - save_file_string = output_file_description(save_file); - /* Set up to write to the capture file. */ - snapshot_length = wtap_snapshot_length(cf->wth); + snapshot_length = wtap_snapshot_length(cf->provider.wth); if (snapshot_length == 0) { /* Snapshot length of input file not known. */ - snapshot_length = WTAP_MAX_PACKET_SIZE; + snapshot_length = WTAP_MAX_PACKET_SIZE_STANDARD; } tshark_debug("tshark: snapshot_length = %d", snapshot_length); - shb_hdrs = wtap_file_get_shb_for_new_file(cf->wth); - nrb_hdrs = wtap_file_get_nrb_for_new_file(cf->wth); + shb_hdrs = wtap_file_get_shb_for_new_file(cf->provider.wth); + nrb_hdrs = wtap_file_get_nrb_for_new_file(cf->provider.wth); /* If we don't have an application name add Tshark */ if (wtap_block_get_string_option_value(g_array_index(shb_hdrs, wtap_block_t, 0), OPT_SHB_USERAPPL, &shb_user_appl) != WTAP_OPTTYPE_SUCCESS) { @@ -2975,40 +3112,16 @@ load_cap_file(capture_file *cf, char *save_file, int out_file_type, if (pdh == NULL) { /* We couldn't set up to write to the capture file. */ - switch (err) { - - case WTAP_ERR_UNWRITABLE_FILE_TYPE: - cmdarg_err("Capture files can't be written in that format."); - break; - - case WTAP_ERR_UNWRITABLE_ENCAP: - case WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED: - cmdarg_err("The capture file being read can't be written as a " - "\"%s\" file.", wtap_file_type_subtype_short_string(out_file_type)); - break; - - case WTAP_ERR_CANT_OPEN: - cmdarg_err("The %s couldn't be created for some " - "unknown reason.", save_file_string); - break; - - case WTAP_ERR_SHORT_WRITE: - cmdarg_err("A full header couldn't be written to the %s.", - save_file_string); - break; - - default: - cmdarg_err("The %s could not be created: %s.", save_file_string, - wtap_strerror(err)); - break; - } + cfile_dump_open_failure_message("TShark", save_file, err, out_file_type); + success = FALSE; goto out; } } else { + /* Set up to print packet information. */ if (print_packet_info) { if (!write_preamble(cf)) { - err = errno; - show_print_file_io_error(err); + show_print_file_io_error(errno); + success = FALSE; goto out; } } @@ -3029,15 +3142,24 @@ load_cap_file(capture_file *cf, char *save_file, int out_file_type, tshark_debug("tshark: perform_two_pass_analysis, do_dissection=%s", do_dissection ? "TRUE" : "FALSE"); /* Allocate a frame_data_sequence for all the frames. */ - cf->frames = new_frame_data_sequence(); + cf->provider.frames = new_frame_data_sequence(); if (do_dissection) { - gboolean create_proto_tree = FALSE; + gboolean create_proto_tree; - /* If we're going to be applying a filter, we'll need to - create a protocol tree against which to apply the filter. */ - if (cf->rfcode || cf->dfcode) - create_proto_tree = TRUE; + /* + * Determine whether we need to create a protocol tree. + * We do if: + * + * we're going to apply a read filter; + * + * we're going to apply a display filter; + * + * a postdissector wants field values or protocols + * on the first pass. + */ + create_proto_tree = + (cf->rfcode != NULL || cf->dfcode != NULL || postdissectors_want_hfids() || dissect_color); tshark_debug("tshark: create_proto_tree = %s", create_proto_tree ? "TRUE" : "FALSE"); @@ -3047,9 +3169,9 @@ load_cap_file(capture_file *cf, char *save_file, int out_file_type, } tshark_debug("tshark: reading records for first pass"); - while (wtap_read(cf->wth, &err, &err_info, &data_offset)) { - if (process_packet_first_pass(cf, edt, data_offset, wtap_phdr(cf->wth), - wtap_buf_ptr(cf->wth))) { + while (wtap_read(cf->provider.wth, &err, &err_info, &data_offset)) { + if (process_packet_first_pass(cf, edt, data_offset, wtap_phdr(cf->provider.wth), + wtap_buf_ptr(cf->provider.wth))) { /* Stop reading if we have the maximum number of packets; * When the -c option has not been used, max_packet_count * starts at 0, which practically means, never stop reading. @@ -3064,20 +3186,34 @@ load_cap_file(capture_file *cf, char *save_file, int out_file_type, } } + /* + * If we got a read error on the first pass, remember the error, so + * but do the second pass, so we can at least process the packets we + * read, and then report the first-pass error after the second pass + * (and before we report any second-pass errors), so all the the + * errors show up at the end. + */ + if (err != 0) { + err_pass1 = err; + err_info_pass1 = err_info; + err = 0; + err_info = NULL; + } + if (edt) { epan_dissect_free(edt); edt = NULL; } /* Close the sequential I/O side, to free up memory it requires. */ - wtap_sequential_close(cf->wth); + wtap_sequential_close(cf->provider.wth); /* Allow the protocol dissectors to free up memory that they * don't need after the sequential run-through of the packets. */ postseq_cleanup_all_protocols(); - prev_dis = NULL; - prev_cap = NULL; + cf->provider.prev_dis = NULL; + cf->provider.prev_cap = NULL; ws_buffer_init(&buf, 1500); tshark_debug("tshark: done with first pass"); @@ -3085,11 +3221,22 @@ load_cap_file(capture_file *cf, char *save_file, int out_file_type, if (do_dissection) { gboolean create_proto_tree; - if (cf->dfcode || print_details || filtering_tap_listeners || - (tap_flags & TL_REQUIRES_PROTO_TREE) || have_custom_cols(&cf->cinfo)) - create_proto_tree = TRUE; - else - create_proto_tree = FALSE; + /* + * Determine whether we need to create a protocol tree. + * We do if: + * + * we're going to apply a display filter; + * + * we're going to print the protocol tree; + * + * one of the tap listeners requires a protocol tree; + * + * we have custom columns (which require field values, which + * currently requires that we build a protocol tree). + */ + create_proto_tree = + (cf->dfcode || print_details || filtering_tap_listeners || + (tap_flags & TL_REQUIRES_PROTO_TREE) || have_custom_cols(&cf->cinfo) || dissect_color); tshark_debug("tshark: create_proto_tree = %s", create_proto_tree ? "TRUE" : "FALSE"); @@ -3101,8 +3248,8 @@ load_cap_file(capture_file *cf, char *save_file, int out_file_type, } for (framenum = 1; err == 0 && framenum <= cf->count; framenum++) { - fdata = frame_data_sequence_find(cf->frames, framenum); - if (wtap_seek_read(cf->wth, fdata->file_off, &phdr, &buf, &err, + fdata = frame_data_sequence_find(cf->provider.frames, framenum); + if (wtap_seek_read(cf->provider.wth, fdata->file_off, &phdr, &buf, &err, &err_info)) { tshark_debug("tshark: invoking process_packet_second_pass() for frame #%d", framenum); if (process_packet_second_pass(cf, edt, fdata, &phdr, &buf, @@ -3115,74 +3262,13 @@ load_cap_file(capture_file *cf, char *save_file, int out_file_type, if (!wtap_dump(pdh, &phdr, ws_buffer_start_ptr(&buf), &err, &err_info)) { /* Error writing to a capture file */ tshark_debug("tshark: error writing to a capture file (%d)", err); - switch (err) { - - case WTAP_ERR_UNWRITABLE_ENCAP: - /* - * This is a problem with the particular frame we're writing - * and the file type and subtype we're writing; note that, - * and report the frame number and file type/subtype. - * - * XXX - framenum is not necessarily the frame number in - * the input file if there was a read filter. - */ - fprintf(stderr, - "Frame %u of \"%s\" has a network type that can't be saved in a \"%s\" file.\n", - framenum, cf->filename, - wtap_file_type_subtype_short_string(out_file_type)); - break; - - case WTAP_ERR_PACKET_TOO_LARGE: - /* - * This is a problem with the particular frame we're writing - * and the file type and subtype we're writing; note that, - * and report the frame number and file type/subtype. - * - * XXX - framenum is not necessarily the frame number in - * the input file if there was a read filter. - */ - fprintf(stderr, - "Frame %u of \"%s\" is too large for a \"%s\" file.\n", - framenum, cf->filename, - wtap_file_type_subtype_short_string(out_file_type)); - break; - - case WTAP_ERR_UNWRITABLE_REC_TYPE: - /* - * This is a problem with the particular record we're writing - * and the file type and subtype we're writing; note that, - * and report the record number and file type/subtype. - * - * XXX - framenum is not necessarily the record number in - * the input file if there was a read filter. - */ - fprintf(stderr, - "Record %u of \"%s\" has a record type that can't be saved in a \"%s\" file.\n", - framenum, cf->filename, - wtap_file_type_subtype_short_string(out_file_type)); - break; - - case WTAP_ERR_UNWRITABLE_REC_DATA: - /* - * This is a problem with the particular record we're writing - * and the file type and subtype we're writing; note that, - * and report the record number and file type/subtype. - * - * XXX - framenum is not necessarily the record number in - * the input file if there was a read filter. - */ - fprintf(stderr, - "Record %u of \"%s\" has data that can't be saved in a \"%s\" file.\n(%s)\n", - framenum, cf->filename, - wtap_file_type_subtype_short_string(out_file_type), - err_info != NULL ? err_info : "no information supplied"); - g_free(err_info); - break; - - default: - show_capture_file_io_error(save_file, err, FALSE); - break; - } + + /* Report the error. + XXX - framenum is not necessarily the frame number in + the input file if there was a read filter. */ + cfile_write_failure_message("TShark", cf->filename, save_file, + err, err_info, framenum, + out_file_type); wtap_dump_close(pdh, &err); wtap_block_array_free(shb_hdrs); wtap_block_array_free(nrb_hdrs); @@ -3205,17 +3291,34 @@ load_cap_file(capture_file *cf, char *save_file, int out_file_type, else { /* !perform_two_pass_analysis */ framenum = 0; - + gboolean create_proto_tree = FALSE; tshark_debug("tshark: perform one pass analysis, do_dissection=%s", do_dissection ? "TRUE" : "FALSE"); if (do_dissection) { - gboolean create_proto_tree; - - if (cf->rfcode || cf->dfcode || print_details || filtering_tap_listeners || - (tap_flags & TL_REQUIRES_PROTO_TREE) || have_custom_cols(&cf->cinfo)) - create_proto_tree = TRUE; - else - create_proto_tree = FALSE; + /* + * Determine whether we need to create a protocol tree. + * We do if: + * + * we're going to apply a read filter; + * + * we're going to apply a display filter; + * + * we're going to print the protocol tree; + * + * one of the tap listeners is going to apply a filter; + * + * one of the tap listeners requires a protocol tree; + * + * a postdissector wants field values or protocols + * on the first pass; + * + * we have custom columns (which require field values, which + * currently requires that we build a protocol tree). + */ + create_proto_tree = + (cf->rfcode || cf->dfcode || print_details || filtering_tap_listeners || + (tap_flags & TL_REQUIRES_PROTO_TREE) || postdissectors_want_hfids() || + have_custom_cols(&cf->cinfo) || dissect_color); tshark_debug("tshark: create_proto_tree = %s", create_proto_tree ? "TRUE" : "FALSE"); @@ -3226,78 +3329,25 @@ load_cap_file(capture_file *cf, char *save_file, int out_file_type, edt = epan_dissect_new(cf->epan, create_proto_tree, print_packet_info && print_details); } - while (wtap_read(cf->wth, &err, &err_info, &data_offset)) { + while (wtap_read(cf->provider.wth, &err, &err_info, &data_offset)) { framenum++; tshark_debug("tshark: processing packet #%d", framenum); - if (process_packet(cf, edt, data_offset, wtap_phdr(cf->wth), - wtap_buf_ptr(cf->wth), - tap_flags)) { + reset_epan_mem(cf, edt, create_proto_tree, print_packet_info && print_details); + + if (process_packet_single_pass(cf, edt, data_offset, wtap_phdr(cf->provider.wth), + wtap_buf_ptr(cf->provider.wth), tap_flags)) { /* Either there's no read filtering or this packet passed the filter, so, if we're writing to a capture file, write this packet out. */ if (pdh != NULL) { tshark_debug("tshark: writing packet #%d to outfile", framenum); - if (!wtap_dump(pdh, wtap_phdr(cf->wth), wtap_buf_ptr(cf->wth), &err, &err_info)) { + if (!wtap_dump(pdh, wtap_phdr(cf->provider.wth), wtap_buf_ptr(cf->provider.wth), &err, &err_info)) { /* Error writing to a capture file */ tshark_debug("tshark: error writing to a capture file (%d)", err); - switch (err) { - - case WTAP_ERR_UNWRITABLE_ENCAP: - /* - * This is a problem with the particular frame we're writing - * and the file type and subtype we're writing; note that, - * and report the frame number and file type/subtype. - */ - fprintf(stderr, - "Frame %u of \"%s\" has a network type that can't be saved in a \"%s\" file.\n", - framenum, cf->filename, - wtap_file_type_subtype_short_string(out_file_type)); - break; - - case WTAP_ERR_PACKET_TOO_LARGE: - /* - * This is a problem with the particular frame we're writing - * and the file type and subtype we're writing; note that, - * and report the frame number and file type/subtype. - */ - fprintf(stderr, - "Frame %u of \"%s\" is too large for a \"%s\" file.\n", - framenum, cf->filename, - wtap_file_type_subtype_short_string(out_file_type)); - break; - - case WTAP_ERR_UNWRITABLE_REC_TYPE: - /* - * This is a problem with the particular record we're writing - * and the file type and subtype we're writing; note that, - * and report the record number and file type/subtype. - */ - fprintf(stderr, - "Record %u of \"%s\" has a record type that can't be saved in a \"%s\" file.\n", - framenum, cf->filename, - wtap_file_type_subtype_short_string(out_file_type)); - break; - - case WTAP_ERR_UNWRITABLE_REC_DATA: - /* - * This is a problem with the particular record we're writing - * and the file type and subtype we're writing; note that, - * and report the record number and file type/subtype. - */ - fprintf(stderr, - "Record %u of \"%s\" has data that can't be saved in a \"%s\" file.\n(%s)\n", - framenum, cf->filename, - wtap_file_type_subtype_short_string(out_file_type), - err_info != NULL ? err_info : "no information supplied"); - g_free(err_info); - break; - - default: - show_capture_file_io_error(save_file, err, FALSE); - break; - } + cfile_write_failure_message("TShark", cf->filename, save_file, + err, err_info, framenum, out_file_type); wtap_dump_close(pdh, &err); wtap_block_array_free(shb_hdrs); wtap_block_array_free(nrb_hdrs); @@ -3326,7 +3376,7 @@ load_cap_file(capture_file *cf, char *save_file, int out_file_type, wtap_phdr_cleanup(&phdr); - if (err != 0) { + if (err != 0 || err_pass1 != 0) { tshark_debug("tshark: something failed along the line (%d)", err); /* * Print a message noting that the read failed somewhere along the line. @@ -3352,79 +3402,53 @@ load_cap_file(capture_file *cf, char *save_file, int out_file_type, } } #endif - switch (err) { - - case WTAP_ERR_UNSUPPORTED: - cmdarg_err("The file \"%s\" contains record data that TShark doesn't support.\n(%s)", - cf->filename, - err_info != NULL ? err_info : "no information supplied"); - g_free(err_info); - break; - - case WTAP_ERR_SHORT_READ: - cmdarg_err("The file \"%s\" appears to have been cut short in the middle of a packet.", - cf->filename); - break; - - case WTAP_ERR_BAD_FILE: - cmdarg_err("The file \"%s\" appears to be damaged or corrupt.\n(%s)", - cf->filename, - err_info != NULL ? err_info : "no information supplied"); - g_free(err_info); - break; - - case WTAP_ERR_DECOMPRESS: - cmdarg_err("The compressed file \"%s\" appears to be damaged or corrupt.\n" - "(%s)", cf->filename, - err_info != NULL ? err_info : "no information supplied"); - g_free(err_info); - break; - - default: - cmdarg_err("An error occurred while reading the file \"%s\": %s.", - cf->filename, wtap_strerror(err)); - break; + if (err_pass1 != 0) { + /* Error on pass 1 of two-pass processing. */ + cfile_read_failure_message("TShark", cf->filename, err_pass1, + err_info_pass1); } - if (save_file != NULL) { - /* Now close the capture file. */ - if (!wtap_dump_close(pdh, &err)) - show_capture_file_io_error(save_file, err, TRUE); + if (err != 0) { + /* Error on pass 2 of two-pass processing or on the only pass of + one-pass processing. */ + cfile_read_failure_message("TShark", cf->filename, err, err_info); } - } else { - if (save_file != NULL) { - if (pdh && out_file_name_res) { - if (!wtap_dump_set_addrinfo_list(pdh, get_addrinfo_list())) { - cmdarg_err("The file format \"%s\" doesn't support name resolution information.", - wtap_file_type_subtype_short_string(out_file_type)); - } + success = FALSE; + } + if (save_file != NULL) { + if (pdh && out_file_name_res) { + if (!wtap_dump_set_addrinfo_list(pdh, get_addrinfo_list())) { + cmdarg_err("The file format \"%s\" doesn't support name resolution information.", + wtap_file_type_subtype_short_string(out_file_type)); } - /* Now close the capture file. */ - if (!wtap_dump_close(pdh, &err)) - show_capture_file_io_error(save_file, err, TRUE); - } else { - if (print_packet_info) { - if (!write_finale()) { - err = errno; - show_print_file_io_error(err); - } + } + /* Now close the capture file. */ + if (!wtap_dump_close(pdh, &err)) { + cfile_close_failure_message(save_file, err); + success = FALSE; + } + } else { + if (print_packet_info) { + if (!write_finale()) { + show_print_file_io_error(errno); + success = FALSE; } } } out: - wtap_close(cf->wth); - cf->wth = NULL; + wtap_close(cf->provider.wth); + cf->provider.wth = NULL; - g_free(save_file_string); wtap_block_array_free(shb_hdrs); wtap_block_array_free(nrb_hdrs); - return err; + return success; } static gboolean -process_packet(capture_file *cf, epan_dissect_t *edt, gint64 offset, struct wtap_pkthdr *whdr, - const guchar *pd, guint tap_flags) +process_packet_single_pass(capture_file *cf, epan_dissect_t *edt, gint64 offset, + struct wtap_pkthdr *whdr, const guchar *pd, + guint tap_flags) { frame_data fdata; column_info *cinfo; @@ -3442,7 +3466,9 @@ process_packet(capture_file *cf, epan_dissect_t *edt, gint64 offset, struct wtap /* If we're going to print packet information, or we're going to run a read filter, or we're going to process taps, set up to - do a dissection and do so. */ + do a dissection and do so. (This is the one and only pass + over the packets, so, if we'll be printing packet information + or running taps, we'll be doing it here.) */ if (edt) { if (print_packet_info && (gbl_resolv_flags.mac_name || gbl_resolv_flags.network_name || gbl_resolv_flags.transport_name)) @@ -3452,7 +3478,11 @@ process_packet(capture_file *cf, epan_dissect_t *edt, gint64 offset, struct wtap /* If we're running a filter, prime the epan_dissect_t with that filter. */ if (cf->dfcode) - epan_dissect_prime_dfilter(edt, cf->dfcode); + epan_dissect_prime_with_dfilter(edt, cf->dfcode); + + /* This is the first and only pass, so prime the epan_dissect_t + with the hfids postdissectors want on the first pass. */ + prime_epan_dissect_with_postdissector_wanted_hfids(edt); col_custom_prime_edt(edt, &cf->cinfo); @@ -3469,13 +3499,20 @@ process_packet(capture_file *cf, epan_dissect_t *edt, gint64 offset, struct wtap cinfo = NULL; frame_data_set_before_dissect(&fdata, &cf->elapsed_time, - &ref, prev_dis); - if (ref == &fdata) { + &cf->provider.ref, cf->provider.prev_dis); + if (cf->provider.ref == &fdata) { ref_frame = fdata; - ref = &ref_frame; + cf->provider.ref = &ref_frame; } - epan_dissect_run_with_taps(edt, cf->cd_t, whdr, frame_tvbuff_new(&fdata, pd), &fdata, cinfo); + if (dissect_color) { + color_filters_prime_edt(edt); + fdata.flags.need_colorize = 1; + } + + epan_dissect_run_with_taps(edt, cf->cd_t, whdr, + frame_tvbuff_new(&cf->provider, &fdata, pd), + &fdata, cinfo); /* Run the filter if we have it. */ if (cf->dfcode) @@ -3489,28 +3526,12 @@ process_packet(capture_file *cf, epan_dissect_t *edt, gint64 offset, struct wtap if (print_packet_info) { /* We're printing packet information; print the information for this packet. */ + g_assert(edt); print_packet(cf, edt); - /* The ANSI C standard does not appear to *require* that a line-buffered - stream be flushed to the host environment whenever a newline is - written, it just says that, on such a stream, characters "are - intended to be transmitted to or from the host environment as a - block when a new-line character is encountered". - - The Visual C++ 6.0 C implementation doesn't do what is intended; - even if you set a stream to be line-buffered, it still doesn't - flush the buffer at the end of every line. - - So, if the "-l" flag was specified, we flush the standard output - at the end of a packet. This will do the right thing if we're - printing packet summary lines, and, as we print the entire protocol - tree for a single packet without waiting for anything to happen, - it should be as good as line-buffered mode if we're printing - protocol trees. (The whole reason for the "-l" flag in either - tcpdump or TShark is to allow the output of a live capture to - be piped to a program or script and to have that script see the - information for the packet as soon as it's printed, rather than - having to wait until a standard I/O buffer fills up. */ + /* If we're doing "line-buffering", flush the standard output + after every packet. See the comment above, for the "-l" + option, for an explanation of why we do that. */ if (line_buffered) fflush(stdout); @@ -3522,11 +3543,11 @@ process_packet(capture_file *cf, epan_dissect_t *edt, gint64 offset, struct wtap /* this must be set after print_packet() [bug #8160] */ prev_dis_frame = fdata; - prev_dis = &prev_dis_frame; + cf->provider.prev_dis = &prev_dis_frame; } prev_cap_frame = fdata; - prev_cap = &prev_cap_frame; + cf->provider.prev_cap = &prev_cap_frame; if (edt) { epan_dissect_reset(edt); @@ -3555,6 +3576,7 @@ write_preamble(capture_file *cf) return !ferror(stdout); case WRITE_JSON: + case WRITE_JSON_RAW: write_json_preamble(stdout); return !ferror(stdout); @@ -3620,7 +3642,7 @@ put_string_spaces(char *dest, const char *str, size_t str_len, size_t str_with_s } static gboolean -print_columns(capture_file *cf) +print_columns(capture_file *cf, const epan_dissect_t *edt) { char *line_bufp; int i; @@ -3628,10 +3650,16 @@ print_columns(capture_file *cf) size_t column_len; size_t col_len; col_item_t* col_item; + gchar str_format[11]; + const color_filter_t *color_filter = NULL; line_bufp = get_line_buf(256); buf_offset = 0; *line_bufp = '\0'; + + if (dissect_color) + color_filter = edt->pi.fd->color_filter; + for (i = 0; i < cf->cinfo.num_cols; i++) { col_item = &cf->cinfo.columns[i]; /* Skip columns not marked as visible. */ @@ -3726,12 +3754,13 @@ print_columns(capture_file *cf) case COL_DEF_DST: case COL_RES_DST: case COL_UNRES_DST: - put_string(line_bufp + buf_offset, " " UTF8_RIGHTWARDS_ARROW " ", 5); + g_snprintf(str_format, sizeof(str_format), "%s%s%s", delimiter_char, UTF8_RIGHTWARDS_ARROW, delimiter_char); + put_string(line_bufp + buf_offset, str_format, 5); buf_offset += 5; break; default: - put_string(line_bufp + buf_offset, " ", 1); + put_string(line_bufp + buf_offset, delimiter_char, 1); buf_offset += 1; break; } @@ -3745,12 +3774,13 @@ print_columns(capture_file *cf) case COL_DEF_DL_DST: case COL_RES_DL_DST: case COL_UNRES_DL_DST: - put_string(line_bufp + buf_offset, " " UTF8_RIGHTWARDS_ARROW " ", 5); + g_snprintf(str_format, sizeof(str_format), "%s%s%s", delimiter_char, UTF8_RIGHTWARDS_ARROW, delimiter_char); + put_string(line_bufp + buf_offset, str_format, 5); buf_offset += 5; break; default: - put_string(line_bufp + buf_offset, " ", 1); + put_string(line_bufp + buf_offset, delimiter_char, 1); buf_offset += 1; break; } @@ -3764,12 +3794,13 @@ print_columns(capture_file *cf) case COL_DEF_NET_DST: case COL_RES_NET_DST: case COL_UNRES_NET_DST: - put_string(line_bufp + buf_offset, " " UTF8_RIGHTWARDS_ARROW " ", 5); + g_snprintf(str_format, sizeof(str_format), "%s%s%s", delimiter_char, UTF8_RIGHTWARDS_ARROW, delimiter_char); + put_string(line_bufp + buf_offset, str_format, 5); buf_offset += 5; break; default: - put_string(line_bufp + buf_offset, " ", 1); + put_string(line_bufp + buf_offset, delimiter_char, 1); buf_offset += 1; break; } @@ -3783,12 +3814,13 @@ print_columns(capture_file *cf) case COL_DEF_SRC: case COL_RES_SRC: case COL_UNRES_SRC: - put_string(line_bufp + buf_offset, " " UTF8_LEFTWARDS_ARROW " ", 5); + g_snprintf(str_format, sizeof(str_format), "%s%s%s", delimiter_char, UTF8_LEFTWARDS_ARROW, delimiter_char); + put_string(line_bufp + buf_offset, str_format, 5); buf_offset += 5; break; default: - put_string(line_bufp + buf_offset, " ", 1); + put_string(line_bufp + buf_offset, delimiter_char, 1); buf_offset += 1; break; } @@ -3802,12 +3834,13 @@ print_columns(capture_file *cf) case COL_DEF_DL_SRC: case COL_RES_DL_SRC: case COL_UNRES_DL_SRC: - put_string(line_bufp + buf_offset, " " UTF8_LEFTWARDS_ARROW " ", 5); + g_snprintf(str_format, sizeof(str_format), "%s%s%s", delimiter_char, UTF8_LEFTWARDS_ARROW, delimiter_char); + put_string(line_bufp + buf_offset, str_format, 5); buf_offset += 5; break; default: - put_string(line_bufp + buf_offset, " ", 1); + put_string(line_bufp + buf_offset, delimiter_char, 1); buf_offset += 1; break; } @@ -3821,101 +3854,108 @@ print_columns(capture_file *cf) case COL_DEF_NET_SRC: case COL_RES_NET_SRC: case COL_UNRES_NET_SRC: - put_string(line_bufp + buf_offset, " " UTF8_LEFTWARDS_ARROW " ", 5); + g_snprintf(str_format, sizeof(str_format), "%s%s%s", delimiter_char, UTF8_LEFTWARDS_ARROW, delimiter_char); + put_string(line_bufp + buf_offset, str_format, 5); buf_offset += 5; break; default: - put_string(line_bufp + buf_offset, " ", 1); + put_string(line_bufp + buf_offset, delimiter_char, 1); buf_offset += 1; break; } break; default: - put_string(line_bufp + buf_offset, " ", 1); + put_string(line_bufp + buf_offset, delimiter_char, 1); buf_offset += 1; break; } } } - return print_line(print_stream, 0, line_bufp); + + if (dissect_color && color_filter != NULL) + return print_line_color(print_stream, 0, line_bufp, &color_filter->fg_color, &color_filter->bg_color); + else + return print_line(print_stream, 0, line_bufp); } static gboolean print_packet(capture_file *cf, epan_dissect_t *edt) { - print_args_t print_args; - - if (print_summary || output_fields_has_cols(output_fields)) { + if (print_summary || output_fields_has_cols(output_fields)) /* Just fill in the columns. */ epan_dissect_fill_in_columns(edt, FALSE, TRUE); - if (print_summary) { - /* Now print them. */ - switch (output_action) { - - case WRITE_TEXT: - if (!print_columns(cf)) - return FALSE; - break; + /* Print summary columns and/or protocol tree */ + switch (output_action) { - case WRITE_XML: - write_psml_columns(edt, stdout); - return !ferror(stdout); - case WRITE_FIELDS: /*No non-verbose "fields" format */ - case WRITE_JSON: - case WRITE_EK: - g_assert_not_reached(); - break; - } - } - } - if (print_details) { - /* Print the information in the protocol tree. */ - switch (output_action) { - - case WRITE_TEXT: - /* Only initialize the fields that are actually used in proto_tree_print. - * This is particularly important for .range, as that's heap memory which - * we would otherwise have to g_free(). - print_args.to_file = TRUE; - print_args.format = print_format; - print_args.print_summary = print_summary; - print_args.print_formfeed = FALSE; - packet_range_init(&print_args.range, &cfile); - */ - print_args.print_hex = print_hex; - print_args.print_dissections = print_details ? print_dissections_expanded : print_dissections_none; - - if (!proto_tree_print(&print_args, edt, output_only_tables, print_stream)) + case WRITE_TEXT: + if (print_summary && !print_columns(cf, edt)) + return FALSE; + if (print_details) { + if (!proto_tree_print(print_details ? print_dissections_expanded : print_dissections_none, + print_hex, edt, output_only_tables, print_stream)) return FALSE; if (!print_hex) { if (!print_line(print_stream, 0, separator)) return FALSE; } - break; + } + break; - case WRITE_XML: - write_pdml_proto_tree(output_fields, protocolfilter, edt, stdout); + case WRITE_XML: + if (print_summary) { + write_psml_columns(edt, stdout, dissect_color); + return !ferror(stdout); + } + if (print_details) { + write_pdml_proto_tree(output_fields, protocolfilter, protocolfilter_flags, edt, &cf->cinfo, stdout, dissect_color); printf("\n"); return !ferror(stdout); - case WRITE_FIELDS: + } + break; + + case WRITE_FIELDS: + if (print_summary) { + /*No non-verbose "fields" format */ + g_assert_not_reached(); + } + if (print_details) { write_fields_proto_tree(output_fields, edt, &cf->cinfo, stdout); printf("\n"); return !ferror(stdout); - case WRITE_JSON: - print_args.print_hex = print_hex; - write_json_proto_tree(output_fields, &print_args, protocolfilter, edt, stdout); - printf("\n"); + } + break; + + case WRITE_JSON: + if (print_summary) + g_assert_not_reached(); + if (print_details) { + write_json_proto_tree(output_fields, print_dissections_expanded, + print_hex, protocolfilter, protocolfilter_flags, + edt, &cf->cinfo, node_children_grouper, stdout); return !ferror(stdout); - case WRITE_EK: - print_args.print_hex = print_hex; - write_ek_proto_tree(output_fields, &print_args, protocolfilter, edt, stdout); - printf("\n"); + } + break; + + case WRITE_JSON_RAW: + if (print_summary) + g_assert_not_reached(); + if (print_details) { + write_json_proto_tree(output_fields, print_dissections_none, TRUE, + protocolfilter, protocolfilter_flags, + edt, &cf->cinfo, node_children_grouper, stdout); return !ferror(stdout); } + break; + + case WRITE_EK: + write_ek_proto_tree(output_fields, print_summary, print_hex, protocolfilter, + protocolfilter_flags, edt, &cf->cinfo, stdout); + return !ferror(stdout); } + if (print_hex) { if (print_summary || print_details) { if (!print_line(print_stream, 0, "")) @@ -3949,6 +3989,7 @@ write_finale(void) return !ferror(stdout); case WRITE_JSON: + case WRITE_JSON_RAW: write_json_finale(stdout); return !ferror(stdout); @@ -3961,12 +4002,17 @@ write_finale(void) } } +void +cf_close(capture_file *cf) +{ + g_free(cf->filename); +} + cf_status_t cf_open(capture_file *cf, const char *fname, unsigned int type, gboolean is_tempfile, int *err) { wtap *wth; gchar *err_info; - char err_msg[2048+1]; wth = wtap_open_offline(fname, type, err, &err_info, perform_two_pass_analysis); if (wth == NULL) @@ -3978,7 +4024,7 @@ cf_open(capture_file *cf, const char *fname, unsigned int type, gboolean is_temp epan_free(cf->epan); cf->epan = tshark_epan_new(cf); - cf->wth = wth; + cf->provider.wth = wth; cf->f_datalen = 0; /* not used, but set it anyway */ /* Set the file name because we need it to set the follow stream filter. @@ -3992,83 +4038,29 @@ cf_open(capture_file *cf, const char *fname, unsigned int type, gboolean is_temp /* No user changes yet. */ cf->unsaved_changes = FALSE; - cf->cd_t = wtap_file_type_subtype(cf->wth); + cf->cd_t = wtap_file_type_subtype(cf->provider.wth); cf->open_type = type; cf->count = 0; cf->drops_known = FALSE; cf->drops = 0; - cf->snap = wtap_snapshot_length(cf->wth); - if (cf->snap == 0) { - /* Snapshot length not known. */ - cf->has_snap = FALSE; - cf->snap = WTAP_MAX_PACKET_SIZE; - } else - cf->has_snap = TRUE; + cf->snap = wtap_snapshot_length(cf->provider.wth); nstime_set_zero(&cf->elapsed_time); - ref = NULL; - prev_dis = NULL; - prev_cap = NULL; + cf->provider.ref = NULL; + cf->provider.prev_dis = NULL; + cf->provider.prev_cap = NULL; cf->state = FILE_READ_IN_PROGRESS; - wtap_set_cb_new_ipv4(cf->wth, add_ipv4_name); - wtap_set_cb_new_ipv6(cf->wth, (wtap_new_ipv6_callback_t) add_ipv6_name); + wtap_set_cb_new_ipv4(cf->provider.wth, add_ipv4_name); + wtap_set_cb_new_ipv6(cf->provider.wth, (wtap_new_ipv6_callback_t) add_ipv6_name); return CF_OK; fail: - g_snprintf(err_msg, sizeof err_msg, - cf_open_error_message(*err, err_info, FALSE, cf->cd_t), fname); - cmdarg_err("%s", err_msg); + cfile_open_failure_message("TShark", fname, *err, err_info); return CF_ERROR; } -static void -show_capture_file_io_error(const char *fname, int err, gboolean is_close) -{ - char *save_file_string; - - save_file_string = output_file_description(fname); - - switch (err) { - - case ENOSPC: - cmdarg_err("Not all the packets could be written to the %s because there is " - "no space left on the file system.", - save_file_string); - break; - -#ifdef EDQUOT - case EDQUOT: - cmdarg_err("Not all the packets could be written to the %s because you are " - "too close to, or over your disk quota.", - save_file_string); - break; -#endif - - case WTAP_ERR_CANT_CLOSE: - cmdarg_err("The %s couldn't be closed for some unknown reason.", - save_file_string); - break; - - case WTAP_ERR_SHORT_WRITE: - cmdarg_err("Not all the packets could be written to the %s.", - save_file_string); - break; - - default: - if (is_close) { - cmdarg_err("The %s could not be closed: %s.", save_file_string, - wtap_strerror(err)); - } else { - cmdarg_err("An error occurred while writing to the %s: %s.", - save_file_string, wtap_strerror(err)); - } - break; - } - g_free(save_file_string); -} - static void show_print_file_io_error(int err) { @@ -4093,144 +4085,26 @@ show_print_file_io_error(int err) } } -static const char * -cf_open_error_message(int err, gchar *err_info, gboolean for_writing, - int file_type) -{ - const char *errmsg; - static char errmsg_errno[1024+1]; - - if (err < 0) { - /* Wiretap error. */ - switch (err) { - - case WTAP_ERR_NOT_REGULAR_FILE: - errmsg = "The file \"%s\" is a \"special file\" or socket or other non-regular file."; - break; - - case WTAP_ERR_RANDOM_OPEN_PIPE: - /* Seen only when opening a capture file for reading. */ - errmsg = "The file \"%s\" is a pipe or FIFO; TShark can't read pipe or FIFO files in two-pass mode."; - break; - - case WTAP_ERR_FILE_UNKNOWN_FORMAT: - /* Seen only when opening a capture file for reading. */ - errmsg = "The file \"%s\" isn't a capture file in a format TShark understands."; - break; - - case WTAP_ERR_UNSUPPORTED: - /* Seen only when opening a capture file for reading. */ - g_snprintf(errmsg_errno, sizeof(errmsg_errno), - "The file \"%%s\" contains record data that TShark doesn't support.\n" - "(%s)", - err_info != NULL ? err_info : "no information supplied"); - g_free(err_info); - errmsg = errmsg_errno; - break; - - case WTAP_ERR_CANT_WRITE_TO_PIPE: - /* Seen only when opening a capture file for writing. */ - g_snprintf(errmsg_errno, sizeof(errmsg_errno), - "The file \"%%s\" is a pipe, and \"%s\" capture files can't be " - "written to a pipe.", wtap_file_type_subtype_short_string(file_type)); - errmsg = errmsg_errno; - break; - - case WTAP_ERR_UNWRITABLE_FILE_TYPE: - /* Seen only when opening a capture file for writing. */ - errmsg = "TShark doesn't support writing capture files in that format."; - break; - - case WTAP_ERR_UNWRITABLE_ENCAP: - /* Seen only when opening a capture file for writing. */ - g_snprintf(errmsg_errno, sizeof(errmsg_errno), - "TShark can't save this capture as a \"%s\" file.", - wtap_file_type_subtype_short_string(file_type)); - errmsg = errmsg_errno; - break; - - case WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED: - if (for_writing) { - g_snprintf(errmsg_errno, sizeof(errmsg_errno), - "TShark can't save this capture as a \"%s\" file.", - wtap_file_type_subtype_short_string(file_type)); - errmsg = errmsg_errno; - } else - errmsg = "The file \"%s\" is a capture for a network type that TShark doesn't support."; - break; - - case WTAP_ERR_BAD_FILE: - /* Seen only when opening a capture file for reading. */ - g_snprintf(errmsg_errno, sizeof(errmsg_errno), - "The file \"%%s\" appears to be damaged or corrupt.\n" - "(%s)", - err_info != NULL ? err_info : "no information supplied"); - g_free(err_info); - errmsg = errmsg_errno; - break; - - case WTAP_ERR_CANT_OPEN: - if (for_writing) - errmsg = "The file \"%s\" could not be created for some unknown reason."; - else - errmsg = "The file \"%s\" could not be opened for some unknown reason."; - break; - - case WTAP_ERR_SHORT_READ: - errmsg = "The file \"%s\" appears to have been cut short" - " in the middle of a packet or other data."; - break; - - case WTAP_ERR_SHORT_WRITE: - errmsg = "A full header couldn't be written to the file \"%s\"."; - break; - - case WTAP_ERR_COMPRESSION_NOT_SUPPORTED: - errmsg = "This file type cannot be written as a compressed file."; - break; - - case WTAP_ERR_DECOMPRESS: - /* Seen only when opening a capture file for reading. */ - g_snprintf(errmsg_errno, sizeof(errmsg_errno), - "The compressed file \"%%s\" appears to be damaged or corrupt.\n" - "(%s)", - err_info != NULL ? err_info : "no information supplied"); - g_free(err_info); - errmsg = errmsg_errno; - break; - - default: - g_snprintf(errmsg_errno, sizeof(errmsg_errno), - "The file \"%%s\" could not be %s: %s.", - for_writing ? "created" : "opened", - wtap_strerror(err)); - errmsg = errmsg_errno; - break; - } - } else - errmsg = file_open_error_message(err, for_writing); - return errmsg; -} - /* - * Open/create errors are reported with an console message in TShark. + * General errors and warnings are reported with an console message + * in TShark. */ static void -open_failure_message(const char *filename, int err, gboolean for_writing) +failure_warning_message(const char *msg_format, va_list ap) { fprintf(stderr, "tshark: "); - fprintf(stderr, file_open_error_message(err, for_writing), filename); + vfprintf(stderr, msg_format, ap); fprintf(stderr, "\n"); } /* - * General errors are reported with an console message in TShark. + * Open/create errors are reported with an console message in TShark. */ static void -failure_message(const char *msg_format, va_list ap) +open_failure_message(const char *filename, int err, gboolean for_writing) { fprintf(stderr, "tshark: "); - vfprintf(stderr, msg_format, ap); + fprintf(stderr, file_open_error_message(err, for_writing), filename); fprintf(stderr, "\n"); } @@ -4241,7 +4115,7 @@ static void read_failure_message(const char *filename, int err) { cmdarg_err("An error occurred while reading from the file \"%s\": %s.", - filename, g_strerror(err)); + filename, g_strerror(err)); } /* @@ -4251,7 +4125,22 @@ static void write_failure_message(const char *filename, int err) { cmdarg_err("An error occurred while writing to the file \"%s\": %s.", - filename, g_strerror(err)); + filename, g_strerror(err)); +} + +static void reset_epan_mem(capture_file *cf,epan_dissect_t *edt, gboolean tree, gboolean visual) +{ + if (!epan_auto_reset || (cf->count < epan_auto_reset_count)) + return; + + fprintf(stderr, "resetting session.\n"); + + epan_dissect_cleanup(edt); + epan_free(cf->epan); + + cf->epan = tshark_epan_new(cf); + epan_dissect_init(edt, cf->epan, tree, visual); + cf->count = 0; } /*