TODO SMB2 NegotiateContext....
[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-or-later
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 /*
121  * General errors and warnings are reported with an console message
122  * in mergecap.
123  */
124 static void
125 failure_warning_message(const char *msg_format, va_list ap)
126 {
127   fprintf(stderr, "mergecap: ");
128   vfprintf(stderr, msg_format, ap);
129   fprintf(stderr, "\n");
130 }
131
132 static void
133 list_capture_types(void) {
134   int i;
135   struct string_elem *captypes;
136   GSList *list = NULL;
137
138   captypes = g_new(struct string_elem,WTAP_NUM_FILE_TYPES_SUBTYPES);
139
140   fprintf(stderr, "mergecap: The available capture file types for the \"-F\" flag are:\n");
141   for (i = 0; i < WTAP_NUM_FILE_TYPES_SUBTYPES; i++) {
142     if (wtap_dump_can_open(i)) {
143       captypes[i].sstr = wtap_file_type_subtype_short_string(i);
144       captypes[i].lstr = wtap_file_type_subtype_string(i);
145       list = g_slist_insert_sorted(list, &captypes[i], string_compare);
146     }
147   }
148   g_slist_foreach(list, string_elem_print, NULL);
149   g_slist_free(list);
150   g_free(captypes);
151 }
152
153 static void
154 list_idb_merge_modes(void) {
155   int i;
156
157   fprintf(stderr, "mergecap: The available IDB merge modes for the \"-I\" flag are:\n");
158   for (i = 0; i < IDB_MERGE_MODE_MAX; i++) {
159     fprintf(stderr, "    %s\n", merge_idb_merge_mode_to_string(i));
160   }
161 }
162
163 static gboolean
164 merge_callback(merge_event event, int num,
165                const merge_in_file_t in_files[], const guint in_file_count,
166                void *data _U_)
167 {
168   guint i;
169
170   switch (event) {
171
172     case MERGE_EVENT_INPUT_FILES_OPENED:
173       for (i = 0; i < in_file_count; i++) {
174         fprintf(stderr, "mergecap: %s is type %s.\n", in_files[i].filename,
175                 wtap_file_type_subtype_string(wtap_file_type_subtype(in_files[i].wth)));
176       }
177       break;
178
179     case MERGE_EVENT_FRAME_TYPE_SELECTED:
180       /* for this event, num = frame_type */
181       if (num == WTAP_ENCAP_PER_PACKET) {
182         /*
183          * Find out why we had to choose WTAP_ENCAP_PER_PACKET.
184          */
185         int first_frame_type, this_frame_type;
186
187         first_frame_type = wtap_file_encap(in_files[0].wth);
188         for (i = 1; i < in_file_count; i++) {
189           this_frame_type = wtap_file_encap(in_files[i].wth);
190           if (first_frame_type != this_frame_type) {
191             fprintf(stderr, "mergecap: multiple frame encapsulation types detected\n");
192             fprintf(stderr, "          defaulting to WTAP_ENCAP_PER_PACKET\n");
193             fprintf(stderr, "          %s had type %s (%s)\n",
194                     in_files[0].filename,
195                     wtap_encap_string(first_frame_type),
196                     wtap_encap_short_string(first_frame_type));
197             fprintf(stderr, "          %s had type %s (%s)\n",
198                     in_files[i].filename,
199                     wtap_encap_string(this_frame_type),
200                     wtap_encap_short_string(this_frame_type));
201             break;
202           }
203         }
204       }
205       fprintf(stderr, "mergecap: selected frame_type %s (%s)\n",
206               wtap_encap_string(num),
207               wtap_encap_short_string(num));
208       break;
209
210     case MERGE_EVENT_READY_TO_MERGE:
211       fprintf(stderr, "mergecap: ready to merge records\n");
212       break;
213
214     case MERGE_EVENT_RECORD_WAS_READ:
215       /* for this event, num = count */
216       fprintf(stderr, "Record: %d\n", num);
217       break;
218
219     case MERGE_EVENT_DONE:
220       fprintf(stderr, "mergecap: merging complete\n");
221       break;
222   }
223
224   /* false = do not stop merging */
225   return FALSE;
226 }
227
228 static int
229 real_main(int argc, char *argv[])
230 {
231   GString            *comp_info_str;
232   GString            *runtime_info_str;
233   char               *init_progfile_dir_error;
234   int                 opt;
235   static const struct option long_options[] = {
236       {"help", no_argument, NULL, 'h'},
237       {"version", no_argument, NULL, 'V'},
238       {0, 0, 0, 0 }
239   };
240   gboolean            do_append          = FALSE;
241   gboolean            verbose            = FALSE;
242   int                 in_file_count      = 0;
243   guint32             snaplen            = 0;
244 #ifdef PCAP_NG_DEFAULT
245   int                 file_type          = WTAP_FILE_TYPE_SUBTYPE_PCAPNG; /* default to pcap format */
246 #else
247   int                 file_type          = WTAP_FILE_TYPE_SUBTYPE_PCAP; /* default to pcapng format */
248 #endif
249   int                 err                = 0;
250   gchar              *err_info           = NULL;
251   int                 err_fileno;
252   guint32             err_framenum;
253   char               *out_filename       = NULL;
254   merge_result        status             = MERGE_OK;
255   idb_merge_mode      mode               = IDB_MERGE_MODE_MAX;
256   merge_progress_callback_t cb;
257
258   cmdarg_err_init(mergecap_cmdarg_err, mergecap_cmdarg_err_cont);
259
260 #ifdef _WIN32
261   create_app_running_mutex();
262 #endif /* _WIN32 */
263
264   /* Get the compile-time version information string */
265   comp_info_str = get_compiled_version_info(NULL, NULL);
266
267   /* Get the run-time version information string */
268   runtime_info_str = get_runtime_version_info(NULL);
269
270   /* Add it to the information to be reported on a crash. */
271   ws_add_crash_info("Mergecap (Wireshark) %s\n"
272        "\n"
273        "%s"
274        "\n"
275        "%s",
276     get_ws_vcs_version_info(), comp_info_str->str, runtime_info_str->str);
277   g_string_free(comp_info_str, TRUE);
278   g_string_free(runtime_info_str, TRUE);
279
280   /*
281    * Get credential information for later use.
282    */
283   init_process_policies();
284
285   /*
286    * Attempt to get the pathname of the directory containing the
287    * executable file.
288    */
289   init_progfile_dir_error = init_progfile_dir(argv[0]);
290   if (init_progfile_dir_error != NULL) {
291     fprintf(stderr,
292             "mergecap: Can't get pathname of directory containing the mergecap program: %s.\n",
293             init_progfile_dir_error);
294     g_free(init_progfile_dir_error);
295   }
296
297   init_report_message(failure_warning_message, failure_warning_message,
298                       NULL, NULL, NULL);
299
300   wtap_init(TRUE);
301
302   /* Process the options first */
303   while ((opt = getopt_long(argc, argv, "aF:hI:s:vVw:", long_options, NULL)) != -1) {
304
305     switch (opt) {
306     case 'a':
307       do_append = !do_append;
308       break;
309
310     case 'F':
311       file_type = wtap_short_string_to_file_type_subtype(optarg);
312       if (file_type < 0) {
313         fprintf(stderr, "mergecap: \"%s\" isn't a valid capture file type\n",
314                 optarg);
315         list_capture_types();
316         status = MERGE_ERR_INVALID_OPTION;
317         goto clean_exit;
318       }
319       break;
320
321     case 'h':
322       printf("Mergecap (Wireshark) %s\n"
323              "Merge two or more capture files into one.\n"
324              "See https://www.wireshark.org for more information.\n",
325              get_ws_vcs_version_info());
326       print_usage(stdout);
327       goto clean_exit;
328       break;
329
330     case 'I':
331       mode = merge_string_to_idb_merge_mode(optarg);
332       if (mode == IDB_MERGE_MODE_MAX) {
333         fprintf(stderr, "mergecap: \"%s\" isn't a valid IDB merge mode\n",
334                 optarg);
335         list_idb_merge_modes();
336         status = MERGE_ERR_INVALID_OPTION;
337         goto clean_exit;
338       }
339       break;
340
341     case 's':
342       snaplen = get_nonzero_guint32(optarg, "snapshot length");
343       break;
344
345     case 'v':
346       verbose = TRUE;
347       break;
348
349     case 'V':
350       comp_info_str = get_compiled_version_info(NULL, NULL);
351       runtime_info_str = get_runtime_version_info(NULL);
352       show_version("Mergecap (Wireshark)", comp_info_str, runtime_info_str);
353       g_string_free(comp_info_str, TRUE);
354       g_string_free(runtime_info_str, TRUE);
355       goto clean_exit;
356       break;
357
358     case 'w':
359       out_filename = optarg;
360       break;
361
362     case '?':              /* Bad options if GNU getopt */
363       switch(optopt) {
364       case'F':
365         list_capture_types();
366         break;
367       case'I':
368         list_idb_merge_modes();
369         break;
370       default:
371         print_usage(stderr);
372       }
373       status = MERGE_ERR_INVALID_OPTION;
374       goto clean_exit;
375       break;
376     }
377   }
378
379   cb.callback_func = merge_callback;
380   cb.data = NULL;
381
382   /* check for proper args; at a minimum, must have an output
383    * filename and one input file
384    */
385   in_file_count = argc - optind;
386   if (!out_filename) {
387     fprintf(stderr, "mergecap: an output filename must be set with -w\n");
388     fprintf(stderr, "          run with -h for help\n");
389     status = MERGE_ERR_INVALID_OPTION;
390     goto clean_exit;
391   }
392   if (in_file_count < 1) {
393     fprintf(stderr, "mergecap: No input files were specified\n");
394     return 1;
395   }
396
397   /* setting IDB merge mode must use PCAPNG output */
398   if (mode != IDB_MERGE_MODE_MAX && file_type != WTAP_FILE_TYPE_SUBTYPE_PCAPNG) {
399     fprintf(stderr, "The IDB merge mode can only be used with PCAPNG output format\n");
400     status = MERGE_ERR_INVALID_OPTION;
401     goto clean_exit;
402   }
403
404   /* if they didn't set IDB merge mode, set it to our default */
405   if (mode == IDB_MERGE_MODE_MAX) {
406     mode = IDB_MERGE_MODE_ALL_SAME;
407   }
408
409   /* open the outfile */
410   if (strcmp(out_filename, "-") == 0) {
411     /* merge the files to the standard output */
412     status = merge_files_to_stdout(file_type,
413                                    (const char *const *) &argv[optind],
414                                    in_file_count, do_append, mode, snaplen,
415                                    "mergecap", verbose ? &cb : NULL,
416                                    &err, &err_info, &err_fileno, &err_framenum);
417   } else {
418     /* merge the files to the outfile */
419     status = merge_files(out_filename, file_type,
420                          (const char *const *) &argv[optind], in_file_count,
421                          do_append, mode, snaplen, "mergecap", verbose ? &cb : NULL,
422                          &err, &err_info, &err_fileno, &err_framenum);
423   }
424
425   switch (status) {
426     case MERGE_OK:
427       break;
428
429     case MERGE_USER_ABORTED:
430       /* we don't catch SIGINT/SIGTERM (yet?), so we couldn't have aborted */
431       g_assert(FALSE);
432       break;
433
434     case MERGE_ERR_CANT_OPEN_INFILE:
435       cfile_open_failure_message("mergecap", argv[optind + err_fileno],
436                                  err, err_info);
437       break;
438
439     case MERGE_ERR_CANT_OPEN_OUTFILE:
440       cfile_dump_open_failure_message("mergecap", out_filename, err, file_type);
441       break;
442
443     case MERGE_ERR_CANT_READ_INFILE:
444       cfile_read_failure_message("mergecap", argv[optind + err_fileno],
445                                  err, err_info);
446       break;
447
448     case MERGE_ERR_BAD_PHDR_INTERFACE_ID:
449       cmdarg_err("Record %u of \"%s\" has an interface ID that does not match any IDB in its file.",
450                  err_framenum, argv[optind + err_fileno]);
451       break;
452
453     case MERGE_ERR_CANT_WRITE_OUTFILE:
454        cfile_write_failure_message("mergecap", argv[optind + err_fileno],
455                                    out_filename, err, err_info, err_framenum,
456                                    file_type);
457        break;
458
459     case MERGE_ERR_CANT_CLOSE_OUTFILE:
460         cfile_close_failure_message(out_filename, err);
461         break;
462
463     default:
464       cmdarg_err("Unknown merge_files error %d", status);
465       break;
466   }
467
468 clean_exit:
469   wtap_cleanup();
470   free_progdirs();
471   return (status == MERGE_OK) ? 0 : 2;
472 }
473
474 #ifdef _WIN32
475 int
476 wmain(int argc, wchar_t *wc_argv[])
477 {
478   char **argv;
479
480   argv = arg_list_utf_16to8(argc, wc_argv);
481   return real_main(argc, argv);
482 }
483 #else
484 int
485 main(int argc, char *argv[])
486 {
487   return real_main(argc, argv);
488 }
489 #endif
490
491 /*
492  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
493  *
494  * Local variables:
495  * c-basic-offset: 2
496  * tab-width: 8
497  * indent-tabs-mode: nil
498  * End:
499  *
500  * vi: set shiftwidth=2 tabstop=8 expandtab:
501  * :indentSize=2:tabSize=8:noTabs=true:
502  */