Start using SPDX license identifiers.
[metze/wireshark/wip.git] / mergecap.c
1 /* Combine dump files, either by appending or by merging by timestamp
2  *
3  * Wireshark - Network traffic analyzer
4  * By Gerald Combs <gerald@wireshark.org>
5  * Copyright 1998 Gerald Combs
6  *
7  * SPDX-License-Identifier: GPL-2.0+
8  *
9  * Mergecap written by Scott Renfro <scott@renfro.org> based on
10  * editcap by Richard Sharpe and Guy Harris
11  *
12  */
13
14 #include <config.h>
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <errno.h>
19 #include <glib.h>
20
21 #ifdef HAVE_GETOPT_H
22 #include <getopt.h>
23 #endif
24
25 #include <string.h>
26
27 #include <wiretap/wtap.h>
28
29 #ifndef HAVE_GETOPT_LONG
30 #include <wsutil/wsgetopt.h>
31 #endif
32
33 #include <wsutil/clopts_common.h>
34 #include <wsutil/cmdarg_err.h>
35 #include <wsutil/crash_info.h>
36 #include <wsutil/filesystem.h>
37 #include <wsutil/file_util.h>
38 #include <wsutil/privileges.h>
39 #include <wsutil/strnatcmp.h>
40 #include <version_info.h>
41
42 #ifdef HAVE_PLUGINS
43 #include <wsutil/plugins.h>
44 #endif
45
46 #include <wsutil/report_message.h>
47
48 #include <wiretap/merge.h>
49
50 #ifdef _WIN32
51 #include <wsutil/unicode-utils.h>
52 #endif /* _WIN32 */
53
54 #include "ui/failure_message.h"
55
56 /*
57  * Show the usage
58  */
59 static void
60 print_usage(FILE *output)
61 {
62   fprintf(output, "\n");
63   fprintf(output, "Usage: mergecap [options] -w <outfile>|- <infile> [<infile> ...]\n");
64   fprintf(output, "\n");
65   fprintf(output, "Output:\n");
66   fprintf(output, "  -a                concatenate rather than merge files.\n");
67   fprintf(output, "                    default is to merge based on frame timestamps.\n");
68   fprintf(output, "  -s <snaplen>      truncate packets to <snaplen> bytes of data.\n");
69   fprintf(output, "  -w <outfile>|-    set the output filename to <outfile> or '-' for stdout.\n");
70   fprintf(output, "  -F <capture type> set the output file type; default is pcapng.\n");
71   fprintf(output, "                    an empty \"-F\" option will list the file types.\n");
72   fprintf(output, "  -I <IDB merge mode> set the merge mode for Interface Description Blocks; default is 'all'.\n");
73   fprintf(output, "                    an empty \"-I\" option will list the merge modes.\n");
74   fprintf(output, "\n");
75   fprintf(output, "Miscellaneous:\n");
76   fprintf(output, "  -h                display this help and exit.\n");
77   fprintf(output, "  -v                verbose output.\n");
78 }
79
80 /*
81  * Report an error in command-line arguments.
82  */
83 static void
84 mergecap_cmdarg_err(const char *fmt, va_list ap)
85 {
86   fprintf(stderr, "mergecap: ");
87   vfprintf(stderr, fmt, ap);
88   fprintf(stderr, "\n");
89 }
90
91 /*
92  * Report additional information for an error in command-line arguments.
93  */
94 static void
95 mergecap_cmdarg_err_cont(const char *fmt, va_list ap)
96 {
97   vfprintf(stderr, fmt, ap);
98   fprintf(stderr, "\n");
99 }
100
101 struct string_elem {
102   const char *sstr;     /* The short string */
103   const char *lstr;     /* The long string */
104 };
105
106 static gint
107 string_compare(gconstpointer a, gconstpointer b)
108 {
109   return strcmp(((const struct string_elem *)a)->sstr,
110                 ((const struct string_elem *)b)->sstr);
111 }
112
113 static void
114 string_elem_print(gpointer data, gpointer not_used _U_)
115 {
116   fprintf(stderr, "    %s - %s\n", ((struct string_elem *)data)->sstr,
117           ((struct string_elem *)data)->lstr);
118 }
119
120 #ifdef HAVE_PLUGINS
121 /*
122  * General errors and warnings are reported with an console message
123  * in mergecap.
124  */
125 static void
126 failure_warning_message(const char *msg_format, va_list ap)
127 {
128   fprintf(stderr, "mergecap: ");
129   vfprintf(stderr, msg_format, ap);
130   fprintf(stderr, "\n");
131 }
132 #endif
133
134 static void
135 list_capture_types(void) {
136   int i;
137   struct string_elem *captypes;
138   GSList *list = NULL;
139
140   captypes = g_new(struct string_elem,WTAP_NUM_FILE_TYPES_SUBTYPES);
141
142   fprintf(stderr, "mergecap: The available capture file types for the \"-F\" flag are:\n");
143   for (i = 0; i < WTAP_NUM_FILE_TYPES_SUBTYPES; i++) {
144     if (wtap_dump_can_open(i)) {
145       captypes[i].sstr = wtap_file_type_subtype_short_string(i);
146       captypes[i].lstr = wtap_file_type_subtype_string(i);
147       list = g_slist_insert_sorted(list, &captypes[i], string_compare);
148     }
149   }
150   g_slist_foreach(list, string_elem_print, NULL);
151   g_slist_free(list);
152   g_free(captypes);
153 }
154
155 static void
156 list_idb_merge_modes(void) {
157   int i;
158
159   fprintf(stderr, "mergecap: The available IDB merge modes for the \"-I\" flag are:\n");
160   for (i = 0; i < IDB_MERGE_MODE_MAX; i++) {
161     fprintf(stderr, "    %s\n", merge_idb_merge_mode_to_string(i));
162   }
163 }
164
165 static gboolean
166 merge_callback(merge_event event, int num,
167                const merge_in_file_t in_files[], const guint in_file_count,
168                void *data _U_)
169 {
170   guint i;
171
172   switch (event) {
173
174     case MERGE_EVENT_INPUT_FILES_OPENED:
175       for (i = 0; i < in_file_count; i++) {
176         fprintf(stderr, "mergecap: %s is type %s.\n", in_files[i].filename,
177                 wtap_file_type_subtype_string(wtap_file_type_subtype(in_files[i].wth)));
178       }
179       break;
180
181     case MERGE_EVENT_FRAME_TYPE_SELECTED:
182       /* for this event, num = frame_type */
183       if (num == WTAP_ENCAP_PER_PACKET) {
184         /*
185          * Find out why we had to choose WTAP_ENCAP_PER_PACKET.
186          */
187         int first_frame_type, this_frame_type;
188
189         first_frame_type = wtap_file_encap(in_files[0].wth);
190         for (i = 1; i < in_file_count; i++) {
191           this_frame_type = wtap_file_encap(in_files[i].wth);
192           if (first_frame_type != this_frame_type) {
193             fprintf(stderr, "mergecap: multiple frame encapsulation types detected\n");
194             fprintf(stderr, "          defaulting to WTAP_ENCAP_PER_PACKET\n");
195             fprintf(stderr, "          %s had type %s (%s)\n",
196                     in_files[0].filename,
197                     wtap_encap_string(first_frame_type),
198                     wtap_encap_short_string(first_frame_type));
199             fprintf(stderr, "          %s had type %s (%s)\n",
200                     in_files[i].filename,
201                     wtap_encap_string(this_frame_type),
202                     wtap_encap_short_string(this_frame_type));
203             break;
204           }
205         }
206       }
207       fprintf(stderr, "mergecap: selected frame_type %s (%s)\n",
208               wtap_encap_string(num),
209               wtap_encap_short_string(num));
210       break;
211
212     case MERGE_EVENT_READY_TO_MERGE:
213       fprintf(stderr, "mergecap: ready to merge records\n");
214       break;
215
216     case MERGE_EVENT_PACKET_WAS_READ:
217       /* for this event, num = count */
218       fprintf(stderr, "Record: %d\n", num);
219       break;
220
221     case MERGE_EVENT_DONE:
222       fprintf(stderr, "mergecap: merging complete\n");
223       break;
224   }
225
226   /* false = do not stop merging */
227   return FALSE;
228 }
229
230
231 int
232 main(int argc, char *argv[])
233 {
234   GString            *comp_info_str;
235   GString            *runtime_info_str;
236   char               *init_progfile_dir_error;
237   int                 opt;
238   static const struct option long_options[] = {
239       {"help", no_argument, NULL, 'h'},
240       {"version", no_argument, NULL, 'V'},
241       {0, 0, 0, 0 }
242   };
243   gboolean            do_append          = FALSE;
244   gboolean            verbose            = FALSE;
245   int                 in_file_count      = 0;
246   guint32             snaplen            = 0;
247 #ifdef PCAP_NG_DEFAULT
248   int                 file_type          = WTAP_FILE_TYPE_SUBTYPE_PCAPNG; /* default to pcap format */
249 #else
250   int                 file_type          = WTAP_FILE_TYPE_SUBTYPE_PCAP; /* default to pcapng format */
251 #endif
252   int                 err                = 0;
253   gchar              *err_info           = NULL;
254   int                 err_fileno;
255   guint32             err_framenum;
256   char               *out_filename       = NULL;
257   merge_result        status             = MERGE_OK;
258   idb_merge_mode      mode               = IDB_MERGE_MODE_MAX;
259   merge_progress_callback_t cb;
260
261   cmdarg_err_init(mergecap_cmdarg_err, mergecap_cmdarg_err_cont);
262
263 #ifdef _WIN32
264   arg_list_utf_16to8(argc, argv);
265   create_app_running_mutex();
266 #endif /* _WIN32 */
267
268   /* Get the compile-time version information string */
269   comp_info_str = get_compiled_version_info(NULL, NULL);
270
271   /* Get the run-time version information string */
272   runtime_info_str = get_runtime_version_info(NULL);
273
274   /* Add it to the information to be reported on a crash. */
275   ws_add_crash_info("Mergecap (Wireshark) %s\n"
276        "\n"
277        "%s"
278        "\n"
279        "%s",
280     get_ws_vcs_version_info(), comp_info_str->str, runtime_info_str->str);
281   g_string_free(comp_info_str, TRUE);
282   g_string_free(runtime_info_str, TRUE);
283
284   /*
285    * Get credential information for later use.
286    */
287   init_process_policies();
288
289   /*
290    * Attempt to get the pathname of the directory containing the
291    * executable file.
292    */
293   init_progfile_dir_error = init_progfile_dir(argv[0], main);
294   if (init_progfile_dir_error != NULL) {
295     fprintf(stderr,
296             "mergecap: Can't get pathname of directory containing the mergecap program: %s.\n",
297             init_progfile_dir_error);
298     g_free(init_progfile_dir_error);
299   }
300
301   wtap_init();
302
303 #ifdef HAVE_PLUGINS
304   init_report_message(failure_warning_message, failure_warning_message,
305                       NULL, NULL, NULL);
306
307   /* Scan for plugins.  This does *not* call their registration routines;
308      that's done later.
309
310      Don't report failures to load plugins because most (non-wiretap)
311      plugins *should* fail to load (because we're not linked against
312      libwireshark and dissector plugins need libwireshark).*/
313   scan_plugins(DONT_REPORT_LOAD_FAILURE);
314
315   /* Register all libwiretap plugin modules. */
316   register_all_wiretap_modules();
317 #endif
318
319   /* Process the options first */
320   while ((opt = getopt_long(argc, argv, "aF:hI:s:vVw:", long_options, NULL)) != -1) {
321
322     switch (opt) {
323     case 'a':
324       do_append = !do_append;
325       break;
326
327     case 'F':
328       file_type = wtap_short_string_to_file_type_subtype(optarg);
329       if (file_type < 0) {
330         fprintf(stderr, "mergecap: \"%s\" isn't a valid capture file type\n",
331                 optarg);
332         list_capture_types();
333         status = MERGE_ERR_INVALID_OPTION;
334         goto clean_exit;
335       }
336       break;
337
338     case 'h':
339       printf("Mergecap (Wireshark) %s\n"
340              "Merge two or more capture files into one.\n"
341              "See https://www.wireshark.org for more information.\n",
342              get_ws_vcs_version_info());
343       print_usage(stdout);
344       goto clean_exit;
345       break;
346
347     case 'I':
348       mode = merge_string_to_idb_merge_mode(optarg);
349       if (mode == IDB_MERGE_MODE_MAX) {
350         fprintf(stderr, "mergecap: \"%s\" isn't a valid IDB merge mode\n",
351                 optarg);
352         list_idb_merge_modes();
353         status = MERGE_ERR_INVALID_OPTION;
354         goto clean_exit;
355       }
356       break;
357
358     case 's':
359       snaplen = get_nonzero_guint32(optarg, "snapshot length");
360       break;
361
362     case 'v':
363       verbose = TRUE;
364       break;
365
366     case 'V':
367       comp_info_str = get_compiled_version_info(NULL, NULL);
368       runtime_info_str = get_runtime_version_info(NULL);
369       show_version("Mergecap (Wireshark)", comp_info_str, runtime_info_str);
370       g_string_free(comp_info_str, TRUE);
371       g_string_free(runtime_info_str, TRUE);
372       goto clean_exit;
373       break;
374
375     case 'w':
376       out_filename = optarg;
377       break;
378
379     case '?':              /* Bad options if GNU getopt */
380       switch(optopt) {
381       case'F':
382         list_capture_types();
383         break;
384       case'I':
385         list_idb_merge_modes();
386         break;
387       default:
388         print_usage(stderr);
389       }
390       status = MERGE_ERR_INVALID_OPTION;
391       goto clean_exit;
392       break;
393     }
394   }
395
396   cb.callback_func = merge_callback;
397   cb.data = NULL;
398
399   /* check for proper args; at a minimum, must have an output
400    * filename and one input file
401    */
402   in_file_count = argc - optind;
403   if (!out_filename) {
404     fprintf(stderr, "mergecap: an output filename must be set with -w\n");
405     fprintf(stderr, "          run with -h for help\n");
406     status = MERGE_ERR_INVALID_OPTION;
407     goto clean_exit;
408   }
409   if (in_file_count < 1) {
410     fprintf(stderr, "mergecap: No input files were specified\n");
411     return 1;
412   }
413
414   /* setting IDB merge mode must use PCAPNG output */
415   if (mode != IDB_MERGE_MODE_MAX && file_type != WTAP_FILE_TYPE_SUBTYPE_PCAPNG) {
416     fprintf(stderr, "The IDB merge mode can only be used with PCAPNG output format\n");
417     status = MERGE_ERR_INVALID_OPTION;
418     goto clean_exit;
419   }
420
421   /* if they didn't set IDB merge mode, set it to our default */
422   if (mode == IDB_MERGE_MODE_MAX) {
423     mode = IDB_MERGE_MODE_ALL_SAME;
424   }
425
426   /* open the outfile */
427   if (strcmp(out_filename, "-") == 0) {
428     /* merge the files to the standard output */
429     status = merge_files_to_stdout(file_type,
430                                    (const char *const *) &argv[optind],
431                                    in_file_count, do_append, mode, snaplen,
432                                    "mergecap", verbose ? &cb : NULL,
433                                    &err, &err_info, &err_fileno, &err_framenum);
434   } else {
435     /* merge the files to the outfile */
436     status = merge_files(out_filename, file_type,
437                          (const char *const *) &argv[optind], in_file_count,
438                          do_append, mode, snaplen, "mergecap", verbose ? &cb : NULL,
439                          &err, &err_info, &err_fileno, &err_framenum);
440   }
441
442   switch (status) {
443     case MERGE_OK:
444       break;
445
446     case MERGE_USER_ABORTED:
447       /* we don't catch SIGINT/SIGTERM (yet?), so we couldn't have aborted */
448       g_assert(FALSE);
449       break;
450
451     case MERGE_ERR_CANT_OPEN_INFILE:
452       cfile_open_failure_message("mergecap", argv[optind + err_fileno],
453                                  err, err_info);
454       break;
455
456     case MERGE_ERR_CANT_OPEN_OUTFILE:
457       cfile_dump_open_failure_message("mergecap", out_filename, err, file_type);
458       break;
459
460     case MERGE_ERR_CANT_READ_INFILE:
461       cfile_read_failure_message("mergecap", argv[optind + err_fileno],
462                                  err, err_info);
463       break;
464
465     case MERGE_ERR_BAD_PHDR_INTERFACE_ID:
466       cmdarg_err("Record %u of \"%s\" has an interface ID that does not match any IDB in its file.",
467                  err_framenum, argv[optind + err_fileno]);
468       break;
469
470     case MERGE_ERR_CANT_WRITE_OUTFILE:
471        cfile_write_failure_message("mergecap", argv[optind + err_fileno],
472                                    out_filename, err, err_info, err_framenum,
473                                    file_type);
474        break;
475
476     case MERGE_ERR_CANT_CLOSE_OUTFILE:
477         cfile_close_failure_message(out_filename, err);
478         break;
479
480     default:
481       cmdarg_err("Unknown merge_files error %d", status);
482       break;
483   }
484
485 clean_exit:
486   wtap_cleanup();
487   free_progdirs();
488 #ifdef HAVE_PLUGINS
489   plugins_cleanup();
490 #endif
491   return (status == MERGE_OK) ? 0 : 2;
492 }
493
494 /*
495  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
496  *
497  * Local variables:
498  * c-basic-offset: 2
499  * tab-width: 8
500  * indent-tabs-mode: nil
501  * End:
502  *
503  * vi: set shiftwidth=2 tabstop=8 expandtab:
504  * :indentSize=2:tabSize=8:noTabs=true:
505  */
506