test: allow running pytest without specifying the tests directory
[metze/wireshark/wip.git] / file.c
diff --git a/file.c b/file.c
index 438ca5508d8da399d06091f90016689d0ca2a95c..58518d68aca8dcbb86ca3227b98bca32903a74b4 100644 (file)
--- a/file.c
+++ b/file.c
@@ -5,7 +5,7 @@
  * By Gerald Combs <gerald@wireshark.org>
  * Copyright 1998 Gerald Combs
  *
- * SPDX-License-Identifier: GPL-2.0+
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include <config.h>
@@ -21,6 +21,7 @@
 #include <wsutil/tempfile.h>
 #include <wsutil/file_util.h>
 #include <wsutil/filesystem.h>
+#include <wsutil/json_dumper.h>
 #include <version_info.h>
 
 #include <wiretap/merge.h>
@@ -41,6 +42,7 @@
 #include <epan/strutil.h>
 #include <epan/addr_resolv.h>
 #include <epan/color_filters.h>
+#include <epan/secrets.h>
 
 #include "cfile.h"
 #include "file.h"
@@ -54,9 +56,7 @@
 #include "ui/ws_ui_util.h"
 
 /* Needed for addrinfo */
-#ifdef HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
+#include <sys/types.h>
 
 #ifdef HAVE_SYS_SOCKET_H
 #include <sys/socket.h>
 # include <ws2tcpip.h>
 #endif
 
-#ifdef HAVE_LIBPCAP
-gboolean auto_scroll_live; /* GTK+ only? */
-#endif
-
-static int read_packet(capture_file *cf, dfilter_t *dfcode, epan_dissect_t *edt,
-    column_info *cinfo, gint64 offset);
+static gboolean read_record(capture_file *cf, dfilter_t *dfcode,
+    epan_dissect_t *edt, column_info *cinfo, gint64 offset);
 
 static void rescan_packets(capture_file *cf, const char *action, const char *action_item, gboolean redissect);
 
@@ -266,18 +262,13 @@ cf_open(capture_file *cf, const char *fname, unsigned int type, gboolean is_temp
      and fill in the information for this file. */
   cf_close(cf);
 
-  /* Initialize the packet header. */
-  wtap_phdr_init(&cf->phdr);
+  /* Initialize the record metadata. */
+  wtap_rec_init(&cf->rec);
 
   /* XXX - we really want to initialize this after we've read all
      the packets, so we know how much we'll ultimately need. */
   ws_buffer_init(&cf->buf, 1500);
 
-  /* Create new epan session for dissection.
-   * (The old one was freed in cf_close().)
-   */
-  cf->epan = ws_epan_new(cf);
-
   /* We're about to start reading the file. */
   cf->state = FILE_READ_IN_PROGRESS;
 
@@ -319,6 +310,11 @@ cf_open(capture_file *cf, const char *fname, unsigned int type, gboolean is_temp
   cf->provider.prev_cap = NULL;
   cf->cum_bytes = 0;
 
+  /* Create new epan session for dissection.
+   * (The old one was freed in cf_close().)
+   */
+  cf->epan = ws_epan_new(cf);
+
   packet_list_queue_draw();
   cf_callback_invoke(cf_cb_file_opened, cf);
 
@@ -329,6 +325,7 @@ cf_open(capture_file *cf, const char *fname, unsigned int type, gboolean is_temp
 
   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);
+  wtap_set_cb_new_secrets(cf->provider.wth, secrets_wtap_callback);
 
   return CF_OK;
 
@@ -363,6 +360,7 @@ cf_close(capture_file *cf)
 
   /* Die if we're in the middle of reading a file. */
   g_assert(cf->state != FILE_READ_IN_PROGRESS);
+  g_assert(!cf->read_lock);
 
   cf_callback_invoke(cf_cb_file_closing, cf);
 
@@ -387,8 +385,8 @@ cf_close(capture_file *cf)
   /* no open_routine type */
   cf->open_type = WTAP_TYPE_AUTO;
 
-  /* Clean up the packet header. */
-  wtap_phdr_cleanup(&cf->phdr);
+  /* Clean up the record metadata. */
+  wtap_rec_cleanup(&cf->rec);
 
   /* Free up the packet buffer. */
   ws_buffer_free(&cf->buf);
@@ -504,6 +502,18 @@ cf_read(capture_file *cf, gboolean reloading)
   gboolean             compiled;
   volatile gboolean    is_read_aborted = FALSE;
 
+  /* The update_progress_dlg call below might end up accepting a user request to
+   * trigger redissection/rescans which can modify/destroy the dissection
+   * context ("cf->epan"). That condition should be prevented by callers, but in
+   * case it occurs let's fail gracefully.
+   */
+  if (cf->read_lock) {
+    g_warning("Failing due to recursive cf_read(\"%s\", %d) call!",
+              cf->filename, reloading);
+    return CF_READ_ERROR;
+  }
+  cf->read_lock = TRUE;
+
   /* Compile the current display filter.
    * We assume this will not fail since cf->dfilter is only set in
    * cf_filter IFF the filter was valid.
@@ -540,9 +550,9 @@ cf_read(capture_file *cf, gboolean reloading)
   else
     cf_callback_invoke(cf_cb_file_read_started, cf);
 
-  /* Record whether the file is compressed.
+  /* Record the file's compression type.
      XXX - do we know this at open time? */
-  cf->iscompressed = wtap_iscompressed(cf->provider.wth);
+  cf->compression_type = wtap_get_compression_type(cf->provider.wth);
 
   /* The packet list window will be empty until the file is completly loaded */
   packet_list_freeze();
@@ -603,6 +613,12 @@ cf_read(capture_file *cf, gboolean reloading)
           packets_bar_update();
           g_timer_start(prog_timer);
         }
+        /*
+         * The previous GUI triggers should not have destroyed the running
+         * session. If that did happen, it could blow up when read_record tries
+         * to use the destroyed edt.session, so detect it right here.
+         */
+        g_assert(edt.session == cf->epan);
       }
 
       if (cf->state == FILE_READ_ABORTED) {
@@ -621,7 +637,7 @@ cf_read(capture_file *cf, gboolean reloading)
            hours even on fast machines) just to see that it was the wrong file. */
         break;
       }
-      read_packet(cf, dfcode, &edt, cinfo, data_offset);
+      read_record(cf, dfcode, &edt, cinfo, data_offset);
     }
   }
   CATCH(OutOfMemoryError) {
@@ -685,6 +701,10 @@ cf_read(capture_file *cf, gboolean reloading)
     packet_list_select_first_row();
   }
 
+  /* It is safe again to execute redissections. */
+  g_assert(cf->read_lock);
+  cf->read_lock = FALSE;
+
   if (is_read_aborted) {
     /*
      * Well, the user decided to exit Wireshark while reading this *offline*
@@ -692,9 +712,16 @@ cf_read(capture_file *cf, gboolean reloading)
      * cf_continue_tail). Clean up accordingly.
      */
     cf_close(cf);
+    cf->redissection_queued = RESCAN_NONE;
     return CF_READ_ABORTED;
   }
 
+  if (cf->redissection_queued != RESCAN_NONE) {
+    /* Redissection was queued up. Clear the request and perform it now. */
+    gboolean redissect = cf->redissection_queued == RESCAN_REDISSECT;
+    rescan_packets(cf, "Reprocessing", "all packets", redissect);
+  }
+
   if (cf->stop_flag) {
     simple_message_box(ESD_TYPE_WARN, NULL,
                   "The remaining packets in the file were discarded.\n"
@@ -756,7 +783,6 @@ cf_continue_tail(capture_file *cf, volatile int to_read, int *err)
 
   *err = 0;
 
-  packet_list_check_end();
   /* Don't freeze/thaw the list when doing live capture */
   /*packet_list_freeze();*/
 
@@ -782,7 +808,7 @@ cf_continue_tail(capture_file *cf, volatile int to_read, int *err)
            aren't any packets left to read) exit. */
         break;
       }
-      if (read_packet(cf, dfcode, &edt, (column_info *) cinfo, data_offset) != -1) {
+      if (read_record(cf, dfcode, &edt, cinfo, data_offset)) {
         newly_displayed_packets++;
       }
       to_read--;
@@ -825,7 +851,7 @@ cf_continue_tail(capture_file *cf, volatile int to_read, int *err)
 
   /* moving to the end of the packet list - if the user requested so and
      we have some new packets. */
-  if (newly_displayed_packets && auto_scroll_live && cf->count != 0)
+  if (newly_displayed_packets && cf->count != 0)
       packet_list_moveto_end();
 
   if (cf->state == FILE_READ_ABORTED) {
@@ -903,7 +929,6 @@ cf_finish_tail(capture_file *cf, int *err)
     return CF_READ_ERROR;
   }
 
-  packet_list_check_end();
   /* Don't freeze/thaw the list when doing live capture */
   /*packet_list_freeze();*/
 
@@ -916,7 +941,7 @@ cf_finish_tail(capture_file *cf, int *err)
          aren't any packets left to read) exit. */
       break;
     }
-    read_packet(cf, dfcode, &edt, cinfo, data_offset);
+    read_record(cf, dfcode, &edt, cinfo, data_offset);
   }
 
   /* Cleanup and release all dfilter resources */
@@ -937,9 +962,6 @@ cf_finish_tail(capture_file *cf, int *err)
     return CF_READ_ABORTED;
   }
 
-  if (auto_scroll_live && cf->count != 0)
-    packet_list_moveto_end();
-
   /* We're done reading sequentially through the file. */
   cf->state = FILE_READ_DONE;
 
@@ -1003,6 +1025,53 @@ cf_get_display_name(capture_file *cf)
   return displayname;
 }
 
+gchar *
+cf_get_basename(capture_file *cf)
+{
+  gchar *displayname;
+
+  /* Return a name to use in the GUI for the basename for files to
+     which we save statistics */
+  if (!cf->is_tempfile) {
+    /* Get the last component of the file name, and use that. */
+    if (cf->filename) {
+      displayname = g_filename_display_basename(cf->filename);
+
+      /* If the file name ends with any extension that corresponds
+         to a file type we support - including compressed versions
+         of those files - strip it off. */
+      size_t displayname_len = strlen(displayname);
+      GSList *extensions = wtap_get_all_file_extensions_list();
+      GSList *suffix;
+      for (suffix = extensions; suffix != NULL; suffix = g_slist_next(suffix)) {
+        /* Does the file name end with that extension? */
+        const char *extension = (char *)suffix->data;
+        size_t extension_len = strlen(extension);
+        if (displayname_len > extension_len &&
+          displayname[displayname_len - extension_len - 1] == '.' &&
+          strcmp(&displayname[displayname_len - extension_len], extension) == 0) {
+            /* Yes.  Strip the extension off, and return the result. */
+            displayname[displayname_len - extension_len - 1] = '\0';
+            break;
+        }
+      }
+      wtap_free_extensions_list(extensions);
+    } else {
+      displayname=g_strdup("");
+    }
+  } else {
+    /* The file we read is a temporary file from a live capture or
+       a merge operation; we don't mention its name, but, if it's
+       from a capture, give the source of the capture. */
+    if (cf->source) {
+      displayname = g_strdup(cf->source);
+    } else {
+      displayname = g_strdup("");
+    }
+  }
+  return displayname;
+}
+
 void cf_set_tempfile_source(capture_file *cf, gchar *source) {
   if (cf->source) {
     g_free(cf->source);
@@ -1072,13 +1141,11 @@ void cf_set_rfcode(capture_file *cf, dfilter_t *rfcode)
   cf->rfcode = rfcode;
 }
 
-static int
+static void
 add_packet_to_packet_list(frame_data *fdata, capture_file *cf,
     epan_dissect_t *edt, dfilter_t *dfcode, column_info *cinfo,
-    struct wtap_pkthdr *phdr, const guint8 *buf, gboolean add_to_packet_list)
+    wtap_rec *rec, const guint8 *buf, gboolean add_to_packet_list)
 {
-  gint            row               = -1;
-
   frame_data_set_before_dissect(fdata, &cf->elapsed_time,
                                 &cf->provider.ref, cf->provider.prev_dis);
   cf->provider.prev_cap = fdata;
@@ -1092,26 +1159,26 @@ add_packet_to_packet_list(frame_data *fdata, capture_file *cf,
    * TODO: actually detect that situation or maybe apply other optimizations? */
   if (edt->tree && color_filters_used()) {
     color_filters_prime_edt(edt);
-    fdata->flags.need_colorize = 1;
+    fdata->need_colorize = 1;
   }
 #endif
 
-  if (!fdata->flags.visited) {
+  if (!fdata->visited) {
     /* 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);
   }
 
   /* Dissect the frame. */
-  epan_dissect_run_with_taps(edt, cf->cd_t, phdr,
+  epan_dissect_run_with_taps(edt, cf->cd_t, rec,
                              frame_tvbuff_new(&cf->provider, fdata, buf),
                              fdata, cinfo);
 
   /* If we don't have a display filter, set "passed_dfilter" to 1. */
   if (dfcode != NULL) {
-    fdata->flags.passed_dfilter = dfilter_apply_edt(dfcode, edt) ? 1 : 0;
+    fdata->passed_dfilter = dfilter_apply_edt(dfcode, edt) ? 1 : 0;
 
-    if (fdata->flags.passed_dfilter) {
+    if (fdata->passed_dfilter) {
       /* This frame passed the display filter but it may depend on other
        * (potentially not displayed) frames.  Find those frames and mark them
        * as depended upon.
@@ -1119,17 +1186,17 @@ add_packet_to_packet_list(frame_data *fdata, capture_file *cf,
       g_slist_foreach(edt->pi.dependent_frames, find_and_mark_frame_depended_upon, cf->provider.frames);
     }
   } else
-    fdata->flags.passed_dfilter = 1;
+    fdata->passed_dfilter = 1;
 
-  if (fdata->flags.passed_dfilter || fdata->flags.ref_time)
+  if (fdata->passed_dfilter || fdata->ref_time)
     cf->displayed_count++;
 
   if (add_to_packet_list) {
     /* We fill the needed columns from new_packet_list */
-      row = packet_list_append(cinfo, fdata);
+    packet_list_append(cinfo, fdata);
   }
 
-  if (fdata->flags.passed_dfilter || fdata->flags.ref_time)
+  if (fdata->passed_dfilter || fdata->ref_time)
   {
     frame_data_set_after_dissect(fdata, &cf->cum_bytes);
     cf->provider.prev_dis = fdata;
@@ -1143,22 +1210,23 @@ add_packet_to_packet_list(frame_data *fdata, capture_file *cf,
   }
 
   epan_dissect_reset(edt);
-  return row;
 }
 
-/* read in a new packet */
-/* returns the row of the new packet in the packet list or -1 if not displayed */
-static int
-read_packet(capture_file *cf, dfilter_t *dfcode, epan_dissect_t *edt,
+/*
+ * Read in a new record.
+ * Returns TRUE if the packet was added to the packet (record) list,
+ * FALSE otherwise.
+ */
+static gboolean
+read_record(capture_file *cf, dfilter_t *dfcode, epan_dissect_t *edt,
             column_info *cinfo, gint64 offset)
 {
-  struct wtap_pkthdr *phdr = wtap_phdr(cf->provider.wth);
-  const guint8 *buf = wtap_buf_ptr(cf->provider.wth);
+  wtap_rec     *rec = wtap_get_rec(cf->provider.wth);
+  const guint8 *buf = wtap_get_buf_ptr(cf->provider.wth);
   frame_data    fdlocal;
-  guint32       framenum;
   frame_data   *fdata;
   gboolean      passed = TRUE;
-  int           row = -1;
+  gboolean      added = FALSE;
 
   /* Add this packet's link-layer encapsulation type to cf->linktypes, if
      it's not already there.
@@ -1166,20 +1234,20 @@ read_packet(capture_file *cf, dfilter_t *dfcode, epan_dissect_t *edt,
      link-layer encapsulation type, it'd be O(N^2) to read the file, but
      there are probably going to be a small number of encapsulation types
      in a file. */
-  cf_add_encapsulation_type(cf, phdr->pkt_encap);
-
-  /* The frame number of this packet is one more than the count of
-     frames in the file so far. */
-  framenum = cf->count + 1;
+  if (rec->rec_type == REC_TYPE_PACKET) {
+    cf_add_encapsulation_type(cf, rec->rec_header.packet_header.pkt_encap);
+  }
 
-  frame_data_init(&fdlocal, framenum, phdr, offset, cf->cum_bytes);
+  /* The frame number of this packet, if we add it to the set of frames,
+     would be one more than the count of frames in the file so far. */
+  frame_data_init(&fdlocal, cf->count + 1, rec, offset, cf->cum_bytes);
 
   if (cf->rfcode) {
     epan_dissect_t rf_edt;
 
     epan_dissect_init(&rf_edt, cf->epan, TRUE, FALSE);
     epan_dissect_prime_with_dfilter(&rf_edt, cf->rfcode);
-    epan_dissect_run(&rf_edt, cf->cd_t, phdr,
+    epan_dissect_run(&rf_edt, cf->cd_t, rec,
                      frame_tvbuff_new(&cf->provider, &fdlocal, buf),
                      &fdlocal, NULL);
     passed = dfilter_apply_edt(cf->rfcode, &rf_edt);
@@ -1187,21 +1255,25 @@ read_packet(capture_file *cf, dfilter_t *dfcode, epan_dissect_t *edt,
   }
 
   if (passed) {
+    added = TRUE;
+
     /* This does a shallow copy of fdlocal, which is good enough. */
     fdata = frame_data_sequence_add(cf->provider.frames, &fdlocal);
 
     cf->count++;
-    if (phdr->opt_comment != NULL)
+    if (rec->opt_comment != NULL)
       cf->packet_comment_count++;
     cf->f_datalen = offset + fdlocal.cap_len;
 
-    if (!cf->redissecting) {
-      row = add_packet_to_packet_list(fdata, cf, edt, dfcode,
-                                      cinfo, phdr, buf, TRUE);
+    /* When a redissection is in progress (or queued), do not process packets.
+     * This will be done once all (new) packets have been scanned. */
+    if (!cf->redissecting && cf->redissection_queued == RESCAN_NONE) {
+      add_packet_to_packet_list(fdata, cf, edt, dfcode,
+                                cinfo, rec, buf, TRUE);
     }
   }
 
-  return row;
+  return added;
 }
 
 
@@ -1246,15 +1318,8 @@ merge_callback(merge_event event, int num _U_,
       g_get_current_time(&cb_data->start_time);
       break;
 
-    case MERGE_EVENT_PACKET_WAS_READ:
+    case MERGE_EVENT_RECORD_WAS_READ:
       {
-        gint64 data_offset = 0;
-
-        /* Get the sum of the data offsets in all of the files. */
-        data_offset = 0;
-        for (i = 0; i < in_file_count; i++)
-          data_offset += in_files[i].data_offset;
-
         /* Create the progress bar if necessary.
            We check on every iteration of the loop, so that it takes no
            longer than the standard time to create it (otherwise, for a
@@ -1445,12 +1510,18 @@ cf_filter_packets(capture_file *cf, gchar *dftext, gboolean force)
 
 
   /* Now rescan the packet list, applying the new filter, but not
-     throwing away information constructed on a previous pass. */
-  if (cf->state != FILE_CLOSED) {
-    if (dftext == NULL) {
-      rescan_packets(cf, "Resetting", "Filter", FALSE);
-    } else {
-      rescan_packets(cf, "Filtering", dftext, FALSE);
+   * throwing away information constructed on a previous pass.
+   * If a dissection is already in progress, queue it.
+   */
+  if (cf->redissection_queued == RESCAN_NONE) {
+    if (cf->read_lock) {
+      cf->redissection_queued = RESCAN_SCAN;
+    } else if (cf->state != FILE_CLOSED) {
+      if (dftext == NULL) {
+        rescan_packets(cf, "Resetting", "Filter", FALSE);
+      } else {
+        rescan_packets(cf, "Filtering", dftext, FALSE);
+      }
     }
   }
 
@@ -1469,19 +1540,33 @@ cf_reftime_packets(capture_file *cf)
 void
 cf_redissect_packets(capture_file *cf)
 {
+  if (cf->read_lock || cf->redissection_queued == RESCAN_SCAN) {
+    /* Dissection in progress, signal redissection rather than rescanning. That
+     * would destroy the current (in-progress) dissection in "cf_read" which
+     * will cause issues when "cf_read" tries to add packets to the list.
+     * If a previous rescan was requested, "upgrade" it to a full redissection.
+     */
+    cf->redissection_queued = RESCAN_REDISSECT;
+  }
+  if (cf->redissection_queued != RESCAN_NONE) {
+    /* Redissection is (already) queued, wait for "cf_read" to finish. */
+    return;
+  }
+
   if (cf->state != FILE_CLOSED) {
+    /* Restart dissection in case no cf_read is pending. */
     rescan_packets(cf, "Reprocessing", "all packets", TRUE);
   }
 }
 
 gboolean
 cf_read_record_r(capture_file *cf, const frame_data *fdata,
-                 struct wtap_pkthdr *phdr, Buffer *buf)
+                 wtap_rec *rec, Buffer *buf)
 {
   int    err;
   gchar *err_info;
 
-  if (!wtap_seek_read(cf->provider.wth, fdata->file_off, phdr, buf, &err, &err_info)) {
+  if (!wtap_seek_read(cf->provider.wth, fdata->file_off, rec, buf, &err, &err_info)) {
     cfile_read_failure_alert_box(cf->filename, err, err_info);
     return FALSE;
   }
@@ -1491,7 +1576,7 @@ cf_read_record_r(capture_file *cf, const frame_data *fdata,
 gboolean
 cf_read_record(capture_file *cf, frame_data *fdata)
 {
-  return cf_read_record_r(cf, fdata, &cf->phdr, &cf->buf);
+  return cf_read_record_r(cf, fdata, &cf->rec, &cf->buf);
 }
 
 /* Rescan the list of packets, reconstructing the CList.
@@ -1529,6 +1614,12 @@ rescan_packets(capture_file *cf, const char *action, const char *action_item, gb
   gboolean    add_to_packet_list = FALSE;
   gboolean    compiled;
   guint32     frames_count;
+  gboolean    queued_rescan_type = RESCAN_NONE;
+
+  /* Rescan in progress, clear pending actions. */
+  cf->redissection_queued = RESCAN_NONE;
+  g_assert(!cf->read_lock);
+  cf->read_lock = TRUE;
 
   /* Compile the current display filter.
    * We assume this will not fail since cf->dfilter is only set in
@@ -1685,6 +1776,13 @@ rescan_packets(capture_file *cf, const char *action, const char *action_item, gb
       g_timer_start(prog_timer);
     }
 
+    queued_rescan_type = cf->redissection_queued;
+    if (queued_rescan_type != RESCAN_NONE) {
+      /* A redissection was requested while an existing redissection was
+       * pending. */
+      break;
+    }
+
     if (cf->stop_flag) {
       /* Well, the user decided to abort the filtering.  Just stop.
 
@@ -1713,7 +1811,7 @@ rescan_packets(capture_file *cf, const char *action, const char *action_item, gb
     }
 
     /* Frame dependencies from the previous dissection/filtering are no longer valid. */
-    fdata->flags.dependent_of_displayed = 0;
+    fdata->dependent_of_displayed = 0;
 
     if (!cf_read_record(cf, fdata))
       break; /* error reading the frame */
@@ -1721,13 +1819,13 @@ rescan_packets(capture_file *cf, const char *action, const char *action_item, gb
     /* If the previous frame is displayed, and we haven't yet seen the
        selected frame, remember that frame - it's the closest one we've
        yet seen before the selected frame. */
-    if (prev_frame_num != -1 && !selected_frame_seen && prev_frame->flags.passed_dfilter) {
+    if (prev_frame_num != -1 && !selected_frame_seen && prev_frame->passed_dfilter) {
       preceding_frame_num = prev_frame_num;
       preceding_frame = prev_frame;
     }
 
     add_packet_to_packet_list(fdata, cf, &edt, dfcode,
-                                    cinfo, &cf->phdr,
+                                    cinfo, &cf->rec,
                                     ws_buffer_start_ptr(&cf->buf),
                                     add_to_packet_list);
 
@@ -1735,13 +1833,13 @@ rescan_packets(capture_file *cf, const char *action, const char *action_item, gb
        seen displayed after the selected frame, remember this frame -
        it's the closest one we've yet seen at or after the selected
        frame. */
-    if (fdata->flags.passed_dfilter && selected_frame_seen && following_frame_num == -1) {
+    if (fdata->passed_dfilter && selected_frame_seen && following_frame_num == -1) {
       following_frame_num = fdata->num;
       following_frame = fdata;
     }
     if (fdata == selected_frame) {
       selected_frame_seen = TRUE;
-      if (fdata->flags.passed_dfilter)
+      if (fdata->passed_dfilter)
           selected_frame_num = fdata->num;
     }
 
@@ -1851,6 +1949,17 @@ rescan_packets(capture_file *cf, const char *action, const char *action_item, gb
 
   /* Cleanup and release all dfilter resources */
   dfilter_free(dfcode);
+
+  /* It is safe again to execute redissections. */
+  g_assert(cf->read_lock);
+  cf->read_lock = FALSE;
+
+  /* If another rescan (due to dfilter change) or redissection (due to profile
+   * change) was requested, the rescan above is aborted and restarted here. */
+  if (queued_rescan_type != RESCAN_NONE) {
+    redissect = redissect || queued_rescan_type == RESCAN_REDISSECT;
+    rescan_packets(cf, "Reprocessing", "all packets", redissect);
+  }
 }
 
 
@@ -1887,7 +1996,7 @@ ref_time_packets(capture_file *cf)
         cf->provider.ref = fdata;
       /* if this frames is marked as a reference time frame, reset
         firstsec and firstusec to this frame */
-    if (fdata->flags.ref_time)
+    if (fdata->ref_time)
         cf->provider.ref = fdata;
 
     /* If we don't have the time stamp of the previous displayed packet,
@@ -1912,7 +2021,7 @@ ref_time_packets(capture_file *cf)
 
     /* If this frame is displayed, get the time elapsed between the
      previous displayed packet and this packet. */
-    if ( fdata->flags.passed_dfilter ) {
+    if ( fdata->passed_dfilter ) {
         fdata->prev_dis_num = cf->provider.prev_dis->num;
         cf->provider.prev_dis = fdata;
     }
@@ -1920,11 +2029,11 @@ ref_time_packets(capture_file *cf)
     /*
      * Byte counts
      */
-    if ( (fdata->flags.passed_dfilter) || (fdata->flags.ref_time) ) {
+    if ( (fdata->passed_dfilter) || (fdata->ref_time) ) {
         /* This frame either passed the display filter list or is marked as
         a time reference frame.  All time reference frames are displayed
         even if they don't pass the display filter */
-        if (fdata->flags.ref_time) {
+        if (fdata->ref_time) {
             /* if this was a TIME REF frame we should reset the cum_bytes field */
             cf->cum_bytes = fdata->pkt_len;
             fdata->cum_bytes = cf->cum_bytes;
@@ -1946,12 +2055,13 @@ static psp_return_t
 process_specified_records(capture_file *cf, packet_range_t *range,
     const char *string1, const char *string2, gboolean terminate_is_stop,
     gboolean (*callback)(capture_file *, frame_data *,
-                         struct wtap_pkthdr *, const guint8 *, void *),
+                         wtap_rec *, const guint8 *, void *),
     void *callback_args,
     gboolean show_progress_bar)
 {
   guint32          framenum;
   frame_data      *fdata;
+  wtap_rec         rec;
   Buffer           buf;
   psp_return_t     ret     = PSP_FINISHED;
 
@@ -1962,9 +2072,8 @@ process_specified_records(capture_file *cf, packet_range_t *range,
   GTimeVal         progbar_start_time;
   gchar            progbar_status_str[100];
   range_process_e  process_this;
-  struct wtap_pkthdr phdr;
 
-  wtap_phdr_init(&phdr);
+  wtap_rec_init(&rec);
   ws_buffer_init(&buf, 1500);
 
   g_timer_start(prog_timer);
@@ -1973,6 +2082,12 @@ process_specified_records(capture_file *cf, packet_range_t *range,
   /* Progress so far. */
   progbar_val = 0.0f;
 
+  if (cf->read_lock) {
+    g_warning("Failing due to nested process_specified_records(\"%s\") call!", cf->filename);
+    return PSP_FAILED;
+  }
+  cf->read_lock = TRUE;
+
   cf->stop_flag = FALSE;
   g_get_current_time(&progbar_start_time);
 
@@ -2039,13 +2154,13 @@ process_specified_records(capture_file *cf, packet_range_t *range,
     }
 
     /* Get the packet */
-    if (!cf_read_record_r(cf, fdata, &phdr, &buf)) {
+    if (!cf_read_record_r(cf, fdata, &rec, &buf)) {
       /* Attempt to get the packet failed. */
       ret = PSP_FAILED;
       break;
     }
     /* Process the packet */
-    if (!callback(cf, fdata, &phdr, ws_buffer_start_ptr(&buf), callback_args)) {
+    if (!callback(cf, fdata, &rec, ws_buffer_start_ptr(&buf), callback_args)) {
       /* Callback failed.  We assume it reported the error appropriately. */
       ret = PSP_FAILED;
       break;
@@ -2058,7 +2173,10 @@ process_specified_records(capture_file *cf, packet_range_t *range,
     destroy_progress_dlg(progbar);
   g_timer_destroy(prog_timer);
 
-  wtap_phdr_cleanup(&phdr);
+  g_assert(cf->read_lock);
+  cf->read_lock = FALSE;
+
+  wtap_rec_cleanup(&rec);
   ws_buffer_free(&buf);
 
   return ret;
@@ -2070,13 +2188,12 @@ typedef struct {
 } retap_callback_args_t;
 
 static gboolean
-retap_packet(capture_file *cf, frame_data *fdata,
-             struct wtap_pkthdr *phdr, const guint8 *pd,
-             void *argsp)
+retap_packet(capture_file *cf, frame_data *fdata, wtap_rec *rec,
+             const guint8 *pd, void *argsp)
 {
   retap_callback_args_t *args = (retap_callback_args_t *)argsp;
 
-  epan_dissect_run_with_taps(&args->edt, cf->cd_t, phdr,
+  epan_dissect_run_with_taps(&args->edt, cf->cd_t, rec,
                              frame_tvbuff_new(&cf->provider, fdata, pd),
                              fdata, args->cinfo);
   epan_dissect_reset(&args->edt);
@@ -2131,6 +2248,7 @@ cf_retap_packets(capture_file *cf)
                                   "all packets", TRUE, retap_packet,
                                   &callback_args, TRUE);
 
+  packet_range_cleanup(&range);
   epan_dissect_cleanup(&callback_args.edt);
 
   cf_callback_invoke(cf_cb_file_retap_finished, cf);
@@ -2170,9 +2288,8 @@ typedef struct {
 } print_callback_args_t;
 
 static gboolean
-print_packet(capture_file *cf, frame_data *fdata,
-             struct wtap_pkthdr *phdr, const guint8 *pd,
-             void *argsp)
+print_packet(capture_file *cf, frame_data *fdata, wtap_rec *rec,
+             const guint8 *pd, void *argsp)
 {
   print_callback_args_t *args = (print_callback_args_t *)argsp;
   int             i;
@@ -2188,17 +2305,24 @@ print_packet(capture_file *cf, frame_data *fdata,
      information. */
   if (args->print_args->print_summary) {
     col_custom_prime_edt(&args->edt, &cf->cinfo);
-    epan_dissect_run(&args->edt, cf->cd_t, phdr,
+    epan_dissect_run(&args->edt, cf->cd_t, rec,
                      frame_tvbuff_new(&cf->provider, fdata, pd),
                      fdata, &cf->cinfo);
     epan_dissect_fill_in_columns(&args->edt, FALSE, TRUE);
   } else
-    epan_dissect_run(&args->edt, cf->cd_t, phdr,
+    epan_dissect_run(&args->edt, cf->cd_t, rec,
                      frame_tvbuff_new(&cf->provider, fdata, pd), fdata, NULL);
 
   if (args->print_formfeed) {
     if (!new_page(args->print_args->stream))
       goto fail;
+
+    /*
+     * Print another header line if we print a packet summary on the
+     * new page.
+     */
+    if (args->print_args->print_col_headings)
+        args->print_header_line = TRUE;
   } else {
       if (args->print_separator) {
         if (!print_line(args->print_args->stream, 0, ""))
@@ -2372,6 +2496,12 @@ cf_print_packets(capture_file *cf, print_args_t *print_args,
         }
     }
 
+    /* if num_visible_col is 0, we are done */
+    if (num_visible_col == 0) {
+      g_free(callback_args.header_line_buf);
+      return CF_PRINT_OK;
+    }
+
     /* Find the widths for each of the columns - maximum of the
        width of the title and the width of the data - and construct
        a buffer with a line containing the column titles. */
@@ -2406,7 +2536,7 @@ cf_print_packets(capture_file *cf, print_args_t *print_args,
 
       /* Find the length of the string for this column. */
       column_len = (int) strlen(cf->cinfo.columns[i].col_title);
-      if (callback_args.col_widths[i] > column_len)
+      if (callback_args.col_widths[visible_col_count] > column_len)
         column_len = callback_args.col_widths[visible_col_count];
 
       /* Make sure there's room in the line buffer for the column; if not,
@@ -2500,21 +2630,21 @@ typedef struct {
   FILE *fh;
   epan_dissect_t edt;
   print_args_t *print_args;
+  json_dumper jdumper;
 } write_packet_callback_args_t;
 
 static gboolean
-write_pdml_packet(capture_file *cf, frame_data *fdata,
-                  struct wtap_pkthdr *phdr, const guint8 *pd,
-          void *argsp)
+write_pdml_packet(capture_file *cf, frame_data *fdata, wtap_rec *rec,
+                  const guint8 *pd, void *argsp)
 {
   write_packet_callback_args_t *args = (write_packet_callback_args_t *)argsp;
 
   /* Create the protocol tree, but don't fill in the column information. */
-  epan_dissect_run(&args->edt, cf->cd_t, phdr,
+  epan_dissect_run(&args->edt, cf->cd_t, rec,
                    frame_tvbuff_new(&cf->provider, fdata, pd), fdata, NULL);
 
   /* Write out the information in that tree. */
-  write_pdml_proto_tree(NULL, NULL, PF_NONE, &args->edt, args->fh, FALSE);
+  write_pdml_proto_tree(NULL, NULL, PF_NONE, &args->edt, &cf->cinfo, args->fh, FALSE);
 
   epan_dissect_reset(&args->edt);
 
@@ -2579,15 +2709,14 @@ cf_write_pdml_packets(capture_file *cf, print_args_t *print_args)
 }
 
 static gboolean
-write_psml_packet(capture_file *cf, frame_data *fdata,
-                  struct wtap_pkthdr *phdr, const guint8 *pd,
-          void *argsp)
+write_psml_packet(capture_file *cf, frame_data *fdata, wtap_rec *rec,
+                  const guint8 *pd, void *argsp)
 {
   write_packet_callback_args_t *args = (write_packet_callback_args_t *)argsp;
 
   /* Fill in the column information */
   col_custom_prime_edt(&args->edt, &cf->cinfo);
-  epan_dissect_run(&args->edt, cf->cd_t, phdr,
+  epan_dissect_run(&args->edt, cf->cd_t, rec,
                    frame_tvbuff_new(&cf->provider, fdata, pd),
                    fdata, &cf->cinfo);
   epan_dissect_fill_in_columns(&args->edt, FALSE, TRUE);
@@ -2664,15 +2793,14 @@ cf_write_psml_packets(capture_file *cf, print_args_t *print_args)
 }
 
 static gboolean
-write_csv_packet(capture_file *cf, frame_data *fdata,
-                 struct wtap_pkthdr *phdr, const guint8 *pd,
-                 void *argsp)
+write_csv_packet(capture_file *cf, frame_data *fdata, wtap_rec *rec,
+                 const guint8 *pd, void *argsp)
 {
   write_packet_callback_args_t *args = (write_packet_callback_args_t *)argsp;
 
   /* Fill in the column information */
   col_custom_prime_edt(&args->edt, &cf->cinfo);
-  epan_dissect_run(&args->edt, cf->cd_t, phdr,
+  epan_dissect_run(&args->edt, cf->cd_t, rec,
                    frame_tvbuff_new(&cf->provider, fdata, pd),
                    fdata, &cf->cinfo);
   epan_dissect_fill_in_columns(&args->edt, FALSE, TRUE);
@@ -2741,13 +2869,12 @@ cf_write_csv_packets(capture_file *cf, print_args_t *print_args)
 }
 
 static gboolean
-carrays_write_packet(capture_file *cf, frame_data *fdata,
-             struct wtap_pkthdr *phdr,
-             const guint8 *pd, void *argsp)
+carrays_write_packet(capture_file *cf, frame_data *fdata, wtap_rec *rec,
+                     const guint8 *pd, void *argsp)
 {
   write_packet_callback_args_t *args = (write_packet_callback_args_t *)argsp;
 
-  epan_dissect_run(&args->edt, cf->cd_t, phdr,
+  epan_dissect_run(&args->edt, cf->cd_t, rec,
                    frame_tvbuff_new(&cf->provider, fdata, pd), fdata, NULL);
   write_carrays_hex_data(fdata->num, args->fh, &args->edt);
   epan_dissect_reset(&args->edt);
@@ -2803,20 +2930,20 @@ cf_write_carrays_packets(capture_file *cf, print_args_t *print_args)
 }
 
 static gboolean
-write_json_packet(capture_file *cf, frame_data *fdata,
-                  struct wtap_pkthdr *phdr, const guint8 *pd,
-          void *argsp)
+write_json_packet(capture_file *cf, frame_data *fdata, wtap_rec *rec,
+                  const guint8 *pd, void *argsp)
 {
   write_packet_callback_args_t *args = (write_packet_callback_args_t *)argsp;
 
   /* Create the protocol tree, but don't fill in the column information. */
-  epan_dissect_run(&args->edt, cf->cd_t, phdr,
+  epan_dissect_run(&args->edt, cf->cd_t, rec,
                    frame_tvbuff_new(&cf->provider, fdata, pd), fdata, NULL);
 
   /* Write out the information in that tree. */
   write_json_proto_tree(NULL, args->print_args->print_dissections,
                         args->print_args->print_hex, NULL, PF_NONE,
-                        &args->edt, proto_node_group_children_by_unique, args->fh);
+                        &args->edt, &cf->cinfo, proto_node_group_children_by_unique,
+                        &args->jdumper);
 
   epan_dissect_reset(&args->edt);
 
@@ -2834,7 +2961,7 @@ cf_write_json_packets(capture_file *cf, print_args_t *print_args)
   if (fh == NULL)
     return CF_PRINT_OPEN_ERROR; /* attempt to open destination failed */
 
-  write_json_preamble(fh);
+  callback_args.jdumper = write_json_preamble(fh);
   if (ferror(fh)) {
     fclose(fh);
     return CF_PRINT_WRITE_ERROR;
@@ -2868,7 +2995,7 @@ cf_write_json_packets(capture_file *cf, print_args_t *print_args)
     return CF_PRINT_WRITE_ERROR;
   }
 
-  write_json_finale(fh);
+  write_json_finale(&callback_args.jdumper);
   if (ferror(fh)) {
     fclose(fh);
     return CF_PRINT_WRITE_ERROR;
@@ -2918,7 +3045,7 @@ match_protocol_tree(capture_file *cf, frame_data *fdata, void *criterion)
   /* Construct the protocol tree, including the displayed text */
   epan_dissect_init(&edt, cf->epan, TRUE, TRUE);
   /* We don't need the column information */
-  epan_dissect_run(&edt, cf->cd_t, &cf->phdr,
+  epan_dissect_run(&edt, cf->cd_t, &cf->rec,
                    frame_tvbuff_new_buffer(&cf->provider, fdata, &cf->buf),
                    fdata, NULL);
 
@@ -3032,7 +3159,7 @@ match_summary_line(capture_file *cf, frame_data *fdata, void *criterion)
   /* Don't bother constructing the protocol tree */
   epan_dissect_init(&edt, cf->epan, FALSE, FALSE);
   /* Get the column information */
-  epan_dissect_run(&edt, cf->cd_t, &cf->phdr,
+  epan_dissect_run(&edt, cf->cd_t, &cf->rec,
                    frame_tvbuff_new_buffer(&cf->provider, fdata, &cf->buf),
                    fdata, &cf->cinfo);
 
@@ -3379,7 +3506,7 @@ match_dfilter(capture_file *cf, frame_data *fdata, void *criterion)
 
   epan_dissect_init(&edt, cf->epan, TRUE, FALSE);
   epan_dissect_prime_with_dfilter(&edt, sfcode);
-  epan_dissect_run(&edt, cf->cd_t, &cf->phdr,
+  epan_dissect_run(&edt, cf->cd_t, &cf->rec,
                    frame_tvbuff_new_buffer(&cf->provider, fdata, &cf->buf),
                    fdata, NULL);
   result = dfilter_apply_edt(sfcode, &edt) ? MR_MATCHED : MR_NOTMATCHED;
@@ -3396,7 +3523,7 @@ cf_find_packet_marked(capture_file *cf, search_direction dir)
 static match_result
 match_marked(capture_file *cf _U_, frame_data *fdata, void *criterion _U_)
 {
-  return fdata->flags.marked ? MR_MATCHED : MR_NOTMATCHED;
+  return fdata->marked ? MR_MATCHED : MR_NOTMATCHED;
 }
 
 gboolean
@@ -3408,7 +3535,7 @@ cf_find_packet_time_reference(capture_file *cf, search_direction dir)
 static match_result
 match_time_reference(capture_file *cf _U_, frame_data *fdata, void *criterion _U_)
 {
-  return fdata->flags.ref_time ? MR_MATCHED : MR_NOTMATCHED;
+  return fdata->ref_time ? MR_MATCHED : MR_NOTMATCHED;
 }
 
 static gboolean
@@ -3418,6 +3545,7 @@ find_packet(capture_file *cf,
 {
   frame_data  *start_fd;
   guint32      framenum;
+  guint32      prev_framenum;
   frame_data  *fdata;
   frame_data  *new_fd = NULL;
   progdlg_t   *progbar = NULL;
@@ -3432,132 +3560,130 @@ find_packet(capture_file *cf,
 
   start_fd = cf->current_frame;
   if (start_fd != NULL)  {
-    /* Iterate through the list of packets, starting at the packet we've
-       picked, calling a routine to run the filter on the packet, see if
-       it matches, and stop if so.  */
-    count = 0;
-    framenum = start_fd->num;
+    prev_framenum = start_fd->num;
+  } else {
+    prev_framenum = 0;  /* No start packet selected. */
+  }
 
-    g_timer_start(prog_timer);
-    /* Progress so far. */
-    progbar_val = 0.0f;
+  /* Iterate through the list of packets, starting at the packet we've
+     picked, calling a routine to run the filter on the packet, see if
+     it matches, and stop if so.  */
+  count = 0;
+  framenum = prev_framenum;
+
+  g_timer_start(prog_timer);
+  /* Progress so far. */
+  progbar_val = 0.0f;
 
-    cf->stop_flag = FALSE;
-    g_get_current_time(&start_time);
+  cf->stop_flag = FALSE;
+  g_get_current_time(&start_time);
 
-    title = cf->sfilter?cf->sfilter:"";
-    for (;;) {
-      /* Create the progress bar if necessary.
-         We check on every iteration of the loop, so that it takes no
-         longer than the standard time to create it (otherwise, for a
+  title = cf->sfilter?cf->sfilter:"";
+  for (;;) {
+    /* Create the progress bar if necessary.
+       We check on every iteration of the loop, so that it takes no
+       longer than the standard time to create it (otherwise, for a
          large file, we might take considerably longer than that standard
-         time in order to get to the next progress bar step). */
-      if (progbar == NULL)
-         progbar = delayed_create_progress_dlg(cf->window, "Searching", title,
-           FALSE, &cf->stop_flag, &start_time, progbar_val);
+       time in order to get to the next progress bar step). */
+    if (progbar == NULL)
+       progbar = delayed_create_progress_dlg(cf->window, "Searching", title,
+         FALSE, &cf->stop_flag, &start_time, progbar_val);
 
-      /*
-       * Update the progress bar, but do it only after PROGBAR_UPDATE_INTERVAL
-       * has elapsed. Calling update_progress_dlg and packets_bar_update will
-       * likely trigger UI paint events, which might take a while depending on
-       * the platform and display. Reset our timer *after* painting.
+    /*
+     * Update the progress bar, but do it only after PROGBAR_UPDATE_INTERVAL
+     * has elapsed. Calling update_progress_dlg and packets_bar_update will
+     * likely trigger UI paint events, which might take a while depending on
+     * the platform and display. Reset our timer *after* painting.
+     */
+    if (g_timer_elapsed(prog_timer, NULL) > PROGBAR_UPDATE_INTERVAL) {
+      /* let's not divide by zero. I should never be started
+       * with count == 0, so let's assert that
        */
-      if (g_timer_elapsed(prog_timer, NULL) > PROGBAR_UPDATE_INTERVAL) {
-        /* let's not divide by zero. I should never be started
-         * with count == 0, so let's assert that
-         */
-        g_assert(cf->count > 0);
-
-        progbar_val = (gfloat) count / cf->count;
+      g_assert(cf->count > 0);
 
-        g_snprintf(status_str, sizeof(status_str),
-                    "%4u of %u packets", count, cf->count);
-        update_progress_dlg(progbar, progbar_val, status_str);
+      progbar_val = (gfloat) count / cf->count;
 
-        g_timer_start(prog_timer);
-      }
+      g_snprintf(status_str, sizeof(status_str),
+                  "%4u of %u packets", count, cf->count);
+      update_progress_dlg(progbar, progbar_val, status_str);
 
-      if (cf->stop_flag) {
-        /* Well, the user decided to abort the search.  Go back to the
-           frame where we started. */
-        new_fd = start_fd;
-        break;
-      }
+      g_timer_start(prog_timer);
+    }
 
-      /* Go past the current frame. */
-      if (dir == SD_BACKWARD) {
-        /* Go on to the previous frame. */
-        if (framenum == 1) {
-          /*
-           * XXX - other apps have a bit more of a detailed message
-           * for this, and instead of offering "OK" and "Cancel",
-           * they offer things such as "Continue" and "Cancel";
-           * we need an API for popping up alert boxes with
-           * {Verb} and "Cancel".
-           */
-
-          if (prefs.gui_find_wrap)
-          {
-              statusbar_push_temporary_msg("Search reached the beginning. Continuing at end.");
-              framenum = cf->count;     /* wrap around */
-          }
-          else
-          {
-              statusbar_push_temporary_msg("Search reached the beginning.");
-              framenum = start_fd->num; /* stay on previous packet */
-          }
-        } else
-          framenum--;
-      } else {
-        /* Go on to the next frame. */
-        if (framenum == cf->count) {
-          if (prefs.gui_find_wrap)
-          {
-              statusbar_push_temporary_msg("Search reached the end. Continuing at beginning.");
-              framenum = 1;             /* wrap around */
-          }
-          else
-          {
-              statusbar_push_temporary_msg("Search reached the end.");
-              framenum = start_fd->num; /* stay on previous packet */
-          }
-        } else
-          framenum++;
-      }
-      fdata = frame_data_sequence_find(cf->provider.frames, framenum);
+    if (cf->stop_flag) {
+      /* Well, the user decided to abort the search.  Go back to the
+         frame where we started. */
+      new_fd = start_fd;
+      break;
+    }
 
-      count++;
+    /* Go past the current frame. */
+    if (dir == SD_BACKWARD) {
+      /* Go on to the previous frame. */
+      if (framenum <= 1) {
+        /*
+         * XXX - other apps have a bit more of a detailed message
+         * for this, and instead of offering "OK" and "Cancel",
+         * they offer things such as "Continue" and "Cancel";
+         * we need an API for popping up alert boxes with
+         * {Verb} and "Cancel".
+         */
 
-      /* Is this packet in the display? */
-      if (fdata->flags.passed_dfilter) {
-        /* Yes.  Does it match the search criterion? */
-        result = (*match_function)(cf, fdata, criterion);
-        if (result == MR_ERROR) {
-          /* Error; our caller has reported the error.  Go back to the frame
-             where we started. */
-          new_fd = start_fd;
-          break;
-        } else if (result == MR_MATCHED) {
-          /* Yes.  Go to the new frame. */
-          new_fd = fdata;
-          break;
+        if (prefs.gui_find_wrap) {
+          statusbar_push_temporary_msg("Search reached the beginning. Continuing at end.");
+          framenum = cf->count;     /* wrap around */
+        } else {
+          statusbar_push_temporary_msg("Search reached the beginning.");
+          framenum = prev_framenum; /* stay on previous packet */
         }
-      }
+      } else
+        framenum--;
+    } else {
+      /* Go on to the next frame. */
+      if (framenum == cf->count) {
+        if (prefs.gui_find_wrap) {
+          statusbar_push_temporary_msg("Search reached the end. Continuing at beginning.");
+          framenum = 1;             /* wrap around */
+        } else {
+          statusbar_push_temporary_msg("Search reached the end.");
+          framenum = prev_framenum; /* stay on previous packet */
+        }
+      } else
+        framenum++;
+    }
 
-      if (fdata == start_fd) {
-        /* We're back to the frame we were on originally, and that frame
-           doesn't match the search filter.  The search failed. */
+    fdata = frame_data_sequence_find(cf->provider.frames, framenum);
+    count++;
+
+    /* Is this packet in the display? */
+    if (fdata && fdata->passed_dfilter) {
+      /* Yes.  Does it match the search criterion? */
+      result = (*match_function)(cf, fdata, criterion);
+      if (result == MR_ERROR) {
+        /* Error; our caller has reported the error.  Go back to the frame
+           where we started. */
+        new_fd = start_fd;
+        break;
+      } else if (result == MR_MATCHED) {
+        /* Yes.  Go to the new frame. */
+        new_fd = fdata;
         break;
       }
     }
 
-    /* We're done scanning the packets; destroy the progress bar if it
-       was created. */
-    if (progbar != NULL)
-      destroy_progress_dlg(progbar);
-    g_timer_destroy(prog_timer);
+    if (fdata == start_fd) {
+      /* We're back to the frame we were on originally, and that frame
+         doesn't match the search filter.  The search failed. */
+      break;
+    }
   }
 
+  /* We're done scanning the packets; destroy the progress bar if it
+     was created. */
+  if (progbar != NULL)
+    destroy_progress_dlg(progbar);
+  g_timer_destroy(prog_timer);
+
   if (new_fd != NULL) {
     /* Find and select */
     cf->search_in_progress = TRUE;
@@ -3597,7 +3723,7 @@ cf_goto_frame(capture_file *cf, guint fnumber)
     statusbar_push_temporary_msg("There is no packet number %u.", fnumber);
     return FALSE;   /* we failed to go to that packet */
   }
-  if (!fdata->flags.passed_dfilter) {
+  if (!fdata->passed_dfilter) {
     /* that packet currently isn't displayed */
     /* XXX - add it to the set of displayed packets? */
     statusbar_push_temporary_msg("Packet number %u isn't displayed.", fnumber);
@@ -3667,14 +3793,12 @@ cf_select_packet(capture_file *cf, int row)
   cf->edt = epan_dissect_new(cf->epan, TRUE, TRUE);
 
   tap_build_interesting(cf->edt);
-  epan_dissect_run(cf->edt, cf->cd_t, &cf->phdr,
+  epan_dissect_run(cf->edt, cf->cd_t, &cf->rec,
                    frame_tvbuff_new_buffer(&cf->provider, cf->current_frame, &cf->buf),
                    cf->current_frame, NULL);
 
   dfilter_macro_build_ftv_cache(cf->edt->tree);
 
-  cf_callback_invoke(cf_cb_packet_selected, cf);
-
   if (old_edt != NULL)
     epan_dissect_free(old_edt);
 
@@ -3692,33 +3816,19 @@ cf_unselect_packet(capture_file *cf)
   cf->current_frame = NULL;
   cf->current_row = 0;
 
-  cf_callback_invoke(cf_cb_packet_unselected, cf);
-
-  /* No protocol tree means no selected field. */
-  cf_unselect_field(cf);
-
   /* Destroy the epan_dissect_t for the unselected packet. */
   if (old_edt != NULL)
     epan_dissect_free(old_edt);
 }
 
-/* Unset the selected protocol tree field, if any. */
-void
-cf_unselect_field(capture_file *cf)
-{
-  cf->finfo_selected = NULL;
-
-  cf_callback_invoke(cf_cb_field_unselected, cf);
-}
-
 /*
  * Mark a particular frame.
  */
 void
 cf_mark_frame(capture_file *cf, frame_data *frame)
 {
-  if (! frame->flags.marked) {
-    frame->flags.marked = TRUE;
+  if (! frame->marked) {
+    frame->marked = TRUE;
     if (cf->count > cf->marked_count)
       cf->marked_count++;
   }
@@ -3730,8 +3840,8 @@ cf_mark_frame(capture_file *cf, frame_data *frame)
 void
 cf_unmark_frame(capture_file *cf, frame_data *frame)
 {
-  if (frame->flags.marked) {
-    frame->flags.marked = FALSE;
+  if (frame->marked) {
+    frame->marked = FALSE;
     if (cf->marked_count > 0)
       cf->marked_count--;
   }
@@ -3743,8 +3853,8 @@ cf_unmark_frame(capture_file *cf, frame_data *frame)
 void
 cf_ignore_frame(capture_file *cf, frame_data *frame)
 {
-  if (! frame->flags.ignored) {
-    frame->flags.ignored = TRUE;
+  if (! frame->ignored) {
+    frame->ignored = TRUE;
     if (cf->count > cf->ignored_count)
       cf->ignored_count++;
   }
@@ -3756,8 +3866,8 @@ cf_ignore_frame(capture_file *cf, frame_data *frame)
 void
 cf_unignore_frame(capture_file *cf, frame_data *frame)
 {
-  if (frame->flags.ignored) {
-    frame->flags.ignored = FALSE;
+  if (frame->ignored) {
+    frame->ignored = FALSE;
     if (cf->ignored_count > 0)
       cf->ignored_count--;
   }
@@ -3826,22 +3936,22 @@ cf_get_packet_comment(capture_file *cf, const frame_data *fd)
   char *comment;
 
   /* fetch user comment */
-  if (fd->flags.has_user_comment)
+  if (fd->has_user_comment)
     return g_strdup(cap_file_provider_get_user_comment(&cf->provider, fd));
 
   /* fetch phdr comment */
-  if (fd->flags.has_phdr_comment) {
-    struct wtap_pkthdr phdr; /* Packet header */
-    Buffer buf; /* Packet data */
+  if (fd->has_phdr_comment) {
+    wtap_rec rec; /* Record metadata */
+    Buffer buf;   /* Record data */
 
-    wtap_phdr_init(&phdr);
+    wtap_rec_init(&rec);
     ws_buffer_init(&buf, 1500);
 
-    if (!cf_read_record_r(cf, fd, &phdr, &buf))
+    if (!cf_read_record_r(cf, fd, &rec, &buf))
       { /* XXX, what we can do here? */ }
 
-    comment = phdr.opt_comment;
-    wtap_phdr_cleanup(&phdr);
+    comment = rec.opt_comment;
+    wtap_rec_cleanup(&rec);
     ws_buffer_free(&buf);
     return comment;
   }
@@ -3925,67 +4035,36 @@ typedef struct {
  * up a message box for the failure.
  */
 static gboolean
-save_record(capture_file *cf, frame_data *fdata,
-            struct wtap_pkthdr *phdr, const guint8 *pd,
-            void *argsp)
+save_record(capture_file *cf, frame_data *fdata, wtap_rec *rec,
+            const guint8 *pd, void *argsp)
 {
   save_callback_args_t *args = (save_callback_args_t *)argsp;
-  struct wtap_pkthdr    hdr;
+  wtap_rec      new_rec;
   int           err;
   gchar        *err_info;
   const char   *pkt_comment;
 
-  if (fdata->flags.has_user_comment)
+  /* Copy the record information from what was read in from the file. */
+  new_rec = *rec;
+
+  /* Make changes based on anything that the user has done but that
+     hasn't been saved yet. */
+  if (fdata->has_user_comment)
     pkt_comment = cap_file_provider_get_user_comment(&cf->provider, fdata);
   else
-    pkt_comment = phdr->opt_comment;
-
-  /* init the wtap header for saving */
-  /* TODO: reuse phdr */
-  /* XXX - these are the only flags that correspond to data that we have
-     in the frame_data structure and that matter on a per-packet basis.
-
-     For WTAP_HAS_CAP_LEN, either the file format has separate "captured"
-     and "on the wire" lengths, or it doesn't.
-
-     For WTAP_HAS_DROP_COUNT, Wiretap doesn't actually supply the value
-     to its callers.
-
-     For WTAP_HAS_PACK_FLAGS, we currently don't save the FCS length
-     from the packet flags. */
-  hdr.rec_type = phdr->rec_type;
-  hdr.presence_flags = 0;
-  if (fdata->flags.has_ts)
-    hdr.presence_flags |= WTAP_HAS_TS;
-  if (phdr->presence_flags & WTAP_HAS_INTERFACE_ID)
-    hdr.presence_flags |= WTAP_HAS_INTERFACE_ID;
-  if (phdr->presence_flags & WTAP_HAS_PACK_FLAGS)
-    hdr.presence_flags |= WTAP_HAS_PACK_FLAGS;
-  hdr.ts           = phdr->ts;
-  hdr.caplen       = phdr->caplen;
-  hdr.len          = phdr->len;
-  hdr.pkt_encap    = phdr->pkt_encap;
-  /* pcapng */
-  hdr.interface_id = phdr->interface_id;   /* identifier of the interface. */
-  /* options */
-  hdr.pack_flags   = phdr->pack_flags;
-  hdr.opt_comment  = g_strdup(pkt_comment);
-  hdr.has_comment_changed = fdata->flags.has_user_comment ? TRUE : FALSE;
-
-  /* pseudo */
-  hdr.pseudo_header = phdr->pseudo_header;
-#if 0
-  hdr.drop_count   =
-  hdr.pack_flags   =     /* XXX - 0 for now (any value for "we don't have it"?) */
-#endif
+    pkt_comment = rec->opt_comment;
+  new_rec.opt_comment  = g_strdup(pkt_comment);
+  new_rec.has_comment_changed = fdata->has_user_comment ? TRUE : FALSE;
+  /* XXX - what if times have been shifted? */
+
   /* and save the packet */
-  if (!wtap_dump(args->pdh, &hdr, pd, &err, &err_info)) {
+  if (!wtap_dump(args->pdh, &new_rec, pd, &err, &err_info)) {
     cfile_write_failure_alert_box(NULL, args->fname, err, err_info, fdata->num,
                                   args->file_type);
     return FALSE;
   }
 
-  g_free(hdr.opt_comment);
+  g_free(new_rec.opt_comment);
   return TRUE;
 }
 
@@ -4100,7 +4179,7 @@ cf_has_unsaved_data(capture_file *cf)
 static cf_read_status_t
 rescan_file(capture_file *cf, const char *fname, gboolean is_tempfile)
 {
-  const struct wtap_pkthdr *phdr;
+  const wtap_rec      *rec;
   int                  err;
   gchar               *err_info;
   gchar               *name_ptr;
@@ -4154,9 +4233,9 @@ rescan_file(capture_file *cf, const char *fname, gboolean is_tempfile)
 
   cf_callback_invoke(cf_cb_file_rescan_started, cf);
 
-  /* Record whether the file is compressed.
+  /* Record the file's compression type.
      XXX - do we know this at open time? */
-  cf->iscompressed = wtap_iscompressed(cf->provider.wth);
+  cf->compression_type = wtap_get_compression_type(cf->provider.wth);
 
   /* Find the size of the file. */
   size = wtap_file_size(cf->provider.wth, NULL);
@@ -4167,7 +4246,7 @@ rescan_file(capture_file *cf, const char *fname, gboolean is_tempfile)
   g_get_current_time(&start_time);
 
   framenum = 0;
-  phdr = wtap_phdr(cf->provider.wth);
+  rec = wtap_get_rec(cf->provider.wth);
   while ((wtap_read(cf->provider.wth, &err, &err_info, &data_offset))) {
     framenum++;
     fdata = frame_data_sequence_find(cf->provider.frames, framenum);
@@ -4212,7 +4291,9 @@ rescan_file(capture_file *cf, const char *fname, gboolean is_tempfile)
        link-layer encapsulation type, it'd be O(N^2) to read the file, but
        there are probably going to be a small number of encapsulation types
        in a file. */
-    cf_add_encapsulation_type(cf, phdr->pkt_encap);
+    if (rec->rec_type == REC_TYPE_PACKET) {
+      cf_add_encapsulation_type(cf, rec->rec_header.packet_header.pkt_encap);
+    }
   }
 
   /* Free the display name */
@@ -4257,8 +4338,8 @@ rescan_file(capture_file *cf, const char *fname, gboolean is_tempfile)
 
 cf_write_status_t
 cf_save_records(capture_file *cf, const char *fname, guint save_format,
-                gboolean compressed, gboolean discard_comments,
-                gboolean dont_reopen)
+                wtap_compression_type compression_type,
+                gboolean discard_comments, gboolean dont_reopen)
 {
   gchar           *err_info;
   gchar           *fname_new = NULL;
@@ -4278,17 +4359,24 @@ cf_save_records(capture_file *cf, const char *fname, guint save_format,
   save_callback_args_t callback_args;
   gboolean needs_reload = FALSE;
 
+  /* XXX caller should avoid saving the file while a read is pending
+   * (e.g. by delaying the save action) */
+  if (cf->read_lock) {
+    g_warning("cf_save_records(\"%s\") while the file is being read, potential crash ahead", fname);
+  }
+
   cf_callback_invoke(cf_cb_file_save_started, (gpointer)fname);
 
   addr_lists = get_addrinfo_list();
 
-  if (save_format == cf->cd_t && compressed == cf->iscompressed
+  if (save_format == cf->cd_t && compression_type == cf->compression_type
       && !discard_comments && !cf->unsaved_changes
-      && !(addr_lists && wtap_dump_has_name_resolution(save_format))) {
-    /* We're saving in the format it's already in, and we're
-       not discarding comments, and there are no changes we have
-       in memory that aren't saved to the file, and we have no name
-       resolution blocks to write, so we can just move or copy the raw data. */
+      && (wtap_addrinfo_list_empty(addr_lists) || !wtap_dump_has_name_resolution(save_format))) {
+    /* We're saving in the format it's already in, and we're not discarding
+       comments, and there are no changes we have in memory that aren't saved
+       to the file, and we have no name resolution information to write or
+       the file format we're saving in doesn't support writing name
+       resolution information, so we can just move or copy the raw data. */
 
     if (cf->is_tempfile) {
       /* The file being saved is a temporary file from a live
@@ -4335,6 +4423,7 @@ cf_save_records(capture_file *cf, const char *fname, guint save_format,
         }
       }
 #else
+      /* Windows - copy the file to its new location. */
       how_to_save = SAVE_WITH_COPY;
 #endif
     } else {
@@ -4364,18 +4453,18 @@ cf_save_records(capture_file *cf, const char *fname, guint save_format,
        or moving the capture file, we have to do it by writing the packets
        out in Wiretap. */
 
-    GArray                      *shb_hdrs = NULL;
-    wtapng_iface_descriptions_t *idb_inf = NULL;
-    GArray                      *nrb_hdrs = NULL;
+    wtap_dump_params params;
     int encap;
 
-    /* XXX: what free's this shb_hdr? */
-    shb_hdrs = wtap_file_get_shb_for_new_file(cf->provider.wth);
-    idb_inf = wtap_file_get_idb_info(cf->provider.wth);
-    nrb_hdrs = wtap_file_get_nrb_for_new_file(cf->provider.wth);
+    /* XXX: what free's params.shb_hdr? */
+    wtap_dump_params_init(&params, cf->provider.wth);
 
     /* Determine what file encapsulation type we should use. */
     encap = wtap_dump_file_encap_type(cf->linktypes);
+    params.encap = encap;
+
+    /* Use the snaplen from cf (XXX - does wtap_dump_params_init handle that?) */
+    params.snaplen = cf->snap;
 
     if (file_exists(fname)) {
       /* We're overwriting an existing file; write out to a new file,
@@ -4386,14 +4475,14 @@ cf_save_records(capture_file *cf, const char *fname, guint save_format,
          we *HAVE* to do that, otherwise we're overwriting the file
          from which we're reading the packets that we're writing!) */
       fname_new = g_strdup_printf("%s~", fname);
-      pdh = wtap_dump_open_ng(fname_new, save_format, encap, cf->snap,
-                              compressed, shb_hdrs, idb_inf, nrb_hdrs, &err);
+      pdh = wtap_dump_open(fname_new, save_format, compression_type, &params,
+                           &err);
     } else {
-      pdh = wtap_dump_open_ng(fname, save_format, encap, cf->snap,
-                              compressed, shb_hdrs, idb_inf, nrb_hdrs, &err);
+      pdh = wtap_dump_open(fname, save_format, compression_type, &params, &err);
     }
-    g_free(idb_inf);
-    idb_inf = NULL;
+    /* XXX idb_inf is documented to be used until wtap_dump_close. */
+    g_free(params.idb_inf);
+    params.idb_inf = NULL;
 
     if (pdh == NULL) {
       cfile_dump_open_failure_alert_box(fname, err, save_format);
@@ -4472,6 +4561,14 @@ cf_save_records(capture_file *cf, const char *fname, guint save_format,
     }
   }
 
+  /* If this was a temporary file, and we didn't do the save by doing
+     a move, so the tempoary file is still around under its old name,
+     remove it. */
+  if (cf->is_tempfile && how_to_save != SAVE_WITH_MOVE) {
+    /* If this fails, there's not much we can do, so just ignore errors. */
+    ws_unlink(cf->filename);
+  }
+
   cf_callback_invoke(cf_cb_file_save_finished, NULL);
   cf->unsaved_changes = FALSE;
 
@@ -4490,7 +4587,7 @@ cf_save_records(capture_file *cf, const char *fname, guint save_format,
       break;
 
     case SAVE_WITH_COPY:
-      /* We just copied the file, s all the information other than
+      /* We just copied the file, so all the information other than
          the wtap structure, the filename, and the "is temporary"
          status applies to the new file; just update that. */
       wtap_close(cf->provider.wth);
@@ -4568,8 +4665,8 @@ cf_save_records(capture_file *cf, const char *fname, guint save_format,
       for (framenum = 1; framenum <= cf->count; framenum++) {
         fdata = frame_data_sequence_find(cf->provider.frames, framenum);
 
-        fdata->flags.has_phdr_comment = FALSE;
-        fdata->flags.has_user_comment = FALSE;
+        fdata->has_phdr_comment = FALSE;
+        fdata->has_user_comment = FALSE;
       }
 
       if (cf->provider.frames_user_comments) {
@@ -4600,19 +4697,15 @@ fail:
 cf_write_status_t
 cf_export_specified_packets(capture_file *cf, const char *fname,
                             packet_range_t *range, guint save_format,
-                            gboolean compressed)
+                            wtap_compression_type compression_type)
 {
   gchar                       *fname_new = NULL;
   int                          err;
   wtap_dumper                 *pdh;
   save_callback_args_t         callback_args;
-  GArray                      *shb_hdrs = NULL;
-  wtapng_iface_descriptions_t *idb_inf = NULL;
-  GArray                      *nrb_hdrs = NULL;
+  wtap_dump_params             params;
   int                          encap;
 
-  cf_callback_invoke(cf_cb_file_export_specified_packets_started, (gpointer)fname);
-
   packet_range_process_init(range);
 
   /* We're writing out specified packets from the specified capture
@@ -4620,13 +4713,15 @@ cf_export_specified_packets(capture_file *cf, const char *fname,
      written, don't special-case the operation - read each packet
      and then write it out if it's one of the specified ones. */
 
-  /* XXX: what free's this shb_hdr? */
-  shb_hdrs = wtap_file_get_shb_for_new_file(cf->provider.wth);
-  idb_inf = wtap_file_get_idb_info(cf->provider.wth);
-  nrb_hdrs = wtap_file_get_nrb_for_new_file(cf->provider.wth);
+  /* XXX: what free's params.shb_hdr? */
+  wtap_dump_params_init(&params, cf->provider.wth);
 
   /* Determine what file encapsulation type we should use. */
   encap = wtap_dump_file_encap_type(cf->linktypes);
+  params.encap = encap;
+
+  /* Use the snaplen from cf (XXX - does wtap_dump_params_init handle that?) */
+  params.snaplen = cf->snap;
 
   if (file_exists(fname)) {
     /* We're overwriting an existing file; write out to a new file,
@@ -4637,14 +4732,14 @@ cf_export_specified_packets(capture_file *cf, const char *fname,
        we *HAVE* to do that, otherwise we're overwriting the file
        from which we're reading the packets that we're writing!) */
     fname_new = g_strdup_printf("%s~", fname);
-    pdh = wtap_dump_open_ng(fname_new, save_format, encap, cf->snap,
-                            compressed, shb_hdrs, idb_inf, nrb_hdrs, &err);
+    pdh = wtap_dump_open(fname_new, save_format, compression_type, &params,
+                         &err);
   } else {
-    pdh = wtap_dump_open_ng(fname, save_format, encap, cf->snap,
-                            compressed, shb_hdrs, idb_inf, nrb_hdrs, &err);
+    pdh = wtap_dump_open(fname, save_format, compression_type, &params, &err);
   }
-  g_free(idb_inf);
-  idb_inf = NULL;
+  /* XXX idb_inf is documented to be used until wtap_dump_close. */
+  g_free(params.idb_inf);
+  params.idb_inf = NULL;
 
   if (pdh == NULL) {
     cfile_dump_open_failure_alert_box(fname, err, save_format);
@@ -4679,7 +4774,6 @@ cf_export_specified_packets(capture_file *cf, const char *fname,
       wtap_dump_close(pdh, &err);
       if (fname_new != NULL)
         ws_unlink(fname_new);
-      cf_callback_invoke(cf_cb_file_export_specified_packets_stopped, NULL);
       return CF_WRITE_ABORTED;
     break;
 
@@ -4708,7 +4802,6 @@ cf_export_specified_packets(capture_file *cf, const char *fname,
     }
   }
 
-  cf_callback_invoke(cf_cb_file_export_specified_packets_finished, NULL);
   return CF_WRITE_OK;
 
 fail:
@@ -4722,7 +4815,6 @@ fail:
     ws_unlink(fname_new);
     g_free(fname_new);
   }
-  cf_callback_invoke(cf_cb_file_export_specified_packets_failed, NULL);
   return CF_WRITE_ERROR;
 }
 
@@ -4771,6 +4863,11 @@ cf_reload(capture_file *cf) {
   gboolean  is_tempfile;
   int       err;
 
+  if (cf->read_lock) {
+    g_warning("Failing cf_reload(\"%s\") since a read is in progress", cf->filename);
+    return;
+  }
+
   /* If the file could be opened, "cf_open()" calls "cf_close()"
      to get rid of state for the old capture file before filling in state
      for the new capture file.  "cf_close()" will remove the file if