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.
19 * Just make sure we include the prototype for strptime as well
20 * (needed for glibc 2.2) but make sure we do this only if not
37 #ifdef HAVE_SYS_TIME_H
44 #include "wsutil/wsgetopt.h"
48 #include <wsutil/unicode-utils.h>
49 #include <process.h> /* getpid */
50 #ifdef HAVE_WINSOCK2_H
55 #ifdef NEED_STRPTIME_H
56 # include "wsutil/strptime.h"
59 #include "epan/crypt/md5.h"
60 #include "epan/plugins.h"
61 #include "epan/report_err.h"
62 #include "epan/filesystem.h"
63 #include <wsutil/privileges.h>
64 #include "epan/nstime.h"
66 #include "svnversion.h"
69 * Some globals so we can pass things to various routines
81 * Duplicate frame detection
83 typedef struct _fd_hash_t {
84 md5_byte_t digest[16];
89 #define DEFAULT_DUP_DEPTH 5 /* Used with -d */
90 #define MAX_DUP_DEPTH 1000000 /* the maximum window (and actual size of fd_hash[]) for de-duplication */
92 fd_hash_t fd_hash[MAX_DUP_DEPTH];
93 int dup_window = DEFAULT_DUP_DEPTH;
94 int cur_dup_entry = 0;
96 #define ONE_MILLION 1000000
97 #define ONE_BILLION 1000000000
99 /* Weights of different errors we can introduce */
100 /* We should probably make these command-line arguments */
101 /* XXX - Should we add a bit-level error? */
102 #define ERR_WT_BIT 5 /* Flip a random bit */
103 #define ERR_WT_BYTE 5 /* Substitute a random byte */
104 #define ERR_WT_ALNUM 5 /* Substitute a random character in [A-Za-z0-9] */
105 #define ERR_WT_FMT 2 /* Substitute "%s" */
106 #define ERR_WT_AA 1 /* Fill the remainder of the buffer with 0xAA */
107 #define ERR_WT_TOTAL (ERR_WT_BIT + ERR_WT_BYTE + ERR_WT_ALNUM + ERR_WT_FMT + ERR_WT_AA)
109 #define ALNUM_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
110 #define ALNUM_LEN (sizeof(ALNUM_CHARS) - 1)
113 struct time_adjustment {
118 #define MAX_SELECTIONS 512
119 static struct select_item selectfrm[MAX_SELECTIONS];
120 static int max_selected = -1;
121 static int keep_em = 0;
122 #ifdef PCAP_NG_DEFAULT
123 static int out_file_type = WTAP_FILE_PCAPNG; /* default to pcapng */
125 static int out_file_type = WTAP_FILE_PCAP; /* default to pcap */
127 static int out_frame_type = -2; /* Leave frame type alone */
128 static int verbose = 0; /* Not so verbose */
129 static struct time_adjustment time_adj = {{0, 0}, 0}; /* no adjustment */
130 static nstime_t relative_time_window = {0, 0}; /* de-dup time window */
131 static double err_prob = 0.0;
132 static time_t starttime = 0;
133 static time_t stoptime = 0;
134 static gboolean check_startstop = FALSE;
135 static gboolean dup_detect = FALSE;
136 static gboolean dup_detect_by_time = FALSE;
138 static int do_strict_time_adjustment = FALSE;
139 static struct time_adjustment strict_time_adj = {{0, 0}, 0}; /* strict time adjustment */
140 static nstime_t previous_time = {0, 0}; /* previous time */
142 static int find_dct2000_real_data(guint8 *buf);
145 abs_time_to_str_with_sec_resolution(const struct wtap_nstime *abs_time)
148 gchar *buf = g_malloc(16);
151 /* calling localtime() on MSVC 2005 with huge values causes it to crash */
152 /* XXX - find the exact value that still does work */
153 /* XXX - using _USE_32BIT_TIME_T might be another way to circumvent this problem */
154 if(abs_time->secs > 2000000000) {
158 tmp = localtime(&abs_time->secs);
160 g_snprintf(buf, 16, "%d%02d%02d%02d%02d%02d",
174 fileset_get_filename_by_pattern(guint idx, const struct wtap_nstime *time_val,
175 gchar *fprefix, gchar *fsuffix)
181 timestr = abs_time_to_str_with_sec_resolution(time_val);
182 g_snprintf(filenum, sizeof(filenum), "%05u", idx);
183 abs_str = g_strconcat(fprefix, "_", filenum, "_", timestr, fsuffix, NULL);
190 fileset_extract_prefix_suffix(const char *fname, gchar **fprefix, gchar **fsuffix)
192 char *pfx, *last_pathsep;
195 save_file = g_strdup(fname);
196 if (save_file == NULL) {
197 fprintf(stderr, "editcap: Out of memory\n");
201 last_pathsep = strrchr(save_file, G_DIR_SEPARATOR);
202 pfx = strrchr(save_file,'.');
203 if (pfx != NULL && (last_pathsep == NULL || pfx > last_pathsep)) {
204 /* The pathname has a "." in it, and it's in the last component
205 of the pathname (because there is either only one component,
206 i.e. last_pathsep is null as there are no path separators,
207 or the "." is after the path separator before the last
210 Treat it as a separator between the rest of the file name and
211 the file name suffix, and arrange that the names given to the
212 ring buffer files have the specified suffix, i.e. put the
213 changing part of the name *before* the suffix. */
215 *fprefix = g_strdup(save_file);
216 pfx[0] = '.'; /* restore capfile_name */
217 *fsuffix = g_strdup(pfx);
219 /* Either there's no "." in the pathname, or it's in a directory
220 component, so the last component has no suffix. */
221 *fprefix = g_strdup(save_file);
228 /* Add a selection item, a simple parser for now */
230 add_selection(char *sel)
235 if (++max_selected >= MAX_SELECTIONS) {
236 /* Let the user know we stopped selecting */
237 printf("Out of room for packet selections!\n");
241 printf("Add_Selected: %s\n", sel);
243 if ((locn = strchr(sel, '-')) == NULL) { /* No dash, so a single number? */
245 printf("Not inclusive ...");
247 selectfrm[max_selected].inclusive = 0;
248 selectfrm[max_selected].first = atoi(sel);
250 printf(" %i\n", selectfrm[max_selected].first);
255 printf("Inclusive ...");
258 selectfrm[max_selected].inclusive = 1;
259 selectfrm[max_selected].first = atoi(sel);
260 selectfrm[max_selected].second = atoi(next);
262 printf(" %i, %i\n", selectfrm[max_selected].first, selectfrm[max_selected].second);
269 /* Was the packet selected? */
276 for (i = 0; i<= max_selected; i++) {
278 if (selectfrm[i].inclusive) {
279 if (selectfrm[i].first <= recno && selectfrm[i].second >= recno)
283 if (recno == selectfrm[i].first)
292 /* is the packet in the selected timeframe */
294 check_timestamp(wtap *wth)
296 struct wtap_pkthdr* pkthdr = wtap_phdr(wth);
298 return ( pkthdr->ts.secs >= starttime ) && ( pkthdr->ts.secs < stoptime );
302 set_time_adjustment(char *optarg_str_p)
311 /* skip leading whitespace */
312 while (*optarg_str_p == ' ' || *optarg_str_p == '\t') {
316 /* check for a negative adjustment */
317 if (*optarg_str_p == '-') {
318 time_adj.is_negative = 1;
322 /* collect whole number of seconds, if any */
323 if (*optarg_str_p == '.') { /* only fractional (i.e., .5 is ok) */
327 val = strtol(optarg_str_p, &frac, 10);
328 if (frac == NULL || frac == optarg_str_p || val == LONG_MIN || val == LONG_MAX) {
329 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
333 if (val < 0) { /* implies '--' since we caught '-' above */
334 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
339 time_adj.tv.tv_sec = val;
341 /* now collect the partial seconds, if any */
342 if (*frac != '\0') { /* chars left, so get fractional part */
343 val = strtol(&(frac[1]), &end, 10);
344 /* if more than 6 fractional digits truncate to 6 */
345 if((end - &(frac[1])) > 6) {
346 frac[7] = 't'; /* 't' for truncate */
347 val = strtol(&(frac[1]), &end, 10);
349 if (*frac != '.' || end == NULL || end == frac
350 || val < 0 || val > ONE_MILLION || val == LONG_MIN || val == LONG_MAX) {
351 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
357 return; /* no fractional digits */
360 /* adjust fractional portion from fractional to numerator
361 * e.g., in "1.5" from 5 to 500000 since .5*10^6 = 500000 */
362 if (frac && end) { /* both are valid */
363 frac_digits = end - frac - 1; /* fractional digit count (remember '.') */
364 while(frac_digits < 6) { /* this is frac of 10^6 */
369 time_adj.tv.tv_usec = val;
373 set_strict_time_adj(char *optarg_str_p)
382 /* skip leading whitespace */
383 while (*optarg_str_p == ' ' || *optarg_str_p == '\t') {
388 * check for a negative adjustment
389 * A negative strict adjustment value is a flag
390 * to adjust all frames by the specifed delta time.
392 if (*optarg_str_p == '-') {
393 strict_time_adj.is_negative = 1;
397 /* collect whole number of seconds, if any */
398 if (*optarg_str_p == '.') { /* only fractional (i.e., .5 is ok) */
402 val = strtol(optarg_str_p, &frac, 10);
403 if (frac == NULL || frac == optarg_str_p || val == LONG_MIN || val == LONG_MAX) {
404 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
408 if (val < 0) { /* implies '--' since we caught '-' above */
409 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
414 strict_time_adj.tv.tv_sec = val;
416 /* now collect the partial seconds, if any */
417 if (*frac != '\0') { /* chars left, so get fractional part */
418 val = strtol(&(frac[1]), &end, 10);
419 /* if more than 6 fractional digits truncate to 6 */
420 if((end - &(frac[1])) > 6) {
421 frac[7] = 't'; /* 't' for truncate */
422 val = strtol(&(frac[1]), &end, 10);
424 if (*frac != '.' || end == NULL || end == frac
425 || val < 0 || val > ONE_MILLION || val == LONG_MIN || val == LONG_MAX) {
426 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
432 return; /* no fractional digits */
435 /* adjust fractional portion from fractional to numerator
436 * e.g., in "1.5" from 5 to 500000 since .5*10^6 = 500000 */
437 if (frac && end) { /* both are valid */
438 frac_digits = end - frac - 1; /* fractional digit count (remember '.') */
439 while(frac_digits < 6) { /* this is frac of 10^6 */
444 strict_time_adj.tv.tv_usec = val;
448 set_rel_time(char *optarg_str_p)
457 /* skip leading whitespace */
458 while (*optarg_str_p == ' ' || *optarg_str_p == '\t') {
462 /* ignore negative adjustment */
463 if (*optarg_str_p == '-') {
467 /* collect whole number of seconds, if any */
468 if (*optarg_str_p == '.') { /* only fractional (i.e., .5 is ok) */
472 val = strtol(optarg_str_p, &frac, 10);
473 if (frac == NULL || frac == optarg_str_p || val == LONG_MIN || val == LONG_MAX) {
474 fprintf(stderr, "1: editcap: \"%s\" isn't a valid rel time value\n",
478 if (val < 0) { /* implies '--' since we caught '-' above */
479 fprintf(stderr, "2: editcap: \"%s\" isn't a valid rel time value\n",
484 relative_time_window.secs = val;
486 /* now collect the partial seconds, if any */
487 if (*frac != '\0') { /* chars left, so get fractional part */
488 val = strtol(&(frac[1]), &end, 10);
489 /* if more than 9 fractional digits truncate to 9 */
490 if((end - &(frac[1])) > 9) {
491 frac[10] = 't'; /* 't' for truncate */
492 val = strtol(&(frac[1]), &end, 10);
494 if (*frac != '.' || end == NULL || end == frac
495 || val < 0 || val > ONE_BILLION || val == LONG_MIN || val == LONG_MAX) {
496 fprintf(stderr, "3: editcap: \"%s\" isn't a valid rel time value\n",
502 return; /* no fractional digits */
505 /* adjust fractional portion from fractional to numerator
506 * e.g., in "1.5" from 5 to 500000000 since .5*10^9 = 500000000 */
507 if (frac && end) { /* both are valid */
508 frac_digits = end - frac - 1; /* fractional digit count (remember '.') */
509 while(frac_digits < 9) { /* this is frac of 10^9 */
514 relative_time_window.nsecs = val;
518 is_duplicate(guint8* fd, guint32 len) {
523 if (cur_dup_entry >= dup_window)
526 /* Calculate our digest */
528 md5_append(&ms, fd, len);
529 md5_finish(&ms, fd_hash[cur_dup_entry].digest);
531 fd_hash[cur_dup_entry].len = len;
533 /* Look for duplicates */
534 for (i = 0; i < dup_window; i++) {
535 if (i == cur_dup_entry)
538 if (fd_hash[i].len == fd_hash[cur_dup_entry].len &&
539 memcmp(fd_hash[i].digest, fd_hash[cur_dup_entry].digest, 16) == 0) {
548 is_duplicate_rel_time(guint8* fd, guint32 len, const nstime_t *current) {
553 if (cur_dup_entry >= dup_window)
556 /* Calculate our digest */
558 md5_append(&ms, fd, len);
559 md5_finish(&ms, fd_hash[cur_dup_entry].digest);
561 fd_hash[cur_dup_entry].len = len;
562 fd_hash[cur_dup_entry].time.secs = current->secs;
563 fd_hash[cur_dup_entry].time.nsecs = current->nsecs;
566 * Look for relative time related duplicates.
567 * This is hopefully a reasonably efficient mechanism for
568 * finding duplicates by rel time in the fd_hash[] cache.
569 * We check starting from the most recently added hash
570 * entries and work backwards towards older packets.
571 * This approach allows the dup test to be terminated
572 * when the relative time of a cached entry is found to
573 * be beyond the dup time window.
575 * Of course this assumes that the input trace file is
576 * "well-formed" in the sense that the packet timestamps are
577 * in strict chronologically increasing order (which is NOT
578 * always the case!!).
580 * The fd_hash[] table was deliberatly created large (1,000,000).
581 * Looking for time related duplicates in large trace files with
582 * non-fractional dup time window values can potentially take
583 * a long time to complete.
586 for (i = cur_dup_entry - 1;; i--) {
594 if (i == cur_dup_entry) {
596 * We've decremented back to where we started.
602 if (nstime_is_unset(&(fd_hash[i].time))) {
604 * We've decremented to an unused fd_hash[] entry.
610 nstime_delta(&delta, current, &fd_hash[i].time);
612 if(delta.secs < 0 || delta.nsecs < 0)
615 * A negative delta implies that the current packet
616 * has an absolute timestamp less than the cached packet
617 * that it is being compared to. This is NOT a normal
618 * situation since trace files usually have packets in
619 * chronological order (oldest to newest).
621 * There are several possible ways to deal with this:
622 * 1. 'continue' dup checking with the next cached frame.
623 * 2. 'break' from looking for a duplicate of the current frame.
624 * 3. Take the absolute value of the delta and see if that
625 * falls within the specifed dup time window.
627 * Currently this code does option 1. But it would pretty
628 * easy to add yet-another-editcap-option to select one of
629 * the other behaviors for dealing with out-of-sequence
635 cmp = nstime_cmp(&delta, &relative_time_window);
639 * The delta time indicates that we are now looking at
640 * cached packets beyond the specified dup time window.
644 } else if (fd_hash[i].len == fd_hash[cur_dup_entry].len &&
645 memcmp(fd_hash[i].digest, fd_hash[cur_dup_entry].digest, 16) == 0) {
654 usage(gboolean is_error)
663 fprintf(output, "Editcap %s"
665 " (" SVNVERSION " from " SVNPATH ")"
668 fprintf(output, "Edit and/or translate the format of capture files.\n");
669 fprintf(output, "See http://www.wireshark.org for more information.\n");
670 fprintf(output, "\n");
671 fprintf(output, "Usage: editcap [options] ... <infile> <outfile> [ <packet#>[-<packet#>] ... ]\n");
672 fprintf(output, "\n");
673 fprintf(output, "<infile> and <outfile> must both be present.\n");
674 fprintf(output, "A single packet or a range of packets can be selected.\n");
675 fprintf(output, "\n");
676 fprintf(output, "Packet selection:\n");
677 fprintf(output, " -r keep the selected packets; default is to delete them.\n");
678 fprintf(output, " -A <start time> only output packets whose timestamp is after (or equal\n");
679 fprintf(output, " to) the given time (format as YYYY-MM-DD hh:mm:ss).\n");
680 fprintf(output, " -B <stop time> only output packets whose timestamp is before the\n");
681 fprintf(output, " given time (format as YYYY-MM-DD hh:mm:ss).\n");
682 fprintf(output, "\n");
683 fprintf(output, "Duplicate packet removal:\n");
684 fprintf(output, " -d remove packet if duplicate (window == %d).\n", DEFAULT_DUP_DEPTH);
685 fprintf(output, " -D <dup window> remove packet if duplicate; configurable <dup window>\n");
686 fprintf(output, " Valid <dup window> values are 0 to %d.\n", MAX_DUP_DEPTH);
687 fprintf(output, " NOTE: A <dup window> of 0 with -v (verbose option) is\n");
688 fprintf(output, " useful to print MD5 hashes.\n");
689 fprintf(output, " -w <dup time window> remove packet if duplicate packet is found EQUAL TO OR\n");
690 fprintf(output, " LESS THAN <dup time window> prior to current packet.\n");
691 fprintf(output, " A <dup time window> is specified in relative seconds\n");
692 fprintf(output, " (e.g. 0.000001).\n");
693 fprintf(output, "\n");
694 fprintf(output, " NOTE: The use of the 'Duplicate packet removal' options with\n");
695 fprintf(output, " other editcap options except -v may not always work as expected.\n");
696 fprintf(output, " Specifically the -r, -t or -S options will very likely NOT have the\n");
697 fprintf(output, " desired effect if combined with the -d, -D or -w.\n");
698 fprintf(output, "\n");
699 fprintf(output, "Packet manipulation:\n");
700 fprintf(output, " -s <snaplen> truncate each packet to max. <snaplen> bytes of data.\n");
701 fprintf(output, " -C <choplen> chop each packet by <choplen> bytes. Positive values\n");
702 fprintf(output, " chop at the packet beginning, negative values at the\n");
703 fprintf(output, " packet end.\n");
704 fprintf(output, " -t <time adjustment> adjust the timestamp of each packet;\n");
705 fprintf(output, " <time adjustment> is in relative seconds (e.g. -0.5).\n");
706 fprintf(output, " -S <strict adjustment> adjust timestamp of packets if necessary to insure\n");
707 fprintf(output, " strict chronological increasing order. The <strict\n");
708 fprintf(output, " adjustment> is specified in relative seconds with\n");
709 fprintf(output, " values of 0 or 0.000001 being the most reasonable.\n");
710 fprintf(output, " A negative adjustment value will modify timestamps so\n");
711 fprintf(output, " that each packet's delta time is the absolute value\n");
712 fprintf(output, " of the adjustment specified. A value of -0 will set\n");
713 fprintf(output, " all packets to the timestamp of the first packet.\n");
714 fprintf(output, " -E <error probability> set the probability (between 0.0 and 1.0 incl.)\n");
715 fprintf(output, " that a particular packet byte will be randomly changed.\n");
716 fprintf(output, "\n");
717 fprintf(output, "Output File(s):\n");
718 fprintf(output, " -c <packets per file> split the packet output to different files\n");
719 fprintf(output, " based on uniform packet counts\n");
720 fprintf(output, " with a maximum of <packets per file> each.\n");
721 fprintf(output, " -i <seconds per file> split the packet output to different files\n");
722 fprintf(output, " based on uniform time intervals\n");
723 fprintf(output, " with a maximum of <seconds per file> each.\n");
724 fprintf(output, " -F <capture type> set the output file type; default is pcapng.\n");
725 fprintf(output, " an empty \"-F\" option will list the file types.\n");
726 fprintf(output, " -T <encap type> set the output file encapsulation type;\n");
727 fprintf(output, " default is the same as the input file.\n");
728 fprintf(output, " an empty \"-T\" option will list the encapsulation types.\n");
729 fprintf(output, "\n");
730 fprintf(output, "Miscellaneous:\n");
731 fprintf(output, " -h display this help and exit.\n");
732 fprintf(output, " -v verbose output.\n");
733 fprintf(output, " If -v is used with any of the 'Duplicate Packet\n");
734 fprintf(output, " Removal' options (-d, -D or -w) then Packet lengths\n");
735 fprintf(output, " and MD5 hashes are printed to standard-out.\n");
736 fprintf(output, "\n");
740 const char *sstr; /* The short string */
741 const char *lstr; /* The long string */
745 string_compare(gconstpointer a, gconstpointer b)
747 return strcmp(((const struct string_elem *)a)->sstr,
748 ((const struct string_elem *)b)->sstr);
752 string_elem_print(gpointer data, gpointer not_used _U_)
754 fprintf(stderr, " %s - %s\n",
755 ((struct string_elem *)data)->sstr,
756 ((struct string_elem *)data)->lstr);
760 list_capture_types(void) {
762 struct string_elem *captypes;
765 captypes = g_malloc(sizeof(struct string_elem) * WTAP_NUM_FILE_TYPES);
766 fprintf(stderr, "editcap: The available capture file types for the \"-F\" flag are:\n");
767 for (i = 0; i < WTAP_NUM_FILE_TYPES; i++) {
768 if (wtap_dump_can_open(i)) {
769 captypes[i].sstr = wtap_file_type_short_string(i);
770 captypes[i].lstr = wtap_file_type_string(i);
771 list = g_slist_insert_sorted(list, &captypes[i], string_compare);
774 g_slist_foreach(list, string_elem_print, NULL);
780 list_encap_types(void) {
782 struct string_elem *encaps;
785 encaps = g_malloc(sizeof(struct string_elem) * WTAP_NUM_ENCAP_TYPES);
786 fprintf(stderr, "editcap: The available encapsulation types for the \"-T\" flag are:\n");
787 for (i = 0; i < WTAP_NUM_ENCAP_TYPES; i++) {
788 encaps[i].sstr = wtap_encap_short_string(i);
789 if (encaps[i].sstr != NULL) {
790 encaps[i].lstr = wtap_encap_string(i);
791 list = g_slist_insert_sorted(list, &encaps[i], string_compare);
794 g_slist_foreach(list, string_elem_print, NULL);
801 * Don't report failures to load plugins because most (non-wiretap) plugins
802 * *should* fail to load (because we're not linked against libwireshark and
803 * dissector plugins need libwireshark).
806 failure_message(const char *msg_format _U_, va_list ap _U_)
813 main(int argc, char *argv[])
821 guint32 snaplen = 0; /* No limit */
822 int choplen = 0; /* No chop */
823 wtap_dumper *pdh = NULL;
824 unsigned int count = 1;
825 unsigned duplicate_count = 0;
827 struct wtap_pkthdr snap_phdr;
828 const struct wtap_pkthdr *phdr;
830 wtapng_section_t *shb_hdr;
831 wtapng_iface_descriptions_t *idb_inf;
833 guint32 read_count = 0;
834 int split_packet_count = 0;
835 int written_count = 0;
836 char *filename = NULL;
837 gboolean ts_okay = TRUE;
838 int secs_per_block = 0;
840 nstime_t block_start;
841 gchar *fprefix = NULL;
842 gchar *fsuffix = NULL;
846 char* init_progfile_dir_error;
850 arg_list_utf_16to8(argc, argv);
854 * Get credential information for later use.
856 init_process_policies();
859 /* Register wiretap plugins */
860 if ((init_progfile_dir_error = init_progfile_dir(argv[0], main))) {
861 g_warning("editcap: init_progfile_dir(): %s", init_progfile_dir_error);
862 g_free(init_progfile_dir_error);
864 init_report_err(failure_message,NULL,NULL,NULL);
869 /* Process the options */
870 while ((opt = getopt(argc, argv, "A:B:c:C:dD:E:F:hrs:i:t:S:T:vw:")) !=-1) {
875 err_prob = strtod(optarg, &p);
876 if (p == optarg || err_prob < 0.0 || err_prob > 1.0) {
877 fprintf(stderr, "editcap: probability \"%s\" must be between 0.0 and 1.0\n",
881 srand( (unsigned int) (time(NULL) + getpid()) );
885 out_file_type = wtap_short_string_to_file_type(optarg);
886 if (out_file_type < 0) {
887 fprintf(stderr, "editcap: \"%s\" isn't a valid capture file type\n\n",
889 list_capture_types();
895 split_packet_count = strtol(optarg, &p, 10);
896 if (p == optarg || *p != '\0') {
897 fprintf(stderr, "editcap: \"%s\" isn't a valid packet count\n",
901 if (split_packet_count <= 0) {
902 fprintf(stderr, "editcap: \"%d\" packet count must be larger than zero\n",
909 choplen = strtol(optarg, &p, 10);
910 if (p == optarg || *p != '\0') {
911 fprintf(stderr, "editcap: \"%s\" isn't a valid chop length\n",
919 dup_detect_by_time = FALSE;
920 dup_window = DEFAULT_DUP_DEPTH;
925 dup_detect_by_time = FALSE;
926 dup_window = strtol(optarg, &p, 10);
927 if (p == optarg || *p != '\0') {
928 fprintf(stderr, "editcap: \"%s\" isn't a valid duplicate window value\n",
932 if (dup_window < 0 || dup_window > MAX_DUP_DEPTH) {
933 fprintf(stderr, "editcap: \"%d\" duplicate window value must be between 0 and %d inclusive.\n",
934 dup_window, MAX_DUP_DEPTH);
941 dup_detect_by_time = TRUE;
942 dup_window = MAX_DUP_DEPTH;
943 set_rel_time(optarg);
946 case '?': /* Bad options if GNU getopt */
949 list_capture_types();
966 keep_em = !keep_em; /* Just invert */
970 snaplen = strtol(optarg, &p, 10);
971 if (p == optarg || *p != '\0') {
972 fprintf(stderr, "editcap: \"%s\" isn't a valid snapshot length\n",
979 set_time_adjustment(optarg);
983 set_strict_time_adj(optarg);
984 do_strict_time_adjustment = TRUE;
988 out_frame_type = wtap_short_string_to_encap(optarg);
989 if (out_frame_type < 0) {
990 fprintf(stderr, "editcap: \"%s\" isn't a valid encapsulation type\n\n",
998 verbose = !verbose; /* Just invert */
1001 case 'i': /* break capture file based on time interval */
1002 secs_per_block = atoi(optarg);
1003 if(secs_per_block <= 0) {
1004 fprintf(stderr, "editcap: \"%s\" isn't a valid time interval\n\n", optarg);
1013 memset(&starttm,0,sizeof(struct tm));
1015 if(!strptime(optarg,"%Y-%m-%d %T",&starttm)) {
1016 fprintf(stderr, "editcap: \"%s\" isn't a valid time format\n\n", optarg);
1020 check_startstop = TRUE;
1021 starttm.tm_isdst = -1;
1023 starttime = mktime(&starttm);
1031 memset(&stoptm,0,sizeof(struct tm));
1033 if(!strptime(optarg,"%Y-%m-%d %T",&stoptm)) {
1034 fprintf(stderr, "editcap: \"%s\" isn't a valid time format\n\n", optarg);
1037 check_startstop = TRUE;
1038 stoptm.tm_isdst = -1;
1039 stoptime = mktime(&stoptm);
1047 printf("Optind = %i, argc = %i\n", optind, argc);
1050 if ((argc - optind) < 1) {
1057 if (check_startstop && !stoptime) {
1059 /* XXX: will work until 2035 */
1060 memset(&stoptm,0,sizeof(struct tm));
1061 stoptm.tm_year = 135;
1062 stoptm.tm_mday = 31;
1065 stoptime = mktime(&stoptm);
1068 nstime_set_unset(&block_start);
1070 if (starttime > stoptime) {
1071 fprintf(stderr, "editcap: start time is after the stop time\n");
1075 if (split_packet_count > 0 && secs_per_block > 0) {
1076 fprintf(stderr, "editcap: can't split on both packet count and time interval\n");
1077 fprintf(stderr, "editcap: at the same time\n");
1081 wth = wtap_open_offline(argv[optind], &err, &err_info, FALSE);
1084 fprintf(stderr, "editcap: Can't open %s: %s\n", argv[optind],
1085 wtap_strerror(err));
1088 case WTAP_ERR_UNSUPPORTED:
1089 case WTAP_ERR_UNSUPPORTED_ENCAP:
1090 case WTAP_ERR_BAD_FILE:
1091 fprintf(stderr, "(%s)\n", err_info);
1100 fprintf(stderr, "File %s is a %s capture file.\n", argv[optind],
1101 wtap_file_type_string(wtap_file_type(wth)));
1104 shb_hdr = wtap_file_get_shb_info(wth);
1105 idb_inf = wtap_file_get_idb_info(wth);
1108 * Now, process the rest, if any ... we only write if there is an extra
1109 * argument or so ...
1112 if ((argc - optind) >= 2) {
1114 if (out_frame_type == -2)
1115 out_frame_type = wtap_file_encap(wth);
1117 for (i = optind + 2; i < argc; i++)
1118 if (add_selection(argv[i]) == FALSE)
1121 if (dup_detect || dup_detect_by_time) {
1122 for (i = 0; i < dup_window; i++) {
1123 memset(&fd_hash[i].digest, 0, 16);
1125 nstime_set_unset(&fd_hash[i].time);
1129 while (wtap_read(wth, &err, &err_info, &data_offset)) {
1132 phdr = wtap_phdr(wth);
1133 buf = wtap_buf_ptr(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 /* If we don't have an application name add Editcap */
1148 if(shb_hdr->shb_user_appl == NULL) {
1149 g_snprintf(appname, sizeof(appname), "Editcap " VERSION);
1150 shb_hdr->shb_user_appl = appname;
1153 pdh = wtap_dump_open_ng(filename, out_file_type, out_frame_type,
1154 snaplen ? MIN(snaplen, wtap_snapshot_length(wth)) : wtap_snapshot_length(wth),
1155 FALSE /* compressed */, shb_hdr, idb_inf, &err);
1158 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1159 wtap_strerror(err));
1166 if (secs_per_block > 0) {
1167 while ((phdr->ts.secs - block_start.secs > secs_per_block) ||
1168 (phdr->ts.secs - block_start.secs == secs_per_block &&
1169 phdr->ts.nsecs >= block_start.nsecs )) { /* time for the next file */
1171 if (!wtap_dump_close(pdh, &err)) {
1172 fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
1173 wtap_strerror(err));
1176 block_start.secs = block_start.secs + secs_per_block; /* reset for next interval */
1178 filename = fileset_get_filename_by_pattern(block_cnt++, &phdr->ts, fprefix, fsuffix);
1182 fprintf(stderr, "Continuing writing in file %s\n", filename);
1185 pdh = wtap_dump_open_ng(filename, out_file_type, out_frame_type,
1186 snaplen ? MIN(snaplen, wtap_snapshot_length(wth)) : wtap_snapshot_length(wth),
1187 FALSE /* compressed */, shb_hdr, idb_inf, &err);
1190 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1191 wtap_strerror(err));
1197 if (split_packet_count > 0) {
1199 /* time for the next file? */
1200 if (written_count > 0 &&
1201 written_count % split_packet_count == 0) {
1202 if (!wtap_dump_close(pdh, &err)) {
1203 fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
1204 wtap_strerror(err));
1209 filename = fileset_get_filename_by_pattern(block_cnt++, &phdr->ts, fprefix, fsuffix);
1213 fprintf(stderr, "Continuing writing in file %s\n", filename);
1216 pdh = wtap_dump_open_ng(filename, out_file_type, out_frame_type,
1217 snaplen ? MIN(snaplen, wtap_snapshot_length(wth)) : wtap_snapshot_length(wth),
1218 FALSE /* compressed */, shb_hdr, idb_inf, &err);
1220 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1221 wtap_strerror(err));
1227 if (check_startstop)
1228 ts_okay = check_timestamp(wth);
1230 if ( ts_okay && ((!selected(count) && !keep_em) || (selected(count) && keep_em)) ) {
1232 if (verbose && !dup_detect && !dup_detect_by_time)
1233 printf("Packet: %u\n", count);
1235 /* We simply write it, perhaps after truncating it; we could do other
1236 things, like modify it. */
1238 phdr = wtap_phdr(wth);
1240 if (snaplen != 0 && phdr->caplen > snaplen) {
1242 snap_phdr.caplen = snaplen;
1248 if (((signed int) phdr->caplen + choplen) > 0)
1249 snap_phdr.caplen += choplen;
1251 snap_phdr.caplen = 0;
1253 } else if (choplen > 0) {
1255 if (phdr->caplen > (unsigned int) choplen) {
1256 snap_phdr.caplen -= choplen;
1259 snap_phdr.caplen = 0;
1264 * Do we adjust timestamps to insure strict chronologically order?
1267 if (do_strict_time_adjustment) {
1268 if (previous_time.secs || previous_time.nsecs) {
1269 if (!strict_time_adj.is_negative) {
1273 current.secs = phdr->ts.secs;
1274 current.nsecs = phdr->ts.nsecs;
1276 nstime_delta(&delta, ¤t, &previous_time);
1278 if (delta.secs < 0 || delta.nsecs < 0)
1281 * A negative delta indicates that the current packet
1282 * has an absolute timestamp less than the previous packet
1283 * that it is being compared to. This is NOT a normal
1284 * situation since trace files usually have packets in
1285 * chronological order (oldest to newest).
1287 /* printf("++out of order, need to adjust this packet!\n"); */
1289 snap_phdr.ts.secs = previous_time.secs + strict_time_adj.tv.tv_sec;
1290 snap_phdr.ts.nsecs = previous_time.nsecs;
1291 if (snap_phdr.ts.nsecs + strict_time_adj.tv.tv_usec * 1000 > ONE_MILLION * 1000) {
1293 snap_phdr.ts.secs++;
1294 snap_phdr.ts.nsecs += (strict_time_adj.tv.tv_usec - ONE_MILLION) * 1000;
1296 snap_phdr.ts.nsecs += strict_time_adj.tv.tv_usec * 1000;
1302 * A negative strict time adjustment is requested.
1303 * Unconditionally set each timestamp to previous
1304 * packet's timestamp plus delta.
1307 snap_phdr.ts.secs = previous_time.secs + strict_time_adj.tv.tv_sec;
1308 snap_phdr.ts.nsecs = previous_time.nsecs;
1309 if (snap_phdr.ts.nsecs + strict_time_adj.tv.tv_usec * 1000 > ONE_MILLION * 1000) {
1311 snap_phdr.ts.secs++;
1312 snap_phdr.ts.nsecs += (strict_time_adj.tv.tv_usec - ONE_MILLION) * 1000;
1314 snap_phdr.ts.nsecs += strict_time_adj.tv.tv_usec * 1000;
1319 previous_time.secs = phdr->ts.secs;
1320 previous_time.nsecs = phdr->ts.nsecs;
1323 /* assume that if the frame's tv_sec is 0, then
1324 * the timestamp isn't supported */
1325 if (phdr->ts.secs > 0 && time_adj.tv.tv_sec != 0) {
1327 if (time_adj.is_negative)
1328 snap_phdr.ts.secs -= time_adj.tv.tv_sec;
1330 snap_phdr.ts.secs += time_adj.tv.tv_sec;
1334 /* assume that if the frame's tv_sec is 0, then
1335 * the timestamp isn't supported */
1336 if (phdr->ts.secs > 0 && time_adj.tv.tv_usec != 0) {
1338 if (time_adj.is_negative) { /* subtract */
1339 if (snap_phdr.ts.nsecs/1000 < time_adj.tv.tv_usec) { /* borrow */
1340 snap_phdr.ts.secs--;
1341 snap_phdr.ts.nsecs += ONE_MILLION * 1000;
1343 snap_phdr.ts.nsecs -= time_adj.tv.tv_usec * 1000;
1345 if (snap_phdr.ts.nsecs + time_adj.tv.tv_usec * 1000 > ONE_MILLION * 1000) {
1347 snap_phdr.ts.secs++;
1348 snap_phdr.ts.nsecs += (time_adj.tv.tv_usec - ONE_MILLION) * 1000;
1350 snap_phdr.ts.nsecs += time_adj.tv.tv_usec * 1000;
1356 /* suppress duplicates by packet window */
1358 if (is_duplicate(buf, phdr->caplen)) {
1360 fprintf(stdout, "Skipped: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1361 for (i = 0; i < 16; i++) {
1362 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1364 fprintf(stdout, "\n");
1371 fprintf(stdout, "Packet: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1372 for (i = 0; i < 16; i++) {
1373 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1375 fprintf(stdout, "\n");
1380 /* suppress duplicates by time window */
1381 if (dup_detect_by_time) {
1384 current.secs = phdr->ts.secs;
1385 current.nsecs = phdr->ts.nsecs;
1387 if (is_duplicate_rel_time(buf, phdr->caplen, ¤t)) {
1389 fprintf(stdout, "Skipped: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1390 for (i = 0; i < 16; i++) {
1391 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1393 fprintf(stdout, "\n");
1400 fprintf(stdout, "Packet: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1401 for (i = 0; i < 16; i++) {
1402 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1404 fprintf(stdout, "\n");
1409 /* Random error mutation */
1410 if (err_prob > 0.0) {
1411 int real_data_start = 0;
1412 /* Protect non-protocol data */
1413 if (wtap_file_type(wth) == WTAP_FILE_CATAPULT_DCT2000) {
1414 real_data_start = find_dct2000_real_data(buf);
1416 for (i = real_data_start; i < (int) phdr->caplen; i++) {
1417 if (rand() <= err_prob * RAND_MAX) {
1418 err_type = rand() / (RAND_MAX / ERR_WT_TOTAL + 1);
1420 if (err_type < ERR_WT_BIT) {
1421 buf[i] ^= 1 << (rand() / (RAND_MAX / 8 + 1));
1422 err_type = ERR_WT_TOTAL;
1424 err_type -= ERR_WT_BYTE;
1427 if (err_type < ERR_WT_BYTE) {
1428 buf[i] = rand() / (RAND_MAX / 255 + 1);
1429 err_type = ERR_WT_TOTAL;
1431 err_type -= ERR_WT_BYTE;
1434 if (err_type < ERR_WT_ALNUM) {
1435 buf[i] = ALNUM_CHARS[rand() / (RAND_MAX / ALNUM_LEN + 1)];
1436 err_type = ERR_WT_TOTAL;
1438 err_type -= ERR_WT_ALNUM;
1441 if (err_type < ERR_WT_FMT) {
1442 if ((unsigned int)i < phdr->caplen - 2)
1443 g_strlcpy((char*) &buf[i], "%s", 2);
1444 err_type = ERR_WT_TOTAL;
1446 err_type -= ERR_WT_FMT;
1449 if (err_type < ERR_WT_AA) {
1450 for (j = i; j < (int) phdr->caplen; j++) {
1459 if (!wtap_dump(pdh, phdr, wtap_pseudoheader(wth), buf, &err)) {
1462 case WTAP_ERR_UNSUPPORTED_ENCAP:
1464 * This is a problem with the particular frame we're writing;
1465 * note that, and give the frame number.
1467 fprintf(stderr, "editcap: Frame %u of \"%s\" has a network type that can't be saved in a file with that format\n.",
1468 read_count, argv[optind]);
1472 fprintf(stderr, "editcap: Error writing to %s: %s\n",
1473 filename, wtap_strerror(err));
1488 /* Print a message noting that the read failed somewhere along the line. */
1490 "editcap: An error occurred while reading \"%s\": %s.\n",
1491 argv[optind], wtap_strerror(err));
1494 case WTAP_ERR_UNSUPPORTED:
1495 case WTAP_ERR_UNSUPPORTED_ENCAP:
1496 case WTAP_ERR_BAD_FILE:
1497 fprintf(stderr, "(%s)\n", err_info);
1504 /* No valid packages found, open the outfile so we can write an empty header */
1506 filename = g_strdup(argv[optind+1]);
1508 pdh = wtap_dump_open_ng(filename, out_file_type, out_frame_type,
1509 snaplen ? MIN(snaplen, wtap_snapshot_length(wth)): wtap_snapshot_length(wth),
1510 FALSE /* compressed */, shb_hdr, idb_inf, &err);
1512 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1513 wtap_strerror(err));
1521 if (!wtap_dump_close(pdh, &err)) {
1523 fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
1524 wtap_strerror(err));
1533 fprintf(stdout, "%u packet%s seen, %u packet%s skipped with duplicate window of %u packets.\n",
1534 count - 1, plurality(count - 1, "", "s"),
1535 duplicate_count, plurality(duplicate_count, "", "s"), dup_window);
1536 } else if (dup_detect_by_time) {
1537 fprintf(stdout, "%u packet%s seen, %u packet%s skipped with duplicate time window equal to or less than %ld.%09ld seconds.\n",
1538 count - 1, plurality(count - 1, "", "s"),
1539 duplicate_count, plurality(duplicate_count, "", "s"),
1540 (long)relative_time_window.secs, (long int)relative_time_window.nsecs);
1546 /* Skip meta-information read from file to return offset of real
1548 static int find_dct2000_real_data(guint8 *buf)
1552 for (n=0; buf[n] != '\0'; n++); /* Context name */
1554 n++; /* Context port number */
1555 for (; buf[n] != '\0'; n++); /* Timestamp */
1557 for (; buf[n] != '\0'; n++); /* Protocol name */
1559 for (; buf[n] != '\0'; n++); /* Variant number (as string) */
1561 for (; buf[n] != '\0'; n++); /* Outhdr (as string) */
1563 n += 2; /* Direction & encap */