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.
21 * Just make sure we include the prototype for strptime as well
22 * (needed for glibc 2.2) but make sure we do this only if not
39 #ifdef HAVE_SYS_TIME_H
48 #include "wsutil/wsgetopt.h"
54 #include <process.h> /* getpid */
55 #ifdef HAVE_WINSOCK2_H
60 #ifdef NEED_STRPTIME_H
61 # include "wsutil/strptime.h"
64 #include "epan/crypt/crypt-md5.h"
65 #include "epan/plugins.h"
66 #include "epan/report_err.h"
67 #include "epan/filesystem.h"
68 #include <wsutil/privileges.h>
69 #include "epan/nstime.h"
71 #include "svnversion.h"
74 * Some globals so we can pass things to various routines
86 * Duplicate frame detection
88 typedef struct _fd_hash_t {
89 md5_byte_t digest[16];
94 #define DEFAULT_DUP_DEPTH 5 /* Used with -d */
95 #define MAX_DUP_DEPTH 1000000 /* the maximum window (and actual size of fd_hash[]) for de-duplication */
97 fd_hash_t fd_hash[MAX_DUP_DEPTH];
98 int dup_window = DEFAULT_DUP_DEPTH;
99 int cur_dup_entry = 0;
101 #define ONE_MILLION 1000000
102 #define ONE_BILLION 1000000000
104 /* Weights of different errors we can introduce */
105 /* We should probably make these command-line arguments */
106 /* XXX - Should we add a bit-level error? */
107 #define ERR_WT_BIT 5 /* Flip a random bit */
108 #define ERR_WT_BYTE 5 /* Substitute a random byte */
109 #define ERR_WT_ALNUM 5 /* Substitute a random character in [A-Za-z0-9] */
110 #define ERR_WT_FMT 2 /* Substitute "%s" */
111 #define ERR_WT_AA 1 /* Fill the remainder of the buffer with 0xAA */
112 #define ERR_WT_TOTAL (ERR_WT_BIT + ERR_WT_BYTE + ERR_WT_ALNUM + ERR_WT_FMT + ERR_WT_AA)
114 #define ALNUM_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
115 #define ALNUM_LEN (sizeof(ALNUM_CHARS) - 1)
118 struct time_adjustment {
123 #define MAX_SELECTIONS 512
124 static struct select_item selectfrm[MAX_SELECTIONS];
125 static int max_selected = -1;
126 static int keep_em = 0;
127 static int out_file_type = WTAP_FILE_PCAP; /* default to "libpcap" */
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 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 printf("Out of room for packet selections!\n");
242 printf("Add_Selected: %s\n", sel);
244 if ((locn = strchr(sel, '-')) == NULL) { /* No dash, so a single number? */
246 printf("Not inclusive ...");
248 selectfrm[max_selected].inclusive = 0;
249 selectfrm[max_selected].first = atoi(sel);
251 printf(" %i\n", selectfrm[max_selected].first);
256 printf("Inclusive ...");
259 selectfrm[max_selected].inclusive = 1;
260 selectfrm[max_selected].first = atoi(sel);
261 selectfrm[max_selected].second = atoi(next);
263 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 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
334 if (val < 0) { /* implies '--' since we caught '-' above */
335 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 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 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
409 if (val < 0) { /* implies '--' since we caught '-' above */
410 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 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 fprintf(stderr, "1: editcap: \"%s\" isn't a valid rel time value\n",
479 if (val < 0) { /* implies '--' since we caught '-' above */
480 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 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 fprintf(output, "Edit and/or translate the format of capture files.\n");
670 fprintf(output, "See http://www.wireshark.org for more information.\n");
671 fprintf(output, "\n");
672 fprintf(output, "Usage: editcap [options] ... <infile> <outfile> [ <packet#>[-<packet#>] ... ]\n");
673 fprintf(output, "\n");
674 fprintf(output, "<infile> and <outfile> must both be present.\n");
675 fprintf(output, "A single packet or a range of packets can be selected.\n");
676 fprintf(output, "\n");
677 fprintf(output, "Packet selection:\n");
678 fprintf(output, " -r keep the selected packets; default is to delete them.\n");
679 fprintf(output, " -A <start time> only output packets whose timestamp is after (or equal\n");
680 fprintf(output, " to) the given time (format as YYYY-MM-DD hh:mm:ss).\n");
681 fprintf(output, " -B <stop time> only output packets whose timestamp is before the\n");
682 fprintf(output, " given time (format as YYYY-MM-DD hh:mm:ss).\n");
683 fprintf(output, "\n");
684 fprintf(output, "Duplicate packet removal:\n");
685 fprintf(output, " -d remove packet if duplicate (window == %d).\n", DEFAULT_DUP_DEPTH);
686 fprintf(output, " -D <dup window> remove packet if duplicate; configurable <dup window>\n");
687 fprintf(output, " Valid <dup window> values are 0 to %d.\n", MAX_DUP_DEPTH);
688 fprintf(output, " NOTE: A <dup window> of 0 with -v (verbose option) is\n");
689 fprintf(output, " useful to print MD5 hashes.\n");
690 fprintf(output, " -w <dup time window> remove packet if duplicate packet is found EQUAL TO OR\n");
691 fprintf(output, " LESS THAN <dup time window> prior to current packet.\n");
692 fprintf(output, " A <dup time window> is specified in relative seconds\n");
693 fprintf(output, " (e.g. 0.000001).\n");
694 fprintf(output, "\n");
695 fprintf(output, " NOTE: The use of the 'Duplicate packet removal' options with\n");
696 fprintf(output, " other editcap options except -v may not always work as expected.\n");
697 fprintf(output, " Specifically the -r, -t or -S options will very likely NOT have the\n");
698 fprintf(output, " desired effect if combined with the -d, -D or -w.\n");
699 fprintf(output, "\n");
700 fprintf(output, "Packet manipulation:\n");
701 fprintf(output, " -s <snaplen> truncate each packet to max. <snaplen> bytes of data.\n");
702 fprintf(output, " -C <choplen> chop each packet at the end by <choplen> bytes.\n");
703 fprintf(output, " -t <time adjustment> adjust the timestamp of each packet;\n");
704 fprintf(output, " <time adjustment> is in relative seconds (e.g. -0.5).\n");
705 fprintf(output, " -S <strict adjustment> adjust timestamp of packets if necessary to insure\n");
706 fprintf(output, " strict chronological increasing order. The <strict\n");
707 fprintf(output, " adjustment> is specified in relative seconds with\n");
708 fprintf(output, " values of 0 or 0.000001 being the most reasonable.\n");
709 fprintf(output, " A negative adjustment value will modify timestamps so\n");
710 fprintf(output, " that each packet's delta time is the absolute value\n");
711 fprintf(output, " of the adjustment specified. A value of -0 will set\n");
712 fprintf(output, " all packets to the timestamp of the first packet.\n");
713 fprintf(output, " -E <error probability> set the probability (between 0.0 and 1.0 incl.)\n");
714 fprintf(output, " that a particular packet byte will be randomly changed.\n");
715 fprintf(output, "\n");
716 fprintf(output, "Output File(s):\n");
717 fprintf(output, " -c <packets per file> split the packet output to different files\n");
718 fprintf(output, " based on uniform packet counts\n");
719 fprintf(output, " with a maximum of <packets per file> each.\n");
720 fprintf(output, " -i <seconds per file> split the packet output to different files\n");
721 fprintf(output, " based on uniform time intervals\n");
722 fprintf(output, " with a maximum of <seconds per file> each.\n");
723 fprintf(output, " -F <capture type> set the output file type; default is libpcap.\n");
724 fprintf(output, " an empty \"-F\" option will list the file types.\n");
725 fprintf(output, " -T <encap type> set the output file encapsulation type;\n");
726 fprintf(output, " default is the same as the input file.\n");
727 fprintf(output, " an empty \"-T\" option will list the encapsulation types.\n");
728 fprintf(output, "\n");
729 fprintf(output, "Miscellaneous:\n");
730 fprintf(output, " -h display this help and exit.\n");
731 fprintf(output, " -v verbose output.\n");
732 fprintf(output, " If -v is used with any of the 'Duplicate Packet\n");
733 fprintf(output, " Removal' options (-d, -D or -w) then Packet lengths\n");
734 fprintf(output, " and MD5 hashes are printed to standard-out.\n");
735 fprintf(output, "\n");
739 const char *sstr; /* The short string */
740 const char *lstr; /* The long string */
744 string_compare(gconstpointer a, gconstpointer b)
746 return strcmp(((struct string_elem *)a)->sstr,
747 ((struct string_elem *)b)->sstr);
751 string_elem_print(gpointer data, gpointer not_used _U_)
753 fprintf(stderr, " %s - %s\n",
754 ((struct string_elem *)data)->sstr,
755 ((struct string_elem *)data)->lstr);
759 list_capture_types(void) {
761 struct string_elem *captypes;
764 captypes = g_malloc(sizeof(struct string_elem) * WTAP_NUM_FILE_TYPES);
765 fprintf(stderr, "editcap: The available capture file types for the \"-F\" flag are:\n");
766 for (i = 0; i < WTAP_NUM_FILE_TYPES; i++) {
767 if (wtap_dump_can_open(i)) {
768 captypes[i].sstr = wtap_file_type_short_string(i);
769 captypes[i].lstr = wtap_file_type_string(i);
770 list = g_slist_insert_sorted(list, &captypes[i], string_compare);
773 g_slist_foreach(list, string_elem_print, NULL);
779 list_encap_types(void) {
781 struct string_elem *encaps;
784 encaps = g_malloc(sizeof(struct string_elem) * WTAP_NUM_ENCAP_TYPES);
785 fprintf(stderr, "editcap: The available encapsulation types for the \"-T\" flag are:\n");
786 for (i = 0; i < WTAP_NUM_ENCAP_TYPES; i++) {
787 encaps[i].sstr = wtap_encap_short_string(i);
788 if (encaps[i].sstr != NULL) {
789 encaps[i].lstr = wtap_encap_string(i);
790 list = g_slist_insert_sorted(list, &encaps[i], string_compare);
793 g_slist_foreach(list, string_elem_print, NULL);
800 * Don't report failures to load plugins because most (non-wiretap) plugins
801 * *should* fail to load (because we're not linked against libwireshark and
802 * dissector plugins need libwireshark).
805 failure_message(const char *msg_format _U_, va_list ap _U_)
812 main(int argc, char *argv[])
825 unsigned int snaplen = 0; /* No limit */
826 unsigned int choplen = 0; /* No chop */
827 wtap_dumper *pdh = NULL;
829 unsigned duplicate_count = 0;
831 struct wtap_pkthdr snap_phdr;
832 const struct wtap_pkthdr *phdr;
835 int split_packet_count = 0;
836 int written_count = 0;
837 char *filename = NULL;
839 int secs_per_block = 0;
841 nstime_t block_start;
842 gchar *fprefix = NULL;
843 gchar *fsuffix = NULL;
846 char* init_progfile_dir_error;
850 /* Convert our arg list to UTF-8. */
851 wc_argv = CommandLineToArgvW(GetCommandLineW(), &wc_argc);
852 if (wc_argv && wc_argc == argc) {
853 for (i = 0; i < argc; i++) {
854 argv[i] = g_utf16_to_utf8(wc_argv[i], -1, NULL, NULL, NULL);
856 } /* XXX else bail because something is horribly, horribly wrong? */
860 * Get credential information for later use.
862 init_process_policies();
865 /* Register wiretap plugins */
866 if ((init_progfile_dir_error = init_progfile_dir(argv[0], main))) {
867 g_warning("capinfos: init_progfile_dir(): %s", init_progfile_dir_error);
868 g_free(init_progfile_dir_error);
870 init_report_err(failure_message,NULL,NULL,NULL);
875 /* Process the options */
876 while ((opt = getopt(argc, argv, "A:B:c:C:dD:E:F:hrs:i:t:S:T:vw:")) !=-1) {
881 err_prob = strtod(optarg, &p);
882 if (p == optarg || err_prob < 0.0 || err_prob > 1.0) {
883 fprintf(stderr, "editcap: probability \"%s\" must be between 0.0 and 1.0\n",
887 srand( (unsigned int) (time(NULL) + getpid()) );
891 out_file_type = wtap_short_string_to_file_type(optarg);
892 if (out_file_type < 0) {
893 fprintf(stderr, "editcap: \"%s\" isn't a valid capture file type\n\n",
895 list_capture_types();
901 split_packet_count = strtol(optarg, &p, 10);
902 if (p == optarg || *p != '\0') {
903 fprintf(stderr, "editcap: \"%s\" isn't a valid packet count\n",
907 if (split_packet_count <= 0) {
908 fprintf(stderr, "editcap: \"%d\" packet count must be larger than zero\n",
915 choplen = strtol(optarg, &p, 10);
916 if (p == optarg || *p != '\0') {
917 fprintf(stderr, "editcap: \"%s\" isn't a valid chop length\n",
925 dup_detect_by_time = FALSE;
926 dup_window = DEFAULT_DUP_DEPTH;
931 dup_detect_by_time = FALSE;
932 dup_window = strtol(optarg, &p, 10);
933 if (p == optarg || *p != '\0') {
934 fprintf(stderr, "editcap: \"%s\" isn't a valid duplicate window value\n",
938 if (dup_window < 0 || dup_window > MAX_DUP_DEPTH) {
939 fprintf(stderr, "editcap: \"%d\" duplicate window value must be between 0 and %d inclusive.\n",
940 dup_window, MAX_DUP_DEPTH);
947 dup_detect_by_time = TRUE;
948 dup_window = MAX_DUP_DEPTH;
949 set_rel_time(optarg);
952 case '?': /* Bad options if GNU getopt */
955 list_capture_types();
972 keep_em = !keep_em; /* Just invert */
976 snaplen = strtol(optarg, &p, 10);
977 if (p == optarg || *p != '\0') {
978 fprintf(stderr, "editcap: \"%s\" isn't a valid snapshot length\n",
985 set_time_adjustment(optarg);
989 set_strict_time_adj(optarg);
990 do_strict_time_adjustment = TRUE;
994 out_frame_type = wtap_short_string_to_encap(optarg);
995 if (out_frame_type < 0) {
996 fprintf(stderr, "editcap: \"%s\" isn't a valid encapsulation type\n\n",
1004 verbose = !verbose; /* Just invert */
1007 case 'i': /* break capture file based on time interval */
1008 secs_per_block = atoi(optarg);
1009 if(secs_per_block <= 0) {
1010 fprintf(stderr, "editcap: \"%s\" isn't a valid time interval\n\n", optarg);
1019 memset(&starttm,0,sizeof(struct tm));
1021 if(!strptime(optarg,"%Y-%m-%d %T",&starttm)) {
1022 fprintf(stderr, "editcap: \"%s\" isn't a valid time format\n\n", optarg);
1026 check_startstop = TRUE;
1027 starttm.tm_isdst = -1;
1029 starttime = mktime(&starttm);
1037 memset(&stoptm,0,sizeof(struct tm));
1039 if(!strptime(optarg,"%Y-%m-%d %T",&stoptm)) {
1040 fprintf(stderr, "editcap: \"%s\" isn't a valid time format\n\n", optarg);
1043 check_startstop = TRUE;
1044 stoptm.tm_isdst = -1;
1045 stoptime = mktime(&stoptm);
1053 printf("Optind = %i, argc = %i\n", optind, argc);
1056 if ((argc - optind) < 1) {
1063 if (check_startstop && !stoptime) {
1065 /* XXX: will work until 2035 */
1066 memset(&stoptm,0,sizeof(struct tm));
1067 stoptm.tm_year = 135;
1068 stoptm.tm_mday = 31;
1071 stoptime = mktime(&stoptm);
1074 nstime_set_unset(&block_start);
1076 if (starttime > stoptime) {
1077 fprintf(stderr, "editcap: start time is after the stop time\n");
1081 if (split_packet_count > 0 && secs_per_block > 0) {
1082 fprintf(stderr, "editcap: can't split on both packet count and time interval\n");
1083 fprintf(stderr, "editcap: at the same time\n");
1087 wth = wtap_open_offline(argv[optind], &err, &err_info, FALSE);
1090 fprintf(stderr, "editcap: Can't open %s: %s\n", argv[optind],
1091 wtap_strerror(err));
1094 case WTAP_ERR_UNSUPPORTED:
1095 case WTAP_ERR_UNSUPPORTED_ENCAP:
1096 case WTAP_ERR_BAD_RECORD:
1097 fprintf(stderr, "(%s)\n", err_info);
1106 fprintf(stderr, "File %s is a %s capture file.\n", argv[optind],
1107 wtap_file_type_string(wtap_file_type(wth)));
1111 * Now, process the rest, if any ... we only write if there is an extra
1112 * argument or so ...
1115 if ((argc - optind) >= 2) {
1117 if (out_frame_type == -2)
1118 out_frame_type = wtap_file_encap(wth);
1120 for (i = optind + 2; i < argc; i++)
1121 if (add_selection(argv[i]) == FALSE)
1124 if (dup_detect || dup_detect_by_time) {
1125 for (i = 0; i < dup_window; i++) {
1126 memset(&fd_hash[i].digest, 0, 16);
1128 nstime_set_unset(&fd_hash[i].time);
1132 while (wtap_read(wth, &err, &err_info, &data_offset)) {
1133 phdr = wtap_phdr(wth);
1135 if (nstime_is_unset(&block_start)) { /* should only be the first packet */
1136 block_start.secs = phdr->ts.secs;
1137 block_start.nsecs = phdr->ts.nsecs;
1139 if (split_packet_count > 0 || secs_per_block > 0) {
1140 if (!fileset_extract_prefix_suffix(argv[optind+1], &fprefix, &fsuffix))
1143 filename = fileset_get_filename_by_pattern(block_cnt++, &phdr->ts, fprefix, fsuffix);
1145 filename = g_strdup(argv[optind+1]);
1147 pdh = wtap_dump_open(filename, out_file_type,
1148 out_frame_type, wtap_snapshot_length(wth),
1149 FALSE /* compressed */, &err);
1151 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1152 wtap_strerror(err));
1159 if (secs_per_block > 0) {
1160 while ((phdr->ts.secs - block_start.secs > secs_per_block) ||
1161 (phdr->ts.secs - block_start.secs == secs_per_block &&
1162 phdr->ts.nsecs >= block_start.nsecs )) { /* time for the next file */
1164 if (!wtap_dump_close(pdh, &err)) {
1165 fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
1166 wtap_strerror(err));
1169 block_start.secs = block_start.secs + secs_per_block; /* reset for next interval */
1171 filename = fileset_get_filename_by_pattern(block_cnt++, &phdr->ts, fprefix, fsuffix);
1175 fprintf(stderr, "Continuing writing in file %s\n", filename);
1178 pdh = wtap_dump_open(filename, out_file_type,
1179 out_frame_type, wtap_snapshot_length(wth), FALSE /* compressed */, &err);
1182 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1183 wtap_strerror(err));
1189 if (split_packet_count > 0) {
1191 /* time for the next file? */
1192 if (written_count > 0 &&
1193 written_count % split_packet_count == 0) {
1194 if (!wtap_dump_close(pdh, &err)) {
1195 fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
1196 wtap_strerror(err));
1201 filename = fileset_get_filename_by_pattern(block_cnt++, &phdr->ts, fprefix, fsuffix);
1205 fprintf(stderr, "Continuing writing in file %s\n", filename);
1208 pdh = wtap_dump_open(filename, out_file_type,
1209 out_frame_type, wtap_snapshot_length(wth), FALSE /* compressed */, &err);
1211 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1212 wtap_strerror(err));
1218 check_ts = check_timestamp(wth);
1220 if ( ((check_startstop && check_ts) || (!check_startstop && !check_ts)) && ((!selected(count) && !keep_em) ||
1221 (selected(count) && keep_em)) ) {
1223 if (verbose && !dup_detect && !dup_detect_by_time)
1224 printf("Packet: %u\n", count);
1226 /* We simply write it, perhaps after truncating it; we could do other
1227 things, like modify it. */
1229 phdr = wtap_phdr(wth);
1231 if (choplen != 0 && phdr->caplen > choplen) {
1233 snap_phdr.caplen -= choplen;
1237 if (snaplen != 0 && phdr->caplen > snaplen) {
1239 snap_phdr.caplen = snaplen;
1244 * Do we adjust timestamps to insure strict chronologically order?
1247 if (do_strict_time_adjustment) {
1248 if (previous_time.secs || previous_time.nsecs) {
1249 if (!strict_time_adj.is_negative) {
1253 current.secs = phdr->ts.secs;
1254 current.nsecs = phdr->ts.nsecs;
1256 nstime_delta(&delta, ¤t, &previous_time);
1258 if (delta.secs < 0 || delta.nsecs < 0)
1261 * A negative delta indicates that the current packet
1262 * has an absolute timestamp less than the previous packet
1263 * that it is being compared to. This is NOT a normal
1264 * situation since trace files usually have packets in
1265 * chronological order (oldest to newest).
1267 /* printf("++out of order, need to adjust this packet!\n"); */
1269 snap_phdr.ts.secs = previous_time.secs + strict_time_adj.tv.tv_sec;
1270 snap_phdr.ts.nsecs = previous_time.nsecs;
1271 if (snap_phdr.ts.nsecs + strict_time_adj.tv.tv_usec * 1000 > ONE_MILLION * 1000) {
1273 snap_phdr.ts.secs++;
1274 snap_phdr.ts.nsecs += (strict_time_adj.tv.tv_usec - ONE_MILLION) * 1000;
1276 snap_phdr.ts.nsecs += strict_time_adj.tv.tv_usec * 1000;
1282 * A negative strict time adjustment is requested.
1283 * Unconditionally set each timestamp to previous
1284 * packet's timestamp plus delta.
1287 snap_phdr.ts.secs = previous_time.secs + strict_time_adj.tv.tv_sec;
1288 snap_phdr.ts.nsecs = previous_time.nsecs;
1289 if (snap_phdr.ts.nsecs + strict_time_adj.tv.tv_usec * 1000 > ONE_MILLION * 1000) {
1291 snap_phdr.ts.secs++;
1292 snap_phdr.ts.nsecs += (strict_time_adj.tv.tv_usec - ONE_MILLION) * 1000;
1294 snap_phdr.ts.nsecs += strict_time_adj.tv.tv_usec * 1000;
1299 previous_time.secs = phdr->ts.secs;
1300 previous_time.nsecs = phdr->ts.nsecs;
1303 /* assume that if the frame's tv_sec is 0, then
1304 * the timestamp isn't supported */
1305 if (phdr->ts.secs > 0 && time_adj.tv.tv_sec != 0) {
1307 if (time_adj.is_negative)
1308 snap_phdr.ts.secs -= time_adj.tv.tv_sec;
1310 snap_phdr.ts.secs += time_adj.tv.tv_sec;
1314 /* assume that if the frame's tv_sec is 0, then
1315 * the timestamp isn't supported */
1316 if (phdr->ts.secs > 0 && time_adj.tv.tv_usec != 0) {
1318 if (time_adj.is_negative) { /* subtract */
1319 if (snap_phdr.ts.nsecs/1000 < time_adj.tv.tv_usec) { /* borrow */
1320 snap_phdr.ts.secs--;
1321 snap_phdr.ts.nsecs += ONE_MILLION * 1000;
1323 snap_phdr.ts.nsecs -= time_adj.tv.tv_usec * 1000;
1325 if (snap_phdr.ts.nsecs + time_adj.tv.tv_usec * 1000 > ONE_MILLION * 1000) {
1327 snap_phdr.ts.secs++;
1328 snap_phdr.ts.nsecs += (time_adj.tv.tv_usec - ONE_MILLION) * 1000;
1330 snap_phdr.ts.nsecs += time_adj.tv.tv_usec * 1000;
1336 /* suppress duplicates by packet window */
1338 buf = wtap_buf_ptr(wth);
1339 if (is_duplicate(buf, phdr->caplen)) {
1341 fprintf(stdout, "Skipped: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1342 for (i = 0; i < 16; i++) {
1343 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1345 fprintf(stdout, "\n");
1352 fprintf(stdout, "Packet: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1353 for (i = 0; i < 16; i++) {
1354 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1356 fprintf(stdout, "\n");
1361 /* suppress duplicates by time window */
1362 if (dup_detect_by_time) {
1365 current.secs = phdr->ts.secs;
1366 current.nsecs = phdr->ts.nsecs;
1368 buf = wtap_buf_ptr(wth);
1370 if (is_duplicate_rel_time(buf, phdr->caplen, ¤t)) {
1372 fprintf(stdout, "Skipped: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1373 for (i = 0; i < 16; i++) {
1374 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1376 fprintf(stdout, "\n");
1383 fprintf(stdout, "Packet: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1384 for (i = 0; i < 16; i++) {
1385 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1387 fprintf(stdout, "\n");
1392 /* Random error mutation */
1393 if (err_prob > 0.0) {
1394 int real_data_start = 0;
1395 buf = wtap_buf_ptr(wth);
1396 /* Protect non-protocol data */
1397 if (wtap_file_type(wth) == WTAP_FILE_CATAPULT_DCT2000) {
1398 real_data_start = find_dct2000_real_data(buf);
1400 for (i = real_data_start; i < (int) phdr->caplen; i++) {
1401 if (rand() <= err_prob * RAND_MAX) {
1402 err_type = rand() / (RAND_MAX / ERR_WT_TOTAL + 1);
1404 if (err_type < ERR_WT_BIT) {
1405 buf[i] ^= 1 << (rand() / (RAND_MAX / 8 + 1));
1406 err_type = ERR_WT_TOTAL;
1408 err_type -= ERR_WT_BYTE;
1411 if (err_type < ERR_WT_BYTE) {
1412 buf[i] = rand() / (RAND_MAX / 255 + 1);
1413 err_type = ERR_WT_TOTAL;
1415 err_type -= ERR_WT_BYTE;
1418 if (err_type < ERR_WT_ALNUM) {
1419 buf[i] = ALNUM_CHARS[rand() / (RAND_MAX / ALNUM_LEN + 1)];
1420 err_type = ERR_WT_TOTAL;
1422 err_type -= ERR_WT_ALNUM;
1425 if (err_type < ERR_WT_FMT) {
1426 if ((unsigned int)i < phdr->caplen - 2)
1427 strncpy((char*) &buf[i], "%s", 2);
1428 err_type = ERR_WT_TOTAL;
1430 err_type -= ERR_WT_FMT;
1433 if (err_type < ERR_WT_AA) {
1434 for (j = i; j < (int) phdr->caplen; j++) {
1443 if (!wtap_dump(pdh, phdr, wtap_pseudoheader(wth), wtap_buf_ptr(wth),
1445 fprintf(stderr, "editcap: Error writing to %s: %s\n",
1446 filename, wtap_strerror(err));
1458 /* Print a message noting that the read failed somewhere along the line. */
1460 "editcap: An error occurred while reading \"%s\": %s.\n",
1461 argv[optind], wtap_strerror(err));
1464 case WTAP_ERR_UNSUPPORTED:
1465 case WTAP_ERR_UNSUPPORTED_ENCAP:
1466 case WTAP_ERR_BAD_RECORD:
1467 fprintf(stderr, "(%s)\n", err_info);
1474 /* No valid packages found, open the outfile so we can write an empty header */
1476 filename = g_strdup(argv[optind+1]);
1478 pdh = wtap_dump_open(filename, out_file_type,
1479 out_frame_type, wtap_snapshot_length(wth), FALSE /* compressed */, &err);
1481 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1482 wtap_strerror(err));
1487 if (!wtap_dump_close(pdh, &err)) {
1489 fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
1490 wtap_strerror(err));
1498 fprintf(stdout, "%u packet%s seen, %u packet%s skipped with duplicate window of %u packets.\n",
1499 count - 1, plurality(count - 1, "", "s"),
1500 duplicate_count, plurality(duplicate_count, "", "s"), dup_window);
1501 } else if (dup_detect_by_time) {
1502 fprintf(stdout, "%u packet%s seen, %u packet%s skipped with duplicate time window equal to or less than %ld.%09ld seconds.\n",
1503 count - 1, plurality(count - 1, "", "s"),
1504 duplicate_count, plurality(duplicate_count, "", "s"),
1505 (long)relative_time_window.secs, (long int)relative_time_window.nsecs);
1511 /* Skip meta-information read from file to return offset of real
1513 static int find_dct2000_real_data(guint8 *buf)
1517 for (n=0; buf[n] != '\0'; n++); /* Context name */
1519 n++; /* Context port number */
1520 for (; buf[n] != '\0'; n++); /* Timestamp */
1522 for (; buf[n] != '\0'; n++); /* Protocol name */
1524 for (; buf[n] != '\0'; n++); /* Variant number (as string) */
1526 for (; buf[n] != '\0'; n++); /* Outhdr (as string) */
1528 n += 2; /* Direction & encap */