Improve file merging for mergecap and wireshark

Refactor the file merging code by removing the duplicate logic from mergecap.c
and file.c's cf_merge_files(), into a new merge_files() function in merge.c.
Also the following user-visible changes:

 * Removed the '-T' encap type option from mergecap, as it's illogical for
   mergecap and would complicate common merge code.

 * Input files with IDBs of different name, speed, tsprecision, etc., will produce
   an output PCAPNG file with separate IDBs, even if their encap types are the same.

 * Added a '-I' IDB merge mode option for mergecap, to control how IDBs are merged.

 * Changed Wireshark's drag-and-drop merging to use PCAPNG instead of PCAP.

Bug: 8795
Bug: 7381
Change-Id: Icc30d217e093d6f40114422204afd2e332834f71
Reviewed-on: https://code.wireshark.org/review/10058
Petri-Dish: Hadriel Kaplan <hadrielk@yahoo.com>
Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org>
Reviewed-by: Anders Broman <a.broman58@gmail.com>
This commit is contained in:
Hadriel Kaplan 2015-08-16 12:37:11 -04:00 committed by Anders Broman
parent a3c175a823
commit 8fcdcb9c95
9 changed files with 1141 additions and 736 deletions

View File

@ -11,10 +11,13 @@ libwiretap.so.0 libwiretap0 #MINVER#
init_open_routines@Base 1.12.0~rc1
merge_append_read_packet@Base 1.12.0~rc1
merge_close_in_files@Base 1.12.0~rc1
merge_files@Base 1.99.9
merge_idb_merge_mode_to_string@Base 1.99.9
merge_max_snapshot_length@Base 1.12.0~rc1
merge_open_in_files@Base 1.12.0~rc1
merge_read_packet@Base 1.12.0~rc1
merge_select_frame_type@Base 1.12.0~rc1
merge_string_to_idb_merge_mode@Base 1.99.9
open_info_name_to_type@Base 1.12.0~rc1
open_routines@Base 1.12.0~rc1
register_all_wiretap_modules@Base 1.12.0~rc1
@ -65,6 +68,7 @@ libwiretap.so.0 libwiretap0 #MINVER#
wtap_fstat@Base 1.9.1
wtap_get_all_file_extensions_list@Base 1.12.0~rc1
wtap_get_bytes_dumped@Base 1.9.1
wtap_get_debug_if_descr@Base 1.99.9
wtap_get_file_extension_type_extensions@Base 1.12.0~rc1
wtap_get_file_extension_type_name@Base 1.12.0~rc1
wtap_get_file_extensions_list@Base 1.9.1

View File

