summaryrefslogtreecommitdiff
path: root/tshark.c
diff options
context:
space:
mode:
authorKovarththanan Rajaratnam <kovarththanan.rajaratnam@gmail.com>2009-09-22 19:28:00 +0000
committerKovarththanan Rajaratnam <kovarththanan.rajaratnam@gmail.com>2009-09-22 19:28:00 +0000
commit760eb47fc1aa68af953a8074a37952ec5178dc02 (patch)
treee26790ecc5d2f499f397652073155774b317df96 /tshark.c
parent2cacda18231358dc5916af95b148ced357599c68 (diff)
downloadwireshark-760eb47fc1aa68af953a8074a37952ec5178dc02.tar.gz
Add initial support for "two pass analysis" in tshark. This allows tshark to arrive at the same protocol tree as the Wireshark GUI. Before this change tshark only supported a single scan over the file. This effectively means that packets cannot use data that are gathered from frames that appear after the current frame. By scanning twice we give the dissector the opportunity to make forward references.
svn path=/trunk/; revision=30076
Diffstat (limited to 'tshark.c')
-rw-r--r--tshark.c302
1 files changed, 277 insertions, 25 deletions
diff --git a/tshark.c b/tshark.c
index b43bdb7d67..0088b8ae84 100644
--- a/tshark.c
+++ b/tshark.c
@@ -112,6 +112,8 @@ static nstime_t prev_cap_ts;
static gboolean print_packet_info; /* TRUE if we're to print packet information */
+static gboolean perform_two_pass_analysis;
+
/*
* The way the packet decode is to be written.
*/
@@ -764,7 +766,7 @@ main(int argc, char *argv[])
GLogLevelFlags log_flags;
int optind_initial;
-#define OPTSTRING_INIT "a:b:c:C:d:De:E:f:F:G:hi:K:lLnN:o:pqr:R:s:St:T:vVw:xX:y:z:"
+#define OPTSTRING_INIT "a:b:c:C:d:De:E:f:F:G:hi:K:lLnN:o:pPqr:R:s:St:T:vVw:xX:y:z:"
#ifdef HAVE_LIBPCAP
#ifdef _WIN32
#define OPTSTRING_WIN32 "B:"
@@ -1081,6 +1083,11 @@ main(int argc, char *argv[])
arg_error = TRUE;
#endif
break;
+#if GLIB_CHECK_VERSION(2,10,0)
+ case 'P': /* Perform two pass analysis */
+ perform_two_pass_analysis = TRUE;
+ break;
+#endif
case 'n': /* No name resolution */
g_resolv_flags = RESOLV_NONE;
break;
@@ -1622,6 +1629,11 @@ main(int argc, char *argv[])
#endif
}
+#if GLIB_CHECK_VERSION(2,10,0)
+ if (cfile.plist_start != NULL)
+ g_slice_free_chain(frame_data, cfile.plist_start, next);
+#endif
+
draw_tap_listeners(TRUE);
funnel_dump_all_text_windows();
epan_cleanup();
@@ -2165,6 +2177,181 @@ capture_cleanup(int signum _U_)
#endif /* _WIN32 */
#endif /* HAVE_LIBPCAP */
+#if GLIB_CHECK_VERSION(2,10,0)
+static gboolean
+process_packet_first_pass(capture_file *cf,
+ gint64 offset, const struct wtap_pkthdr *whdr,
+ union wtap_pseudo_header *pseudo_header, const guchar *pd)
+{
+ frame_data *fdata = g_slice_new(frame_data);
+ epan_dissect_t edt;
+ gboolean passed;
+
+ /* Count this packet. */
+ cf->count++;
+
+ /* If we're not running a display filter and we're not printing any
+ packet information, we don't need to do a dissection. This means
+ that all packets can be marked as 'passed'. */
+ passed = TRUE;
+
+ frame_data_init(fdata, cf->count, whdr, offset, cum_bytes);
+
+ /* 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. */
+ if (do_dissection) {
+ if (g_resolv_flags)
+ /* Grab any resolved addresses */
+ host_name_lookup_process(NULL);
+
+ /* The protocol tree will be "visible", i.e., printed, only if we're
+ printing packet details, which is true if we're printing stuff
+ ("print_packet_info" is true) and we're in verbose mode ("verbose"
+ is true). */
+ epan_dissect_init(&edt, FALSE, FALSE);
+
+ /* 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);
+
+ frame_data_set_before_dissect(fdata, &cf->elapsed_time,
+ &first_ts, &prev_dis_ts, &prev_cap_ts);
+
+ epan_dissect_run(&edt, pseudo_header, pd, fdata, NULL);
+
+ /* Run the read filter if we have one. */
+ if (cf->rfcode)
+ passed = dfilter_apply_edt(cf->rfcode, &edt);
+ }
+
+ if (passed) {
+ frame_data_set_after_dissect(fdata, &cum_bytes, &prev_dis_ts);
+ cap_file_add_fdata(cf, fdata);
+ }
+ else
+ g_slice_free(frame_data, fdata);
+
+ if (do_dissection)
+ epan_dissect_cleanup(&edt);
+
+ return passed;
+}
+
+static gboolean
+process_packet_second_pass(capture_file *cf, frame_data *fdata,
+ gint64 offset, union wtap_pseudo_header *pseudo_header, const guchar *pd,
+ gboolean filtering_tap_listeners, guint tap_flags)
+{
+ gboolean create_proto_tree;
+ column_info *cinfo;
+ epan_dissect_t edt;
+ gboolean passed;
+
+ /* If we're not running a display filter and we're not printing any
+ packet information, we don't need to do a dissection. This means
+ that all packets can be marked as 'passed'. */
+ passed = TRUE;
+
+ /* 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. */
+ if (do_dissection) {
+ if (g_resolv_flags)
+ /* Grab any resolved addresses */
+ host_name_lookup_process(NULL);
+
+ if (cf->rfcode || verbose || filtering_tap_listeners ||
+ (tap_flags & TL_REQUIRES_PROTO_TREE) || have_custom_cols(&cf->cinfo))
+ create_proto_tree = TRUE;
+ else
+ create_proto_tree = FALSE;
+
+ /* The protocol tree will be "visible", i.e., printed, only if we're
+ printing packet details, which is true if we're printing stuff
+ ("print_packet_info" is true) and we're in verbose mode ("verbose"
+ is true). */
+ epan_dissect_init(&edt, create_proto_tree, print_packet_info && verbose);
+
+ /* 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);
+
+ col_custom_prime_edt(&edt, &cf->cinfo);
+
+ tap_queue_init(&edt);
+
+ /* We only need the columns if either
+
+ 1) some tap needs the columns
+
+ or
+
+ 2) we're printing packet info but we're *not* verbose; in verbose
+ mode, we print the protocol tree, not the protocol summary. */
+ if ((tap_flags & TL_REQUIRES_COLUMNS) || (print_packet_info && !verbose))
+ cinfo = &cf->cinfo;
+ else
+ cinfo = NULL;
+
+ epan_dissect_run(&edt, pseudo_header, pd, fdata, cinfo);
+
+ tap_push_tapped_queue(&edt);
+
+ /* Run the read filter if we have one. */
+ if (cf->rfcode)
+ passed = dfilter_apply_edt(cf->rfcode, &edt);
+ }
+
+ if (passed) {
+ /* Process this packet. */
+ if (print_packet_info) {
+ /* We're printing packet information; print the information for
+ this packet. */
+ if (do_dissection)
+ print_packet(cf, &edt);
+ else
+ print_packet(cf, NULL);
+
+ /* 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 (line_buffered)
+ fflush(stdout);
+
+ if (ferror(stdout)) {
+ show_print_file_io_error(errno);
+ exit(2);
+ }
+ }
+ }
+
+ if (do_dissection) {
+ epan_dissect_cleanup(&edt);
+ }
+ return passed;
+}
+#endif
+
static int
load_cap_file(capture_file *cf, char *save_file, int out_file_type,
int max_packet_count, gint64 max_byte_count)
@@ -2241,34 +2428,99 @@ load_cap_file(capture_file *cf, char *save_file, int out_file_type,
/* Get the union of the flags for all tap listeners. */
tap_flags = union_of_tap_listener_flags();
- while (wtap_read(cf->wth, &err, &err_info, &data_offset)) {
- if (process_packet(cf, data_offset, wtap_phdr(cf->wth),
- wtap_pseudoheader(cf->wth), wtap_buf_ptr(cf->wth),
- filtering_tap_listeners, 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) {
- if (!wtap_dump(pdh, wtap_phdr(cf->wth),
- wtap_pseudoheader(cf->wth), wtap_buf_ptr(cf->wth),
- &err)) {
- /* Error writing to a capture file */
- show_capture_file_io_error(save_file, err, FALSE);
- wtap_dump_close(pdh, &err);
- exit(2);
+ if (perform_two_pass_analysis) {
+#if GLIB_CHECK_VERSION(2,10,0)
+ frame_data *fdata;
+ int old_max_packet_count = max_packet_count;
+
+ while (wtap_read(cf->wth, &err, &err_info, &data_offset)) {
+ if (process_packet_first_pass(cf, data_offset, wtap_phdr(cf->wth),
+ wtap_pseudoheader(cf->wth), wtap_buf_ptr(cf->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.
+ * (unless we roll over max_packet_count ?)
+ */
+ if( (--max_packet_count == 0) || (max_byte_count != 0 && data_offset >= max_byte_count)) {
+ err = 0; /* This is not an error */
+ break;
}
}
- /* 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.
- * (unless we roll over max_packet_count ?)
- */
- if( (--max_packet_count == 0) || (max_byte_count != 0 && data_offset >= max_byte_count)) {
- err = 0; /* This is not an error */
- break;
+ }
+
+ /* Close the sequential I/O side, to free up memory it requires. */
+ wtap_sequential_close(cf->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();
+
+ max_packet_count = old_max_packet_count;
+
+ for (fdata = cf->plist_start; err == 0 && fdata != NULL; fdata = fdata->next) {
+ if (wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
+ cf->pd, fdata->cap_len, &err, &err_info)) {
+ if (process_packet_second_pass(cf, fdata, fdata->file_off,
+ &cf->pseudo_header, cf->pd,
+ filtering_tap_listeners, 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) {
+ if (!wtap_dump(pdh, wtap_phdr(cf->wth),
+ wtap_pseudoheader(cf->wth), wtap_buf_ptr(cf->wth),
+ &err)) {
+ /* Error writing to a capture file */
+ show_capture_file_io_error(save_file, err, FALSE);
+ wtap_dump_close(pdh, &err);
+ exit(2);
+ }
+ }
+ /* 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.
+ * (unless we roll over max_packet_count ?)
+ */
+ if( (--max_packet_count == 0) || (max_byte_count != 0 && data_offset >= max_byte_count)) {
+ err = 0; /* This is not an error */
+ break;
+ }
+ }
}
}
+#endif
}
+ else {
+ while (wtap_read(cf->wth, &err, &err_info, &data_offset)) {
+ if (process_packet(cf, data_offset, wtap_phdr(cf->wth),
+ wtap_pseudoheader(cf->wth), wtap_buf_ptr(cf->wth),
+ filtering_tap_listeners, 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) {
+ if (!wtap_dump(pdh, wtap_phdr(cf->wth),
+ wtap_pseudoheader(cf->wth), wtap_buf_ptr(cf->wth),
+ &err)) {
+ /* Error writing to a capture file */
+ show_capture_file_io_error(save_file, err, FALSE);
+ wtap_dump_close(pdh, &err);
+ exit(2);
+ }
+ }
+ /* 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.
+ * (unless we roll over max_packet_count ?)
+ */
+ if( (--max_packet_count == 0) || (max_byte_count != 0 && data_offset >= max_byte_count)) {
+ err = 0; /* This is not an error */
+ break;
+ }
+ }
+ }
+ }
+
if (err != 0) {
/* Print a message noting that the read failed somewhere along the line. */
switch (err) {
@@ -3009,7 +3261,7 @@ cf_open(capture_file *cf, const char *fname, gboolean is_tempfile, int *err)
gchar *err_info;
char err_msg[2048+1];
- wth = wtap_open_offline(fname, err, &err_info, FALSE);
+ wth = wtap_open_offline(fname, err, &err_info, perform_two_pass_analysis);
if (wth == NULL)
goto fail;