TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags
[wireshark-sm.git] / mergecap.c
blob185d54717c3598051a7192f2d7da606e19cbfdce
1 /* Combine dump files, either by appending or by merging by timestamp
3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <gerald@wireshark.org>
5 * Copyright 1998 Gerald Combs
7 * SPDX-License-Identifier: GPL-2.0-or-later
9 * Mergecap written by Scott Renfro <scott@renfro.org> based on
10 * editcap by Richard Sharpe and Guy Harris
14 #include <config.h>
15 #define WS_LOG_DOMAIN LOG_DOMAIN_MAIN
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <glib.h>
21 #include <wsutil/ws_getopt.h>
23 #include <string.h>
25 #include <wiretap/wtap.h>
27 #include <wsutil/clopts_common.h>
28 #include <wsutil/cmdarg_err.h>
29 #include <wsutil/filesystem.h>
30 #include <wsutil/file_util.h>
31 #include <wsutil/privileges.h>
32 #include <wsutil/strnatcmp.h>
33 #include <wsutil/ws_assert.h>
34 #include <wsutil/wslog.h>
36 #include <cli_main.h>
37 #include <wsutil/version_info.h>
39 #ifdef HAVE_PLUGINS
40 #include <wsutil/plugins.h>
41 #endif
43 #include <wiretap/merge.h>
45 #include "ui/failure_message.h"
47 #define LONGOPT_COMPRESS LONGOPT_BASE_APPLICATION+1
50 * Show the usage
52 static void
53 print_usage(FILE *output)
55 fprintf(output, "\n");
56 fprintf(output, "Usage: mergecap [options] -w <outfile>|- <infile> [<infile> ...]\n");
57 fprintf(output, "\n");
58 fprintf(output, "Output:\n");
59 fprintf(output, " -a concatenate rather than merge files.\n");
60 fprintf(output, " default is to merge based on frame timestamps.\n");
61 fprintf(output, " -s <snaplen> truncate packets to <snaplen> bytes of data.\n");
62 fprintf(output, " -w <outfile>|- set the output filename to <outfile> or '-' for stdout.\n");
63 fprintf(output, " if the output filename has the .gz extension, it will be compressed to a gzip archive\n");
64 fprintf(output, " -F <capture type> set the output file type; default is pcapng.\n");
65 fprintf(output, " an empty \"-F\" option will list the file types.\n");
66 fprintf(output, " -I <IDB merge mode> set the merge mode for Interface Description Blocks; default is 'all'.\n");
67 fprintf(output, " an empty \"-I\" option will list the merge modes.\n");
68 fprintf(output, " --compress <type> compress the output file using the type compression format.\n");
69 fprintf(output, "\n");
70 fprintf(output, "Miscellaneous:\n");
71 fprintf(output, " -h, --help display this help and exit.\n");
72 fprintf(output, " -V verbose output.\n");
73 fprintf(output, " -v, --version print version information and exit.\n");
76 static void
77 list_capture_types(void) {
78 GArray *writable_type_subtypes;
80 fprintf(stderr, "mergecap: The available capture file types for the \"-F\" flag are:\n");
81 writable_type_subtypes = wtap_get_writable_file_types_subtypes(FT_SORT_BY_NAME);
82 for (unsigned i = 0; i < writable_type_subtypes->len; i++) {
83 int ft = g_array_index(writable_type_subtypes, int, i);
84 fprintf(stderr, " %s - %s\n", wtap_file_type_subtype_name(ft),
85 wtap_file_type_subtype_description(ft));
87 g_array_free(writable_type_subtypes, TRUE);
90 static void
91 list_idb_merge_modes(void) {
92 int i;
94 fprintf(stderr, "mergecap: The available IDB merge modes for the \"-I\" flag are:\n");
95 for (i = 0; i < IDB_MERGE_MODE_MAX; i++) {
96 fprintf(stderr, " %s\n", merge_idb_merge_mode_to_string(i));
100 static void
101 list_output_compression_types(void) {
102 GSList *output_compression_types;
104 fprintf(stderr, "mergecap: The available output compress type(s) for the \"--compress\" flag are:\n");
105 output_compression_types = wtap_get_all_output_compression_type_names_list();
106 for (GSList *compression_type = output_compression_types;
107 compression_type != NULL;
108 compression_type = g_slist_next(compression_type)) {
109 fprintf(stderr, " %s\n", (const char *)compression_type->data);
112 g_slist_free(output_compression_types);
115 static bool
116 merge_callback(merge_event event, int num,
117 const merge_in_file_t in_files[], const unsigned in_file_count,
118 void *data _U_)
120 unsigned i;
122 switch (event) {
124 case MERGE_EVENT_INPUT_FILES_OPENED:
125 for (i = 0; i < in_file_count; i++) {
126 fprintf(stderr, "mergecap: %s is type %s.\n", in_files[i].filename,
127 wtap_file_type_subtype_description(wtap_file_type_subtype(in_files[i].wth)));
129 break;
131 case MERGE_EVENT_FRAME_TYPE_SELECTED:
132 /* for this event, num = frame_type */
133 if (num == WTAP_ENCAP_PER_PACKET) {
135 * Find out why we had to choose WTAP_ENCAP_PER_PACKET.
137 int first_frame_type, this_frame_type;
139 first_frame_type = wtap_file_encap(in_files[0].wth);
140 for (i = 1; i < in_file_count; i++) {
141 this_frame_type = wtap_file_encap(in_files[i].wth);
142 if (first_frame_type != this_frame_type) {
143 fprintf(stderr, "mergecap: multiple frame encapsulation types detected\n");
144 fprintf(stderr, " defaulting to WTAP_ENCAP_PER_PACKET\n");
145 fprintf(stderr, " %s had type %s (%s)\n",
146 in_files[0].filename,
147 wtap_encap_description(first_frame_type),
148 wtap_encap_name(first_frame_type));
149 fprintf(stderr, " %s had type %s (%s)\n",
150 in_files[i].filename,
151 wtap_encap_description(this_frame_type),
152 wtap_encap_name(this_frame_type));
153 break;
157 fprintf(stderr, "mergecap: selected frame_type %s (%s)\n",
158 wtap_encap_description(num),
159 wtap_encap_name(num));
160 break;
162 case MERGE_EVENT_READY_TO_MERGE:
163 fprintf(stderr, "mergecap: ready to merge records\n");
164 break;
166 case MERGE_EVENT_RECORD_WAS_READ:
167 /* for this event, num = count */
168 fprintf(stderr, "Record: %d\n", num);
169 break;
171 case MERGE_EVENT_DONE:
172 fprintf(stderr, "mergecap: merging complete\n");
173 break;
176 /* false = do not stop merging */
177 return false;
181 main(int argc, char *argv[])
183 char *configuration_init_error;
184 int opt;
185 static const struct ws_option long_options[] = {
186 {"help", ws_no_argument, NULL, 'h'},
187 {"version", ws_no_argument, NULL, 'v'},
188 {"compress", ws_required_argument, NULL, LONGOPT_COMPRESS},
189 {0, 0, 0, 0 }
191 bool do_append = false;
192 bool verbose = false;
193 int in_file_count = 0;
194 uint32_t snaplen = 0;
195 int file_type = WTAP_FILE_TYPE_SUBTYPE_UNKNOWN;
196 char *out_filename = NULL;
197 bool status = true;
198 idb_merge_mode mode = IDB_MERGE_MODE_MAX;
199 wtap_compression_type compression_type = WTAP_UNKNOWN_COMPRESSION;
200 merge_progress_callback_t cb;
202 /* Set the program name. */
203 g_set_prgname("mergecap");
205 cmdarg_err_init(stderr_cmdarg_err, stderr_cmdarg_err_cont);
207 /* Initialize log handler early so we can have proper logging during startup. */
208 ws_log_init(vcmdarg_err);
210 /* Early logging command-line initialization. */
211 ws_log_parse_args(&argc, argv, vcmdarg_err, 1);
213 ws_noisy("Finished log init and parsing command line log arguments");
215 #ifdef _WIN32
216 create_app_running_mutex();
217 #endif /* _WIN32 */
220 * Get credential information for later use.
222 init_process_policies();
225 * Attempt to get the pathname of the directory containing the
226 * executable file.
228 configuration_init_error = configuration_init(argv[0]);
229 if (configuration_init_error != NULL) {
230 cmdarg_err(
231 "Can't get pathname of directory containing the mergecap program: %s.",
232 configuration_init_error);
233 g_free(configuration_init_error);
236 /* Initialize the version information. */
237 ws_init_version_info("Mergecap", NULL, NULL);
239 init_report_failure_message("mergecap");
241 wtap_init(true);
243 /* Process the options first */
244 while ((opt = ws_getopt_long(argc, argv, "aF:hI:s:vVw:", long_options, NULL)) != -1) {
246 switch (opt) {
247 case 'a':
248 do_append = !do_append;
249 break;
251 case 'F':
252 file_type = wtap_name_to_file_type_subtype(ws_optarg);
253 if (file_type < 0) {
254 cmdarg_err("\"%s\" isn't a valid capture file type",
255 ws_optarg);
256 list_capture_types();
257 status = false;
258 goto clean_exit;
260 break;
262 case 'h':
263 show_help_header("Merge two or more capture files into one.");
264 print_usage(stdout);
265 goto clean_exit;
266 break;
268 case 'I':
269 mode = merge_string_to_idb_merge_mode(ws_optarg);
270 if (mode == IDB_MERGE_MODE_MAX) {
271 cmdarg_err("\"%s\" isn't a valid IDB merge mode",
272 ws_optarg);
273 list_idb_merge_modes();
274 status = false;
275 goto clean_exit;
277 break;
279 case 's':
280 snaplen = get_nonzero_uint32(ws_optarg, "snapshot length");
281 break;
283 case 'V':
284 verbose = true;
285 break;
287 case 'v':
288 show_version();
289 goto clean_exit;
290 break;
292 case 'w':
293 out_filename = ws_optarg;
294 break;
296 case LONGOPT_COMPRESS:
297 compression_type = wtap_name_to_compression_type(ws_optarg);
298 if (compression_type == WTAP_UNKNOWN_COMPRESSION) {
299 cmdarg_err("\"%s\" isn't a valid output compression mode",
300 ws_optarg);
301 list_output_compression_types();
302 goto clean_exit;
304 break;
305 case '?': /* Bad options if GNU getopt */
306 switch(ws_optopt) {
307 case'F':
308 list_capture_types();
309 break;
310 case'I':
311 list_idb_merge_modes();
312 break;
313 case LONGOPT_COMPRESS:
314 list_output_compression_types();
315 break;
316 default:
317 print_usage(stderr);
319 status = false;
320 goto clean_exit;
321 break;
325 /* Default to pcapng when writing. */
326 if (file_type == WTAP_FILE_TYPE_SUBTYPE_UNKNOWN)
327 file_type = wtap_pcapng_file_type_subtype();
329 cb.callback_func = merge_callback;
330 cb.data = NULL;
332 /* check for proper args; at a minimum, must have an output
333 * filename and one input file
335 in_file_count = argc - ws_optind;
336 if (!out_filename) {
337 cmdarg_err("an output filename must be set with -w");
338 cmdarg_err_cont("run with -h for help");
339 status = false;
340 goto clean_exit;
342 if (in_file_count < 1) {
343 cmdarg_err("No input files were specified");
344 return 1;
347 if (compression_type == WTAP_UNKNOWN_COMPRESSION) {
348 /* An explicitly specified compression type overrides filename
349 * magic. (Should we allow specifying "no" compression with, e.g.
350 * a ".gz" extension?) */
351 const char *sfx = strrchr(out_filename, '.');
352 if (sfx) {
353 compression_type = wtap_extension_to_compression_type(sfx + 1);
357 if (compression_type == WTAP_UNKNOWN_COMPRESSION) {
358 compression_type = WTAP_UNCOMPRESSED;
361 if (!wtap_can_write_compression_type(compression_type)) {
362 cmdarg_err("Output files can't be written as %s",
363 wtap_compression_type_description(compression_type));
364 status = false;
365 goto clean_exit;
368 if (compression_type != WTAP_UNCOMPRESSED && !wtap_dump_can_compress(file_type)) {
369 cmdarg_err("The file format %s can't be written to output compressed format",
370 wtap_file_type_subtype_name(file_type));
371 status = false;
372 goto clean_exit;
376 * Setting IDB merge mode must use a file format that supports
377 * (and thus requires) interface ID and information blocks.
379 if (mode != IDB_MERGE_MODE_MAX &&
380 wtap_file_type_subtype_supports_block(file_type, WTAP_BLOCK_IF_ID_AND_INFO) == BLOCK_NOT_SUPPORTED) {
381 cmdarg_err("The IDB merge mode can only be used with an output format that identifies interfaces");
382 status = false;
383 goto clean_exit;
386 /* if they didn't set IDB merge mode, set it to our default */
387 if (mode == IDB_MERGE_MODE_MAX) {
388 mode = IDB_MERGE_MODE_ALL_SAME;
391 /* open the outfile */
392 if (strcmp(out_filename, "-") == 0) {
393 /* merge the files to the standard output */
394 status = merge_files_to_stdout(file_type,
395 (const char *const *) &argv[ws_optind],
396 in_file_count, do_append, mode, snaplen,
397 get_appname_and_version(),
398 verbose ? &cb : NULL, compression_type);
399 } else {
400 /* merge the files to the outfile */
401 status = merge_files(out_filename, file_type,
402 (const char *const *) &argv[ws_optind], in_file_count,
403 do_append, mode, snaplen, get_appname_and_version(),
404 verbose ? &cb : NULL, compression_type);
407 clean_exit:
408 wtap_cleanup();
409 free_progdirs();
410 return status ? 0 : 2;