@ -9,8 +9,8 @@ B<mergecap>
S<[ B<-a> ]>
S<[ B<-F> E<lt>I<file format>E<gt> ]>
S<[ B<-h> ]>
S<[ B<-I> E<lt>I<IDB merge mode>E<gt> ]>
S<[ B<-s> E<lt>I<snaplen>E<gt> ]>
S<[ B<-T> E<lt>I<encapsulation type>E<gt> ]>
S<[ B<-v> ]>
S<[ B<-V> ]>
S<B<-w> E<lt>I<outfile>E<gt>|->
@ -79,6 +79,28 @@ first input file.
Prints the version and options and exits.
=item -I E<lt>IDB merge modeE<gt>
Sets the Interface Description Block (IDB) merge mode to use during merging.
B<mergecap -I> provides a list of the available IDB merge modes.
Every input file has one or more IDBs, which describe the interface(s) the
capture was performed on originally. This includes encapsulation type,
interface name, etc. When mergecap merges multiple input files, it has to
merge these IDBs somehow for the new merged output file. This flag controls
how that is accomplished. The currently available modes are:
* 'B<none>': no merging of IDBs is performed, and instead all IDBs are
copied to the merged output file.
* 'B<all>': IDBs are merged only if all input files have the same number
of IDBs, and each IDB matches their respective entry in the
other files. This is the default mode.
* 'B<any>': Any and all duplicate IDBs are merged into one IDB, regardless
of what file they are in.
Note that an IDB is only considered a matching duplicate if it has the same
encapsulation type, name, speed, time precision, comments, description, etc.
=item -s E<lt>snaplenE<gt>
Sets the snapshot length to use when writing the data.
@ -92,22 +114,6 @@ appear to reject Ethernet frames larger than the standard Ethernet MTU,
making them incapable of handling gigabit Ethernet captures if jumbo
frames were used).
=item -T E<lt>encapsulation typeE<gt>
Sets the packet encapsulation type of the output capture file.
If the B<-T> flag is used to specify a frame encapsulation type, the
encapsulation type of the output capture file will be forced to the
specified type, rather than being the type appropriate to the
encapsulation type of the input capture files.
Note that this merely
forces the encapsulation type of the output file to be the specified
type; the packet headers of the packets will not be translated from the
encapsulation type of the input capture file to the specified
encapsulation type (for example, it will not translate an Ethernet
capture to an FDDI capture if an Ethernet capture is read and 'B<-T
fddi>' is specified).
=item -v
Causes B<mergecap> to print a number of messages while it's working.

519
file.c
View File

@ -1230,413 +1230,186 @@ read_packet(capture_file *cf, dfilter_t *dfcode, epan_dissect_t *edt,
return row;
}
typedef struct _callback_data_t {
gint64 f_len;
gint64 progbar_nextstep;
gint64 progbar_quantum;
GTimeVal start_time;
progdlg_t *progbar;
gboolean stop_flag;
} callback_data_t;
static gboolean
merge_callback(merge_event event, int num _U_,
const merge_in_file_t in_files[], const guint in_file_count,
void *data)
{
guint i;
callback_data_t *cb_data = (callback_data_t*) data;
g_assert(cb_data != NULL);
switch (event) {
case MERGE_EVENT_INPUT_FILES_OPENED:
/* do nothing */
break;
case MERGE_EVENT_FRAME_TYPE_SELECTED:
/* do nothing */
break;
case MERGE_EVENT_READY_TO_MERGE:
/* Get the sum of the sizes of all the files. */
for (i = 0; i < in_file_count; i++)
cb_data->f_len += in_files[i].size;
/* When we reach the value that triggers a progress bar update,
bump that value by this amount. */
cb_data->progbar_quantum = cb_data->f_len / N_PROGBAR_UPDATES;
g_get_current_time(&cb_data->start_time);
break;
case MERGE_EVENT_PACKET_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
large file, we might take considerably longer than that standard
time in order to get to the next progress bar step). */
if (cb_data->progbar == NULL) {
cb_data->progbar = delayed_create_progress_dlg(NULL, "Merging", "files",
FALSE, &cb_data->stop_flag, &cb_data->start_time, 0.0f);
}
/* Update the progress bar, but do it only N_PROGBAR_UPDATES times;
when we update it, we have to run the GTK+ main loop to get it
to repaint what's pending, and doing so may involve an "ioctl()"
to see if there's any pending input from an X server, and doing
that for every packet can be costly, especially on a big file. */
if (data_offset >= cb_data->progbar_nextstep) {
float progbar_val;
gint64 file_pos = 0;
/* Get the sum of the seek positions in all of the files. */
for (i = 0; i < in_file_count; i++)
file_pos += wtap_read_so_far(in_files[i].wth);
progbar_val = (gfloat) file_pos / (gfloat) cb_data->f_len;
if (progbar_val > 1.0f) {
/* Some file probably grew while we were reading it.
That "shouldn't happen", so we'll just clip the progress
value at 1.0. */
progbar_val = 1.0f;
}
if (cb_data->progbar != NULL) {
gchar status_str[100];
g_snprintf(status_str, sizeof(status_str),
"%" G_GINT64_MODIFIER "dKB of %" G_GINT64_MODIFIER "dKB",
file_pos / 1024, cb_data->f_len / 1024);
update_progress_dlg(cb_data->progbar, progbar_val, status_str);
}
cb_data->progbar_nextstep += cb_data->progbar_quantum;
}
}
break;
case MERGE_EVENT_DONE:
/* We're done merging the files; destroy the progress bar if it was created. */
if (cb_data->progbar != NULL)
destroy_progress_dlg(cb_data->progbar);
break;
}
return cb_data->stop_flag;
}
cf_status_t
cf_merge_files(char **out_filenamep, int in_file_count,
char *const *in_filenames, int file_type, gboolean do_append)
{
merge_in_file_t *in_files, *in_file;
char *out_filename;
char *tmpname;
int out_fd;
wtap_dumper *pdh;
int open_err, read_err, write_err, close_err;
gchar *err_info, *write_err_info = NULL;
int err_fileno;
int i;
gboolean got_read_error = FALSE, got_write_error = FALSE;
gint64 data_offset;
progdlg_t *progbar = NULL;
gboolean stop_flag = FALSE;
gint64 f_len, file_pos;
float progbar_val;
GTimeVal start_time;
gchar status_str[100];
gint64 progbar_nextstep;
gint64 progbar_quantum;
gchar *display_basename;
int selected_frame_type;
gboolean fake_interface_ids = FALSE;
char *out_filename;
char *tmpname;
int out_fd;
int err = 0;
gchar *err_info = NULL;
int err_fileno;
merge_result status;
merge_progress_callback_t cb;
/* open the input files */
if (!merge_open_in_files(in_file_count, in_filenames, &in_files,
&open_err, &err_info, &err_fileno)) {
g_free(in_files);
cf_open_failure_alert_box(in_filenames[err_fileno], open_err, err_info,
FALSE, 0);
return CF_ERROR;
}
if (*out_filenamep != NULL) {
out_filename = *out_filenamep;
out_fd = ws_open(out_filename, O_CREAT|O_TRUNC|O_BINARY, 0600);
if (out_fd == -1)
open_err = errno;
err = errno;
} else {
out_fd = create_tempfile(&tmpname, "wireshark");
if (out_fd == -1)
open_err = errno;
err = errno;
out_filename = g_strdup(tmpname);
*out_filenamep = out_filename;
}
if (out_fd == -1) {
err_info = NULL;
merge_close_in_files(in_file_count, in_files);
g_free(in_files);
cf_open_failure_alert_box(out_filename, open_err, NULL, TRUE, file_type);
cf_open_failure_alert_box(out_filename, err, NULL, TRUE, file_type);
return CF_ERROR;
}
selected_frame_type = merge_select_frame_type(in_file_count, in_files);
/* prepare our callback routine */
cb.callback_func = merge_callback;
cb.data = g_malloc0(sizeof(callback_data_t));
/* If we are trying to merge a number of libpcap files with different encapsulation types
* change the output file type to pcapng and create SHB and IDB:s for the new file use the
* interface index stored in in_files per file to change the phdr before writing the datablock.
* XXX should it be an option to convert to pcapng?
*
* We need something similar when merging pcapng files possibly with an option to say
* the same interface(s) used in all in files. SHBs comments should be merged together.
*/
if ((selected_frame_type == WTAP_ENCAP_PER_PACKET)&&(file_type == WTAP_FILE_TYPE_SUBTYPE_PCAP)) {
/* Write output in pcapng format */
wtapng_section_t *shb_hdr;
wtapng_iface_descriptions_t *idb_inf, *idb_inf_merge_file;
wtapng_if_descr_t int_data, *file_int_data;
GString *comment_gstr;
guint itf_count, itf_id = 0;
/* merge the files */
status = merge_files(out_fd, out_filename, file_type,
(const char *const *) in_filenames, in_file_count,
do_append, IDB_MERGE_MODE_ALL_SAME, 0 /* snaplen */,
"Wireshark", &cb, &err, &err_info, &err_fileno);
fake_interface_ids = TRUE;
/* Create SHB info */
shb_hdr = wtap_file_get_shb_for_new_file(in_files[0].wth);
comment_gstr = g_string_new("");
g_string_append_printf(comment_gstr, "%s \n",shb_hdr->opt_comment);
g_string_append_printf(comment_gstr, "File created by merging: \n");
file_type = WTAP_FILE_TYPE_SUBTYPE_PCAPNG;
g_free(cb.data);
for (i = 0; i < in_file_count; i++) {
g_string_append_printf(comment_gstr, "File%d: %s \n",i+1,in_files[i].filename);
}
/* TODO: handle comments from each file being merged */
if (shb_hdr->opt_comment)
g_free(shb_hdr->opt_comment);
shb_hdr->opt_comment = g_string_free(comment_gstr, FALSE); /* NULL if not available */
shb_hdr->shb_user_appl = g_strdup("Wireshark"); /* NULL if not available, UTF-8 string containing the name */
/* of the application used to create this section. */
switch (status) {
case MERGE_OK:
break;
/* TODO: handle name resolution info from each file being merged */
case MERGE_USER_ABORTED:
/* this isn't really an error, though we will return CF_ERROR later */
break;
/* create fake IDB info */
idb_inf = g_new(wtapng_iface_descriptions_t,1);
/* TODO make this the number of DIFFERENT encapsulation types
* check that snaplength is the same too?
*/
idb_inf->interface_data = g_array_new(FALSE, FALSE, sizeof(wtapng_if_descr_t));
case MERGE_ERR_CANT_OPEN_INFILE:
cf_open_failure_alert_box(in_filenames[err_fileno], err, err_info,
FALSE, 0);
break;
for (i = 0; i < in_file_count; i++) {
idb_inf_merge_file = wtap_file_get_idb_info(in_files[i].wth);
for (itf_count = 0; itf_count < idb_inf_merge_file->interface_data->len; itf_count++) {
/* read the interface data from the in file to our combined interface data */
file_int_data = &g_array_index (idb_inf_merge_file->interface_data, wtapng_if_descr_t, itf_count);
int_data.wtap_encap = file_int_data->wtap_encap;
int_data.time_units_per_second = file_int_data->time_units_per_second;
int_data.link_type = file_int_data->link_type;
int_data.snap_len = file_int_data->snap_len;
int_data.if_name = g_strdup(file_int_data->if_name);
int_data.opt_comment = NULL;
int_data.if_description = NULL;
int_data.if_speed = 0;
int_data.if_tsresol = file_int_data->if_tsresol;
int_data.if_filter_str = NULL;
int_data.bpf_filter_len = 0;
int_data.if_filter_bpf_bytes = NULL;
int_data.if_os = NULL;
int_data.if_fcslen = -1;
int_data.num_stat_entries = 0; /* Number of ISB:s */
int_data.interface_statistics = NULL;
g_array_append_val(idb_inf->interface_data, int_data);
}
g_free(idb_inf_merge_file);
/* Set fake interface Id in per file data */
in_files[i].interface_id = itf_id;
itf_id += itf_count;
}
pdh = wtap_dump_fdopen_ng(out_fd, file_type,
selected_frame_type,
merge_max_snapshot_length(in_file_count, in_files),
FALSE /* compressed */, shb_hdr, idb_inf /* wtapng_iface_descriptions_t *idb_inf */,
NULL, &open_err);
if (pdh == NULL) {
ws_close(out_fd);
merge_close_in_files(in_file_count, in_files);
g_free(in_files);
cf_open_failure_alert_box(out_filename, open_err, err_info, TRUE,
case MERGE_ERR_CANT_OPEN_OUTFILE:
cf_open_failure_alert_box(out_filename, err, err_info, TRUE,
file_type);
return CF_ERROR;
}
} else {
pdh = wtap_dump_fdopen(out_fd, file_type,
selected_frame_type,
merge_max_snapshot_length(in_file_count, in_files),
FALSE /* compressed */, &open_err);
if (pdh == NULL) {
ws_close(out_fd);
merge_close_in_files(in_file_count, in_files);
g_free(in_files);
cf_open_failure_alert_box(out_filename, open_err, err_info, TRUE,
file_type);
return CF_ERROR;
}
}
/* Get the sum of the sizes of all the files. */
f_len = 0;
for (i = 0; i < in_file_count; i++)
f_len += in_files[i].size;
/* Update the progress bar when it gets to this value. */
progbar_nextstep = 0;
/* When we reach the value that triggers a progress bar update,
bump that value by this amount. */
progbar_quantum = f_len/N_PROGBAR_UPDATES;
/* Progress so far. */
progbar_val = 0.0f;
g_get_current_time(&start_time);
/* do the merge (or append) */
for (;;) {
if (do_append)
in_file = merge_append_read_packet(in_file_count, in_files, &read_err,
&err_info);
else
in_file = merge_read_packet(in_file_count, in_files, &read_err,
&err_info);
if (in_file == NULL) {
/* EOF */
break;
}
if (read_err != 0) {
/* I/O error reading from in_file */
got_read_error = TRUE;
case MERGE_ERR_CANT_READ_INFILE: /* fall through */
case MERGE_ERR_BAD_PHDR_INTERFACE_ID:
case MERGE_ERR_CANT_WRITE_OUTFILE:
case MERGE_ERR_CANT_CLOSE_OUTFILE:
default:
simple_error_message_box("%s", err_info ? err_info : "unknown error");
break;
}
/* 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
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(NULL, "Merging", "files",
FALSE, &stop_flag, &start_time, progbar_val);
}
/* Update the progress bar, but do it only N_PROGBAR_UPDATES times;
when we update it, we have to run the GTK+ main loop to get it
to repaint what's pending, and doing so may involve an "ioctl()"
to see if there's any pending input from an X server, and doing
that for every packet can be costly, especially on a big file. */
if (data_offset >= progbar_nextstep) {
/* Get the sum of the seek positions in all of the files. */
file_pos = 0;
for (i = 0; i < in_file_count; i++)
file_pos += wtap_read_so_far(in_files[i].wth);
progbar_val = (gfloat) file_pos / (gfloat) f_len;
if (progbar_val > 1.0f) {
/* Some file probably grew while we were reading it.
That "shouldn't happen", so we'll just clip the progress
value at 1.0. */
progbar_val = 1.0f;
}
if (progbar != NULL) {
g_snprintf(status_str, sizeof(status_str),
"%" G_GINT64_MODIFIER "dKB of %" G_GINT64_MODIFIER "dKB",
file_pos / 1024, f_len / 1024);
update_progress_dlg(progbar, progbar_val, status_str);
}
progbar_nextstep += progbar_quantum;
}
if (stop_flag) {
/* Well, the user decided to abort the merge. */
break;
}
/* If we have WTAP_ENCAP_PER_PACKET and the infiles are of type
* WTAP_FILE_TYPE_SUBTYPE_PCAP, we need to set the interface id
* in the paket header = the interface index we used in the IDBs
* interface description for this file(encapsulation type).
*/
if (fake_interface_ids) {
struct wtap_pkthdr *phdr;
phdr = wtap_phdr(in_file->wth);
if (phdr->presence_flags & WTAP_HAS_INTERFACE_ID) {
phdr->interface_id += in_file->interface_id;
} else {
phdr->interface_id = in_file->interface_id;
phdr->presence_flags = phdr->presence_flags | WTAP_HAS_INTERFACE_ID;
}
}
if (!wtap_dump(pdh, wtap_phdr(in_file->wth),
wtap_buf_ptr(in_file->wth), &write_err, &write_err_info)) {
got_write_error = TRUE;
break;
}
}
/* We're done merging the files; destroy the progress bar if it was created. */
if (progbar != NULL)
destroy_progress_dlg(progbar);
g_free(err_info);
merge_close_in_files(in_file_count, in_files);
if (!got_write_error) {
if (!wtap_dump_close(pdh, &write_err))
got_write_error = TRUE;
} else {
/*
* We already got a write error; no need to report another
* write error on close.
*
* Don't overwrite the earlier write error.
*/
(void)wtap_dump_close(pdh, &close_err);
}
if (got_read_error) {
/*
* Find the file on which we got the error, and report the error.
*/
for (i = 0; i < in_file_count; i++) {
if (in_files[i].state == GOT_ERROR) {
/* Put up a message box noting that a read failed somewhere along
the line. */
display_basename = g_filename_display_basename(in_files[i].filename);
switch (read_err) {
case WTAP_ERR_SHORT_READ:
simple_error_message_box(
"The capture file %s appears to have been cut short"
" in the middle of a packet.", display_basename);
break;
case WTAP_ERR_BAD_FILE:
simple_error_message_box(
"The capture file %s appears to be damaged or corrupt.\n(%s)",
display_basename, err_info);
g_free(err_info);
break;
case WTAP_ERR_DECOMPRESS:
simple_error_message_box(
"The compressed capture file %s appears to be damaged or corrupt.\n"
"(%s)", display_basename,
err_info != NULL ? err_info : "no information supplied");
g_free(err_info);
break;
default:
simple_error_message_box(
"An error occurred while reading the"
" capture file %s: %s.",
display_basename, wtap_strerror(read_err));
break;
}
g_free(display_basename);
}
}
}
if (got_write_error) {
/* Put up an alert box for the write error. */
if (write_err < 0) {
/* Wiretap error. */
switch (write_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.
*/
display_basename = g_filename_display_basename(in_file ? in_file->filename : "UNKNOWN");
simple_error_message_box(
"Frame %u of \"%s\" has a network type that can't be saved in a \"%s\" file.",
in_file ? in_file->packet_num : 0, display_basename,
wtap_file_type_subtype_string(file_type));
g_free(display_basename);
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.
*/
display_basename = g_filename_display_basename(in_file ? in_file->filename : "UNKNOWN");
simple_error_message_box(
"Frame %u of \"%s\" is too large for a \"%s\" file.",
in_file ? in_file->packet_num : 0, display_basename,
wtap_file_type_subtype_string(file_type));
g_free(display_basename);
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.
*/
display_basename = g_filename_display_basename(in_file ? in_file->filename : "UNKNOWN");
simple_error_message_box(
"Record %u of \"%s\" has a record type that can't be saved in a \"%s\" file.",
in_file ? in_file->packet_num : 0, display_basename,
wtap_file_type_subtype_string(file_type));
g_free(display_basename);
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 frame number and file type/subtype.
*/
display_basename = g_filename_display_basename(in_file ? in_file->filename : "UNKNOWN");
simple_error_message_box(
"Record %u of \"%s\" has data that can't be saved in a \"%s\" file.\n(%s)",
in_file ? in_file->packet_num : 0, display_basename,
wtap_file_type_subtype_string(file_type),
write_err_info != NULL ? write_err_info : "no information supplied");
g_free(write_err_info);
g_free(display_basename);
break;
default:
display_basename = g_filename_display_basename(out_filename);
simple_error_message_box(
"An error occurred while writing to the file \"%s\": %s.",
out_filename, wtap_strerror(write_err));
g_free(display_basename);
break;
}
} else {
/* OS error. */
write_failure_alert_box(out_filename, write_err);
}
}
if (got_read_error || got_write_error || stop_flag) {
if (status != MERGE_OK) {
/* Callers aren't expected to treat an error or an explicit abort
differently - we put up error dialogs ourselves, so they don't
have to. */

View File

@ -84,9 +84,8 @@ print_usage(FILE *output)
fprintf(output, " -w <outfile>|- set the output filename to <outfile> or '-' for stdout.\n");
fprintf(output, " -F <capture type> set the output file type; default is pcapng.\n");
fprintf(output, " an empty \"-F\" option will list the file types.\n");
fprintf(output, " -T <encap type> set the output file encapsulation type;\n");
fprintf(output, " default is the same as the first input file.\n");
fprintf(output, " an empty \"-T\" option will list the encapsulation types.\n");
fprintf(output, " -I <IDB merge mode> set the merge mode for Interface Description Blocks; default is 'all'.\n");
fprintf(output, " an empty \"-I\" option will list the merge modes.\n");
fprintf(output, "\n");
fprintf(output, "Miscellaneous:\n");
fprintf(output, " -h display this help and exit.\n");
@ -126,13 +125,6 @@ string_compare(gconstpointer a, gconstpointer b)
((const struct string_elem *)b)->sstr);
}
static gint
string_nat_compare(gconstpointer a, gconstpointer b)
{
return ws_ascii_strnatcmp(((const struct string_elem *)a)->sstr,
((const struct string_elem *)b)->sstr);
}
static void
string_elem_print(gpointer data, gpointer not_used _U_)
{
@ -162,23 +154,13 @@ list_capture_types(void) {
}
static void
list_encap_types(void) {
list_idb_merge_modes(void) {
int i;
struct string_elem *encaps;
GSList *list = NULL;
encaps = g_new(struct string_elem,WTAP_NUM_ENCAP_TYPES);
fprintf(stderr, "mergecap: The available encapsulation types for the \"-T\" flag are:\n");
for (i = 0; i < WTAP_NUM_ENCAP_TYPES; i++) {
encaps[i].sstr = wtap_encap_short_string(i);
if (encaps[i].sstr != NULL) {
encaps[i].lstr = wtap_encap_string(i);
list = g_slist_insert_sorted(list, &encaps[i], string_nat_compare);
}
fprintf(stderr, "mergecap: The available IDB merge modes for the \"-I\" flag are:\n");
for (i = 0; i < IDB_MERGE_MODE_MAX; i++) {
fprintf(stderr, " %s\n", merge_idb_merge_mode_to_string(i));
}
g_slist_foreach(list, string_elem_print, NULL);
g_slist_free(list);
g_free(encaps);
}
static void
@ -207,6 +189,73 @@ get_mergecap_runtime_info(GString *str)
#endif
}
static gboolean
merge_callback(merge_event event, int num,
const merge_in_file_t in_files[], const guint in_file_count,
void *data _U_)
{
guint i;
switch (event) {
case MERGE_EVENT_INPUT_FILES_OPENED:
for (i = 0; i < in_file_count; i++) {
fprintf(stderr, "mergecap: %s is type %s.\n", in_files[i].filename,
wtap_file_type_subtype_string(wtap_file_type_subtype(in_files[i].wth)));
}
break;
case MERGE_EVENT_FRAME_TYPE_SELECTED:
/* for this event, num = frame_type */
if (num == WTAP_ENCAP_PER_PACKET) {
/*
* Find out why we had to choose WTAP_ENCAP_PER_PACKET.
*/
int first_frame_type, this_frame_type;
first_frame_type = wtap_file_encap(in_files[0].wth);
for (i = 1; i < in_file_count; i++) {
this_frame_type = wtap_file_encap(in_files[i].wth);
if (first_frame_type != this_frame_type) {
fprintf(stderr, "mergecap: multiple frame encapsulation types detected\n");
fprintf(stderr, " defaulting to WTAP_ENCAP_PER_PACKET\n");
fprintf(stderr, " %s had type %s (%s)\n",
in_files[0].filename,
wtap_encap_string(first_frame_type),
wtap_encap_short_string(first_frame_type));
fprintf(stderr, " %s had type %s (%s)\n",
in_files[i].filename,
wtap_encap_string(this_frame_type),
wtap_encap_short_string(this_frame_type));
break;
}
}
}
fprintf(stderr, "mergecap: selected frame_type %s (%s)\n",
wtap_encap_string(num),
wtap_encap_short_string(num));
break;
case MERGE_EVENT_READY_TO_MERGE:
fprintf(stderr, "mergecap: ready to merge records\n");
break;
case MERGE_EVENT_PACKET_WAS_READ:
/* for this event, num = count */
fprintf(stderr, "Record: %d\n", num);
break;
case MERGE_EVENT_DONE:
fprintf(stderr, "mergecap: merging complete\n");
break;
}
/* false = do not stop merging */
return FALSE;
}
int
main(int argc, char *argv[])
{
@ -229,18 +278,15 @@ DIAG_ON(cast-qual)
#else
int file_type = WTAP_FILE_TYPE_SUBTYPE_PCAP; /* default to pcapng format */
#endif
int frame_type = -2;
int out_fd;
merge_in_file_t *in_files = NULL, *in_file;
int i;
struct wtap_pkthdr *phdr, snap_phdr;
wtap_dumper *pdh;
int open_err, read_err = 0, write_err, close_err;
gchar *err_info, *write_err_info = NULL;
int err = 0;
gchar *err_info = NULL;
int err_fileno;
char *out_filename = NULL;
gboolean got_read_error = FALSE, got_write_error = FALSE;
int count;
merge_result status;
idb_merge_mode mode = IDB_MERGE_MODE_MAX;
gboolean use_stdout = FALSE;
merge_progress_callback_t cb;
cmdarg_err_init(mergecap_cmdarg_err, mergecap_cmdarg_err_cont);
@ -264,7 +310,7 @@ DIAG_ON(cast-qual)
get_ws_vcs_version_info(), comp_info_str->str, runtime_info_str->str);
/* Process the options first */
while ((opt = getopt_long(argc, argv, "aF:hs:T:vVw:", long_options, NULL)) != -1) {
while ((opt = getopt_long(argc, argv, "aF:hI:s:vVw:", long_options, NULL)) != -1) {
switch (opt) {
case 'a':
@ -290,20 +336,20 @@ DIAG_ON(cast-qual)
exit(0);
break;
case 's':
snaplen = get_positive_int(optarg, "snapshot length");
break;
case 'T':
frame_type = wtap_short_string_to_encap(optarg);
if (frame_type < 0) {
fprintf(stderr, "mergecap: \"%s\" isn't a valid encapsulation type\n",
case 'I':
mode = merge_string_to_idb_merge_mode(optarg);
if (mode == IDB_MERGE_MODE_MAX) {
fprintf(stderr, "mergecap: \"%s\" isn't a valid IDB merge mode\n",
optarg);
list_encap_types();
list_idb_merge_modes();
exit(1);
}
break;
case 's':
snaplen = get_positive_int(optarg, "snapshot length");
break;
case 'v':
verbose = TRUE;
break;
@ -324,8 +370,8 @@ DIAG_ON(cast-qual)
case'F':
list_capture_types();
break;
case'T':
list_encap_types();
case'I':
list_idb_merge_modes();
break;
default:
print_usage(stderr);
@ -335,6 +381,9 @@ DIAG_ON(cast-qual)
}
}
cb.callback_func = merge_callback;
cb.data = NULL;
/* check for proper args; at a minimum, must have an output
* filename and one input file
*/
@ -349,72 +398,21 @@ DIAG_ON(cast-qual)
return 1;
}
/* open the input files */
if (!merge_open_in_files(in_file_count, &argv[optind], &in_files,
&open_err, &err_info, &err_fileno)) {
fprintf(stderr, "mergecap: Can't open %s: %s\n", argv[optind + err_fileno],
wtap_strerror(open_err));
if (err_info != NULL) {
fprintf(stderr, "(%s)\n", err_info);
g_free(err_info);
}
return 2;
/* setting IDB merge mode must use PCAPNG output */
if (mode != IDB_MERGE_MODE_MAX && file_type != WTAP_FILE_TYPE_SUBTYPE_PCAPNG) {
fprintf(stderr, "The IDB merge mode can only be used with PCAPNG output format\n");
return 1;
}
if (verbose) {
for (i = 0; i < in_file_count; i++)
fprintf(stderr, "mergecap: %s is type %s.\n", argv[optind + i],
wtap_file_type_subtype_string(wtap_file_type_subtype(in_files[i].wth)));
}
if (snaplen == 0) {
/*
* Snapshot length not specified - default to the maximum of the
* snapshot lengths of the input files.
*/
snaplen = merge_max_snapshot_length(in_file_count, in_files);
}
/* set the outfile frame type */
if (frame_type == -2) {
/*
* Default to the appropriate frame type for the input files.
*/
frame_type = merge_select_frame_type(in_file_count, in_files);
if (verbose) {
if (frame_type == WTAP_ENCAP_PER_PACKET) {
/*
* Find out why we had to choose WTAP_ENCAP_PER_PACKET.
*/
int first_frame_type, this_frame_type;
first_frame_type = wtap_file_encap(in_files[0].wth);
for (i = 1; i < in_file_count; i++) {
this_frame_type = wtap_file_encap(in_files[i].wth);
if (first_frame_type != this_frame_type) {
fprintf(stderr, "mergecap: multiple frame encapsulation types detected\n");
fprintf(stderr, " defaulting to WTAP_ENCAP_PER_PACKET\n");
fprintf(stderr, " %s had type %s (%s)\n",
in_files[0].filename,
wtap_encap_string(first_frame_type),
wtap_encap_short_string(first_frame_type));
fprintf(stderr, " %s had type %s (%s)\n",
in_files[i].filename,
wtap_encap_string(this_frame_type),
wtap_encap_short_string(this_frame_type));
break;
}
}
}
fprintf(stderr, "mergecap: selected frame_type %s (%s)\n",
wtap_encap_string(frame_type),
wtap_encap_short_string(frame_type));
}
/* if they didn't set IDB merge mode, set it to our default */
if (mode == IDB_MERGE_MODE_MAX) {
mode = IDB_MERGE_MODE_ALL_SAME;
}
/* open the outfile */
if (strncmp(out_filename, "-", 2) == 0) {
/* use stdout as the outfile */
use_stdout = TRUE;
out_fd = 1 /*stdout*/;
} else {
/* open the outfile */
@ -426,252 +424,45 @@ DIAG_ON(cast-qual)
}
}
/* prepare the outfile */
if(file_type == WTAP_FILE_TYPE_SUBTYPE_PCAPNG ){
wtapng_section_t *shb_hdr;
GString *comment_gstr;
wtapng_iface_descriptions_t *idb_inf = NULL, *idb_inf_merge_file;
wtapng_if_descr_t int_data, *file_int_data;
guint itf_count, itf_id = 0;
/* merge the files */
status = merge_files(out_fd, out_filename, file_type,
(const char *const *) &argv[optind], in_file_count,
do_append, mode, snaplen, "mergecap", verbose ? &cb : NULL,
&err, &err_info, &err_fileno);
shb_hdr = g_new(wtapng_section_t,1);
comment_gstr = g_string_new("File created by merging: \n");
for (i = 0; i < in_file_count; i++) {
g_string_append_printf(comment_gstr, "File%d: %s \n",i+1,in_files[i].filename);
}
shb_hdr->section_length = -1;
/* options */
shb_hdr->opt_comment = comment_gstr->str; /* NULL if not available */
shb_hdr->shb_hardware = NULL; /* NULL if not available, UTF-8 string containing the description of the hardware used to create this section. */
shb_hdr->shb_os = NULL; /* NULL if not available, UTF-8 string containing the name of the operating system used to create this section. */
shb_hdr->shb_user_appl = g_strdup("mergecap"); /* NULL if not available, UTF-8 string containing the name of the application used to create this section. */
if (frame_type == WTAP_ENCAP_PER_PACKET) {
/* create fake IDB info */
idb_inf = g_new(wtapng_iface_descriptions_t,1);
/* TODO make this the number of DIFFERENT encapsulation types
* check that snaplength is the same too?
*/
idb_inf->interface_data = g_array_new(FALSE, FALSE, sizeof(wtapng_if_descr_t));
for (i = 0; i < in_file_count; i++) {
idb_inf_merge_file = wtap_file_get_idb_info(in_files[i].wth);
for (itf_count = 0; itf_count < idb_inf_merge_file->interface_data->len; itf_count++) {
/* read the interface data from the in file to our combined interface data */
file_int_data = &g_array_index (idb_inf_merge_file->interface_data, wtapng_if_descr_t, itf_count);
int_data.wtap_encap = file_int_data->wtap_encap;
int_data.time_units_per_second = file_int_data->time_units_per_second;
int_data.link_type = file_int_data->link_type;
int_data.snap_len = file_int_data->snap_len;
int_data.if_name = g_strdup(file_int_data->if_name);
int_data.opt_comment = NULL;
int_data.if_description = NULL;
int_data.if_speed = 0;
int_data.if_tsresol = file_int_data->if_tsresol;
int_data.if_filter_str = NULL;
int_data.bpf_filter_len = 0;
int_data.if_filter_bpf_bytes = NULL;
int_data.if_os = NULL;
int_data.if_fcslen = -1;
int_data.num_stat_entries = 0; /* Number of ISB:s */
int_data.interface_statistics = NULL;
g_array_append_val(idb_inf->interface_data, int_data);
}
g_free(idb_inf_merge_file);
/* Set fake interface Id in per file data */
in_files[i].interface_id = itf_id;
itf_id += itf_count;
}
} else {
guint8 if_tsresol = 6;
guint64 time_units_per_second = 1000000;
for (i = 0; i < in_file_count; i++) {
idb_inf_merge_file = wtap_file_get_idb_info(in_files[i].wth);
for (itf_count = 0; itf_count < idb_inf_merge_file->interface_data->len; itf_count++) {
file_int_data = &g_array_index (idb_inf_merge_file->interface_data, wtapng_if_descr_t, itf_count);
if (file_int_data->time_units_per_second > time_units_per_second) {
time_units_per_second = file_int_data->time_units_per_second;
if_tsresol = file_int_data->if_tsresol;
}
}
g_free(idb_inf_merge_file);
}
if (time_units_per_second > 1000000) {
/* We are using a better than microsecond precision; let's create a fake IDB */
idb_inf = g_new(wtapng_iface_descriptions_t,1);
idb_inf->interface_data = g_array_new(FALSE, FALSE, sizeof(wtapng_if_descr_t));
int_data.wtap_encap = frame_type;
int_data.time_units_per_second = time_units_per_second;
int_data.link_type = wtap_wtap_encap_to_pcap_encap(frame_type);
int_data.snap_len = snaplen;
int_data.if_name = g_strdup("Unknown/not available in original file format(libpcap)");
int_data.opt_comment = NULL;
int_data.if_description = NULL;
int_data.if_speed = 0;
int_data.if_tsresol = if_tsresol;
int_data.if_filter_str = NULL;
int_data.bpf_filter_len = 0;
int_data.if_filter_bpf_bytes = NULL;
int_data.if_os = NULL;
int_data.if_fcslen = -1;
int_data.num_stat_entries = 0; /* Number of ISB:s */
int_data.interface_statistics = NULL;
g_array_append_val(idb_inf->interface_data, int_data);
}
}
pdh = wtap_dump_fdopen_ng(out_fd, file_type, frame_type, snaplen,
FALSE /* compressed */, shb_hdr, idb_inf, NULL, &open_err);
g_string_free(comment_gstr, TRUE);
} else {
pdh = wtap_dump_fdopen(out_fd, file_type, frame_type, snaplen, FALSE /* compressed */, &open_err);
}
if (pdh == NULL) {
merge_close_in_files(in_file_count, in_files);
g_free(in_files);
fprintf(stderr, "mergecap: Can't open or create %s: %s\n", out_filename,
wtap_strerror(open_err));
exit(1);
}
/* do the merge (or append) */
count = 1;
for (;;) {
if (do_append)
in_file = merge_append_read_packet(in_file_count, in_files, &read_err,
&err_info);
else
in_file = merge_read_packet(in_file_count, in_files, &read_err,
&err_info);
if (in_file == NULL) {
/* EOF */
break;
}
if (read_err != 0) {
/* I/O error reading from in_file */
got_read_error = TRUE;
break;
}
if (verbose)
fprintf(stderr, "Record: %d\n", count++);
/* We simply write it, perhaps after truncating it; we could do other
* things, like modify it. */
phdr = wtap_phdr(in_file->wth);
if (snaplen != 0 && phdr->caplen > snaplen) {
snap_phdr = *phdr;
snap_phdr.caplen = snaplen;
phdr = &snap_phdr;
}
if ((file_type == WTAP_FILE_TYPE_SUBTYPE_PCAPNG) && (frame_type == WTAP_ENCAP_PER_PACKET)) {
if (phdr->presence_flags & WTAP_HAS_INTERFACE_ID) {
phdr->interface_id += in_file->interface_id;
} else {
phdr->interface_id = in_file->interface_id;
phdr->presence_flags = phdr->presence_flags | WTAP_HAS_INTERFACE_ID;
}
}
if (!wtap_dump(pdh, phdr, wtap_buf_ptr(in_file->wth), &write_err, &write_err_info)) {
got_write_error = TRUE;
break;
}
}
merge_close_in_files(in_file_count, in_files);
if (!got_write_error) {
if (!wtap_dump_close(pdh, &write_err))
got_write_error = TRUE;
} else {
/*
* We already got a write error; no need to report another
* write error on close.
*
* Don't overwrite the earlier write error.
*/
(void)wtap_dump_close(pdh, &close_err);
}
if (got_read_error) {
/*
* Find the file on which we got the error, and report the error.
*/
for (i = 0; i < in_file_count; i++) {
if (in_files[i].state == GOT_ERROR) {
fprintf(stderr, "mergecap: Error reading %s: %s\n",
in_files[i].filename, wtap_strerror(read_err));
if (err_info != NULL) {
fprintf(stderr, "(%s)\n", err_info);
g_free(err_info);
}
}
}
}
if (got_write_error) {
switch (write_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 wwriting; note that, and
* report the frame number and file type/subtype.
*/
fprintf(stderr, "mergecap: Frame %u of \"%s\" has a network type that can't be saved in a \"%s\" file.\n",
in_file ? in_file->packet_num : 0, in_file ? in_file->filename : "UNKNOWN",
wtap_file_type_subtype_string(file_type));
switch (status) {
case MERGE_OK:
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 wwriting; note that, and
* report the frame number and file type/subtype.
*/
fprintf(stderr, "mergecap: Frame %u of \"%s\" is too large for a \"%s\" file.\n",
in_file ? in_file->packet_num : 0, in_file ? in_file->filename : "UNKNOWN",
wtap_file_type_subtype_string(file_type));
case MERGE_USER_ABORTED:
/* we don't catch SIGINT/SIGTERM (yet?), so we couldn't have aborted */
g_assert(FALSE);
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 wwriting; note that, and
* report the record number and file type/subtype.
*/
fprintf(stderr, "mergecap: Record %u of \"%s\" has a record type that can't be saved in a \"%s\" file.\n",
in_file ? in_file->packet_num : 0, in_file ? in_file->filename : "UNKNOWN",
wtap_file_type_subtype_string(file_type));
case MERGE_ERR_CANT_OPEN_INFILE:
fprintf(stderr, "mergecap: Can't open %s: %s (%s)\n", argv[optind + err_fileno],
wtap_strerror(err), err_info ? err_info : "no more information");
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 wwriting; note that, and
* report the record number and file type/subtype.
*/
fprintf(stderr, "mergecap: Record %u of \"%s\" has data that can't be saved in a \"%s\" file.\n(%s)\n",
in_file ? in_file->packet_num : 0, in_file ? in_file->filename : "UNKNOWN",
wtap_file_type_subtype_string(file_type),
write_err_info != NULL ? write_err_info : "no information supplied");
g_free(write_err_info);
case MERGE_ERR_CANT_OPEN_OUTFILE:
fprintf(stderr, "mergecap: Can't open or create %s: %s\n", out_filename,
wtap_strerror(err));
if (!use_stdout)
ws_close(out_fd);
break;
case MERGE_ERR_CANT_READ_INFILE: /* fall through */
case MERGE_ERR_BAD_PHDR_INTERFACE_ID:
case MERGE_ERR_CANT_WRITE_OUTFILE:
case MERGE_ERR_CANT_CLOSE_OUTFILE:
default:
fprintf(stderr, "mergecap: Error writing to outfile: %s\n",
wtap_strerror(write_err));
fprintf(stderr, "mergecap: %s\n", err_info ? err_info : "unknown error");
break;
}
}
g_free(in_files);
g_free(err_info);
return (!got_read_error && !got_write_error) ? 0 : 2;
return (status == MERGE_OK) ? 0 : 2;
}
/*

View File

@ -210,7 +210,7 @@ dnd_open_file_cmd(gchar *cf_names_freeme)
/* merge the files in chronological order */
tmpname = NULL;
if (cf_merge_files(&tmpname, in_file_count, in_filenames,
WTAP_FILE_TYPE_SUBTYPE_PCAP, FALSE) == CF_OK) {
WTAP_FILE_TYPE_SUBTYPE_PCAPNG, FALSE) == CF_OK) {
/* Merge succeeded; close the currently-open file and try
to open the merged capture file. */
cf_close(&cfile);

View File

@ -36,11 +36,76 @@
#include <string.h>
#include "merge.h"
#include <wsutil/filesystem.h>
#include "wsutil/os_version_info.h"
static const char* idb_merge_mode_strings[] = {
/* IDB_MERGE_MODE_NONE */
"none",
/* IDB_MERGE_MODE_ALL_SAME */
"all",
/* IDB_MERGE_MODE_ANY_SAME */
"any",
/* IDB_MERGE_MODE_MAX */
"UNKNOWN"
};
idb_merge_mode
merge_string_to_idb_merge_mode(const char *name)
{
int i;
for (i = 0; i < IDB_MERGE_MODE_MAX; i++) {
if (g_strcmp0(name, idb_merge_mode_strings[i]) == 0) {
return (idb_merge_mode) i;
}
}
return IDB_MERGE_MODE_MAX;
}
const char *
merge_idb_merge_mode_to_string(const int mode)
{
if (mode >= 0 && mode < IDB_MERGE_MODE_MAX) {
return idb_merge_mode_strings[mode];
}
return idb_merge_mode_strings[(int)IDB_MERGE_MODE_MAX];
}
static void
cleanup_in_file(merge_in_file_t *in_file)
{
g_assert(in_file != NULL);
wtap_close(in_file->wth);
in_file->wth = NULL;
g_array_free(in_file->idb_index_map, TRUE);
in_file->idb_index_map = NULL;
}
static void
add_idb_index_map(merge_in_file_t *in_file, const guint orig_index, const guint found_index)
{
g_assert(in_file != NULL);
g_assert(in_file->idb_index_map != NULL);
/*
* we didn't really need the orig_index, since just appending to the array
* should result in the orig_index being its location in the array; but we
* pass it into this function to do a sanity check here
*/
g_assert(orig_index == in_file->idb_index_map->len);
g_array_append_val(in_file->idb_index_map, found_index);
}
/*
* Scan through the arguments and open the input files
*/
gboolean
merge_open_in_files(int in_file_count, char *const *in_file_names,
merge_open_in_files(int in_file_count, const char *const *in_file_names,
merge_in_file_t **in_files, int *err, gchar **err_info,
int *err_fileno)
{
@ -50,7 +115,7 @@ merge_open_in_files(int in_file_count, char *const *in_file_names,
merge_in_file_t *files;
gint64 size;
files = (merge_in_file_t *)g_malloc(files_size);
files = (merge_in_file_t *)g_malloc0(files_size);
*in_files = files;
for (i = 0; i < in_file_count; i++) {
@ -59,21 +124,23 @@ merge_open_in_files(int in_file_count, char *const *in_file_names,
files[i].data_offset = 0;
files[i].state = PACKET_NOT_PRESENT;
files[i].packet_num = 0;
if (!files[i].wth) {
/* Close the files we've already opened. */
for (j = 0; j < i; j++)
wtap_close(files[j].wth);
cleanup_in_file(&files[j]);
*err_fileno = i;
return FALSE;
}
size = wtap_file_size(files[i].wth, err);
if (size == -1) {
for (j = 0; j + 1 > j && j <= i; j++)
wtap_close(files[j].wth);
cleanup_in_file(&files[j]);
*err_fileno = i;
return FALSE;
}
files[i].size = size;
files[i].idb_index_map = g_array_new(FALSE, FALSE, sizeof(guint));
}
return TRUE;
}
@ -86,7 +153,7 @@ merge_close_in_files(int count, merge_in_file_t in_files[])
{
int i;
for (i = 0; i < count; i++) {
wtap_close(in_files[i].wth);
cleanup_in_file(&in_files[i]);
}
}
@ -277,7 +344,638 @@ merge_append_read_packet(int in_file_count, merge_in_file_t in_files[],
*/
*err = 0;
return &in_files[i];
}
}
/* creates a section header block for the new output file */
static wtapng_section_t*
create_shb_header(const merge_in_file_t *in_files, const guint in_file_count,
const gchar *app_name)
{
wtapng_section_t *shb_hdr = NULL;
GString *comment_gstr;
GString *os_info_str;
guint i;
shb_hdr = wtap_file_get_shb_for_new_file(in_files[0].wth);
comment_gstr = g_string_new("");
/* TODO: merge comments from all files */
/* very lame way to save comments - does not save them from the other files */
if (shb_hdr->opt_comment && strlen(shb_hdr->opt_comment) > 0) {
g_string_append_printf(comment_gstr, "%s \n",shb_hdr->opt_comment);
}
g_free(shb_hdr->opt_comment);
shb_hdr->opt_comment = NULL;
g_string_append_printf(comment_gstr, "File created by merging: \n");
for (i = 0; i < in_file_count; i++) {
g_string_append_printf(comment_gstr, "File%d: %s \n",i+1,in_files[i].filename);
}
os_info_str = g_string_new("");
get_os_version_info(os_info_str);
shb_hdr->section_length = -1;
/* TODO: handle comments from each file being merged */
shb_hdr->opt_comment = g_string_free(comment_gstr, FALSE); /* section comment */
shb_hdr->shb_hardware = NULL; /* NULL if not available, UTF-8 string containing the */
/* description of the hardware used to create this section. */
shb_hdr->shb_os = g_string_free(os_info_str, FALSE); /* UTF-8 string containing the name */
/* of the operating system used to create this section. */
shb_hdr->shb_user_appl = g_strdup(app_name); /* NULL if not available, UTF-8 string containing the name */
/* of the application used to create this section. */
return shb_hdr;
}
static gboolean
is_duplicate_idb(const wtapng_if_descr_t *idb1, const wtapng_if_descr_t *idb2)
{
g_assert(idb1 && idb2);
/* does not compare filters nor interface statistics */
return (idb1->wtap_encap == idb2->wtap_encap &&
idb1->time_units_per_second == idb2->time_units_per_second &&
idb1->tsprecision == idb2->tsprecision &&
idb1->link_type == idb2->link_type &&
/* XXX: should snaplen not be compared? */
idb1->snap_len == idb2->snap_len &&
idb1->if_speed == idb2->if_speed &&
idb1->if_tsresol == idb2->if_tsresol &&
idb1->if_fcslen == idb2->if_fcslen &&
g_strcmp0(idb1->opt_comment, idb2->opt_comment) == 0 &&
g_strcmp0(idb1->if_name, idb2->if_name) == 0 &&
g_strcmp0(idb1->if_description, idb2->if_description) == 0 &&
g_strcmp0(idb1->if_os, idb2->if_os) == 0);
}
/*
* Returns true if all of the input files have duplicate IDBs to the other files.
*/
static gboolean
all_idbs_are_duplicates(const merge_in_file_t *in_files, const guint in_file_count)
{
const wtapng_iface_descriptions_t *first_idb_list = NULL;
const wtapng_iface_descriptions_t *other_idb_list = NULL;
guint first_idb_list_size, other_idb_list_size;
const wtapng_if_descr_t *first_file_idb, *other_file_idb;
guint i, j;
g_assert(in_files != NULL);
/* get the first file's info */
first_idb_list = wtap_file_get_idb_info(in_files[0].wth);
g_assert(first_idb_list->interface_data);
first_idb_list_size = first_idb_list->interface_data->len;
/* now compare the other input files with that */
for (i = 1; i < in_file_count; i++) {
other_idb_list = wtap_file_get_idb_info(in_files[i].wth);
g_assert(other_idb_list->interface_data);
other_idb_list_size = other_idb_list->interface_data->len;
if (other_idb_list_size != first_idb_list_size) {
return FALSE;
}
for (j = 0; j < other_idb_list_size; j++) {
first_file_idb = &g_array_index(first_idb_list->interface_data, wtapng_if_descr_t, j);
other_file_idb = &g_array_index(other_idb_list->interface_data, wtapng_if_descr_t, j);
if (!is_duplicate_idb(first_file_idb, other_file_idb)) {
return FALSE;
}
}
}
return TRUE;
}
/*
* Returns true if the given input_file_idb is a duplicate of an existing one
* in the merged_idb_list; it's a duplicate if the interface description data
* is all identical to a previous one in another input file. For this
* function, the input file IDB's index does NOT need to match the index
* location of a previous one to be considered a duplicate; any match is
* considered a success. That means it will even match another IDB from its
* own (same) input file.
*/
static gboolean
find_duplicate_idb(const wtapng_if_descr_t *input_file_idb,
const wtapng_iface_descriptions_t *merged_idb_list,
guint *found_index)
{
const wtapng_if_descr_t *merged_idb;
guint i;
g_assert(input_file_idb != NULL);
g_assert(merged_idb_list != NULL);
g_assert(merged_idb_list->interface_data != NULL);
g_assert(found_index != NULL);
for (i = 0; i < merged_idb_list->interface_data->len; i++) {
merged_idb = &g_array_index(merged_idb_list->interface_data, wtapng_if_descr_t, i);
if (is_duplicate_idb(input_file_idb, merged_idb)) {
*found_index = i;
return TRUE;
}
}
return FALSE;
}
/* adds IDB to merged file info, returns its index */
static guint
add_idb_to_merged_file(wtapng_iface_descriptions_t *merged_idb_list,
const wtapng_if_descr_t *input_file_idb)
{
wtapng_if_descr_t idb;
g_assert(merged_idb_list != NULL);
g_assert(merged_idb_list->interface_data != NULL);
g_assert(input_file_idb != NULL);
idb.wtap_encap = input_file_idb->wtap_encap;
idb.time_units_per_second = input_file_idb->time_units_per_second;
idb.tsprecision = input_file_idb->tsprecision;
idb.link_type = input_file_idb->link_type;
idb.snap_len = input_file_idb->snap_len;
idb.if_name = g_strdup(input_file_idb->if_name);
idb.opt_comment = g_strdup(input_file_idb->opt_comment);;
idb.if_description = g_strdup(input_file_idb->if_description);
idb.if_speed = input_file_idb->if_speed;
idb.if_tsresol = input_file_idb->if_tsresol;
idb.if_filter_str = NULL;
idb.bpf_filter_len = 0;
idb.if_filter_bpf_bytes = NULL;
idb.if_os = g_strdup(input_file_idb->if_os);
idb.if_fcslen = input_file_idb->if_fcslen;
idb.num_stat_entries = 0; /* Number of ISB:s */
idb.interface_statistics = NULL;
g_array_append_val(merged_idb_list->interface_data, idb);
return merged_idb_list->interface_data->len - 1;
}
/*
* Create clone IDBs for the merge file, based on the input files and mode.
*/
static wtapng_iface_descriptions_t *
generate_merged_idb(merge_in_file_t *in_files, const guint in_file_count, const idb_merge_mode mode)
{
wtapng_iface_descriptions_t *merged_idb_list = NULL;
wtapng_iface_descriptions_t *input_file_idb_list = NULL;
const wtapng_if_descr_t *input_file_idb = NULL;
guint itf_count, merged_index;
guint i;
/* create new IDB info */
merged_idb_list = g_new(wtapng_iface_descriptions_t,1);
merged_idb_list->interface_data = g_array_new(FALSE, FALSE, sizeof(wtapng_if_descr_t));
if (mode == IDB_MERGE_MODE_ALL_SAME && all_idbs_are_duplicates(in_files, in_file_count)) {
guint num_idbs;
/* they're all the same, so just get the first file's IDBs */
input_file_idb_list = wtap_file_get_idb_info(in_files[0].wth);
/* this is really one more than number of IDBs, but that's good for the for-loops */
num_idbs = input_file_idb_list->interface_data->len;
/* put them in the merged file */
for (itf_count = 0; itf_count < num_idbs; itf_count++) {
input_file_idb = &g_array_index(input_file_idb_list->interface_data,
wtapng_if_descr_t, itf_count);
merged_index = add_idb_to_merged_file(merged_idb_list, input_file_idb);
add_idb_index_map(&in_files[0], itf_count, merged_index);
}
/* and set all the other file index maps the same way */
for (i = 1; i < in_file_count; i++) {
for (itf_count = 0; itf_count < num_idbs; itf_count++) {
add_idb_index_map(&in_files[i], itf_count, itf_count);
}
}
g_free(input_file_idb_list);
}
else {
for (i = 0; i < in_file_count; i++) {
input_file_idb_list = wtap_file_get_idb_info(in_files[i].wth);
for (itf_count = 0; itf_count < input_file_idb_list->interface_data->len; itf_count++) {
input_file_idb = &g_array_index(input_file_idb_list->interface_data,
wtapng_if_descr_t, itf_count);
if (mode == IDB_MERGE_MODE_ANY_SAME &&
find_duplicate_idb(input_file_idb, merged_idb_list, &merged_index))
{
/*
* It's the same as a previous IDB, so we're going to "merge"
* them into one by adding a map from its old IDB index to the new
* one. This will be used later to change the phdr interface_id.
*/
add_idb_index_map(&in_files[i], itf_count, merged_index);
}
else {
/*
* This IDB does not match a previous (or we want to save all IDBs),
* so add the IDB to the merge file, and add a map of the indeces.
*/
merged_index = add_idb_to_merged_file(merged_idb_list, input_file_idb);
add_idb_index_map(&in_files[i], itf_count, merged_index);
}
}
g_free(input_file_idb_list);
}
}
return merged_idb_list;
}
static gboolean
map_phdr_interface_id(struct wtap_pkthdr *phdr, const merge_in_file_t *in_file)
{
guint current_interface_id = 0;
g_assert(phdr != NULL);
g_assert(in_file != NULL);
g_assert(in_file->idb_index_map != NULL);
if (phdr->presence_flags & WTAP_HAS_INTERFACE_ID) {
current_interface_id = phdr->interface_id;
}
if (current_interface_id >= in_file->idb_index_map->len) {
/* this shouldn't happen, but in a malformed input file it could */
return FALSE;
}
phdr->interface_id = g_array_index(in_file->idb_index_map, guint, current_interface_id);
phdr->presence_flags |= WTAP_HAS_INTERFACE_ID;
return TRUE;
}
static gchar*
get_read_error_string(const merge_in_file_t *in_files, const guint in_file_count,
const int *err, gchar **err_info)
{
GString *err_message = g_string_new("");
gchar *display_basename = NULL;
guint i;
g_assert(in_files != NULL);
g_assert(err != NULL);
g_assert(err_info != NULL);
if (*err_info == NULL) {
*err_info = g_strdup("no information supplied");
}
/*
* Find the file on which we got the error, and report the error.
*/
for (i = 0; i < in_file_count; i++) {
if (in_files[i].state == GOT_ERROR) {
display_basename = g_filename_display_basename(in_files[i].filename);
switch (*err) {
case WTAP_ERR_SHORT_READ:
g_string_printf(err_message,
"The capture file %s appears to have been cut short"
" in the middle of a packet.", display_basename);
break;
case WTAP_ERR_BAD_FILE:
g_string_printf(err_message,
"The capture file %s appears to be damaged or corrupt.\n(%s)",
display_basename, *err_info);
break;
case WTAP_ERR_DECOMPRESS:
g_string_printf(err_message,
"The compressed capture file %s appears to be damaged or corrupt.\n"
"(%s)", display_basename, *err_info);
break;
default:
g_string_printf(err_message,
"An error occurred while reading the"
" capture file %s: %s.",
display_basename, wtap_strerror(*err));
break;
}
g_free(display_basename);
break;
}
}
g_free(*err_info);
*err_info = g_string_free(err_message, FALSE);
return *err_info;
}
static gchar*
get_write_error_string(const merge_in_file_t *in_file, const int file_type,
const gchar* out_filename, const int *err, gchar **err_info)
{
GString *err_message = g_string_new("");
gchar *display_basename = NULL;
int write_err;
/* in_file may be NULL */
g_assert(err != NULL);
g_assert(err_info != NULL);
if (*err_info == NULL) {
*err_info = g_strdup("no information supplied");
}
write_err = *err;
display_basename = g_filename_display_basename(in_file ? in_file->filename : "UNKNOWN");
if (write_err < 0) {
switch (write_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 wwriting; note that, and
* report the frame number and file type/subtype.
*/
g_string_printf(err_message,
"Frame %u of \"%s\" has a network type that can't be saved in a \"%s\" file.\n",
in_file ? in_file->packet_num : 0, display_basename,
wtap_file_type_subtype_string(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.
*/
g_string_printf(err_message,
"Frame %u of \"%s\" is too large for a \"%s\" file.",
in_file ? in_file->packet_num : 0, display_basename,
wtap_file_type_subtype_string(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.
*/
g_string_printf(err_message,
"Record %u of \"%s\" has a record type that can't be saved in a \"%s\" file.",
in_file ? in_file->packet_num : 0, display_basename,
wtap_file_type_subtype_string(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 frame number and file type/subtype.
*/
g_string_printf(err_message,
"Record %u of \"%s\" has data that can't be saved in a \"%s\" file.\n(%s)",
in_file ? in_file->packet_num : 0, display_basename,
wtap_file_type_subtype_string(file_type), *err_info);
break;
default:
g_string_printf(err_message,
"An error occurred while writing to the file \"%s\": %s.",
out_filename, wtap_strerror(write_err));
break;
}
}
else {
/* OS error. */
g_string_printf(err_message, file_write_error_message(write_err), out_filename);
}
g_free(display_basename);
g_free(*err_info);
*err_info = g_string_free(err_message, FALSE);
return *err_info;
}
/*
* Merges the files base don given input, and invokes callback during
* execution. Returns MERGE_OK on success, or a MERGE_ERR_XXX on failure; note
* that the passed-in 'err' variable will be more specific to what failed, and
* err_info will have pretty output.
*/
merge_result
merge_files(int out_fd, const gchar* out_filename, const int file_type,
const char *const *in_filenames, const guint in_file_count,
const gboolean do_append, const idb_merge_mode mode,
guint snaplen, const gchar *app_name, merge_progress_callback_t* cb,
int *err, gchar **err_info, int *err_fileno)
{
merge_in_file_t *in_files = NULL, *in_file = NULL;
int frame_type = WTAP_ENCAP_PER_PACKET;
merge_result status = MERGE_OK;
wtap_dumper *pdh;
struct wtap_pkthdr *phdr, snap_phdr;
int count = 0;
gboolean stop_flag = FALSE;
g_assert(out_fd > 0);
g_assert(in_file_count > 0);
g_assert(in_filenames != NULL);
g_assert(err != NULL);
g_assert(err_info != NULL);
g_assert(err_fileno != NULL);
/* if a callback was given, it has to have a callback function ptr */
g_assert((cb != NULL) ? (cb->callback_func != NULL) : TRUE);
/* open the input files */
if (!merge_open_in_files(in_file_count, in_filenames, &in_files,
err, err_info, err_fileno)) {
return MERGE_ERR_CANT_OPEN_INFILE;
}
if (cb)
cb->callback_func(MERGE_EVENT_INPUT_FILES_OPENED, 0, in_files, in_file_count, cb->data);
if (snaplen == 0) {
/* Snapshot length not specified - default to the maximum. */
snaplen = WTAP_MAX_PACKET_SIZE;
}
/*
* This doesn't tell us that much. It tells us what to set the outfile's
* encap type to, but that's all - for example, it does *not* tells us
* whether the input files had the same number of IDBs, for the same exact
* interfaces, and only one IDB each, so it doesn't actually tell us
* whether we can merge IDBs into one or not.
*/
frame_type = merge_select_frame_type(in_file_count, in_files);
if (cb)
cb->callback_func(MERGE_EVENT_FRAME_TYPE_SELECTED, frame_type, in_files, in_file_count, cb->data);
/* prepare the outfile */
if (file_type == WTAP_FILE_TYPE_SUBTYPE_PCAPNG) {
wtapng_section_t *shb_hdr = NULL;
wtapng_iface_descriptions_t *idb_inf = NULL;
shb_hdr = create_shb_header(in_files, in_file_count, app_name);
idb_inf = generate_merged_idb(in_files, in_file_count, mode);
pdh = wtap_dump_fdopen_ng(out_fd, file_type, frame_type, snaplen,
FALSE /* compressed */, shb_hdr, idb_inf,
NULL, err);
}
else {
pdh = wtap_dump_fdopen(out_fd, file_type, frame_type, snaplen, FALSE /* compressed */, err);
}
if (pdh == NULL) {
merge_close_in_files(in_file_count, in_files);
g_free(in_files);
return MERGE_ERR_CANT_OPEN_OUTFILE;
}
if (cb)
cb->callback_func(MERGE_EVENT_READY_TO_MERGE, 0, in_files, in_file_count, cb->data);
for (;;) {
*err = 0;
if (do_append) {
in_file = merge_append_read_packet(in_file_count, in_files, err,
err_info);
}
else {
in_file = merge_read_packet(in_file_count, in_files, err,
err_info);
}
if (in_file == NULL) {
/* EOF */
break;
}
if (*err != 0) {
/* I/O error reading from in_file */
status = MERGE_ERR_CANT_READ_INFILE;
break;
}
count++;
if (cb)
stop_flag = cb->callback_func(MERGE_EVENT_PACKET_WAS_READ, count, in_files, in_file_count, cb->data);
if (stop_flag) {
/* The user decided to abort the merge. */
status = MERGE_USER_ABORTED;
break;
}
phdr = wtap_phdr(in_file->wth);
if (snaplen != 0 && phdr->caplen > snaplen) {
/*
* The dumper will only write up to caplen bytes out, so we only
* need to change that value, instead of cloning the whole packet
* with fewer bytes.
*
* XXX: but do we need to change the IDBs' snap_len?
*/
snap_phdr = *phdr;
snap_phdr.caplen = snaplen;
phdr = &snap_phdr;
}
if (file_type == WTAP_FILE_TYPE_SUBTYPE_PCAPNG) {
if (!map_phdr_interface_id(phdr, in_file)) {
status = MERGE_ERR_BAD_PHDR_INTERFACE_ID;
break;
}
}
if (!wtap_dump(pdh, phdr, wtap_buf_ptr(in_file->wth), err, err_info)) {
status = MERGE_ERR_CANT_WRITE_OUTFILE;
break;
}
}
if (cb)
cb->callback_func(MERGE_EVENT_DONE, count, in_files, in_file_count, cb->data);
merge_close_in_files(in_file_count, in_files);
if (status == MERGE_OK || status == MERGE_USER_ABORTED) {
if (!wtap_dump_close(pdh, err))
status = MERGE_ERR_CANT_CLOSE_OUTFILE;
} else {
/*
* We already got some error; no need to report another error on
* close.
*
* Don't overwrite the earlier error.
*/
int close_err = 0;
(void)wtap_dump_close(pdh, &close_err);
}
if (status != MERGE_OK) {
GString *err_message = NULL;
gchar *display_basename = NULL;
switch(status) {
case MERGE_ERR_CANT_READ_INFILE:
*err_info = get_read_error_string(in_files, in_file_count, err, err_info);
break;
case MERGE_ERR_CANT_WRITE_OUTFILE: /* fall through */
case MERGE_ERR_CANT_CLOSE_OUTFILE:
*err_info = get_write_error_string(in_file, file_type, out_filename, err, err_info);
break;
case MERGE_ERR_BAD_PHDR_INTERFACE_ID:
display_basename = g_filename_display_basename(in_file ? in_file->filename : "UNKNOWN");
if (*err_info != NULL)
g_free(*err_info);
err_message = g_string_new("");
g_string_printf(err_message,
"Record %u of \"%s\" has an interface ID which does not match any IDB in its file.",
in_file ? in_file->packet_num : 0, display_basename);
g_free(display_basename);
*err_info = g_string_free(err_message, FALSE);
break;
case MERGE_USER_ABORTED: /* not really an error */
default:
break;
}
}
g_free(in_files);
return status;
}
/*
* Editor modelines - http://www.wireshark.org/tools/modelines.html

View File

@ -46,8 +46,7 @@ typedef struct merge_in_file_s {
in_file_state_e state;
guint32 packet_num; /* current packet number */
gint64 size; /* file size */
guint32 interface_id; /* identifier of the interface.
* Used for fake interfaces when writing WTAP_ENCAP_PER_PACKET */
GArray *idb_index_map; /* used for mapping the old phdr interface_id values to new during merge */
} merge_in_file_t;
/** Open a number of input files to merge.
@ -61,7 +60,7 @@ typedef struct merge_in_file_s {
* @return TRUE if all files could be opened, FALSE otherwise
*/
WS_DLL_PUBLIC gboolean
merge_open_in_files(int in_file_count, char *const *in_file_names,
merge_open_in_files(int in_file_count, const char *const *in_file_names,
merge_in_file_t **in_files, int *err, gchar **err_info,
int *err_fileno);
@ -120,6 +119,104 @@ WS_DLL_PUBLIC merge_in_file_t *
merge_append_read_packet(int in_file_count, merge_in_file_t in_files[],
int *err, gchar **err_info);
/** Return values from merge_files(). */
typedef enum {
MERGE_OK,
MERGE_USER_ABORTED,
/* below here are true errors */
MERGE_ERR_CANT_OPEN_INFILE,
MERGE_ERR_CANT_OPEN_OUTFILE,
MERGE_ERR_CANT_READ_INFILE,
MERGE_ERR_BAD_PHDR_INTERFACE_ID,
MERGE_ERR_CANT_WRITE_OUTFILE,
MERGE_ERR_CANT_CLOSE_OUTFILE
} merge_result;
/** Merge events, used as an arg in the callback function - indicates when the callback was invoked. */
typedef enum {
MERGE_EVENT_INPUT_FILES_OPENED,
MERGE_EVENT_FRAME_TYPE_SELECTED,
MERGE_EVENT_READY_TO_MERGE,
MERGE_EVENT_PACKET_WAS_READ,
MERGE_EVENT_DONE
} merge_event;
/** Merge mode for IDB info. */
typedef enum {
IDB_MERGE_MODE_NONE = 0, /**< no merging of IDBs is done, all IDBs are copied into merged file */
IDB_MERGE_MODE_ALL_SAME,/**< duplicate IDBs merged only if all the files have the same set of IDBs */
IDB_MERGE_MODE_ANY_SAME, /**< any and all duplicate IDBs are merged into one IDB, even within a file */
IDB_MERGE_MODE_MAX
} idb_merge_mode;
/** Returns the idb_merge_mode for the given string name.
*
* @param name The name of the mode.
* @return The idb_merge_mode, or IDB_MERGE_MODE_MAX on failure.
*/
WS_DLL_PUBLIC idb_merge_mode
merge_string_to_idb_merge_mode(const char *name);
/** Returns the string name for the given number.
*
* @param mode The number of the mode, representing the idb_merge_mode enum value.
* @return The string name, or "UNKNOWN" on failure.
*/
WS_DLL_PUBLIC const char*
merge_idb_merge_mode_to_string(const int mode);
/** @struct merge_progress_callback_t
*
* @brief Callback information for merging.
*
* @details The merge_files() routine can invoke a callback during its execution,
* to enable verbose printing or progress bar updating, for example. This struct
* provides merge_files() with the callback routine to invoke, and optionally
* private data to pass through to the callback each time it is invoked.
* For the callback_func routine's arguments: the event is when the callback
* was invoked, the num is an int specific to the event, in_files is an array
* of the created merge info, in_file_count is the size of the array, data is
* whatever was passed in the data member of this struct. The callback_func
* routine's return value should be TRUE if merging should be aborted.
*/
typedef struct {
gboolean (*callback_func)(merge_event event, int num,
const merge_in_file_t in_files[], const guint in_file_count,
void *data);
void *data; /**< private data to use for passing through to the callback function */
} merge_progress_callback_t;
/** Merge the given input files to the output file descriptor.
*
* @param out_fd The already opened output file decriptor
* @param out_filename The output filename, used in error messages
* @param file_type The WTAP_FILE_TYPE_SUBTYPE_XXX output file type
* @param in_filenames An array of input filenames to merge from
* @param in_file_count The number of entries in in_filenames
* @param do_append Whether to append by file order instead of chronological order
* @param mode The IDB_MERGE_MODE_XXX merge mode for interface data
* @param snaplen The snaplen to limit it to, or 0 to leave as it is in the files
* @param app_name The application name performing the merge, used in SHB info
* @param cb The callback information to use during execution
* @param[out] err Set to the internal WTAP_ERR_XXX error code if it failed
* @param[out] err_info Set to a descriptive error string, which must be g_free'd
* @param[out] err_fileno Set to the input file number which failed, if it failed
* @return the frame type
*/
WS_DLL_PUBLIC merge_result
merge_files(int out_fd, const gchar* out_filename, const int file_type,
const char *const *in_filenames, const guint in_file_count,
const gboolean do_append, const idb_merge_mode mode,
guint snaplen, const gchar *app_name, merge_progress_callback_t* cb,
int *err, gchar **err_info, int *err_fileno);
#ifdef __cplusplus
}
#endif /* __cplusplus */

View File

@ -263,6 +263,30 @@ wtap_file_get_idb_info(wtap *wth)
return idb_info;
}
gchar *
wtap_get_debug_if_descr(const wtapng_if_descr_t *if_descr)
{
GString *info = g_string_new("");
g_assert(if_descr);
g_string_printf(info,
"Interface description: wtap_encap=%d, time_units_per_second=%" G_GINT64_MODIFIER "u"
", tsprecision=%d, link_type=%u, snap_len=%u, opt_comment='%s', if_name='%s'"
", if_description='%s', if_speed=%" G_GINT64_MODIFIER "u, if_tsresol=%d"
", if_filter_str='%s', bpf_filter_len=%u, if_fcslen=%d, num_stat_entries=%u",
if_descr->wtap_encap, if_descr->time_units_per_second, if_descr->tsprecision,
if_descr->link_type, if_descr->snap_len,
if_descr->opt_comment ? if_descr->opt_comment : "NONE",
if_descr->if_name ? if_descr->if_name : "NONE",
if_descr->if_description ? if_descr->if_description : "NONE",
if_descr->if_speed, if_descr->if_tsresol,
if_descr->if_filter_str ? if_descr->if_filter_str : "NONE",
if_descr->bpf_filter_len, if_descr->if_fcslen, if_descr->num_stat_entries);
return g_string_free(info, FALSE);
}
wtapng_name_res_t *
wtap_file_get_nrb_for_new_file(wtap *wth)
{

View File

@ -1734,6 +1734,18 @@ void wtap_write_shb_comment(wtap *wth, gchar *comment);
WS_DLL_PUBLIC
wtapng_iface_descriptions_t *wtap_file_get_idb_info(wtap *wth);
/**
* @brief Gets a debug string of an interface description.
* @details Returns a newly allocated string of debug information about
* the given interface descrption, useful for debugging.
* @note The returned pointer must be g_free'd.
*
* @param if_descr The interface description.
* @return A newly allocated gcahr array string, which must be g_free'd.
*/
WS_DLL_PUBLIC
gchar *wtap_get_debug_if_descr(const wtapng_if_descr_t *if_descr);
/**
* @brief Gets new name resolution info for new file, based on existing info.
* @details Creates a new wtapng_name_res_t name resolution info and only