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"
52 #include <wsutil/unicode-utils.h>
53 #include <process.h> /* getpid */
54 #ifdef HAVE_WINSOCK2_H
59 #ifdef NEED_STRPTIME_H
60 # include "wsutil/strptime.h"
63 #include "epan/crypt/crypt-md5.h"
64 #include "epan/plugins.h"
65 #include "epan/report_err.h"
66 #include "epan/filesystem.h"
67 #include <wsutil/privileges.h>
68 #include "epan/nstime.h"
70 #include "svnversion.h"
73 * Some globals so we can pass things to various routines
85 * Duplicate frame detection
87 typedef struct _fd_hash_t {
88 md5_byte_t digest[16];
93 #define DEFAULT_DUP_DEPTH 5 /* Used with -d */
94 #define MAX_DUP_DEPTH 1000000 /* the maximum window (and actual size of fd_hash[]) for de-duplication */
96 fd_hash_t fd_hash[MAX_DUP_DEPTH];
97 int dup_window = DEFAULT_DUP_DEPTH;
98 int cur_dup_entry = 0;
100 #define ONE_MILLION 1000000
101 #define ONE_BILLION 1000000000
103 /* Weights of different errors we can introduce */
104 /* We should probably make these command-line arguments */
105 /* XXX - Should we add a bit-level error? */
106 #define ERR_WT_BIT 5 /* Flip a random bit */
107 #define ERR_WT_BYTE 5 /* Substitute a random byte */
108 #define ERR_WT_ALNUM 5 /* Substitute a random character in [A-Za-z0-9] */
109 #define ERR_WT_FMT 2 /* Substitute "%s" */
110 #define ERR_WT_AA 1 /* Fill the remainder of the buffer with 0xAA */
111 #define ERR_WT_TOTAL (ERR_WT_BIT + ERR_WT_BYTE + ERR_WT_ALNUM + ERR_WT_FMT + ERR_WT_AA)
113 #define ALNUM_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
114 #define ALNUM_LEN (sizeof(ALNUM_CHARS) - 1)
117 struct time_adjustment {
122 #define MAX_SELECTIONS 512
123 static struct select_item selectfrm[MAX_SELECTIONS];
124 static int max_selected = -1;
125 static int keep_em = 0;
126 #ifdef PCAP_NG_DEFAULT
127 static int out_file_type = WTAP_FILE_PCAPNG; /* default to pcapng */
129 static int out_file_type = WTAP_FILE_PCAP; /* default to pcap */
131 static int out_frame_type = -2; /* Leave frame type alone */
132 static int verbose = 0; /* Not so verbose */
133 static struct time_adjustment time_adj = {{0, 0}, 0}; /* no adjustment */
134 static nstime_t relative_time_window = {0, 0}; /* de-dup time window */
135 static double err_prob = 0.0;
136 static time_t starttime = 0;
137 static time_t stoptime = 0;
138 static gboolean check_startstop = FALSE;
139 static gboolean dup_detect = FALSE;
140 static gboolean dup_detect_by_time = FALSE;
142 static int do_strict_time_adjustment = FALSE;
143 static struct time_adjustment strict_time_adj = {{0, 0}, 0}; /* strict time adjustment */
144 static nstime_t previous_time = {0, 0}; /* previous time */
146 static int find_dct2000_real_data(guint8 *buf);
149 abs_time_to_str_with_sec_resolution(const struct wtap_nstime *abs_time)
152 gchar *buf = g_malloc(16);
155 /* calling localtime() on MSVC 2005 with huge values causes it to crash */
156 /* XXX - find the exact value that still does work */
157 /* XXX - using _USE_32BIT_TIME_T might be another way to circumvent this problem */
158 if(abs_time->secs > 2000000000) {
162 tmp = localtime(&abs_time->secs);
164 g_snprintf(buf, 16, "%d%02d%02d%02d%02d%02d",
178 fileset_get_filename_by_pattern(guint idx, const struct wtap_nstime *time_val,
179 gchar *fprefix, gchar *fsuffix)
185 timestr = abs_time_to_str_with_sec_resolution(time_val);
186 g_snprintf(filenum, sizeof(filenum), "%05u", idx);
187 abs_str = g_strconcat(fprefix, "_", filenum, "_", timestr, fsuffix, NULL);
194 fileset_extract_prefix_suffix(const char *fname, gchar **fprefix, gchar **fsuffix)
196 char *pfx, *last_pathsep;
199 save_file = g_strdup(fname);
200 if (save_file == NULL) {
201 fprintf(stderr, "editcap: Out of memory\n");
205 last_pathsep = strrchr(save_file, G_DIR_SEPARATOR);
206 pfx = strrchr(save_file,'.');
207 if (pfx != NULL && (last_pathsep == NULL || pfx > last_pathsep)) {
208 /* The pathname has a "." in it, and it's in the last component
209 of the pathname (because there is either only one component,
210 i.e. last_pathsep is null as there are no path separators,
211 or the "." is after the path separator before the last
214 Treat it as a separator between the rest of the file name and
215 the file name suffix, and arrange that the names given to the
216 ring buffer files have the specified suffix, i.e. put the
217 changing part of the name *before* the suffix. */
219 *fprefix = g_strdup(save_file);
220 pfx[0] = '.'; /* restore capfile_name */
221 *fsuffix = g_strdup(pfx);
223 /* Either there's no "." in the pathname, or it's in a directory
224 component, so the last component has no suffix. */
225 *fprefix = g_strdup(save_file);
232 /* Add a selection item, a simple parser for now */
234 add_selection(char *sel)
239 if (++max_selected >= MAX_SELECTIONS) {
240 /* Let the user know we stopped selecting */
241 printf("Out of room for packet selections!\n");
245 printf("Add_Selected: %s\n", sel);
247 if ((locn = strchr(sel, '-')) == NULL) { /* No dash, so a single number? */
249 printf("Not inclusive ...");
251 selectfrm[max_selected].inclusive = 0;
252 selectfrm[max_selected].first = atoi(sel);
254 printf(" %i\n", selectfrm[max_selected].first);
259 printf("Inclusive ...");
262 selectfrm[max_selected].inclusive = 1;
263 selectfrm[max_selected].first = atoi(sel);
264 selectfrm[max_selected].second = atoi(next);
266 printf(" %i, %i\n", selectfrm[max_selected].first, selectfrm[max_selected].second);
273 /* Was the packet selected? */
280 for (i = 0; i<= max_selected; i++) {
282 if (selectfrm[i].inclusive) {
283 if (selectfrm[i].first <= recno && selectfrm[i].second >= recno)
287 if (recno == selectfrm[i].first)
296 /* is the packet in the selected timeframe */
298 check_timestamp(wtap *wth)
300 struct wtap_pkthdr* pkthdr = wtap_phdr(wth);
302 return ( pkthdr->ts.secs >= starttime ) && ( pkthdr->ts.secs < stoptime );
306 set_time_adjustment(char *optarg_str_p)
315 /* skip leading whitespace */
316 while (*optarg_str_p == ' ' || *optarg_str_p == '\t') {
320 /* check for a negative adjustment */
321 if (*optarg_str_p == '-') {
322 time_adj.is_negative = 1;
326 /* collect whole number of seconds, if any */
327 if (*optarg_str_p == '.') { /* only fractional (i.e., .5 is ok) */
331 val = strtol(optarg_str_p, &frac, 10);
332 if (frac == NULL || frac == optarg_str_p || val == LONG_MIN || val == LONG_MAX) {
333 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
337 if (val < 0) { /* implies '--' since we caught '-' above */
338 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
343 time_adj.tv.tv_sec = val;
345 /* now collect the partial seconds, if any */
346 if (*frac != '\0') { /* chars left, so get fractional part */
347 val = strtol(&(frac[1]), &end, 10);
348 /* if more than 6 fractional digits truncate to 6 */
349 if((end - &(frac[1])) > 6) {
350 frac[7] = 't'; /* 't' for truncate */
351 val = strtol(&(frac[1]), &end, 10);
353 if (*frac != '.' || end == NULL || end == frac
354 || val < 0 || val > ONE_MILLION || val == LONG_MIN || val == LONG_MAX) {
355 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
361 return; /* no fractional digits */
364 /* adjust fractional portion from fractional to numerator
365 * e.g., in "1.5" from 5 to 500000 since .5*10^6 = 500000 */
366 if (frac && end) { /* both are valid */
367 frac_digits = end - frac - 1; /* fractional digit count (remember '.') */
368 while(frac_digits < 6) { /* this is frac of 10^6 */
373 time_adj.tv.tv_usec = val;
377 set_strict_time_adj(char *optarg_str_p)
386 /* skip leading whitespace */
387 while (*optarg_str_p == ' ' || *optarg_str_p == '\t') {
392 * check for a negative adjustment
393 * A negative strict adjustment value is a flag
394 * to adjust all frames by the specifed delta time.
396 if (*optarg_str_p == '-') {
397 strict_time_adj.is_negative = 1;
401 /* collect whole number of seconds, if any */
402 if (*optarg_str_p == '.') { /* only fractional (i.e., .5 is ok) */
406 val = strtol(optarg_str_p, &frac, 10);
407 if (frac == NULL || frac == optarg_str_p || val == LONG_MIN || val == LONG_MAX) {
408 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
412 if (val < 0) { /* implies '--' since we caught '-' above */
413 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
418 strict_time_adj.tv.tv_sec = val;
420 /* now collect the partial seconds, if any */
421 if (*frac != '\0') { /* chars left, so get fractional part */
422 val = strtol(&(frac[1]), &end, 10);
423 /* if more than 6 fractional digits truncate to 6 */
424 if((end - &(frac[1])) > 6) {
425 frac[7] = 't'; /* 't' for truncate */
426 val = strtol(&(frac[1]), &end, 10);
428 if (*frac != '.' || end == NULL || end == frac
429 || val < 0 || val > ONE_MILLION || val == LONG_MIN || val == LONG_MAX) {
430 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
436 return; /* no fractional digits */
439 /* adjust fractional portion from fractional to numerator
440 * e.g., in "1.5" from 5 to 500000 since .5*10^6 = 500000 */
441 if (frac && end) { /* both are valid */
442 frac_digits = end - frac - 1; /* fractional digit count (remember '.') */
443 while(frac_digits < 6) { /* this is frac of 10^6 */
448 strict_time_adj.tv.tv_usec = val;
452 set_rel_time(char *optarg_str_p)
461 /* skip leading whitespace */
462 while (*optarg_str_p == ' ' || *optarg_str_p == '\t') {
466 /* ignore negative adjustment */
467 if (*optarg_str_p == '-') {
471 /* collect whole number of seconds, if any */
472 if (*optarg_str_p == '.') { /* only fractional (i.e., .5 is ok) */
476 val = strtol(optarg_str_p, &frac, 10);
477 if (frac == NULL || frac == optarg_str_p || val == LONG_MIN || val == LONG_MAX) {
478 fprintf(stderr, "1: editcap: \"%s\" isn't a valid rel time value\n",
482 if (val < 0) { /* implies '--' since we caught '-' above */
483 fprintf(stderr, "2: editcap: \"%s\" isn't a valid rel time value\n",
488 relative_time_window.secs = val;
490 /* now collect the partial seconds, if any */
491 if (*frac != '\0') { /* chars left, so get fractional part */
492 val = strtol(&(frac[1]), &end, 10);
493 /* if more than 9 fractional digits truncate to 9 */
494 if((end - &(frac[1])) > 9) {
495 frac[10] = 't'; /* 't' for truncate */
496 val = strtol(&(frac[1]), &end, 10);
498 if (*frac != '.' || end == NULL || end == frac
499 || val < 0 || val > ONE_BILLION || val == LONG_MIN || val == LONG_MAX) {
500 fprintf(stderr, "3: editcap: \"%s\" isn't a valid rel time value\n",
506 return; /* no fractional digits */
509 /* adjust fractional portion from fractional to numerator
510 * e.g., in "1.5" from 5 to 500000000 since .5*10^9 = 500000000 */
511 if (frac && end) { /* both are valid */
512 frac_digits = end - frac - 1; /* fractional digit count (remember '.') */
513 while(frac_digits < 9) { /* this is frac of 10^9 */
518 relative_time_window.nsecs = val;
522 is_duplicate(guint8* fd, guint32 len) {
527 if (cur_dup_entry >= dup_window)
530 /* Calculate our digest */
532 md5_append(&ms, fd, len);
533 md5_finish(&ms, fd_hash[cur_dup_entry].digest);
535 fd_hash[cur_dup_entry].len = len;
537 /* Look for duplicates */
538 for (i = 0; i < dup_window; i++) {
539 if (i == cur_dup_entry)
542 if (fd_hash[i].len == fd_hash[cur_dup_entry].len &&
543 memcmp(fd_hash[i].digest, fd_hash[cur_dup_entry].digest, 16) == 0) {
552 is_duplicate_rel_time(guint8* fd, guint32 len, const nstime_t *current) {
557 if (cur_dup_entry >= dup_window)
560 /* Calculate our digest */
562 md5_append(&ms, fd, len);
563 md5_finish(&ms, fd_hash[cur_dup_entry].digest);
565 fd_hash[cur_dup_entry].len = len;
566 fd_hash[cur_dup_entry].time.secs = current->secs;
567 fd_hash[cur_dup_entry].time.nsecs = current->nsecs;
570 * Look for relative time related duplicates.
571 * This is hopefully a reasonably efficient mechanism for
572 * finding duplicates by rel time in the fd_hash[] cache.
573 * We check starting from the most recently added hash
574 * entries and work backwards towards older packets.
575 * This approach allows the dup test to be terminated
576 * when the relative time of a cached entry is found to
577 * be beyond the dup time window.
579 * Of course this assumes that the input trace file is
580 * "well-formed" in the sense that the packet timestamps are
581 * in strict chronologically increasing order (which is NOT
582 * always the case!!).
584 * The fd_hash[] table was deliberatly created large (1,000,000).
585 * Looking for time related duplicates in large trace files with
586 * non-fractional dup time window values can potentially take
587 * a long time to complete.
590 for (i = cur_dup_entry - 1;; i--) {
598 if (i == cur_dup_entry) {
600 * We've decremented back to where we started.
606 if (nstime_is_unset(&(fd_hash[i].time))) {
608 * We've decremented to an unused fd_hash[] entry.
614 nstime_delta(&delta, current, &fd_hash[i].time);
616 if(delta.secs < 0 || delta.nsecs < 0)
619 * A negative delta implies that the current packet
620 * has an absolute timestamp less than the cached packet
621 * that it is being compared to. This is NOT a normal
622 * situation since trace files usually have packets in
623 * chronological order (oldest to newest).
625 * There are several possible ways to deal with this:
626 * 1. 'continue' dup checking with the next cached frame.
627 * 2. 'break' from looking for a duplicate of the current frame.
628 * 3. Take the absolute value of the delta and see if that
629 * falls within the specifed dup time window.
631 * Currently this code does option 1. But it would pretty
632 * easy to add yet-another-editcap-option to select one of
633 * the other behaviors for dealing with out-of-sequence
639 cmp = nstime_cmp(&delta, &relative_time_window);
643 * The delta time indicates that we are now looking at
644 * cached packets beyond the specified dup time window.
648 } else if (fd_hash[i].len == fd_hash[cur_dup_entry].len &&
649 memcmp(fd_hash[i].digest, fd_hash[cur_dup_entry].digest, 16) == 0) {
658 usage(gboolean is_error)
667 fprintf(output, "Editcap %s"
669 " (" SVNVERSION " from " SVNPATH ")"
672 fprintf(output, "Edit and/or translate the format of capture files.\n");
673 fprintf(output, "See http://www.wireshark.org for more information.\n");
674 fprintf(output, "\n");
675 fprintf(output, "Usage: editcap [options] ... <infile> <outfile> [ <packet#>[-<packet#>] ... ]\n");
676 fprintf(output, "\n");
677 fprintf(output, "<infile> and <outfile> must both be present.\n");
678 fprintf(output, "A single packet or a range of packets can be selected.\n");
679 fprintf(output, "\n");
680 fprintf(output, "Packet selection:\n");
681 fprintf(output, " -r keep the selected packets; default is to delete them.\n");
682 fprintf(output, " -A <start time> only output packets whose timestamp is after (or equal\n");
683 fprintf(output, " to) the given time (format as YYYY-MM-DD hh:mm:ss).\n");
684 fprintf(output, " -B <stop time> only output packets whose timestamp is before the\n");
685 fprintf(output, " given time (format as YYYY-MM-DD hh:mm:ss).\n");
686 fprintf(output, "\n");
687 fprintf(output, "Duplicate packet removal:\n");
688 fprintf(output, " -d remove packet if duplicate (window == %d).\n", DEFAULT_DUP_DEPTH);
689 fprintf(output, " -D <dup window> remove packet if duplicate; configurable <dup window>\n");
690 fprintf(output, " Valid <dup window> values are 0 to %d.\n", MAX_DUP_DEPTH);
691 fprintf(output, " NOTE: A <dup window> of 0 with -v (verbose option) is\n");
692 fprintf(output, " useful to print MD5 hashes.\n");
693 fprintf(output, " -w <dup time window> remove packet if duplicate packet is found EQUAL TO OR\n");
694 fprintf(output, " LESS THAN <dup time window> prior to current packet.\n");
695 fprintf(output, " A <dup time window> is specified in relative seconds\n");
696 fprintf(output, " (e.g. 0.000001).\n");
697 fprintf(output, "\n");
698 fprintf(output, " NOTE: The use of the 'Duplicate packet removal' options with\n");
699 fprintf(output, " other editcap options except -v may not always work as expected.\n");
700 fprintf(output, " Specifically the -r, -t or -S options will very likely NOT have the\n");
701 fprintf(output, " desired effect if combined with the -d, -D or -w.\n");
702 fprintf(output, "\n");
703 fprintf(output, "Packet manipulation:\n");
704 fprintf(output, " -s <snaplen> truncate each packet to max. <snaplen> bytes of data.\n");
705 fprintf(output, " -C <choplen> chop each packet by <choplen> bytes. Positive values\n");
706 fprintf(output, " chop at the packet beginning, negative values at the\n");
707 fprintf(output, " packet end.\n");
708 fprintf(output, " -t <time adjustment> adjust the timestamp of each packet;\n");
709 fprintf(output, " <time adjustment> is in relative seconds (e.g. -0.5).\n");
710 fprintf(output, " -S <strict adjustment> adjust timestamp of packets if necessary to insure\n");
711 fprintf(output, " strict chronological increasing order. The <strict\n");
712 fprintf(output, " adjustment> is specified in relative seconds with\n");
713 fprintf(output, " values of 0 or 0.000001 being the most reasonable.\n");
714 fprintf(output, " A negative adjustment value will modify timestamps so\n");
715 fprintf(output, " that each packet's delta time is the absolute value\n");
716 fprintf(output, " of the adjustment specified. A value of -0 will set\n");
717 fprintf(output, " all packets to the timestamp of the first packet.\n");
718 fprintf(output, " -E <error probability> set the probability (between 0.0 and 1.0 incl.)\n");
719 fprintf(output, " that a particular packet byte will be randomly changed.\n");
720 fprintf(output, "\n");
721 fprintf(output, "Output File(s):\n");
722 fprintf(output, " -c <packets per file> split the packet output to different files\n");
723 fprintf(output, " based on uniform packet counts\n");
724 fprintf(output, " with a maximum of <packets per file> each.\n");
725 fprintf(output, " -i <seconds per file> split the packet output to different files\n");
726 fprintf(output, " based on uniform time intervals\n");
727 fprintf(output, " with a maximum of <seconds per file> each.\n");
728 fprintf(output, " -F <capture type> set the output file type; default is libpcap.\n");
729 fprintf(output, " an empty \"-F\" option will list the file types.\n");
730 fprintf(output, " -T <encap type> set the output file encapsulation type;\n");
731 fprintf(output, " default is the same as the input file.\n");
732 fprintf(output, " an empty \"-T\" option will list the encapsulation types.\n");
733 fprintf(output, "\n");
734 fprintf(output, "Miscellaneous:\n");
735 fprintf(output, " -h display this help and exit.\n");
736 fprintf(output, " -v verbose output.\n");
737 fprintf(output, " If -v is used with any of the 'Duplicate Packet\n");
738 fprintf(output, " Removal' options (-d, -D or -w) then Packet lengths\n");
739 fprintf(output, " and MD5 hashes are printed to standard-out.\n");
740 fprintf(output, "\n");
744 const char *sstr; /* The short string */
745 const char *lstr; /* The long string */
749 string_compare(gconstpointer a, gconstpointer b)
751 return strcmp(((const struct string_elem *)a)->sstr,
752 ((const struct string_elem *)b)->sstr);
756 string_elem_print(gpointer data, gpointer not_used _U_)
758 fprintf(stderr, " %s - %s\n",
759 ((struct string_elem *)data)->sstr,
760 ((struct string_elem *)data)->lstr);
764 list_capture_types(void) {
766 struct string_elem *captypes;
769 captypes = g_malloc(sizeof(struct string_elem) * WTAP_NUM_FILE_TYPES);
770 fprintf(stderr, "editcap: The available capture file types for the \"-F\" flag are:\n");
771 for (i = 0; i < WTAP_NUM_FILE_TYPES; i++) {
772 if (wtap_dump_can_open(i)) {
773 captypes[i].sstr = wtap_file_type_short_string(i);
774 captypes[i].lstr = wtap_file_type_string(i);
775 list = g_slist_insert_sorted(list, &captypes[i], string_compare);
778 g_slist_foreach(list, string_elem_print, NULL);
784 list_encap_types(void) {
786 struct string_elem *encaps;
789 encaps = g_malloc(sizeof(struct string_elem) * WTAP_NUM_ENCAP_TYPES);
790 fprintf(stderr, "editcap: The available encapsulation types for the \"-T\" flag are:\n");
791 for (i = 0; i < WTAP_NUM_ENCAP_TYPES; i++) {
792 encaps[i].sstr = wtap_encap_short_string(i);
793 if (encaps[i].sstr != NULL) {
794 encaps[i].lstr = wtap_encap_string(i);
795 list = g_slist_insert_sorted(list, &encaps[i], string_compare);
798 g_slist_foreach(list, string_elem_print, NULL);
805 * Don't report failures to load plugins because most (non-wiretap) plugins
806 * *should* fail to load (because we're not linked against libwireshark and
807 * dissector plugins need libwireshark).
810 failure_message(const char *msg_format _U_, va_list ap _U_)
817 main(int argc, char *argv[])
825 guint32 snaplen = 0; /* No limit */
826 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;
838 gboolean ts_okay = TRUE;
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 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("capinfos: 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_RECORD:
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)));
1105 * Now, process the rest, if any ... we only write if there is an extra
1106 * argument or so ...
1109 if ((argc - optind) >= 2) {
1111 if (out_frame_type == -2)
1112 out_frame_type = wtap_file_encap(wth);
1114 for (i = optind + 2; i < argc; i++)
1115 if (add_selection(argv[i]) == FALSE)
1118 if (dup_detect || dup_detect_by_time) {
1119 for (i = 0; i < dup_window; i++) {
1120 memset(&fd_hash[i].digest, 0, 16);
1122 nstime_set_unset(&fd_hash[i].time);
1126 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 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 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 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 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 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 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 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 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 /* 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 fprintf(stdout, "Skipped: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1349 for (i = 0; i < 16; i++) {
1350 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1352 fprintf(stdout, "\n");
1359 fprintf(stdout, "Packet: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1360 for (i = 0; i < 16; i++) {
1361 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1363 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 fprintf(stdout, "Skipped: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1378 for (i = 0; i < 16; i++) {
1379 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1381 fprintf(stdout, "\n");
1388 fprintf(stdout, "Packet: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1389 for (i = 0; i < 16; i++) {
1390 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1392 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_snapshot_length(wth) != 0) && (phdr->caplen > wtap_snapshot_length(wth))) {
1448 fprintf(stderr, "Warning: packet %d too big for file type, skipping it...\n", count);
1453 if (!wtap_dump(pdh, phdr, wtap_pseudoheader(wth), buf, &err)) {
1454 fprintf(stderr, "editcap: Error writing to %s: %s\n",
1455 filename, wtap_strerror(err));
1467 /* Print a message noting that the read failed somewhere along the line. */
1469 "editcap: An error occurred while reading \"%s\": %s.\n",
1470 argv[optind], wtap_strerror(err));
1473 case WTAP_ERR_UNSUPPORTED:
1474 case WTAP_ERR_UNSUPPORTED_ENCAP:
1475 case WTAP_ERR_BAD_RECORD:
1476 fprintf(stderr, "(%s)\n", err_info);
1483 /* No valid packages found, open the outfile so we can write an empty header */
1485 filename = g_strdup(argv[optind+1]);
1487 pdh = wtap_dump_open(filename, out_file_type, out_frame_type,
1488 snaplen ? MIN(snaplen, wtap_snapshot_length(wth)): wtap_snapshot_length(wth),
1489 FALSE /* compressed */, &err);
1491 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1492 wtap_strerror(err));
1497 if (!wtap_dump_close(pdh, &err)) {
1499 fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
1500 wtap_strerror(err));
1508 fprintf(stdout, "%u packet%s seen, %u packet%s skipped with duplicate window of %u packets.\n",
1509 count - 1, plurality(count - 1, "", "s"),
1510 duplicate_count, plurality(duplicate_count, "", "s"), dup_window);
1511 } else if (dup_detect_by_time) {
1512 fprintf(stdout, "%u packet%s seen, %u packet%s skipped with duplicate time window equal to or less than %ld.%09ld seconds.\n",
1513 count - 1, plurality(count - 1, "", "s"),
1514 duplicate_count, plurality(duplicate_count, "", "s"),
1515 (long)relative_time_window.secs, (long int)relative_time_window.nsecs);
1521 /* Skip meta-information read from file to return offset of real
1523 static int find_dct2000_real_data(guint8 *buf)
1527 for (n=0; buf[n] != '\0'; n++); /* Context name */
1529 n++; /* Context port number */
1530 for (; buf[n] != '\0'; n++); /* Timestamp */
1532 for (; buf[n] != '\0'; n++); /* Protocol name */
1534 for (; buf[n] != '\0'; n++); /* Variant number (as string) */
1536 for (; buf[n] != '\0'; n++); /* Outhdr (as string) */
1538 n += 2; /* Direction & encap */