Revert "UNUSED enc_key_id_{equal,hash}"
[wireshark-sm.git] / wiretap / merge.c
blob8633601376a58dc38c6ef1a3f5ea9927d9ac82db
1 /* Combine multiple dump files, either by appending or by merging by timestamp
3 * Written by Scott Renfro <scott@renfro.org> based on
4 * editcap by Richard Sharpe and Guy Harris
6 * Copyright 2013, Scott Renfro <scott[AT]renfro.org>
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1998 Gerald Combs
12 * SPDX-License-Identifier: GPL-2.0-or-later
15 #include "config.h"
17 #define WS_LOG_DOMAIN LOG_DOMAIN_WIRETAP
18 #include "merge.h"
20 #include <stdlib.h>
21 #include <errno.h>
23 #ifndef _WIN32
24 #include <sys/resource.h>
25 #endif
27 #ifdef HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif
31 #if defined(__APPLE__)
32 #include <sys/sysctl.h>
33 #endif
35 #include <string.h>
36 #include "wtap_opttypes.h"
37 #include "wtap-int.h"
39 #include <wsutil/filesystem.h>
40 #include "wsutil/os_version_info.h"
41 #include <wsutil/report_message.h>
42 #include <wsutil/wslog.h>
43 #include <wsutil/ws_assert.h>
46 static const char* idb_merge_mode_strings[] = {
47 /* IDB_MERGE_MODE_NONE */
48 "none",
49 /* IDB_MERGE_MODE_ALL_SAME */
50 "all",
51 /* IDB_MERGE_MODE_ANY_SAME */
52 "any",
53 /* IDB_MERGE_MODE_MAX */
54 "UNKNOWN"
57 idb_merge_mode
58 merge_string_to_idb_merge_mode(const char *name)
60 int i;
61 for (i = 0; i < IDB_MERGE_MODE_MAX; i++) {
62 if (g_strcmp0(name, idb_merge_mode_strings[i]) == 0) {
63 return (idb_merge_mode) i;
66 return IDB_MERGE_MODE_MAX;
69 const char *
70 merge_idb_merge_mode_to_string(const int mode)
72 if (mode >= 0 && mode < IDB_MERGE_MODE_MAX) {
73 return idb_merge_mode_strings[mode];
75 return idb_merge_mode_strings[(int)IDB_MERGE_MODE_MAX];
79 static void
80 cleanup_in_file(merge_in_file_t *in_file)
82 ws_assert(in_file != NULL);
84 wtap_close(in_file->wth);
85 in_file->wth = NULL;
87 g_array_free(in_file->idb_index_map, true);
88 in_file->idb_index_map = NULL;
90 wtap_rec_cleanup(&in_file->rec);
91 ws_buffer_free(&in_file->frame_buffer);
94 static void
95 add_idb_index_map(merge_in_file_t *in_file, const unsigned orig_index _U_, const unsigned found_index)
97 ws_assert(in_file != NULL);
98 ws_assert(in_file->idb_index_map != NULL);
101 * we didn't really need the orig_index, since just appending to the array
102 * should result in the orig_index being its location in the array; but we
103 * pass it into this function to do a sanity check here
105 ws_assert(orig_index == in_file->idb_index_map->len);
107 g_array_append_val(in_file->idb_index_map, found_index);
110 #ifndef _WIN32
111 static bool
112 raise_limit(int resource, unsigned add)
114 struct rlimit rl;
115 /* For now don't try to raise the hard limit; we could if we
116 * have the appropriate privileges (CAP_SYS_RESOURCE on Linux).
118 if ((getrlimit(resource, &rl) == 0) && rl.rlim_cur < rl.rlim_max) {
119 rlim_t old_cur = rl.rlim_cur;
120 /* What open file descriptor limit do we need? */
121 rl.rlim_cur = rl.rlim_cur + add;
122 /* Check for overflow (unlikely). */
123 rl.rlim_cur = MAX(old_cur, rl.rlim_cur);
124 rl.rlim_cur = MIN(rl.rlim_cur, rl.rlim_max);
125 if (setrlimit(resource, &rl) == 0) {
126 return true;
128 #if defined(__APPLE__)
129 /* On Leopard, setrlimit(RLIMIT_NOFILE, ...) fails
130 * on attempts to set rlim_cur above OPEN_MAX, even
131 * if rlim_max > OPEN_MAX. OPEN_MAX is 10240.
133 * Starting with some later version (at least Mojave,
134 * possibly earlier), it can be set to kern.maxfilesperproc
135 * from sysctl, which is _usually_ higher than 10240.
137 * In Big Sur and later, it can always be set to rlim_max.
138 * (That is, setrlimit() will return 0 and getrlimit() will
139 * subsequently return the set value; the enforced limit
140 * is the lesser of that and kern.maxfilesperproc.)
142 if (resource == RLIMIT_NOFILE) {
143 unsigned int nlimit = 0;
144 size_t limit_len = sizeof(nlimit);
145 if (sysctlbyname("kern.maxfilesperproc", &nlimit, &limit_len, NULL, 0) != 0 || nlimit < OPEN_MAX) {
146 rl.rlim_cur = OPEN_MAX;
147 } else {
148 rl.rlim_cur = nlimit;
150 if (setrlimit(RLIMIT_NOFILE, &rl) == 0) {
151 return true;
153 if (rl.rlim_cur > OPEN_MAX) {
154 rl.rlim_cur = OPEN_MAX;
155 if (setrlimit(RLIMIT_NOFILE, &rl) == 0) {
156 return true;
160 #endif
162 return false;
164 #endif
166 /** Open a number of input files to merge.
168 * @param in_file_count number of entries in in_file_names
169 * @param in_file_names filenames of the input files
170 * @param out_files output pointer with filled file array, or NULL
171 * @param err wiretap error, if failed
172 * @param err_info wiretap error string, if failed
173 * @param err_fileno file on which open failed, if failed
174 * @return The number of input files opened, which can be less than
175 * the number requested if the limit of open file descriptors is reached.
177 static unsigned
178 merge_open_in_files(unsigned in_file_count, const char *const *in_file_names,
179 merge_in_file_t **out_files, merge_progress_callback_t* cb,
180 int *err, char **err_info, unsigned *err_fileno)
182 unsigned i = 0;
183 unsigned j;
184 size_t files_size = in_file_count * sizeof(merge_in_file_t);
185 merge_in_file_t *files;
186 int64_t size;
187 #ifndef _WIN32
188 bool try_raise_nofile = false;
189 #endif
191 files = (merge_in_file_t *)g_malloc0(files_size);
192 *out_files = NULL;
194 while (i < in_file_count) {
195 files[i].filename = in_file_names[i];
196 files[i].wth = wtap_open_offline(in_file_names[i], WTAP_TYPE_AUTO, err, err_info, false);
197 files[i].state = RECORD_NOT_PRESENT;
198 files[i].packet_num = 0;
200 if (!files[i].wth) {
201 if (*err == EMFILE && i > 2) {
202 /* We need at least two opened files to merge things if we
203 * are batch processing. (If there was only one file to open
204 * then we can "merge" a single file so long as we don't get
205 * EMFILE, even though that's pointless.)
207 #ifdef _WIN32
208 report_warning("Requested opening %u files but could only open %u: %s\nUsing temporary files to batch process.", in_file_count, i, g_strerror(*err));
209 #else
210 if (!try_raise_nofile) {
211 try_raise_nofile = true;
212 if (raise_limit(RLIMIT_NOFILE, in_file_count - i)) {
213 continue;
216 report_warning("Requested opening %u files but could only open %u: %s\nUsing temporary files to batch process (try ulimit -n to adjust the limit).", in_file_count, i, g_strerror(*err));
217 #endif
218 in_file_count = i;
219 files_size = in_file_count * sizeof(merge_in_file_t);
220 files = (merge_in_file_t *)g_realloc(files, files_size);
221 *err = 0;
222 break;
223 } else {
224 /* Close the files we've already opened. */
225 for (j = 0; j < i; j++)
226 cleanup_in_file(&files[j]);
227 g_free(files);
228 *err_fileno = i;
229 return 0;
232 size = wtap_file_size(files[i].wth, err);
233 if (size == -1) {
234 for (j = 0; j != UINT_MAX && j <= i; j++)
235 cleanup_in_file(&files[j]);
236 g_free(files);
237 *err_fileno = i;
238 return 0;
240 wtap_rec_init(&files[i].rec);
241 ws_buffer_init(&files[i].frame_buffer, 1514);
242 files[i].size = size;
243 files[i].idb_index_map = g_array_new(false, false, sizeof(unsigned));
245 i++;
248 if (cb)
249 cb->callback_func(MERGE_EVENT_INPUT_FILES_OPENED, 0, files, in_file_count, cb->data);
251 *out_files = files;
252 return in_file_count;
255 /** Close the input files again.
257 * @param in_file_count number of entries in in_files
258 * @param in_files input file array to be closed
260 static void
261 merge_close_in_files(int in_file_count, merge_in_file_t in_files[])
263 int i;
264 for (i = 0; i < in_file_count; i++) {
265 cleanup_in_file(&in_files[i]);
269 /** Select an output frame type based on the input files
271 * If all files have the same frame type, then use that.
272 * Otherwise select WTAP_ENCAP_PER_PACKET. If the selected
273 * output file type doesn't support per packet frame types,
274 * then the wtap_dump_open call will fail with a reasonable
275 * error condition.
277 * @param file_type output file type
278 * @param in_file_count number of entries in in_files
279 * @param in_files input file array
280 * @return the frame type
282 static int
283 merge_select_frame_type(const int file_type, int in_file_count, merge_in_file_t in_files[])
285 int i;
286 int selected_frame_type;
288 selected_frame_type = wtap_file_encap(in_files[0].wth);
289 if (!wtap_dump_can_write_encap(file_type, selected_frame_type)) {
290 return WTAP_ENCAP_UNKNOWN;
293 for (i = 1; i < in_file_count; i++) {
294 int this_frame_type = wtap_file_encap(in_files[i].wth);
295 if (!wtap_dump_can_write_encap(file_type, this_frame_type)) {
296 return WTAP_ENCAP_UNKNOWN;
298 if (selected_frame_type != this_frame_type) {
299 selected_frame_type = WTAP_ENCAP_PER_PACKET;
300 break;
304 return selected_frame_type;
308 * returns true if first argument is earlier than second
310 static bool
311 is_earlier(nstime_t *l, nstime_t *r) /* XXX, move to nstime.c */
313 if (l->secs > r->secs) { /* left is later */
314 return false;
315 } else if (l->secs < r->secs) { /* left is earlier */
316 return true;
317 } else if (l->nsecs > r->nsecs) { /* tv_sec equal, l.usec later */
318 return false;
320 /* either one < two or one == two
321 * either way, return one
323 return true;
326 /** Read the next packet, in chronological order, from the set of files to
327 * be merged.
329 * On success, set *err to 0 and return a pointer to the merge_in_file_t
330 * for the file from which the packet was read.
332 * On a read error, set *err to the error and return a pointer to the
333 * merge_in_file_t for the file on which we got an error.
335 * On an EOF (meaning all the files are at EOF), set *err to 0 and return
336 * NULL.
338 * @param in_file_count number of entries in in_files
339 * @param in_files input file array
340 * @param err wiretap error, if failed
341 * @param err_info wiretap error string, if failed
342 * @return pointer to merge_in_file_t for file from which that packet
343 * came or on which we got a read error, or NULL if we're at EOF on
344 * all files
346 static merge_in_file_t *
347 merge_read_packet(int in_file_count, merge_in_file_t in_files[],
348 int *err, char **err_info)
350 int i;
351 int ei = -1;
352 nstime_t tv = NSTIME_INIT_MAX;
353 wtap_rec *rec;
356 * Make sure we have a record available from each file that's not at
357 * EOF, and search for the record with the earliest time stamp or
358 * with no time stamp (those records are treated as earlier than
359 * all other records). Yes, this means you won't get a chronological
360 * merge of those records, but you obviously *can't* get that.
362 for (i = 0; i < in_file_count; i++) {
363 int64_t data_offset;
365 if (in_files[i].state == RECORD_NOT_PRESENT) {
367 * No packet available, and we haven't seen an error or EOF yet,
368 * so try to read the next packet.
370 if (!wtap_read(in_files[i].wth, &in_files[i].rec,
371 &in_files[i].frame_buffer, err, err_info,
372 &data_offset)) {
373 if (*err != 0) {
374 in_files[i].state = GOT_ERROR;
375 return &in_files[i];
377 in_files[i].state = AT_EOF;
378 } else
379 in_files[i].state = RECORD_PRESENT;
382 if (in_files[i].state == RECORD_PRESENT) {
383 rec = &in_files[i].rec;
384 if (!(rec->presence_flags & WTAP_HAS_TS)) {
386 * No time stamp. Pick this record, and stop looking.
388 ei = i;
389 break;
391 if (is_earlier(&rec->ts, &tv)) {
393 * This record's time stamp is earlier than any of the
394 * records we've seen so far. Pick it, for now, but
395 * keep looking.
397 tv = rec->ts;
398 ei = i;
403 if (ei == -1) {
404 /* All the streams are at EOF. Return an EOF indication. */
405 *err = 0;
406 return NULL;
409 /* We'll need to read another packet from this file. */
410 in_files[ei].state = RECORD_NOT_PRESENT;
412 /* Count this packet. */
413 in_files[ei].packet_num++;
416 * Return a pointer to the merge_in_file_t of the file from which the
417 * packet was read.
419 *err = 0;
420 return &in_files[ei];
423 /** Read the next packet, in file sequence order, from the set of files
424 * to be merged.
426 * On success, set *err to 0 and return a pointer to the merge_in_file_t
427 * for the file from which the packet was read.
429 * On a read error, set *err to the error and return a pointer to the
430 * merge_in_file_t for the file on which we got an error.
432 * On an EOF (meaning all the files are at EOF), set *err to 0 and return
433 * NULL.
435 * @param in_file_count number of entries in in_files
436 * @param in_files input file array
437 * @param err wiretap error, if failed
438 * @param err_info wiretap error string, if failed
439 * @return pointer to merge_in_file_t for file from which that packet
440 * came or on which we got a read error, or NULL if we're at EOF on
441 * all files
443 static merge_in_file_t *
444 merge_append_read_packet(int in_file_count, merge_in_file_t in_files[],
445 int *err, char **err_info)
447 int i;
448 int64_t data_offset;
451 * Find the first file not at EOF, and read the next packet from it.
453 for (i = 0; i < in_file_count; i++) {
454 if (in_files[i].state == AT_EOF)
455 continue; /* This file is already at EOF */
456 if (wtap_read(in_files[i].wth, &in_files[i].rec,
457 &in_files[i].frame_buffer, err, err_info,
458 &data_offset))
459 break; /* We have a packet */
460 if (*err != 0) {
461 /* Read error - quit immediately. */
462 in_files[i].state = GOT_ERROR;
463 return &in_files[i];
465 /* EOF - flag this file as being at EOF, and try the next one. */
466 in_files[i].state = AT_EOF;
468 if (i == in_file_count) {
469 /* All the streams are at EOF. Return an EOF indication. */
470 *err = 0;
471 return NULL;
475 * Return a pointer to the merge_in_file_t of the file from which the
476 * packet was read.
478 *err = 0;
479 return &in_files[i];
483 /* creates a section header block for the new output file */
484 static GArray*
485 create_shb_header(const merge_in_file_t *in_files, const unsigned in_file_count,
486 const char *app_name)
488 GArray *shb_hdrs;
489 wtap_block_t shb_hdr;
490 GString *comment_gstr;
491 GString *os_info_str;
492 unsigned i;
493 wtapng_section_mandatory_t* shb_data;
494 size_t opt_len;
495 char *opt_str;
497 shb_hdrs = wtap_file_get_shb_for_new_file(in_files[0].wth);
498 shb_hdr = g_array_index(shb_hdrs, wtap_block_t, 0);
500 comment_gstr = g_string_new("");
503 * TODO: merge comments from all files
505 * XXX - do we want some way to record which comments, hardware/OS/app
506 * descriptions, IDBs, etc.? came from which files?
509 g_string_append_printf(comment_gstr, "File created by merging: \n");
511 for (i = 0; i < in_file_count; i++) {
512 g_string_append_printf(comment_gstr, "File%d: %s \n",i+1,in_files[i].filename);
515 os_info_str = g_string_new("");
516 get_os_version_info(os_info_str);
518 shb_data = (wtapng_section_mandatory_t*)wtap_block_get_mandatory_data(shb_hdr);
519 shb_data->section_length = -1;
520 /* TODO: handle comments from each file being merged */
521 /* XXX: 65535 is the maximum size for an option (hence comment) in pcapng.
522 * Truncate it? Let wiretap/pcapng.c decide what to do? (Currently it
523 * writes nothing without reporting an error.) What if we support other
524 * output file formats later?
526 opt_str = g_string_free(comment_gstr, FALSE);
527 /* XXX: We probably want to prepend (insert at index 0) instead? */
528 wtap_block_add_string_option_owned(shb_hdr, OPT_COMMENT, opt_str);
530 * XXX - and how do we preserve all the OPT_SHB_HARDWARE, OPT_SHB_OS,
531 * and OPT_SHB_USERAPPL values from all the previous files?
533 wtap_block_remove_option(shb_hdr, OPT_SHB_HARDWARE);
534 opt_len = os_info_str->len;
535 opt_str = g_string_free(os_info_str, FALSE);
536 if (opt_str) {
537 wtap_block_set_string_option_value(shb_hdr, OPT_SHB_OS, opt_str, opt_len); /* UTF-8 string containing the name */
538 /* of the operating system used to create this section. */
539 g_free(opt_str);
540 } else {
542 * No OS information; remove the old version.
544 wtap_block_remove_option(shb_hdr, OPT_SHB_OS);
546 wtap_block_set_string_option_value(shb_hdr, OPT_SHB_USERAPPL, app_name, app_name ? strlen(app_name): 0 ); /* NULL if not available, UTF-8 string containing the name */
547 /* of the application used to create this section. */
549 return shb_hdrs;
552 static bool
553 is_duplicate_idb(const wtap_block_t idb1, const wtap_block_t idb2)
555 wtapng_if_descr_mandatory_t *idb1_mand, *idb2_mand;
556 bool have_idb1_value, have_idb2_value;
557 uint64_t idb1_if_speed, idb2_if_speed;
558 uint8_t idb1_if_tsresol, idb2_if_tsresol;
559 uint8_t idb1_if_fcslen, idb2_if_fcslen;
560 char *idb1_opt_comment, *idb2_opt_comment;
561 char *idb1_if_name, *idb2_if_name;
562 char *idb1_if_description, *idb2_if_description;
563 char *idb1_if_hardware, *idb2_if_hardware;
564 char *idb1_if_os, *idb2_if_os;
566 ws_assert(idb1 && idb2);
567 idb1_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(idb1);
568 idb2_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(idb2);
570 ws_debug("merge::is_duplicate_idb() called");
571 ws_debug("idb1_mand->wtap_encap == idb2_mand->wtap_encap: %s",
572 (idb1_mand->wtap_encap == idb2_mand->wtap_encap) ? "true":"false");
573 if (idb1_mand->wtap_encap != idb2_mand->wtap_encap) {
574 /* Clearly not the same interface. */
575 ws_debug("returning false");
576 return false;
579 ws_debug("idb1_mand->time_units_per_second == idb2_mand->time_units_per_second: %s",
580 (idb1_mand->time_units_per_second == idb2_mand->time_units_per_second) ? "true":"false");
581 if (idb1_mand->time_units_per_second != idb2_mand->time_units_per_second) {
583 * Probably not the same interface, and we can't combine them
584 * in any case.
586 ws_debug("returning false");
587 return false;
590 ws_debug("idb1_mand->tsprecision == idb2_mand->tsprecision: %s",
591 (idb1_mand->tsprecision == idb2_mand->tsprecision) ? "true":"false");
592 if (idb1_mand->tsprecision != idb2_mand->tsprecision) {
594 * Probably not the same interface, and we can't combine them
595 * in any case.
597 ws_debug("returning false");
598 return false;
601 /* XXX: should snaplen not be compared? */
602 ws_debug("idb1_mand->snap_len == idb2_mand->snap_len: %s",
603 (idb1_mand->snap_len == idb2_mand->snap_len) ? "true":"false");
604 if (idb1_mand->snap_len != idb2_mand->snap_len) {
605 ws_debug("returning false");
606 return false;
609 /* XXX - what do to if we have only one value? */
610 have_idb1_value = (wtap_block_get_uint64_option_value(idb1, OPT_IDB_SPEED, &idb1_if_speed) == WTAP_OPTTYPE_SUCCESS);
611 have_idb2_value = (wtap_block_get_uint64_option_value(idb2, OPT_IDB_SPEED, &idb2_if_speed) == WTAP_OPTTYPE_SUCCESS);
612 if (have_idb1_value && have_idb2_value) {
613 ws_debug("idb1_if_speed == idb2_if_speed: %s",
614 (idb1_if_speed == idb2_if_speed) ? "true":"false");
615 if (idb1_if_speed != idb2_if_speed) {
616 ws_debug("returning false");
617 return false;
621 /* XXX - what do to if we have only one value? */
622 have_idb1_value = (wtap_block_get_uint8_option_value(idb1, OPT_IDB_TSRESOL, &idb1_if_tsresol) == WTAP_OPTTYPE_SUCCESS);
623 have_idb2_value = (wtap_block_get_uint8_option_value(idb2, OPT_IDB_TSRESOL, &idb2_if_tsresol) == WTAP_OPTTYPE_SUCCESS);
624 if (have_idb1_value && have_idb2_value) {
625 ws_debug("idb1_if_tsresol == idb2_if_tsresol: %s",
626 (idb1_if_tsresol == idb2_if_tsresol) ? "true":"false");
627 if (idb1_if_tsresol != idb2_if_tsresol) {
628 ws_debug("returning false");
629 return false;
633 /* XXX - what do to if we have only one value? */
634 have_idb1_value = (wtap_block_get_uint8_option_value(idb1, OPT_IDB_FCSLEN, &idb1_if_fcslen) == WTAP_OPTTYPE_SUCCESS);
635 have_idb2_value = (wtap_block_get_uint8_option_value(idb2, OPT_IDB_FCSLEN, &idb2_if_fcslen) == WTAP_OPTTYPE_SUCCESS);
636 if (have_idb1_value && have_idb2_value) {
637 ws_debug("idb1_if_fcslen == idb2_if_fcslen: %s",
638 (idb1_if_fcslen == idb2_if_fcslen) ? "true":"false");
639 if (idb1_if_fcslen == idb2_if_fcslen) {
640 ws_debug("returning false");
641 return false;
646 * XXX - handle multiple comments?
647 * XXX - if the comments are different, just combine them if we
648 * decide the two interfaces are really the same? As comments
649 * can be arbitrary strings added by people, the fact that they're
650 * different doesn't necessarily mean the interfaces are different.
652 have_idb1_value = (wtap_block_get_nth_string_option_value(idb1, OPT_COMMENT, 0, &idb1_opt_comment) == WTAP_OPTTYPE_SUCCESS);
653 have_idb2_value = (wtap_block_get_nth_string_option_value(idb2, OPT_COMMENT, 0, &idb2_opt_comment) == WTAP_OPTTYPE_SUCCESS);
654 if (have_idb1_value && have_idb2_value) {
655 ws_debug("g_strcmp0(idb1_opt_comment, idb2_opt_comment) == 0: %s",
656 (g_strcmp0(idb1_opt_comment, idb2_opt_comment) == 0) ? "true":"false");
657 if (g_strcmp0(idb1_opt_comment, idb2_opt_comment) != 0) {
658 ws_debug("returning false");
659 return false;
663 /* XXX - what do to if we have only one value? */
664 have_idb1_value = (wtap_block_get_string_option_value(idb1, OPT_IDB_NAME, &idb1_if_name) == WTAP_OPTTYPE_SUCCESS);
665 have_idb2_value = (wtap_block_get_string_option_value(idb2, OPT_IDB_NAME, &idb2_if_name) == WTAP_OPTTYPE_SUCCESS);
666 if (have_idb1_value && have_idb2_value) {
667 ws_debug("g_strcmp0(idb1_if_name, idb2_if_name) == 0: %s",
668 (g_strcmp0(idb1_if_name, idb2_if_name) == 0) ? "true":"false");
669 if (g_strcmp0(idb1_if_name, idb2_if_name) != 0) {
670 ws_debug("returning false");
671 return false;
675 /* XXX - what do to if we have only one value? */
676 have_idb1_value = (wtap_block_get_string_option_value(idb1, OPT_IDB_DESCRIPTION, &idb1_if_description) == WTAP_OPTTYPE_SUCCESS);
677 have_idb2_value = (wtap_block_get_string_option_value(idb2, OPT_IDB_DESCRIPTION, &idb2_if_description) == WTAP_OPTTYPE_SUCCESS);
678 if (have_idb1_value && have_idb2_value) {
679 ws_debug("g_strcmp0(idb1_if_description, idb2_if_description) == 0: %s",
680 (g_strcmp0(idb1_if_description, idb2_if_description) == 0) ? "true":"false");
681 if (g_strcmp0(idb1_if_description, idb2_if_description) != 0) {
682 ws_debug("returning false");
683 return false;
687 /* XXX - what do to if we have only one value? */
688 have_idb1_value = (wtap_block_get_string_option_value(idb1, OPT_IDB_HARDWARE, &idb1_if_hardware) == WTAP_OPTTYPE_SUCCESS);
689 have_idb2_value = (wtap_block_get_string_option_value(idb2, OPT_IDB_HARDWARE, &idb2_if_hardware) == WTAP_OPTTYPE_SUCCESS);
690 if (have_idb1_value && have_idb2_value) {
691 ws_debug("g_strcmp0(idb1_if_hardware, idb2_if_hardware) == 0: %s",
692 (g_strcmp0(idb1_if_hardware, idb2_if_hardware) == 0) ? "true":"false");
693 if (g_strcmp0(idb1_if_hardware, idb2_if_hardware) != 0) {
694 ws_debug("returning false");
695 return false;
699 /* XXX - what do to if we have only one value? */
700 have_idb1_value = (wtap_block_get_string_option_value(idb1, OPT_IDB_OS, &idb1_if_os) == WTAP_OPTTYPE_SUCCESS);
701 have_idb2_value = (wtap_block_get_string_option_value(idb2, OPT_IDB_OS, &idb2_if_os) == WTAP_OPTTYPE_SUCCESS);
702 if (have_idb1_value && have_idb2_value) {
703 ws_debug("g_strcmp0(idb1_if_os, idb2_if_os) == 0: %s",
704 (g_strcmp0(idb1_if_os, idb2_if_os) == 0) ? "true":"false");
705 if (g_strcmp0(idb1_if_os, idb2_if_os) != 0) {
706 ws_debug("returning false");
707 return false;
711 /* does not compare filters nor interface statistics */
712 ws_debug("returning true");
713 return true;
717 * Returns true if all of the input files have duplicate IDBs to the other files.
719 static bool
720 all_idbs_are_duplicates(const merge_in_file_t *in_files, const unsigned in_file_count)
722 wtapng_iface_descriptions_t *first_idb_list = NULL;
723 wtapng_iface_descriptions_t *other_idb_list = NULL;
724 unsigned first_idb_list_size, other_idb_list_size;
725 wtap_block_t first_file_idb, other_file_idb;
726 unsigned i, j;
728 ws_assert(in_files != NULL);
730 /* get the first file's info */
731 first_idb_list = wtap_file_get_idb_info(in_files[0].wth);
732 ws_assert(first_idb_list->interface_data);
734 first_idb_list_size = first_idb_list->interface_data->len;
736 /* now compare the other input files with that */
737 for (i = 1; i < in_file_count; i++) {
738 other_idb_list = wtap_file_get_idb_info(in_files[i].wth);
739 ws_assert(other_idb_list->interface_data);
740 other_idb_list_size = other_idb_list->interface_data->len;
742 if (other_idb_list_size != first_idb_list_size) {
743 ws_debug("sizes of IDB lists don't match: first=%u, other=%u",
744 first_idb_list_size, other_idb_list_size);
745 g_free(other_idb_list);
746 g_free(first_idb_list);
747 return false;
750 for (j = 0; j < other_idb_list_size; j++) {
751 first_file_idb = g_array_index(first_idb_list->interface_data, wtap_block_t, j);
752 other_file_idb = g_array_index(other_idb_list->interface_data, wtap_block_t, j);
754 if (!is_duplicate_idb(first_file_idb, other_file_idb)) {
755 ws_debug("IDBs at index %d do not match, returning false", j);
756 g_free(other_idb_list);
757 g_free(first_idb_list);
758 return false;
761 g_free(other_idb_list);
764 ws_debug("returning true");
766 g_free(first_idb_list);
768 return true;
772 * Returns true if the given input_file_idb is a duplicate of an existing one
773 * in the merged_idb_list; it's a duplicate if the interface description data
774 * is all identical to a previous one in another input file. For this
775 * function, the input file IDB's index does NOT need to match the index
776 * location of a previous one to be considered a duplicate; any match is
777 * considered a success. That means it will even match another IDB from its
778 * own (same) input file.
780 static bool
781 find_duplicate_idb(const wtap_block_t input_file_idb,
782 const wtapng_iface_descriptions_t *merged_idb_list,
783 unsigned *found_index)
785 wtap_block_t merged_idb;
786 unsigned i;
788 ws_assert(input_file_idb != NULL);
789 ws_assert(merged_idb_list != NULL);
790 ws_assert(merged_idb_list->interface_data != NULL);
791 ws_assert(found_index != NULL);
793 for (i = 0; i < merged_idb_list->interface_data->len; i++) {
794 merged_idb = g_array_index(merged_idb_list->interface_data, wtap_block_t, i);
796 if (is_duplicate_idb(input_file_idb, merged_idb)) {
797 *found_index = i;
798 return true;
802 return false;
805 /* Adds IDB to merged file info. If pdh is not NULL, also tries to
806 * add the IDB to the file (if the file type supports writing IDBs).
807 * returns true on success
808 * (merged_idb_list->interface_data->len - 1 is the new index) */
809 static bool
810 add_idb_to_merged_file(wtapng_iface_descriptions_t *merged_idb_list,
811 const wtap_block_t input_file_idb, wtap_dumper *pdh,
812 int *err, char **err_info)
814 wtap_block_t idb;
815 wtapng_if_descr_mandatory_t* idb_mand;
817 ws_assert(merged_idb_list != NULL);
818 ws_assert(merged_idb_list->interface_data != NULL);
819 ws_assert(input_file_idb != NULL);
821 idb = wtap_block_make_copy(input_file_idb);
822 idb_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(idb);
824 /* Don't copy filter or stat information */
825 idb_mand->num_stat_entries = 0; /* Number of ISB:s */
826 idb_mand->interface_statistics = NULL;
828 if (pdh != NULL) {
829 if (wtap_file_type_subtype_supports_block(wtap_dump_file_type_subtype(pdh), WTAP_BLOCK_IF_ID_AND_INFO) != BLOCK_NOT_SUPPORTED) {
830 if (!wtap_dump_add_idb(pdh, input_file_idb, err, err_info)) {
831 return false;
835 g_array_append_val(merged_idb_list->interface_data, idb);
837 return true;
841 * Create clone IDBs for the merge file for IDBs found in the middle of
842 * input files while processing.
844 static bool
845 process_new_idbs(wtap_dumper *pdh, merge_in_file_t *in_files, const unsigned in_file_count, const idb_merge_mode mode, wtapng_iface_descriptions_t *merged_idb_list, int *err, char **err_info)
847 wtap_block_t input_file_idb;
848 unsigned itf_count, merged_index;
849 unsigned i;
851 for (i = 0; i < in_file_count; i++) {
854 * The number below is the global interface number within wth,
855 * not the number within the section. We will do both mappings
856 * in map_rec_interface_id().
858 itf_count = in_files[i].wth->next_interface_data;
859 while ((input_file_idb = wtap_get_next_interface_description(in_files[i].wth)) != NULL) {
861 /* If we were initially in ALL mode and all the interfaces
862 * did match, then we set the mode to ANY (merge duplicates).
863 * If the interfaces didn't match, then we are still in ALL
864 * mode, but treat that as NONE (write out all IDBs.)
865 * XXX: Should there be separate modes for "match ALL at the start
866 * and ANY later" vs "match ALL at the beginning and NONE later"?
867 * Should there be a two-pass mode for people who want ALL mode to
868 * work for IDBs in the middle of the file? (See #16542)
871 if (mode == IDB_MERGE_MODE_ANY_SAME &&
872 find_duplicate_idb(input_file_idb, merged_idb_list, &merged_index))
874 ws_debug("mode ANY set and found a duplicate");
876 * It's the same as a previous IDB, so we're going to "merge"
877 * them into one by adding a map from its old IDB index to the
878 * new one. This will be used later to change the rec
879 * interface_id.
881 add_idb_index_map(&in_files[i], itf_count, merged_index);
883 else {
884 ws_debug("mode NONE or ALL set or did not find a duplicate");
886 * This IDB does not match a previous (or we want to save all
887 * IDBs), so add the IDB to the merge file, and add a map of
888 * the indices.
890 if (add_idb_to_merged_file(merged_idb_list, input_file_idb, pdh, err, err_info)) {
891 merged_index = merged_idb_list->interface_data->len - 1;
892 add_idb_index_map(&in_files[i], itf_count, merged_index);
893 } else {
894 return false;
897 itf_count = in_files[i].wth->next_interface_data;
901 return true;
905 * Create clone IDBs for the merge file, based on the input files and mode.
907 static wtapng_iface_descriptions_t *
908 generate_merged_idbs(merge_in_file_t *in_files, const unsigned in_file_count, idb_merge_mode * const mode)
910 wtapng_iface_descriptions_t *merged_idb_list = NULL;
911 wtap_block_t input_file_idb;
912 unsigned itf_count, merged_index;
913 unsigned i;
915 /* create new IDB info */
916 merged_idb_list = g_new(wtapng_iface_descriptions_t,1);
917 merged_idb_list->interface_data = g_array_new(false, false, sizeof(wtap_block_t));
919 if (*mode == IDB_MERGE_MODE_ALL_SAME && all_idbs_are_duplicates(in_files, in_file_count)) {
920 ws_debug("mode ALL set and all IDBs are duplicates");
922 /* All files have the same interfaces are the same, so merge any
923 * IDBs found later in the files together with duplicates.
924 * (Note this is also the right thing to do if we have some kind
925 * of two-pass mode and all_idbs_are_duplicates actually did
926 * compare all the IDBs instead of just the ones before any packets.)
928 *mode = IDB_MERGE_MODE_ANY_SAME;
930 /* they're all the same, so just get the first file's IDBs */
931 itf_count = in_files[0].wth->next_interface_data;
932 /* put them in the merged file */
933 while ((input_file_idb = wtap_get_next_interface_description(in_files[0].wth)) != NULL) {
934 add_idb_to_merged_file(merged_idb_list, input_file_idb, NULL, NULL, NULL);
935 merged_index = merged_idb_list->interface_data->len - 1;
936 add_idb_index_map(&in_files[0], itf_count, merged_index);
937 /* and set all the other file index maps the same way */
938 for (i = 1; i < in_file_count; i++) {
939 if (wtap_get_next_interface_description(in_files[i].wth) != NULL) {
940 add_idb_index_map(&in_files[i], itf_count, merged_index);
941 } else {
942 ws_assert_not_reached();
945 itf_count = in_files[0].wth->next_interface_data;
948 else {
949 for (i = 0; i < in_file_count; i++) {
951 itf_count = in_files[i].wth->next_interface_data;
952 while ((input_file_idb = wtap_get_next_interface_description(in_files[i].wth)) != NULL) {
954 if (*mode == IDB_MERGE_MODE_ANY_SAME &&
955 find_duplicate_idb(input_file_idb, merged_idb_list, &merged_index))
957 ws_debug("mode ANY set and found a duplicate");
959 * It's the same as a previous IDB, so we're going to "merge"
960 * them into one by adding a map from its old IDB index to the new
961 * one. This will be used later to change the rec interface_id.
963 add_idb_index_map(&in_files[i], itf_count, merged_index);
965 else {
966 ws_debug("mode NONE set or did not find a duplicate");
968 * This IDB does not match a previous (or we want to save all IDBs),
969 * so add the IDB to the merge file, and add a map of the indices.
971 add_idb_to_merged_file(merged_idb_list, input_file_idb, NULL, NULL, NULL);
972 merged_index = merged_idb_list->interface_data->len - 1;
973 add_idb_index_map(&in_files[i], itf_count, merged_index);
975 itf_count = in_files[i].wth->next_interface_data;
980 return merged_idb_list;
983 static bool
984 map_rec_interface_id(wtap_rec *rec, const merge_in_file_t *in_file)
986 unsigned current_interface_id = 0;
987 ws_assert(rec != NULL);
988 ws_assert(in_file != NULL);
989 ws_assert(in_file->idb_index_map != NULL);
991 if (rec->presence_flags & WTAP_HAS_INTERFACE_ID) {
992 unsigned section_num = (rec->presence_flags & WTAP_HAS_SECTION_NUMBER) ? rec->section_number : 0;
993 current_interface_id = wtap_file_get_shb_global_interface_id(in_file->wth, section_num, rec->rec_header.packet_header.interface_id);
996 if (current_interface_id >= in_file->idb_index_map->len) {
997 /* this shouldn't happen, but in a malformed input file it could */
998 ws_debug("current_interface_id (%u) >= in_file->idb_index_map->len (%u) (ERROR?)",
999 current_interface_id, in_file->idb_index_map->len);
1000 return false;
1003 rec->rec_header.packet_header.interface_id = g_array_index(in_file->idb_index_map, unsigned, current_interface_id);
1004 rec->presence_flags |= WTAP_HAS_INTERFACE_ID;
1006 return true;
1009 /** Return values from internal merge routines. */
1010 typedef enum {
1011 MERGE_OK,
1012 MERGE_USER_ABORTED,
1013 /* below here are true errors */
1014 MERGE_ERR_CANT_OPEN_INFILE,
1015 MERGE_ERR_CANT_OPEN_OUTFILE,
1016 MERGE_ERR_CANT_READ_INFILE,
1017 MERGE_ERR_BAD_PHDR_INTERFACE_ID,
1018 MERGE_ERR_CANT_WRITE_OUTFILE,
1019 MERGE_ERR_CANT_CLOSE_OUTFILE
1020 } merge_result;
1022 static merge_result
1023 merge_process_packets(wtap_dumper *pdh, const int file_type,
1024 merge_in_file_t *in_files, const unsigned in_file_count,
1025 const bool do_append,
1026 const idb_merge_mode mode, unsigned snaplen,
1027 merge_progress_callback_t* cb,
1028 wtapng_iface_descriptions_t *idb_inf,
1029 GArray *nrb_combined, GArray *dsb_combined,
1030 int *err, char **err_info, unsigned *err_fileno,
1031 uint32_t *err_framenum)
1033 merge_result status = MERGE_OK;
1034 merge_in_file_t *in_file;
1035 int count = 0;
1036 bool stop_flag = false;
1037 wtap_rec *rec, snap_rec;
1039 for (;;) {
1040 *err = 0;
1042 if (do_append) {
1043 in_file = merge_append_read_packet(in_file_count, in_files, err,
1044 err_info);
1046 else {
1047 in_file = merge_read_packet(in_file_count, in_files, err,
1048 err_info);
1051 if (in_file == NULL) {
1052 /* We're at EOF on all input files */
1053 break;
1056 if (*err != 0) {
1057 /* I/O error reading from in_file */
1058 if (*err == WTAP_ERR_SHORT_READ) {
1060 * A truncated file is not a fatal error, just stop reading
1061 * from that file, report it, and keep going.
1062 * XXX - What about WTAP_ERR_BAD_FILE? Are there *any*
1063 * read errors, as opposed to not being able to open the file
1064 * or write a record, that make us want to abort the entire
1065 * merge?
1067 report_cfile_read_failure(in_file->filename, *err, *err_info);
1068 *err = 0;
1069 g_free(*err_info);
1070 *err_info = NULL;
1071 continue;
1072 } else {
1073 status = MERGE_ERR_CANT_READ_INFILE;
1074 break;
1078 count++;
1079 if (cb)
1080 stop_flag = cb->callback_func(MERGE_EVENT_RECORD_WAS_READ, count, in_files, in_file_count, cb->data);
1082 if (stop_flag) {
1083 /* The user decided to abort the merge. */
1084 status = MERGE_USER_ABORTED;
1085 break;
1088 rec = &in_file->rec;
1090 if (wtap_file_type_subtype_supports_block(file_type,
1091 WTAP_BLOCK_IF_ID_AND_INFO) != BLOCK_NOT_SUPPORTED) {
1092 if (!process_new_idbs(pdh, in_files, in_file_count, mode, idb_inf, err, err_info)) {
1093 status = MERGE_ERR_CANT_WRITE_OUTFILE;
1094 break;
1098 switch (rec->rec_type) {
1100 case REC_TYPE_PACKET:
1101 if (rec->presence_flags & WTAP_HAS_CAP_LEN) {
1102 if (snaplen != 0 &&
1103 rec->rec_header.packet_header.caplen > snaplen) {
1105 * The dumper will only write up to caplen bytes out,
1106 * so we only need to change that value, instead of
1107 * cloning the whole packet with fewer bytes.
1109 * XXX: but do we need to change the IDBs' snap_len?
1111 snap_rec = *rec;
1112 snap_rec.rec_header.packet_header.caplen = snaplen;
1113 rec = &snap_rec;
1116 break;
1120 * Does this file type support identifying the interfaces on
1121 * which packets arrive?
1123 * That mean that the abstract interface provided by libwiretap
1124 * involves WTAP_BLOCK_IF_ID_AND_INFO blocks.
1126 if (wtap_file_type_subtype_supports_block(file_type,
1127 WTAP_BLOCK_IF_ID_AND_INFO) != BLOCK_NOT_SUPPORTED) {
1129 * XXX - We should do this only for record types
1130 * that pertain to a particular interface; for
1131 * now, we hardcode that, but we need to figure
1132 * out a more general way to handle this.
1134 if (rec->rec_type == REC_TYPE_PACKET) {
1135 if (!map_rec_interface_id(rec, in_file)) {
1136 status = MERGE_ERR_BAD_PHDR_INTERFACE_ID;
1137 break;
1142 * If any DSBs were read before this record, be sure to pass those now
1143 * such that wtap_dump can pick it up.
1145 if (nrb_combined && in_file->wth->nrbs) {
1146 GArray *in_nrb = in_file->wth->nrbs;
1147 for (unsigned i = in_file->nrbs_seen; i < in_nrb->len; i++) {
1148 wtap_block_t wblock = g_array_index(in_nrb, wtap_block_t, i);
1149 g_array_append_val(nrb_combined, wblock);
1150 in_file->nrbs_seen++;
1153 if (dsb_combined && in_file->wth->dsbs) {
1154 GArray *in_dsb = in_file->wth->dsbs;
1155 for (unsigned i = in_file->dsbs_seen; i < in_dsb->len; i++) {
1156 wtap_block_t wblock = g_array_index(in_dsb, wtap_block_t, i);
1157 g_array_append_val(dsb_combined, wblock);
1158 in_file->dsbs_seen++;
1162 if (!wtap_dump(pdh, rec, ws_buffer_start_ptr(&in_file->frame_buffer),
1163 err, err_info)) {
1164 status = MERGE_ERR_CANT_WRITE_OUTFILE;
1165 break;
1167 wtap_rec_reset(rec);
1170 if (cb)
1171 cb->callback_func(MERGE_EVENT_DONE, count, in_files, in_file_count, cb->data);
1173 if (status == MERGE_OK || status == MERGE_USER_ABORTED) {
1174 /* Check for IDBs, NRBs, or DSBs read after the last packet records. */
1175 if (wtap_file_type_subtype_supports_block(file_type,
1176 WTAP_BLOCK_IF_ID_AND_INFO) != BLOCK_NOT_SUPPORTED) {
1177 if (!process_new_idbs(pdh, in_files, in_file_count, mode, idb_inf, err, err_info)) {
1178 status = MERGE_ERR_CANT_WRITE_OUTFILE;
1181 if (nrb_combined) {
1182 for (unsigned j = 0; j < in_file_count; j++) {
1183 in_file = &in_files[j];
1184 GArray *in_nrb = in_file->wth->nrbs;
1185 if (in_nrb) {
1186 for (unsigned i = in_file->nrbs_seen; i < in_nrb->len; i++) {
1187 wtap_block_t wblock = g_array_index(in_nrb, wtap_block_t, i);
1188 g_array_append_val(nrb_combined, wblock);
1189 in_file->nrbs_seen++;
1194 if (dsb_combined) {
1195 for (unsigned j = 0; j < in_file_count; j++) {
1196 in_file = &in_files[j];
1197 GArray *in_dsb = in_file->wth->dsbs;
1198 if (in_dsb) {
1199 for (unsigned i = in_file->dsbs_seen; i < in_dsb->len; i++) {
1200 wtap_block_t wblock = g_array_index(in_dsb, wtap_block_t, i);
1201 g_array_append_val(dsb_combined, wblock);
1202 in_file->dsbs_seen++;
1208 if (status == MERGE_OK || status == MERGE_USER_ABORTED) {
1209 if (!wtap_dump_close(pdh, NULL, err, err_info))
1210 status = MERGE_ERR_CANT_CLOSE_OUTFILE;
1211 } else {
1213 * We already got some error; no need to report another error on
1214 * close.
1216 * Don't overwrite the earlier error.
1218 int close_err = 0;
1219 char *close_err_info = NULL;
1220 (void)wtap_dump_close(pdh, NULL, &close_err, &close_err_info);
1221 g_free(close_err_info);
1224 /* Close the input files after the output file in case the latter still
1225 * holds references to blocks in the input file (such as the DSB). Even if
1226 * those DSBs are only written when wtap_dump is called and nothing bad will
1227 * happen now, let's keep all pointers in pdh valid for correctness sake. */
1228 merge_close_in_files(in_file_count, in_files);
1230 if (status == MERGE_OK || in_file == NULL) {
1231 *err_fileno = 0;
1232 *err_framenum = 0;
1233 } else {
1234 *err_fileno = (unsigned)(in_file - in_files);
1235 *err_framenum = in_file->packet_num;
1238 return status;
1241 static void
1242 tempfile_free(void *data) {
1243 char *filename = (char*)data;
1244 ws_unlink(filename);
1245 g_free(filename);
1248 #define MAX_MERGE_FILES 10000 // Arbitrary
1249 static bool
1250 // NOLINTNEXTLINE(misc-no-recursion)
1251 merge_files_common(const char* out_filename, /* filename in normal output mode,
1252 optional tempdir in tempfile mode (NULL for OS default) */
1253 char **out_filenamep, const char *pfx, /* tempfile mode */
1254 const int file_type, const char *const *in_filenames,
1255 const unsigned in_file_count, const bool do_append,
1256 idb_merge_mode mode, unsigned snaplen,
1257 const char *app_name, merge_progress_callback_t* cb, wtap_compression_type compression_type)
1259 merge_in_file_t *in_files = NULL;
1260 int frame_type = WTAP_ENCAP_PER_PACKET;
1261 unsigned open_file_count;
1262 int err = 0;
1263 char *err_info = NULL;
1264 unsigned err_fileno = 0;
1265 uint32_t err_framenum = 0;
1266 merge_result status = MERGE_OK;
1267 wtap_dumper *pdh;
1268 GArray *shb_hdrs = NULL;
1269 wtapng_iface_descriptions_t *idb_inf = NULL;
1270 GArray *nrb_combined = NULL;
1271 GArray *dsb_combined = NULL;
1272 GPtrArray *temp_files = NULL;
1273 int dup_fd;
1275 ws_assert(in_file_count > 0);
1276 ws_assert(in_file_count < MAX_MERGE_FILES);
1277 ws_assert(in_filenames != NULL);
1279 /* if a callback was given, it has to have a callback function ptr */
1280 ws_assert((cb != NULL) ? (cb->callback_func != NULL) : true);
1282 ws_debug("merge_files: begin");
1284 for (unsigned total_file_count = 0; total_file_count < in_file_count && status == MERGE_OK; total_file_count += open_file_count) {
1286 /* Reserve a file descriptor for the output; if we run out of file
1287 * descriptors we will end up writing to a temp file instead of the
1288 * file or stdout originally requested, but this simplifies EMFILE
1289 * handling.
1291 dup_fd = ws_dup(1);
1292 if (dup_fd == -1) {
1293 report_cfile_dump_open_failure(out_filename, errno, NULL, file_type);
1294 return false;
1297 /* open the input files */
1298 open_file_count = merge_open_in_files(in_file_count - total_file_count, &in_filenames[total_file_count], &in_files, cb, &err, &err_info, &err_fileno);
1299 if (open_file_count == 0) {
1300 ws_debug("merge_open_in_files() failed with err=%d", err);
1301 report_cfile_open_failure(in_filenames[err_fileno], err, err_info);
1302 return false;
1305 if (snaplen == 0) {
1306 /* Snapshot length not specified - default to the maximum. */
1307 snaplen = WTAP_MAX_PACKET_SIZE_STANDARD;
1311 * This doesn't tell us that much. It tells us what to set the outfile's
1312 * encap type to, but that's all - for example, it does *not* tell us
1313 * whether the input files had the same number of IDBs, for the same exact
1314 * interfaces, and only one IDB each, so it doesn't actually tell us
1315 * whether we can merge IDBs into one or not.
1317 * XXX: If an input file is WTAP_ENCAP_PER_PACKET, just because the
1318 * output file format (e.g. pcapng) can write WTAP_ENCAP_PER_PACKET,
1319 * that doesn't mean that the format can actually write all the IDBs.
1321 frame_type = merge_select_frame_type(file_type, open_file_count, in_files);
1322 ws_debug("got frame_type=%d", frame_type);
1324 if (cb)
1325 cb->callback_func(MERGE_EVENT_FRAME_TYPE_SELECTED, frame_type, in_files, open_file_count, cb->data);
1327 /* prepare the outfile */
1328 wtap_dump_params params = WTAP_DUMP_PARAMS_INIT;
1329 params.encap = frame_type;
1330 params.snaplen = snaplen;
1332 * Does this file type support identifying the interfaces on
1333 * which packets arrive?
1335 * That mean that the abstract interface provided by libwiretap
1336 * involves WTAP_BLOCK_IF_ID_AND_INFO blocks.
1338 if (wtap_file_type_subtype_supports_block(file_type,
1339 WTAP_BLOCK_IF_ID_AND_INFO) != BLOCK_NOT_SUPPORTED) {
1340 shb_hdrs = create_shb_header(in_files, open_file_count, app_name);
1341 ws_debug("SHB created");
1343 idb_inf = generate_merged_idbs(in_files, open_file_count, &mode);
1344 ws_debug("IDB merge operation complete, got %u IDBs", idb_inf ? idb_inf->interface_data->len : 0);
1346 /* We do our own mapping of interface numbers */
1347 params.shb_iface_to_global = NULL;
1348 /* XXX other blocks like ISB are now discarded. */
1349 params.shb_hdrs = shb_hdrs;
1350 params.idb_inf = idb_inf;
1352 if (wtap_file_type_subtype_supports_block(file_type,
1353 WTAP_BLOCK_NAME_RESOLUTION) != BLOCK_NOT_SUPPORTED) {
1354 nrb_combined = g_array_new(false, false, sizeof(wtap_block_t));
1355 params.nrbs_growing = nrb_combined;
1357 if (wtap_file_type_subtype_supports_block(file_type,
1358 WTAP_BLOCK_DECRYPTION_SECRETS) != BLOCK_NOT_SUPPORTED) {
1359 dsb_combined = g_array_new(false, false, sizeof(wtap_block_t));
1360 params.dsbs_growing = dsb_combined;
1362 ws_close(dup_fd);
1363 if (open_file_count < in_file_count) {
1364 if (temp_files == NULL) {
1365 temp_files = g_ptr_array_new_with_free_func(tempfile_free);
1367 char* temp_filename;
1368 /* If out_filenamep is not null, then out_filename is the
1369 * desired tempdir, so let's use that.
1371 pdh = wtap_dump_open_tempfile(out_filenamep ? out_filename : NULL,
1372 &temp_filename,
1373 pfx ? pfx : "mergecap", file_type,
1374 compression_type, &params, &err,
1375 &err_info);
1376 if (pdh) {
1377 g_ptr_array_add(temp_files, temp_filename);
1379 } else if (out_filenamep) {
1380 pdh = wtap_dump_open_tempfile(out_filename, out_filenamep, pfx, file_type,
1381 compression_type, &params, &err,
1382 &err_info);
1383 } else if (out_filename) {
1384 pdh = wtap_dump_open(out_filename, file_type, compression_type,
1385 &params, &err, &err_info);
1386 } else {
1387 pdh = wtap_dump_open_stdout(file_type, compression_type, &params,
1388 &err, &err_info);
1390 if (pdh == NULL) {
1391 merge_close_in_files(open_file_count, in_files);
1392 g_free(in_files);
1393 wtap_block_array_free(shb_hdrs);
1394 wtap_free_idb_info(idb_inf);
1395 if (nrb_combined) {
1396 g_array_free(nrb_combined, true);
1398 if (dsb_combined) {
1399 g_array_free(dsb_combined, true);
1401 if (temp_files) {
1402 g_ptr_array_free(temp_files, true);
1404 report_cfile_dump_open_failure(out_filename, err, err_info, file_type);
1405 return false;
1408 if (cb)
1409 cb->callback_func(MERGE_EVENT_READY_TO_MERGE, 0, in_files, open_file_count, cb->data);
1411 status = merge_process_packets(pdh, file_type, in_files, open_file_count,
1412 do_append, mode, snaplen, cb,
1413 idb_inf, nrb_combined, dsb_combined,
1414 &err, &err_info,
1415 &err_fileno, &err_framenum);
1417 g_free(in_files);
1418 wtap_block_array_free(shb_hdrs);
1419 wtap_free_idb_info(idb_inf);
1420 if (nrb_combined) {
1421 g_array_free(nrb_combined, true);
1422 nrb_combined = NULL;
1424 if (dsb_combined) {
1425 g_array_free(dsb_combined, true);
1426 dsb_combined = NULL;
1431 if (status != MERGE_OK) {
1433 * Failed. Clean up and return false.
1435 switch (status) {
1436 case MERGE_USER_ABORTED:
1437 /* This isn't an error, so no need to report anything */
1438 break;
1440 case MERGE_ERR_CANT_OPEN_INFILE:
1441 report_cfile_open_failure(in_filenames[err_fileno], err, err_info);
1442 break;
1444 case MERGE_ERR_CANT_OPEN_OUTFILE:
1445 report_cfile_dump_open_failure(out_filename, err, err_info, file_type);
1446 break;
1448 case MERGE_ERR_CANT_READ_INFILE:
1449 report_cfile_read_failure(in_filenames[err_fileno], err, err_info);
1450 break;
1452 case MERGE_ERR_BAD_PHDR_INTERFACE_ID:
1453 report_failure("Record %u of \"%s\" has an interface ID that does not match any IDB in its file.",
1454 err_framenum, in_filenames[err_fileno]);
1455 break;
1457 case MERGE_ERR_CANT_WRITE_OUTFILE:
1458 report_cfile_write_failure(in_filenames[err_fileno], out_filename,
1459 err, err_info, err_framenum, file_type);
1460 break;
1462 case MERGE_ERR_CANT_CLOSE_OUTFILE:
1463 report_cfile_close_failure(out_filename, err, err_info);
1464 break;
1466 default:
1467 report_failure("Unknown merge_files error %d", status);
1468 break;
1470 if (temp_files != NULL)
1471 g_ptr_array_free(temp_files, true);
1472 return false;
1475 if (temp_files != NULL) {
1476 // We recurse here, but we're limited by MAX_MERGE_FILES
1477 status = merge_files_common(out_filename, out_filenamep, pfx,
1478 file_type, (const char**)temp_files->pdata,
1479 temp_files->len, do_append, mode, snaplen, app_name, cb, compression_type);
1480 /* If that failed, it has already reported an error */
1481 g_ptr_array_free(temp_files, true);
1484 return status == MERGE_OK;
1488 * Merges the files to an output file whose name is supplied as an argument,
1489 * based on given input, and invokes callback during execution. Returns
1490 * MERGE_OK on success, or a MERGE_ERR_XXX on failure.
1492 bool
1493 merge_files(const char* out_filename, const int file_type,
1494 const char *const *in_filenames, const unsigned in_file_count,
1495 const bool do_append, const idb_merge_mode mode,
1496 unsigned snaplen, const char *app_name, merge_progress_callback_t* cb, const wtap_compression_type compression_type)
1498 ws_assert(out_filename != NULL);
1499 ws_assert(in_file_count > 0);
1500 ws_assert(in_filenames != NULL);
1502 /* #19402: ensure we aren't appending to one of our inputs */
1503 if (do_append) {
1504 unsigned int i;
1505 for (i = 0; i < in_file_count; i++) {
1506 if (files_identical(out_filename, in_filenames[i])) {
1507 report_failure("Output file %s is same as input file %s; "
1508 "appending would create infinite loop",
1509 out_filename, in_filenames[i]);
1510 return false;
1515 return merge_files_common(out_filename, NULL, NULL,
1516 file_type, in_filenames, in_file_count,
1517 do_append, mode, snaplen, app_name, cb, compression_type);
1521 * Merges the files to a temporary file based on given input, and invokes
1522 * callback during execution. Returns MERGE_OK on success, or a MERGE_ERR_XXX
1523 * on failure.
1525 bool
1526 merge_files_to_tempfile(const char *tmpdir, char **out_filenamep, const char *pfx,
1527 const int file_type, const char *const *in_filenames,
1528 const unsigned in_file_count, const bool do_append,
1529 const idb_merge_mode mode, unsigned snaplen,
1530 const char *app_name, merge_progress_callback_t* cb)
1532 ws_assert(out_filenamep != NULL);
1534 /* no temporary file name yet */
1535 *out_filenamep = NULL;
1537 return merge_files_common(tmpdir, out_filenamep, pfx,
1538 file_type, in_filenames, in_file_count,
1539 do_append, mode, snaplen, app_name, cb, WTAP_UNCOMPRESSED);
1543 * Merges the files to the standard output based on given input, and invokes
1544 * callback during execution. Returns MERGE_OK on success, or a MERGE_ERR_XXX
1545 * on failure.
1547 bool
1548 merge_files_to_stdout(const int file_type, const char *const *in_filenames,
1549 const unsigned in_file_count, const bool do_append,
1550 const idb_merge_mode mode, unsigned snaplen,
1551 const char *app_name, merge_progress_callback_t* cb,
1552 wtap_compression_type compression_type)
1554 return merge_files_common(NULL, NULL, NULL,
1555 file_type, in_filenames, in_file_count,
1556 do_append, mode, snaplen, app_name, cb, compression_type);
1560 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1562 * Local Variables:
1563 * c-basic-offset: 4
1564 * tab-width: 8
1565 * indent-tabs-mode: nil
1566 * End:
1568 * vi: set shiftwidth=4 tabstop=8 expandtab:
1569 * :indentSize=4:tabSize=8:noTabs=true: