1 /* Edit capture files. We can delete packets, adjust timestamps, or
2 * simply convert from one format to another format.
6 * Originally written by Richard Sharpe.
7 * Improved by Guy Harris.
8 * Further improved by Richard Sharpe.
16 * Just make sure we include the prototype for strptime as well
17 * (needed for glibc 2.2) but make sure we do this only if not
25 # define _XOPEN_SOURCE
30 #include <glib/gprintf.h>
38 #ifdef HAVE_SYS_TIME_H
45 #include "wsutil/wsgetopt.h"
49 #include <wsutil/unicode-utils.h>
50 #include <process.h> /* getpid */
51 #ifdef HAVE_WINSOCK2_H
56 #ifdef NEED_STRPTIME_H
57 # include "wsutil/strptime.h"
60 #include "epan/crypt/md5.h"
61 #include "epan/plugins.h"
62 #include "epan/report_err.h"
63 #include "epan/filesystem.h"
64 #include <wsutil/privileges.h>
65 #include "epan/nstime.h"
67 #include "svnversion.h"
70 * Some globals so we can pass things to various routines
82 * Duplicate frame detection
84 typedef struct _fd_hash_t {
85 md5_byte_t digest[16];
90 #define DEFAULT_DUP_DEPTH 5 /* Used with -d */
91 #define MAX_DUP_DEPTH 1000000 /* the maximum window (and actual size of fd_hash[]) for de-duplication */
93 fd_hash_t fd_hash[MAX_DUP_DEPTH];
94 int dup_window = DEFAULT_DUP_DEPTH;
95 int cur_dup_entry = 0;
97 #define ONE_MILLION 1000000
98 #define ONE_BILLION 1000000000
100 /* Weights of different errors we can introduce */
101 /* We should probably make these command-line arguments */
102 /* XXX - Should we add a bit-level error? */
103 #define ERR_WT_BIT 5 /* Flip a random bit */
104 #define ERR_WT_BYTE 5 /* Substitute a random byte */
105 #define ERR_WT_ALNUM 5 /* Substitute a random character in [A-Za-z0-9] */
106 #define ERR_WT_FMT 2 /* Substitute "%s" */
107 #define ERR_WT_AA 1 /* Fill the remainder of the buffer with 0xAA */
108 #define ERR_WT_TOTAL (ERR_WT_BIT + ERR_WT_BYTE + ERR_WT_ALNUM + ERR_WT_FMT + ERR_WT_AA)
110 #define ALNUM_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
111 #define ALNUM_LEN (sizeof(ALNUM_CHARS) - 1)
114 struct time_adjustment {
119 #define MAX_SELECTIONS 512
120 static struct select_item selectfrm[MAX_SELECTIONS];
121 static int max_selected = -1;
122 static int keep_em = 0;
123 #ifdef PCAP_NG_DEFAULT
124 static int out_file_type = WTAP_FILE_PCAPNG; /* default to pcapng */
126 static int out_file_type = WTAP_FILE_PCAP; /* default to pcap */
128 static int out_frame_type = -2; /* Leave frame type alone */
129 static int verbose = 0; /* Not so verbose */
130 static struct time_adjustment time_adj = {{0, 0}, 0}; /* no adjustment */
131 static nstime_t relative_time_window = {0, 0}; /* de-dup time window */
132 static double err_prob = 0.0;
133 static time_t starttime = 0;
134 static time_t stoptime = 0;
135 static gboolean check_startstop = FALSE;
136 static gboolean dup_detect = FALSE;
137 static gboolean dup_detect_by_time = FALSE;
139 static int do_strict_time_adjustment = FALSE;
140 static struct time_adjustment strict_time_adj = {{0, 0}, 0}; /* strict time adjustment */
141 static nstime_t previous_time = {0, 0}; /* previous time */
143 static int find_dct2000_real_data(guint8 *buf);
146 abs_time_to_str_with_sec_resolution(const struct wtap_nstime *abs_time)
149 gchar *buf = g_malloc(16);
152 /* calling localtime() on MSVC 2005 with huge values causes it to crash */
153 /* XXX - find the exact value that still does work */
154 /* XXX - using _USE_32BIT_TIME_T might be another way to circumvent this problem */
155 if(abs_time->secs > 2000000000) {
159 tmp = localtime(&abs_time->secs);
161 g_snprintf(buf, 16, "%d%02d%02d%02d%02d%02d",
175 fileset_get_filename_by_pattern(guint idx, const struct wtap_nstime *time_val,
176 gchar *fprefix, gchar *fsuffix)
182 timestr = abs_time_to_str_with_sec_resolution(time_val);
183 g_snprintf(filenum, sizeof(filenum), "%05u", idx);
184 abs_str = g_strconcat(fprefix, "_", filenum, "_", timestr, fsuffix, NULL);
191 fileset_extract_prefix_suffix(const char *fname, gchar **fprefix, gchar **fsuffix)
193 char *pfx, *last_pathsep;
196 save_file = g_strdup(fname);
197 if (save_file == NULL) {
198 g_fprintf(stderr, "editcap: Out of memory\n");
202 last_pathsep = strrchr(save_file, G_DIR_SEPARATOR);
203 pfx = strrchr(save_file,'.');
204 if (pfx != NULL && (last_pathsep == NULL || pfx > last_pathsep)) {
205 /* The pathname has a "." in it, and it's in the last component
206 of the pathname (because there is either only one component,
207 i.e. last_pathsep is null as there are no path separators,
208 or the "." is after the path separator before the last
211 Treat it as a separator between the rest of the file name and
212 the file name suffix, and arrange that the names given to the
213 ring buffer files have the specified suffix, i.e. put the
214 changing part of the name *before* the suffix. */
216 *fprefix = g_strdup(save_file);
217 pfx[0] = '.'; /* restore capfile_name */
218 *fsuffix = g_strdup(pfx);
220 /* Either there's no "." in the pathname, or it's in a directory
221 component, so the last component has no suffix. */
222 *fprefix = g_strdup(save_file);
229 /* Add a selection item, a simple parser for now */
231 add_selection(char *sel)
236 if (++max_selected >= MAX_SELECTIONS) {
237 /* Let the user know we stopped selecting */
238 g_print("Out of room for packet selections!\n");
242 g_printf("Add_Selected: %s\n", sel);
244 if ((locn = strchr(sel, '-')) == NULL) { /* No dash, so a single number? */
246 g_print("Not inclusive ...");
248 selectfrm[max_selected].inclusive = 0;
249 selectfrm[max_selected].first = atoi(sel);
251 g_printf(" %i\n", selectfrm[max_selected].first);
256 g_print("Inclusive ...");
259 selectfrm[max_selected].inclusive = 1;
260 selectfrm[max_selected].first = atoi(sel);
261 selectfrm[max_selected].second = atoi(next);
263 g_printf(" %i, %i\n", selectfrm[max_selected].first, selectfrm[max_selected].second);
270 /* Was the packet selected? */
277 for (i = 0; i<= max_selected; i++) {
279 if (selectfrm[i].inclusive) {
280 if (selectfrm[i].first <= recno && selectfrm[i].second >= recno)
284 if (recno == selectfrm[i].first)
293 /* is the packet in the selected timeframe */
295 check_timestamp(wtap *wth)
297 struct wtap_pkthdr* pkthdr = wtap_phdr(wth);
299 return ( pkthdr->ts.secs >= starttime ) && ( pkthdr->ts.secs < stoptime );
303 set_time_adjustment(char *optarg_str_p)
312 /* skip leading whitespace */
313 while (*optarg_str_p == ' ' || *optarg_str_p == '\t') {
317 /* check for a negative adjustment */
318 if (*optarg_str_p == '-') {
319 time_adj.is_negative = 1;
323 /* collect whole number of seconds, if any */
324 if (*optarg_str_p == '.') { /* only fractional (i.e., .5 is ok) */
328 val = strtol(optarg_str_p, &frac, 10);
329 if (frac == NULL || frac == optarg_str_p || val == LONG_MIN || val == LONG_MAX) {
330 g_fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
334 if (val < 0) { /* implies '--' since we caught '-' above */
335 g_fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
340 time_adj.tv.tv_sec = val;
342 /* now collect the partial seconds, if any */
343 if (*frac != '\0') { /* chars left, so get fractional part */
344 val = strtol(&(frac[1]), &end, 10);
345 /* if more than 6 fractional digits truncate to 6 */
346 if((end - &(frac[1])) > 6) {
347 frac[7] = 't'; /* 't' for truncate */
348 val = strtol(&(frac[1]), &end, 10);
350 if (*frac != '.' || end == NULL || end == frac
351 || val < 0 || val > ONE_MILLION || val == LONG_MIN || val == LONG_MAX) {
352 g_fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
358 return; /* no fractional digits */
361 /* adjust fractional portion from fractional to numerator
362 * e.g., in "1.5" from 5 to 500000 since .5*10^6 = 500000 */
363 if (frac && end) { /* both are valid */
364 frac_digits = end - frac - 1; /* fractional digit count (remember '.') */
365 while(frac_digits < 6) { /* this is frac of 10^6 */
370 time_adj.tv.tv_usec = val;
374 set_strict_time_adj(char *optarg_str_p)
383 /* skip leading whitespace */
384 while (*optarg_str_p == ' ' || *optarg_str_p == '\t') {
389 * check for a negative adjustment
390 * A negative strict adjustment value is a flag
391 * to adjust all frames by the specifed delta time.
393 if (*optarg_str_p == '-') {
394 strict_time_adj.is_negative = 1;
398 /* collect whole number of seconds, if any */
399 if (*optarg_str_p == '.') { /* only fractional (i.e., .5 is ok) */
403 val = strtol(optarg_str_p, &frac, 10);
404 if (frac == NULL || frac == optarg_str_p || val == LONG_MIN || val == LONG_MAX) {
405 g_fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
409 if (val < 0) { /* implies '--' since we caught '-' above */
410 g_fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
415 strict_time_adj.tv.tv_sec = val;
417 /* now collect the partial seconds, if any */
418 if (*frac != '\0') { /* chars left, so get fractional part */
419 val = strtol(&(frac[1]), &end, 10);
420 /* if more than 6 fractional digits truncate to 6 */
421 if((end - &(frac[1])) > 6) {
422 frac[7] = 't'; /* 't' for truncate */
423 val = strtol(&(frac[1]), &end, 10);
425 if (*frac != '.' || end == NULL || end == frac
426 || val < 0 || val > ONE_MILLION || val == LONG_MIN || val == LONG_MAX) {
427 g_fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
433 return; /* no fractional digits */
436 /* adjust fractional portion from fractional to numerator
437 * e.g., in "1.5" from 5 to 500000 since .5*10^6 = 500000 */
438 if (frac && end) { /* both are valid */
439 frac_digits = end - frac - 1; /* fractional digit count (remember '.') */
440 while(frac_digits < 6) { /* this is frac of 10^6 */
445 strict_time_adj.tv.tv_usec = val;
449 set_rel_time(char *optarg_str_p)
458 /* skip leading whitespace */
459 while (*optarg_str_p == ' ' || *optarg_str_p == '\t') {
463 /* ignore negative adjustment */
464 if (*optarg_str_p == '-') {
468 /* collect whole number of seconds, if any */
469 if (*optarg_str_p == '.') { /* only fractional (i.e., .5 is ok) */
473 val = strtol(optarg_str_p, &frac, 10);
474 if (frac == NULL || frac == optarg_str_p || val == LONG_MIN || val == LONG_MAX) {
475 g_fprintf(stderr, "1: editcap: \"%s\" isn't a valid rel time value\n",
479 if (val < 0) { /* implies '--' since we caught '-' above */
480 g_fprintf(stderr, "2: editcap: \"%s\" isn't a valid rel time value\n",
485 relative_time_window.secs = val;
487 /* now collect the partial seconds, if any */
488 if (*frac != '\0') { /* chars left, so get fractional part */
489 val = strtol(&(frac[1]), &end, 10);
490 /* if more than 9 fractional digits truncate to 9 */
491 if((end - &(frac[1])) > 9) {
492 frac[10] = 't'; /* 't' for truncate */
493 val = strtol(&(frac[1]), &end, 10);
495 if (*frac != '.' || end == NULL || end == frac
496 || val < 0 || val > ONE_BILLION || val == LONG_MIN || val == LONG_MAX) {
497 g_fprintf(stderr, "3: editcap: \"%s\" isn't a valid rel time value\n",
503 return; /* no fractional digits */
506 /* adjust fractional portion from fractional to numerator
507 * e.g., in "1.5" from 5 to 500000000 since .5*10^9 = 500000000 */
508 if (frac && end) { /* both are valid */
509 frac_digits = end - frac - 1; /* fractional digit count (remember '.') */
510 while(frac_digits < 9) { /* this is frac of 10^9 */
515 relative_time_window.nsecs = val;
519 is_duplicate(guint8* fd, guint32 len) {
524 if (cur_dup_entry >= dup_window)
527 /* Calculate our digest */
529 md5_append(&ms, fd, len);
530 md5_finish(&ms, fd_hash[cur_dup_entry].digest);
532 fd_hash[cur_dup_entry].len = len;
534 /* Look for duplicates */
535 for (i = 0; i < dup_window; i++) {
536 if (i == cur_dup_entry)
539 if (fd_hash[i].len == fd_hash[cur_dup_entry].len &&
540 memcmp(fd_hash[i].digest, fd_hash[cur_dup_entry].digest, 16) == 0) {
549 is_duplicate_rel_time(guint8* fd, guint32 len, const nstime_t *current) {
554 if (cur_dup_entry >= dup_window)
557 /* Calculate our digest */
559 md5_append(&ms, fd, len);
560 md5_finish(&ms, fd_hash[cur_dup_entry].digest);
562 fd_hash[cur_dup_entry].len = len;
563 fd_hash[cur_dup_entry].time.secs = current->secs;
564 fd_hash[cur_dup_entry].time.nsecs = current->nsecs;
567 * Look for relative time related duplicates.
568 * This is hopefully a reasonably efficient mechanism for
569 * finding duplicates by rel time in the fd_hash[] cache.
570 * We check starting from the most recently added hash
571 * entries and work backwards towards older packets.
572 * This approach allows the dup test to be terminated
573 * when the relative time of a cached entry is found to
574 * be beyond the dup time window.
576 * Of course this assumes that the input trace file is
577 * "well-formed" in the sense that the packet timestamps are
578 * in strict chronologically increasing order (which is NOT
579 * always the case!!).
581 * The fd_hash[] table was deliberatly created large (1,000,000).
582 * Looking for time related duplicates in large trace files with
583 * non-fractional dup time window values can potentially take
584 * a long time to complete.
587 for (i = cur_dup_entry - 1;; i--) {
595 if (i == cur_dup_entry) {
597 * We've decremented back to where we started.
603 if (nstime_is_unset(&(fd_hash[i].time))) {
605 * We've decremented to an unused fd_hash[] entry.
611 nstime_delta(&delta, current, &fd_hash[i].time);
613 if(delta.secs < 0 || delta.nsecs < 0)
616 * A negative delta implies that the current packet
617 * has an absolute timestamp less than the cached packet
618 * that it is being compared to. This is NOT a normal
619 * situation since trace files usually have packets in
620 * chronological order (oldest to newest).
622 * There are several possible ways to deal with this:
623 * 1. 'continue' dup checking with the next cached frame.
624 * 2. 'break' from looking for a duplicate of the current frame.
625 * 3. Take the absolute value of the delta and see if that
626 * falls within the specifed dup time window.
628 * Currently this code does option 1. But it would pretty
629 * easy to add yet-another-editcap-option to select one of
630 * the other behaviors for dealing with out-of-sequence
636 cmp = nstime_cmp(&delta, &relative_time_window);
640 * The delta time indicates that we are now looking at
641 * cached packets beyond the specified dup time window.
645 } else if (fd_hash[i].len == fd_hash[cur_dup_entry].len &&
646 memcmp(fd_hash[i].digest, fd_hash[cur_dup_entry].digest, 16) == 0) {
655 usage(gboolean is_error)
664 fprintf(output, "Editcap %s"
666 " (" SVNVERSION " from " SVNPATH ")"
669 g_fprintf(output, "Edit and/or translate the format of capture files.\n");
670 g_fprintf(output, "See http://www.wireshark.org for more information.\n");
671 g_fprintf(output, "\n");
672 g_fprintf(output, "Usage: editcap [options] ... <infile> <outfile> [ <packet#>[-<packet#>] ... ]\n");
673 g_fprintf(output, "\n");
674 g_fprintf(output, "<infile> and <outfile> must both be present.\n");
675 g_fprintf(output, "A single packet or a range of packets can be selected.\n");
676 g_fprintf(output, "\n");
677 g_fprintf(output, "Packet selection:\n");
678 g_fprintf(output, " -r keep the selected packets; default is to delete them.\n");
679 g_fprintf(output, " -A <start time> only output packets whose timestamp is after (or equal\n");
680 g_fprintf(output, " to) the given time (format as YYYY-MM-DD hh:mm:ss).\n");
681 g_fprintf(output, " -B <stop time> only output packets whose timestamp is before the\n");
682 g_fprintf(output, " given time (format as YYYY-MM-DD hh:mm:ss).\n");
683 g_fprintf(output, "\n");
684 g_fprintf(output, "Duplicate packet removal:\n");
685 g_fprintf(output, " -d remove packet if duplicate (window == %d).\n", DEFAULT_DUP_DEPTH);
686 g_fprintf(output, " -D <dup window> remove packet if duplicate; configurable <dup window>\n");
687 g_fprintf(output, " Valid <dup window> values are 0 to %d.\n", MAX_DUP_DEPTH);
688 g_fprintf(output, " NOTE: A <dup window> of 0 with -v (verbose option) is\n");
689 g_fprintf(output, " useful to print MD5 hashes.\n");
690 g_fprintf(output, " -w <dup time window> remove packet if duplicate packet is found EQUAL TO OR\n");
691 g_fprintf(output, " LESS THAN <dup time window> prior to current packet.\n");
692 g_fprintf(output, " A <dup time window> is specified in relative seconds\n");
693 g_fprintf(output, " (e.g. 0.000001).\n");
694 g_fprintf(output, "\n");
695 g_fprintf(output, " NOTE: The use of the 'Duplicate packet removal' options with\n");
696 g_fprintf(output, " other editcap options except -v may not always work as expected.\n");
697 g_fprintf(output, " Specifically the -r, -t or -S options will very likely NOT have the\n");
698 g_fprintf(output, " desired effect if combined with the -d, -D or -w.\n");
699 g_fprintf(output, "\n");
700 g_fprintf(output, "Packet manipulation:\n");
701 g_fprintf(output, " -s <snaplen> truncate each packet to max. <snaplen> bytes of data.\n");
702 g_fprintf(output, " -C <choplen> chop each packet by <choplen> bytes. Positive values\n");
703 g_fprintf(output, " chop at the packet beginning, negative values at the\n");
704 g_fprintf(output, " packet end.\n");
705 g_fprintf(output, " -t <time adjustment> adjust the timestamp of each packet;\n");
706 g_fprintf(output, " <time adjustment> is in relative seconds (e.g. -0.5).\n");
707 g_fprintf(output, " -S <strict adjustment> adjust timestamp of packets if necessary to insure\n");
708 g_fprintf(output, " strict chronological increasing order. The <strict\n");
709 g_fprintf(output, " adjustment> is specified in relative seconds with\n");
710 g_fprintf(output, " values of 0 or 0.000001 being the most reasonable.\n");
711 g_fprintf(output, " A negative adjustment value will modify timestamps so\n");
712 g_fprintf(output, " that each packet's delta time is the absolute value\n");
713 g_fprintf(output, " of the adjustment specified. A value of -0 will set\n");
714 g_fprintf(output, " all packets to the timestamp of the first packet.\n");
715 g_fprintf(output, " -E <error probability> set the probability (between 0.0 and 1.0 incl.)\n");
716 g_fprintf(output, " that a particular packet byte will be randomly changed.\n");
717 g_fprintf(output, "\n");
718 g_fprintf(output, "Output File(s):\n");
719 g_fprintf(output, " -c <packets per file> split the packet output to different files\n");
720 g_fprintf(output, " based on uniform packet counts\n");
721 g_fprintf(output, " with a maximum of <packets per file> each.\n");
722 g_fprintf(output, " -i <seconds per file> split the packet output to different files\n");
723 g_fprintf(output, " based on uniform time intervals\n");
724 g_fprintf(output, " with a maximum of <seconds per file> each.\n");
725 g_fprintf(output, " -F <capture type> set the output file type; default is pcapng.\n");
726 g_fprintf(output, " an empty \"-F\" option will list the file types.\n");
727 g_fprintf(output, " -T <encap type> set the output file encapsulation type;\n");
728 g_fprintf(output, " default is the same as the input file.\n");
729 g_fprintf(output, " an empty \"-T\" option will list the encapsulation types.\n");
730 g_fprintf(output, "\n");
731 g_fprintf(output, "Miscellaneous:\n");
732 g_fprintf(output, " -h display this help and exit.\n");
733 g_fprintf(output, " -v verbose output.\n");
734 g_fprintf(output, " If -v is used with any of the 'Duplicate Packet\n");
735 g_fprintf(output, " Removal' options (-d, -D or -w) then Packet lengths\n");
736 g_fprintf(output, " and MD5 hashes are printed to standard-out.\n");
737 g_fprintf(output, "\n");
741 const char *sstr; /* The short string */
742 const char *lstr; /* The long string */
746 string_compare(gconstpointer a, gconstpointer b)
748 return strcmp(((const struct string_elem *)a)->sstr,
749 ((const struct string_elem *)b)->sstr);
753 string_elem_print(gpointer data, gpointer not_used _U_)
755 g_fprintf(stderr, " %s - %s\n",
756 ((struct string_elem *)data)->sstr,
757 ((struct string_elem *)data)->lstr);
761 list_capture_types(void) {
763 struct string_elem *captypes;
766 captypes = g_malloc(sizeof(struct string_elem) * WTAP_NUM_FILE_TYPES);
767 g_fprintf(stderr, "editcap: The available capture file types for the \"-F\" flag are:\n");
768 for (i = 0; i < WTAP_NUM_FILE_TYPES; i++) {
769 if (wtap_dump_can_open(i)) {
770 captypes[i].sstr = wtap_file_type_short_string(i);
771 captypes[i].lstr = wtap_file_type_string(i);
772 list = g_slist_insert_sorted(list, &captypes[i], string_compare);
775 g_slist_foreach(list, string_elem_print, NULL);
781 list_encap_types(void) {
783 struct string_elem *encaps;
786 encaps = g_malloc(sizeof(struct string_elem) * WTAP_NUM_ENCAP_TYPES);
787 g_fprintf(stderr, "editcap: The available encapsulation types for the \"-T\" flag are:\n");
788 for (i = 0; i < WTAP_NUM_ENCAP_TYPES; i++) {
789 encaps[i].sstr = wtap_encap_short_string(i);
790 if (encaps[i].sstr != NULL) {
791 encaps[i].lstr = wtap_encap_string(i);
792 list = g_slist_insert_sorted(list, &encaps[i], string_compare);
795 g_slist_foreach(list, string_elem_print, NULL);
802 * Don't report failures to load plugins because most (non-wiretap) plugins
803 * *should* fail to load (because we're not linked against libwireshark and
804 * dissector plugins need libwireshark).
807 failure_message(const char *msg_format _U_, va_list ap _U_)
814 main(int argc, char *argv[])
822 guint32 snaplen = 0; /* No limit */
823 int choplen = 0; /* No chop */
824 wtap_dumper *pdh = NULL;
826 unsigned duplicate_count = 0;
828 struct wtap_pkthdr snap_phdr;
829 const struct wtap_pkthdr *phdr;
832 guint32 read_count = 0;
833 int split_packet_count = 0;
834 int written_count = 0;
835 char *filename = NULL;
836 gboolean ts_okay = TRUE;
837 int secs_per_block = 0;
839 nstime_t block_start;
840 gchar *fprefix = NULL;
841 gchar *fsuffix = NULL;
844 char* init_progfile_dir_error;
848 arg_list_utf_16to8(argc, argv);
852 * Get credential information for later use.
854 init_process_policies();
857 /* Register wiretap plugins */
858 if ((init_progfile_dir_error = init_progfile_dir(argv[0], main))) {
859 g_warning("capinfos: init_progfile_dir(): %s", init_progfile_dir_error);
860 g_free(init_progfile_dir_error);
862 init_report_err(failure_message,NULL,NULL,NULL);
867 /* Process the options */
868 while ((opt = getopt(argc, argv, "A:B:c:C:dD:E:F:hrs:i:t:S:T:vw:")) !=-1) {
873 err_prob = strtod(optarg, &p);
874 if (p == optarg || err_prob < 0.0 || err_prob > 1.0) {
875 g_fprintf(stderr, "editcap: probability \"%s\" must be between 0.0 and 1.0\n",
879 srand( (unsigned int) (time(NULL) + getpid()) );
883 out_file_type = wtap_short_string_to_file_type(optarg);
884 if (out_file_type < 0) {
885 g_fprintf(stderr, "editcap: \"%s\" isn't a valid capture file type\n\n",
887 list_capture_types();
893 split_packet_count = strtol(optarg, &p, 10);
894 if (p == optarg || *p != '\0') {
895 g_fprintf(stderr, "editcap: \"%s\" isn't a valid packet count\n",
899 if (split_packet_count <= 0) {
900 g_fprintf(stderr, "editcap: \"%d\" packet count must be larger than zero\n",
907 choplen = strtol(optarg, &p, 10);
908 if (p == optarg || *p != '\0') {
909 g_fprintf(stderr, "editcap: \"%s\" isn't a valid chop length\n",
917 dup_detect_by_time = FALSE;
918 dup_window = DEFAULT_DUP_DEPTH;
923 dup_detect_by_time = FALSE;
924 dup_window = strtol(optarg, &p, 10);
925 if (p == optarg || *p != '\0') {
926 g_fprintf(stderr, "editcap: \"%s\" isn't a valid duplicate window value\n",
930 if (dup_window < 0 || dup_window > MAX_DUP_DEPTH) {
931 g_fprintf(stderr, "editcap: \"%d\" duplicate window value must be between 0 and %d inclusive.\n",
932 dup_window, MAX_DUP_DEPTH);
939 dup_detect_by_time = TRUE;
940 dup_window = MAX_DUP_DEPTH;
941 set_rel_time(optarg);
944 case '?': /* Bad options if GNU getopt */
947 list_capture_types();
964 keep_em = !keep_em; /* Just invert */
968 snaplen = strtol(optarg, &p, 10);
969 if (p == optarg || *p != '\0') {
970 g_fprintf(stderr, "editcap: \"%s\" isn't a valid snapshot length\n",
977 set_time_adjustment(optarg);
981 set_strict_time_adj(optarg);
982 do_strict_time_adjustment = TRUE;
986 out_frame_type = wtap_short_string_to_encap(optarg);
987 if (out_frame_type < 0) {
988 g_fprintf(stderr, "editcap: \"%s\" isn't a valid encapsulation type\n\n",
996 verbose = !verbose; /* Just invert */
999 case 'i': /* break capture file based on time interval */
1000 secs_per_block = atoi(optarg);
1001 if(secs_per_block <= 0) {
1002 g_fprintf(stderr, "editcap: \"%s\" isn't a valid time interval\n\n", optarg);
1011 memset(&starttm,0,sizeof(struct tm));
1013 if(!strptime(optarg,"%Y-%m-%d %T",&starttm)) {
1014 g_fprintf(stderr, "editcap: \"%s\" isn't a valid time format\n\n", optarg);
1018 check_startstop = TRUE;
1019 starttm.tm_isdst = -1;
1021 starttime = mktime(&starttm);
1029 memset(&stoptm,0,sizeof(struct tm));
1031 if(!strptime(optarg,"%Y-%m-%d %T",&stoptm)) {
1032 g_fprintf(stderr, "editcap: \"%s\" isn't a valid time format\n\n", optarg);
1035 check_startstop = TRUE;
1036 stoptm.tm_isdst = -1;
1037 stoptime = mktime(&stoptm);
1045 g_printf("Optind = %i, argc = %i\n", optind, argc);
1048 if ((argc - optind) < 1) {
1055 if (check_startstop && !stoptime) {
1057 /* XXX: will work until 2035 */
1058 memset(&stoptm,0,sizeof(struct tm));
1059 stoptm.tm_year = 135;
1060 stoptm.tm_mday = 31;
1063 stoptime = mktime(&stoptm);
1066 nstime_set_unset(&block_start);
1068 if (starttime > stoptime) {
1069 g_fprintf(stderr, "editcap: start time is after the stop time\n");
1073 if (split_packet_count > 0 && secs_per_block > 0) {
1074 g_fprintf(stderr, "editcap: can't split on both packet count and time interval\n");
1075 g_fprintf(stderr, "editcap: at the same time\n");
1079 wth = wtap_open_offline(argv[optind], &err, &err_info, FALSE);
1082 g_fprintf(stderr, "editcap: Can't open %s: %s\n", argv[optind],
1083 wtap_strerror(err));
1086 case WTAP_ERR_UNSUPPORTED:
1087 case WTAP_ERR_UNSUPPORTED_ENCAP:
1088 case WTAP_ERR_BAD_FILE:
1089 g_fprintf(stderr, "(%s)\n", err_info);
1098 g_fprintf(stderr, "File %s is a %s capture file.\n", argv[optind],
1099 wtap_file_type_string(wtap_file_type(wth)));
1103 * Now, process the rest, if any ... we only write if there is an extra
1104 * argument or so ...
1107 if ((argc - optind) >= 2) {
1109 if (out_frame_type == -2)
1110 out_frame_type = wtap_file_encap(wth);
1112 for (i = optind + 2; i < argc; i++)
1113 if (add_selection(argv[i]) == FALSE)
1116 if (dup_detect || dup_detect_by_time) {
1117 for (i = 0; i < dup_window; i++) {
1118 memset(&fd_hash[i].digest, 0, 16);
1120 nstime_set_unset(&fd_hash[i].time);
1124 while (wtap_read(wth, &err, &err_info, &data_offset)) {
1127 phdr = wtap_phdr(wth);
1128 buf = wtap_buf_ptr(wth);
1130 if (nstime_is_unset(&block_start)) { /* should only be the first packet */
1131 block_start.secs = phdr->ts.secs;
1132 block_start.nsecs = phdr->ts.nsecs;
1134 if (split_packet_count > 0 || secs_per_block > 0) {
1135 if (!fileset_extract_prefix_suffix(argv[optind+1], &fprefix, &fsuffix))
1138 filename = fileset_get_filename_by_pattern(block_cnt++, &phdr->ts, fprefix, fsuffix);
1140 filename = g_strdup(argv[optind+1]);
1142 pdh = wtap_dump_open(filename, out_file_type, out_frame_type,
1143 snaplen ? MIN(snaplen, wtap_snapshot_length(wth)) : wtap_snapshot_length(wth),
1144 FALSE /* compressed */, &err);
1146 g_fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1147 wtap_strerror(err));
1154 if (secs_per_block > 0) {
1155 while ((phdr->ts.secs - block_start.secs > secs_per_block) ||
1156 (phdr->ts.secs - block_start.secs == secs_per_block &&
1157 phdr->ts.nsecs >= block_start.nsecs )) { /* time for the next file */
1159 if (!wtap_dump_close(pdh, &err)) {
1160 g_fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
1161 wtap_strerror(err));
1164 block_start.secs = block_start.secs + secs_per_block; /* reset for next interval */
1166 filename = fileset_get_filename_by_pattern(block_cnt++, &phdr->ts, fprefix, fsuffix);
1170 g_fprintf(stderr, "Continuing writing in file %s\n", filename);
1173 pdh = wtap_dump_open(filename, out_file_type, out_frame_type,
1174 snaplen ? MIN(snaplen, wtap_snapshot_length(wth)) : wtap_snapshot_length(wth),
1175 FALSE /* compressed */, &err);
1178 g_fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1179 wtap_strerror(err));
1185 if (split_packet_count > 0) {
1187 /* time for the next file? */
1188 if (written_count > 0 &&
1189 written_count % split_packet_count == 0) {
1190 if (!wtap_dump_close(pdh, &err)) {
1191 g_fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
1192 wtap_strerror(err));
1197 filename = fileset_get_filename_by_pattern(block_cnt++, &phdr->ts, fprefix, fsuffix);
1201 g_fprintf(stderr, "Continuing writing in file %s\n", filename);
1204 pdh = wtap_dump_open(filename, out_file_type, out_frame_type,
1205 snaplen ? MIN(snaplen, wtap_snapshot_length(wth)) : wtap_snapshot_length(wth),
1206 FALSE /* compressed */, &err);
1208 g_fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1209 wtap_strerror(err));
1215 if (check_startstop)
1216 ts_okay = check_timestamp(wth);
1218 if ( ts_okay && ((!selected(count) && !keep_em) || (selected(count) && keep_em)) ) {
1220 if (verbose && !dup_detect && !dup_detect_by_time)
1221 g_printf("Packet: %u\n", count);
1223 /* We simply write it, perhaps after truncating it; we could do other
1224 things, like modify it. */
1226 phdr = wtap_phdr(wth);
1228 if (snaplen != 0 && phdr->caplen > snaplen) {
1230 snap_phdr.caplen = snaplen;
1236 if (((signed int) phdr->caplen + choplen) > 0)
1237 snap_phdr.caplen += choplen;
1239 snap_phdr.caplen = 0;
1241 } else if (choplen > 0) {
1243 if (phdr->caplen > (unsigned int) choplen) {
1244 snap_phdr.caplen -= choplen;
1247 snap_phdr.caplen = 0;
1252 * Do we adjust timestamps to insure strict chronologically order?
1255 if (do_strict_time_adjustment) {
1256 if (previous_time.secs || previous_time.nsecs) {
1257 if (!strict_time_adj.is_negative) {
1261 current.secs = phdr->ts.secs;
1262 current.nsecs = phdr->ts.nsecs;
1264 nstime_delta(&delta, ¤t, &previous_time);
1266 if (delta.secs < 0 || delta.nsecs < 0)
1269 * A negative delta indicates that the current packet
1270 * has an absolute timestamp less than the previous packet
1271 * that it is being compared to. This is NOT a normal
1272 * situation since trace files usually have packets in
1273 * chronological order (oldest to newest).
1275 /* g_printf("++out of order, need to adjust this packet!\n"); */
1277 snap_phdr.ts.secs = previous_time.secs + strict_time_adj.tv.tv_sec;
1278 snap_phdr.ts.nsecs = previous_time.nsecs;
1279 if (snap_phdr.ts.nsecs + strict_time_adj.tv.tv_usec * 1000 > ONE_MILLION * 1000) {
1281 snap_phdr.ts.secs++;
1282 snap_phdr.ts.nsecs += (strict_time_adj.tv.tv_usec - ONE_MILLION) * 1000;
1284 snap_phdr.ts.nsecs += strict_time_adj.tv.tv_usec * 1000;
1290 * A negative strict time adjustment is requested.
1291 * Unconditionally set each timestamp to previous
1292 * packet's timestamp plus delta.
1295 snap_phdr.ts.secs = previous_time.secs + strict_time_adj.tv.tv_sec;
1296 snap_phdr.ts.nsecs = previous_time.nsecs;
1297 if (snap_phdr.ts.nsecs + strict_time_adj.tv.tv_usec * 1000 > ONE_MILLION * 1000) {
1299 snap_phdr.ts.secs++;
1300 snap_phdr.ts.nsecs += (strict_time_adj.tv.tv_usec - ONE_MILLION) * 1000;
1302 snap_phdr.ts.nsecs += strict_time_adj.tv.tv_usec * 1000;
1307 previous_time.secs = phdr->ts.secs;
1308 previous_time.nsecs = phdr->ts.nsecs;
1311 /* assume that if the frame's tv_sec is 0, then
1312 * the timestamp isn't supported */
1313 if (phdr->ts.secs > 0 && time_adj.tv.tv_sec != 0) {
1315 if (time_adj.is_negative)
1316 snap_phdr.ts.secs -= time_adj.tv.tv_sec;
1318 snap_phdr.ts.secs += time_adj.tv.tv_sec;
1322 /* assume that if the frame's tv_sec is 0, then
1323 * the timestamp isn't supported */
1324 if (phdr->ts.secs > 0 && time_adj.tv.tv_usec != 0) {
1326 if (time_adj.is_negative) { /* subtract */
1327 if (snap_phdr.ts.nsecs/1000 < time_adj.tv.tv_usec) { /* borrow */
1328 snap_phdr.ts.secs--;
1329 snap_phdr.ts.nsecs += ONE_MILLION * 1000;
1331 snap_phdr.ts.nsecs -= time_adj.tv.tv_usec * 1000;
1333 if (snap_phdr.ts.nsecs + time_adj.tv.tv_usec * 1000 > ONE_MILLION * 1000) {
1335 snap_phdr.ts.secs++;
1336 snap_phdr.ts.nsecs += (time_adj.tv.tv_usec - ONE_MILLION) * 1000;
1338 snap_phdr.ts.nsecs += time_adj.tv.tv_usec * 1000;
1344 /* suppress duplicates by packet window */
1346 if (is_duplicate(buf, phdr->caplen)) {
1348 g_fprintf(stdout, "Skipped: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1349 for (i = 0; i < 16; i++) {
1350 g_fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1352 g_fprintf(stdout, "\n");
1359 g_fprintf(stdout, "Packet: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1360 for (i = 0; i < 16; i++) {
1361 g_fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1363 g_fprintf(stdout, "\n");
1368 /* suppress duplicates by time window */
1369 if (dup_detect_by_time) {
1372 current.secs = phdr->ts.secs;
1373 current.nsecs = phdr->ts.nsecs;
1375 if (is_duplicate_rel_time(buf, phdr->caplen, ¤t)) {
1377 g_fprintf(stdout, "Skipped: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1378 for (i = 0; i < 16; i++) {
1379 g_fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1381 g_fprintf(stdout, "\n");
1388 g_fprintf(stdout, "Packet: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1389 for (i = 0; i < 16; i++) {
1390 g_fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1392 g_fprintf(stdout, "\n");
1397 /* Random error mutation */
1398 if (err_prob > 0.0) {
1399 int real_data_start = 0;
1400 /* Protect non-protocol data */
1401 if (wtap_file_type(wth) == WTAP_FILE_CATAPULT_DCT2000) {
1402 real_data_start = find_dct2000_real_data(buf);
1404 for (i = real_data_start; i < (int) phdr->caplen; i++) {
1405 if (rand() <= err_prob * RAND_MAX) {
1406 err_type = rand() / (RAND_MAX / ERR_WT_TOTAL + 1);
1408 if (err_type < ERR_WT_BIT) {
1409 buf[i] ^= 1 << (rand() / (RAND_MAX / 8 + 1));
1410 err_type = ERR_WT_TOTAL;
1412 err_type -= ERR_WT_BYTE;
1415 if (err_type < ERR_WT_BYTE) {
1416 buf[i] = rand() / (RAND_MAX / 255 + 1);
1417 err_type = ERR_WT_TOTAL;
1419 err_type -= ERR_WT_BYTE;
1422 if (err_type < ERR_WT_ALNUM) {
1423 buf[i] = ALNUM_CHARS[rand() / (RAND_MAX / ALNUM_LEN + 1)];
1424 err_type = ERR_WT_TOTAL;
1426 err_type -= ERR_WT_ALNUM;
1429 if (err_type < ERR_WT_FMT) {
1430 if ((unsigned int)i < phdr->caplen - 2)
1431 g_strlcpy((char*) &buf[i], "%s", 2);
1432 err_type = ERR_WT_TOTAL;
1434 err_type -= ERR_WT_FMT;
1437 if (err_type < ERR_WT_AA) {
1438 for (j = i; j < (int) phdr->caplen; j++) {
1447 if (!wtap_dump(pdh, phdr, wtap_pseudoheader(wth), buf, &err)) {
1450 case WTAP_ERR_UNSUPPORTED_ENCAP:
1452 * This is a problem with the particular frame we're writing;
1453 * note that, and give the frame number.
1455 g_fprintf(stderr, "editcap: Frame %u of \"%s\" has a network type that can't be saved in a file with that format\n.",
1456 read_count, argv[optind]);
1460 g_fprintf(stderr, "editcap: Error writing to %s: %s\n",
1461 filename, wtap_strerror(err));
1475 /* Print a message noting that the read failed somewhere along the line. */
1477 "editcap: An error occurred while reading \"%s\": %s.\n",
1478 argv[optind], wtap_strerror(err));
1481 case WTAP_ERR_UNSUPPORTED:
1482 case WTAP_ERR_UNSUPPORTED_ENCAP:
1483 case WTAP_ERR_BAD_FILE:
1484 g_fprintf(stderr, "(%s)\n", err_info);
1491 /* No valid packages found, open the outfile so we can write an empty header */
1493 filename = g_strdup(argv[optind+1]);
1495 pdh = wtap_dump_open(filename, out_file_type, out_frame_type,
1496 snaplen ? MIN(snaplen, wtap_snapshot_length(wth)): wtap_snapshot_length(wth),
1497 FALSE /* compressed */, &err);
1499 g_fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1500 wtap_strerror(err));
1505 if (!wtap_dump_close(pdh, &err)) {
1507 g_fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
1508 wtap_strerror(err));
1516 g_fprintf(stdout, "%u packet%s seen, %u packet%s skipped with duplicate window of %u packets.\n",
1517 count - 1, plurality(count - 1, "", "s"),
1518 duplicate_count, plurality(duplicate_count, "", "s"), dup_window);
1519 } else if (dup_detect_by_time) {
1520 g_fprintf(stdout, "%u packet%s seen, %u packet%s skipped with duplicate time window equal to or less than %ld.%09ld seconds.\n",
1521 count - 1, plurality(count - 1, "", "s"),
1522 duplicate_count, plurality(duplicate_count, "", "s"),
1523 (long)relative_time_window.secs, (long int)relative_time_window.nsecs);
1529 /* Skip meta-information read from file to return offset of real
1531 static int find_dct2000_real_data(guint8 *buf)
1535 for (n=0; buf[n] != '\0'; n++); /* Context name */
1537 n++; /* Context port number */
1538 for (; buf[n] != '\0'; n++); /* Timestamp */
1540 for (; buf[n] != '\0'; n++); /* Protocol name */
1542 for (; buf[n] != '\0'; n++); /* Variant number (as string) */
1544 for (; buf[n] != '\0'; n++); /* Outhdr (as string) */
1546 n += 2; /* Direction & encap */