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 <process.h> /* getpid */
53 #ifdef HAVE_WINSOCK2_H
58 #ifdef NEED_STRPTIME_H
59 # include "wsutil/strptime.h"
62 #include "epan/crypt/crypt-md5.h"
63 #include "epan/plugins.h"
64 #include "epan/report_err.h"
65 #include "epan/filesystem.h"
66 #include <wsutil/privileges.h>
67 #include "epan/nstime.h"
69 #include "svnversion.h"
72 * Some globals so we can pass things to various routines
84 * Duplicate frame detection
86 typedef struct _fd_hash_t {
87 md5_byte_t digest[16];
92 #define DEFAULT_DUP_DEPTH 5 /* Used with -d */
93 #define MAX_DUP_DEPTH 1000000 /* the maximum window (and actual size of fd_hash[]) for de-duplication */
95 fd_hash_t fd_hash[MAX_DUP_DEPTH];
96 int dup_window = DEFAULT_DUP_DEPTH;
97 int cur_dup_entry = 0;
99 #define ONE_MILLION 1000000
100 #define ONE_BILLION 1000000000
102 /* Weights of different errors we can introduce */
103 /* We should probably make these command-line arguments */
104 /* XXX - Should we add a bit-level error? */
105 #define ERR_WT_BIT 5 /* Flip a random bit */
106 #define ERR_WT_BYTE 5 /* Substitute a random byte */
107 #define ERR_WT_ALNUM 5 /* Substitute a random character in [A-Za-z0-9] */
108 #define ERR_WT_FMT 2 /* Substitute "%s" */
109 #define ERR_WT_AA 1 /* Fill the remainder of the buffer with 0xAA */
110 #define ERR_WT_TOTAL (ERR_WT_BIT + ERR_WT_BYTE + ERR_WT_ALNUM + ERR_WT_FMT + ERR_WT_AA)
112 #define ALNUM_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
113 #define ALNUM_LEN (sizeof(ALNUM_CHARS) - 1)
116 struct time_adjustment {
121 #define MAX_SELECTIONS 512
122 static struct select_item selectfrm[MAX_SELECTIONS];
123 static int max_selected = -1;
124 static int keep_em = 0;
125 static int out_file_type = WTAP_FILE_PCAP; /* default to "libpcap" */
126 static int out_frame_type = -2; /* Leave frame type alone */
127 static int verbose = 0; /* Not so verbose */
128 static struct time_adjustment time_adj = {{0, 0}, 0}; /* no adjustment */
129 static nstime_t relative_time_window = {0, 0}; /* de-dup time window */
130 static double err_prob = 0.0;
131 static time_t starttime = 0;
132 static time_t stoptime = 0;
133 static gboolean check_startstop = FALSE;
134 static gboolean dup_detect = FALSE;
135 static gboolean dup_detect_by_time = FALSE;
137 static int do_strict_time_adjustment = FALSE;
138 static struct time_adjustment strict_time_adj = {{0, 0}, 0}; /* strict time adjustment */
139 static nstime_t previous_time = {0, 0}; /* previous time */
141 static int find_dct2000_real_data(guint8 *buf);
144 abs_time_to_str_with_sec_resolution(const struct wtap_nstime *abs_time)
147 gchar *buf = g_malloc(16);
150 /* calling localtime() on MSVC 2005 with huge values causes it to crash */
151 /* XXX - find the exact value that still does work */
152 /* XXX - using _USE_32BIT_TIME_T might be another way to circumvent this problem */
153 if(abs_time->secs > 2000000000) {
157 tmp = localtime(&abs_time->secs);
159 g_snprintf(buf, 16, "%d%02d%02d%02d%02d%02d",
173 fileset_get_filename_by_pattern(guint idx, const struct wtap_nstime *time_val,
174 gchar *fprefix, gchar *fsuffix)
180 timestr = abs_time_to_str_with_sec_resolution(time_val);
181 g_snprintf(filenum, sizeof(filenum), "%05u", idx);
182 abs_str = g_strconcat(fprefix, "_", filenum, "_", timestr, fsuffix, NULL);
189 fileset_extract_prefix_suffix(const char *fname, gchar **fprefix, gchar **fsuffix)
191 char *pfx, *last_pathsep;
194 save_file = g_strdup(fname);
195 if (save_file == NULL) {
196 fprintf(stderr, "editcap: Out of memory\n");
200 last_pathsep = strrchr(save_file, G_DIR_SEPARATOR);
201 pfx = strrchr(save_file,'.');
202 if (pfx != NULL && (last_pathsep == NULL || pfx > last_pathsep)) {
203 /* The pathname has a "." in it, and it's in the last component
204 of the pathname (because there is either only one component,
205 i.e. last_pathsep is null as there are no path separators,
206 or the "." is after the path separator before the last
209 Treat it as a separator between the rest of the file name and
210 the file name suffix, and arrange that the names given to the
211 ring buffer files have the specified suffix, i.e. put the
212 changing part of the name *before* the suffix. */
214 *fprefix = g_strdup(save_file);
215 pfx[0] = '.'; /* restore capfile_name */
216 *fsuffix = g_strdup(pfx);
218 /* Either there's no "." in the pathname, or it's in a directory
219 component, so the last component has no suffix. */
220 *fprefix = g_strdup(save_file);
227 /* Add a selection item, a simple parser for now */
229 add_selection(char *sel)
234 if (++max_selected >= MAX_SELECTIONS) {
235 /* Let the user know we stopped selecting */
236 printf("Out of room for packet selections!\n");
240 printf("Add_Selected: %s\n", sel);
242 if ((locn = strchr(sel, '-')) == NULL) { /* No dash, so a single number? */
244 printf("Not inclusive ...");
246 selectfrm[max_selected].inclusive = 0;
247 selectfrm[max_selected].first = atoi(sel);
249 printf(" %i\n", selectfrm[max_selected].first);
254 printf("Inclusive ...");
257 selectfrm[max_selected].inclusive = 1;
258 selectfrm[max_selected].first = atoi(sel);
259 selectfrm[max_selected].second = atoi(next);
261 printf(" %i, %i\n", selectfrm[max_selected].first, selectfrm[max_selected].second);
268 /* Was the packet selected? */
275 for (i = 0; i<= max_selected; i++) {
277 if (selectfrm[i].inclusive) {
278 if (selectfrm[i].first <= recno && selectfrm[i].second >= recno)
282 if (recno == selectfrm[i].first)
291 /* is the packet in the selected timeframe */
293 check_timestamp(wtap *wth)
295 struct wtap_pkthdr* pkthdr = wtap_phdr(wth);
297 return ( pkthdr->ts.secs >= starttime ) && ( pkthdr->ts.secs < stoptime );
301 set_time_adjustment(char *optarg_str_p)
310 /* skip leading whitespace */
311 while (*optarg_str_p == ' ' || *optarg_str_p == '\t') {
315 /* check for a negative adjustment */
316 if (*optarg_str_p == '-') {
317 time_adj.is_negative = 1;
321 /* collect whole number of seconds, if any */
322 if (*optarg_str_p == '.') { /* only fractional (i.e., .5 is ok) */
326 val = strtol(optarg_str_p, &frac, 10);
327 if (frac == NULL || frac == optarg_str_p || val == LONG_MIN || val == LONG_MAX) {
328 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
332 if (val < 0) { /* implies '--' since we caught '-' above */
333 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
338 time_adj.tv.tv_sec = val;
340 /* now collect the partial seconds, if any */
341 if (*frac != '\0') { /* chars left, so get fractional part */
342 val = strtol(&(frac[1]), &end, 10);
343 /* if more than 6 fractional digits truncate to 6 */
344 if((end - &(frac[1])) > 6) {
345 frac[7] = 't'; /* 't' for truncate */
346 val = strtol(&(frac[1]), &end, 10);
348 if (*frac != '.' || end == NULL || end == frac
349 || val < 0 || val > ONE_MILLION || val == LONG_MIN || val == LONG_MAX) {
350 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
356 return; /* no fractional digits */
359 /* adjust fractional portion from fractional to numerator
360 * e.g., in "1.5" from 5 to 500000 since .5*10^6 = 500000 */
361 if (frac && end) { /* both are valid */
362 frac_digits = end - frac - 1; /* fractional digit count (remember '.') */
363 while(frac_digits < 6) { /* this is frac of 10^6 */
368 time_adj.tv.tv_usec = val;
372 set_strict_time_adj(char *optarg_str_p)
381 /* skip leading whitespace */
382 while (*optarg_str_p == ' ' || *optarg_str_p == '\t') {
387 * check for a negative adjustment
388 * A negative strict adjustment value is a flag
389 * to adjust all frames by the specifed delta time.
391 if (*optarg_str_p == '-') {
392 strict_time_adj.is_negative = 1;
396 /* collect whole number of seconds, if any */
397 if (*optarg_str_p == '.') { /* only fractional (i.e., .5 is ok) */
401 val = strtol(optarg_str_p, &frac, 10);
402 if (frac == NULL || frac == optarg_str_p || val == LONG_MIN || val == LONG_MAX) {
403 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
407 if (val < 0) { /* implies '--' since we caught '-' above */
408 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
413 strict_time_adj.tv.tv_sec = val;
415 /* now collect the partial seconds, if any */
416 if (*frac != '\0') { /* chars left, so get fractional part */
417 val = strtol(&(frac[1]), &end, 10);
418 /* if more than 6 fractional digits truncate to 6 */
419 if((end - &(frac[1])) > 6) {
420 frac[7] = 't'; /* 't' for truncate */
421 val = strtol(&(frac[1]), &end, 10);
423 if (*frac != '.' || end == NULL || end == frac
424 || val < 0 || val > ONE_MILLION || val == LONG_MIN || val == LONG_MAX) {
425 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
431 return; /* no fractional digits */
434 /* adjust fractional portion from fractional to numerator
435 * e.g., in "1.5" from 5 to 500000 since .5*10^6 = 500000 */
436 if (frac && end) { /* both are valid */
437 frac_digits = end - frac - 1; /* fractional digit count (remember '.') */
438 while(frac_digits < 6) { /* this is frac of 10^6 */
443 strict_time_adj.tv.tv_usec = val;
447 set_rel_time(char *optarg_str_p)
456 /* skip leading whitespace */
457 while (*optarg_str_p == ' ' || *optarg_str_p == '\t') {
461 /* ignore negative adjustment */
462 if (*optarg_str_p == '-') {
466 /* collect whole number of seconds, if any */
467 if (*optarg_str_p == '.') { /* only fractional (i.e., .5 is ok) */
471 val = strtol(optarg_str_p, &frac, 10);
472 if (frac == NULL || frac == optarg_str_p || val == LONG_MIN || val == LONG_MAX) {
473 fprintf(stderr, "1: editcap: \"%s\" isn't a valid rel time value\n",
477 if (val < 0) { /* implies '--' since we caught '-' above */
478 fprintf(stderr, "2: editcap: \"%s\" isn't a valid rel time value\n",
483 relative_time_window.secs = val;
485 /* now collect the partial seconds, if any */
486 if (*frac != '\0') { /* chars left, so get fractional part */
487 val = strtol(&(frac[1]), &end, 10);
488 /* if more than 9 fractional digits truncate to 9 */
489 if((end - &(frac[1])) > 9) {
490 frac[10] = 't'; /* 't' for truncate */
491 val = strtol(&(frac[1]), &end, 10);
493 if (*frac != '.' || end == NULL || end == frac
494 || val < 0 || val > ONE_BILLION || val == LONG_MIN || val == LONG_MAX) {
495 fprintf(stderr, "3: editcap: \"%s\" isn't a valid rel time value\n",
501 return; /* no fractional digits */
504 /* adjust fractional portion from fractional to numerator
505 * e.g., in "1.5" from 5 to 500000000 since .5*10^9 = 500000000 */
506 if (frac && end) { /* both are valid */
507 frac_digits = end - frac - 1; /* fractional digit count (remember '.') */
508 while(frac_digits < 9) { /* this is frac of 10^9 */
513 relative_time_window.nsecs = val;
517 is_duplicate(guint8* fd, guint32 len) {
522 if (cur_dup_entry >= dup_window)
525 /* Calculate our digest */
527 md5_append(&ms, fd, len);
528 md5_finish(&ms, fd_hash[cur_dup_entry].digest);
530 fd_hash[cur_dup_entry].len = len;
532 /* Look for duplicates */
533 for (i = 0; i < dup_window; i++) {
534 if (i == cur_dup_entry)
537 if (fd_hash[i].len == fd_hash[cur_dup_entry].len &&
538 memcmp(fd_hash[i].digest, fd_hash[cur_dup_entry].digest, 16) == 0) {
547 is_duplicate_rel_time(guint8* fd, guint32 len, const nstime_t *current) {
552 if (cur_dup_entry >= dup_window)
555 /* Calculate our digest */
557 md5_append(&ms, fd, len);
558 md5_finish(&ms, fd_hash[cur_dup_entry].digest);
560 fd_hash[cur_dup_entry].len = len;
561 fd_hash[cur_dup_entry].time.secs = current->secs;
562 fd_hash[cur_dup_entry].time.nsecs = current->nsecs;
565 * Look for relative time related duplicates.
566 * This is hopefully a reasonably efficient mechanism for
567 * finding duplicates by rel time in the fd_hash[] cache.
568 * We check starting from the most recently added hash
569 * entries and work backwards towards older packets.
570 * This approach allows the dup test to be terminated
571 * when the relative time of a cached entry is found to
572 * be beyond the dup time window.
574 * Of course this assumes that the input trace file is
575 * "well-formed" in the sense that the packet timestamps are
576 * in strict chronologically increasing order (which is NOT
577 * always the case!!).
579 * The fd_hash[] table was deliberatly created large (1,000,000).
580 * Looking for time related duplicates in large trace files with
581 * non-fractional dup time window values can potentially take
582 * a long time to complete.
585 for (i = cur_dup_entry - 1;; i--) {
593 if (i == cur_dup_entry) {
595 * We've decremented back to where we started.
601 if (nstime_is_unset(&(fd_hash[i].time))) {
603 * We've decremented to an unused fd_hash[] entry.
609 nstime_delta(&delta, current, &fd_hash[i].time);
611 if(delta.secs < 0 || delta.nsecs < 0)
614 * A negative delta implies that the current packet
615 * has an absolute timestamp less than the cached packet
616 * that it is being compared to. This is NOT a normal
617 * situation since trace files usually have packets in
618 * chronological order (oldest to newest).
620 * There are several possible ways to deal with this:
621 * 1. 'continue' dup checking with the next cached frame.
622 * 2. 'break' from looking for a duplicate of the current frame.
623 * 3. Take the absolute value of the delta and see if that
624 * falls within the specifed dup time window.
626 * Currently this code does option 1. But it would pretty
627 * easy to add yet-another-editcap-option to select one of
628 * the other behaviors for dealing with out-of-sequence
634 cmp = nstime_cmp(&delta, &relative_time_window);
638 * The delta time indicates that we are now looking at
639 * cached packets beyond the specified dup time window.
643 } else if (fd_hash[i].len == fd_hash[cur_dup_entry].len &&
644 memcmp(fd_hash[i].digest, fd_hash[cur_dup_entry].digest, 16) == 0) {
653 usage(gboolean is_error)
662 fprintf(output, "Editcap %s"
664 " (" SVNVERSION " from " SVNPATH ")"
667 fprintf(output, "Edit and/or translate the format of capture files.\n");
668 fprintf(output, "See http://www.wireshark.org for more information.\n");
669 fprintf(output, "\n");
670 fprintf(output, "Usage: editcap [options] ... <infile> <outfile> [ <packet#>[-<packet#>] ... ]\n");
671 fprintf(output, "\n");
672 fprintf(output, "<infile> and <outfile> must both be present.\n");
673 fprintf(output, "A single packet or a range of packets can be selected.\n");
674 fprintf(output, "\n");
675 fprintf(output, "Packet selection:\n");
676 fprintf(output, " -r keep the selected packets; default is to delete them.\n");
677 fprintf(output, " -A <start time> only output packets whose timestamp is after (or equal\n");
678 fprintf(output, " to) the given time (format as YYYY-MM-DD hh:mm:ss).\n");
679 fprintf(output, " -B <stop time> only output packets whose timestamp is before the\n");
680 fprintf(output, " given time (format as YYYY-MM-DD hh:mm:ss).\n");
681 fprintf(output, "\n");
682 fprintf(output, "Duplicate packet removal:\n");
683 fprintf(output, " -d remove packet if duplicate (window == %d).\n", DEFAULT_DUP_DEPTH);
684 fprintf(output, " -D <dup window> remove packet if duplicate; configurable <dup window>\n");
685 fprintf(output, " Valid <dup window> values are 0 to %d.\n", MAX_DUP_DEPTH);
686 fprintf(output, " NOTE: A <dup window> of 0 with -v (verbose option) is\n");
687 fprintf(output, " useful to print MD5 hashes.\n");
688 fprintf(output, " -w <dup time window> remove packet if duplicate packet is found EQUAL TO OR\n");
689 fprintf(output, " LESS THAN <dup time window> prior to current packet.\n");
690 fprintf(output, " A <dup time window> is specified in relative seconds\n");
691 fprintf(output, " (e.g. 0.000001).\n");
692 fprintf(output, "\n");
693 fprintf(output, " NOTE: The use of the 'Duplicate packet removal' options with\n");
694 fprintf(output, " other editcap options except -v may not always work as expected.\n");
695 fprintf(output, " Specifically the -r, -t or -S options will very likely NOT have the\n");
696 fprintf(output, " desired effect if combined with the -d, -D or -w.\n");
697 fprintf(output, "\n");
698 fprintf(output, "Packet manipulation:\n");
699 fprintf(output, " -s <snaplen> truncate each packet to max. <snaplen> bytes of data.\n");
700 fprintf(output, " -C <choplen> chop each packet at the end by <choplen> bytes.\n");
701 fprintf(output, " -t <time adjustment> adjust the timestamp of each packet;\n");
702 fprintf(output, " <time adjustment> is in relative seconds (e.g. -0.5).\n");
703 fprintf(output, " -S <strict adjustment> adjust timestamp of packets if necessary to insure\n");
704 fprintf(output, " strict chronological increasing order. The <strict\n");
705 fprintf(output, " adjustment> is specified in relative seconds with\n");
706 fprintf(output, " values of 0 or 0.000001 being the most reasonable.\n");
707 fprintf(output, " A negative adjustment value will modify timestamps so\n");
708 fprintf(output, " that each packet's delta time is the absolute value\n");
709 fprintf(output, " of the adjustment specified. A value of -0 will set\n");
710 fprintf(output, " all packets to the timestamp of the first packet.\n");
711 fprintf(output, " -E <error probability> set the probability (between 0.0 and 1.0 incl.)\n");
712 fprintf(output, " that a particular packet byte will be randomly changed.\n");
713 fprintf(output, "\n");
714 fprintf(output, "Output File(s):\n");
715 fprintf(output, " -c <packets per file> split the packet output to different files\n");
716 fprintf(output, " based on uniform packet counts\n");
717 fprintf(output, " with a maximum of <packets per file> each.\n");
718 fprintf(output, " -i <seconds per file> split the packet output to different files\n");
719 fprintf(output, " based on uniform time intervals\n");
720 fprintf(output, " with a maximum of <seconds per file> each.\n");
721 fprintf(output, " -F <capture type> set the output file type; default is libpcap.\n");
722 fprintf(output, " an empty \"-F\" option will list the file types.\n");
723 fprintf(output, " -T <encap type> set the output file encapsulation type;\n");
724 fprintf(output, " default is the same as the input file.\n");
725 fprintf(output, " an empty \"-T\" option will list the encapsulation types.\n");
726 fprintf(output, "\n");
727 fprintf(output, "Miscellaneous:\n");
728 fprintf(output, " -h display this help and exit.\n");
729 fprintf(output, " -v verbose output.\n");
730 fprintf(output, " If -v is used with any of the 'Duplicate Packet\n");
731 fprintf(output, " Removal' options (-d, -D or -w) then Packet lengths\n");
732 fprintf(output, " and MD5 hashes are printed to standard-out.\n");
733 fprintf(output, "\n");
737 const char *sstr; /* The short string */
738 const char *lstr; /* The long string */
742 string_compare(gconstpointer a, gconstpointer b)
744 return strcmp(((struct string_elem *)a)->sstr,
745 ((struct string_elem *)b)->sstr);
749 string_elem_print(gpointer data, gpointer not_used _U_)
751 fprintf(stderr, " %s - %s\n",
752 ((struct string_elem *)data)->sstr,
753 ((struct string_elem *)data)->lstr);
757 list_capture_types(void) {
759 struct string_elem *captypes;
762 captypes = g_malloc(sizeof(struct string_elem) * WTAP_NUM_FILE_TYPES);
763 fprintf(stderr, "editcap: The available capture file types for the \"-F\" flag are:\n");
764 for (i = 0; i < WTAP_NUM_FILE_TYPES; i++) {
765 if (wtap_dump_can_open(i)) {
766 captypes[i].sstr = wtap_file_type_short_string(i);
767 captypes[i].lstr = wtap_file_type_string(i);
768 list = g_slist_insert_sorted(list, &captypes[i], string_compare);
771 g_slist_foreach(list, string_elem_print, NULL);
777 list_encap_types(void) {
779 struct string_elem *encaps;
782 encaps = g_malloc(sizeof(struct string_elem) * WTAP_NUM_ENCAP_TYPES);
783 fprintf(stderr, "editcap: The available encapsulation types for the \"-T\" flag are:\n");
784 for (i = 0; i < WTAP_NUM_ENCAP_TYPES; i++) {
785 encaps[i].sstr = wtap_encap_short_string(i);
786 if (encaps[i].sstr != NULL) {
787 encaps[i].lstr = wtap_encap_string(i);
788 list = g_slist_insert_sorted(list, &encaps[i], string_compare);
791 g_slist_foreach(list, string_elem_print, NULL);
798 * Don't report failures to load plugins because most (non-wiretap) plugins
799 * *should* fail to load (because we're not linked against libwireshark and
800 * dissector plugins need libwireshark).
803 failure_message(const char *msg_format _U_, va_list ap _U_)
810 main(int argc, char *argv[])
817 unsigned int snaplen = 0; /* No limit */
818 unsigned int choplen = 0; /* No chop */
819 wtap_dumper *pdh = NULL;
821 unsigned duplicate_count = 0;
823 struct wtap_pkthdr snap_phdr;
824 const struct wtap_pkthdr *phdr;
827 int split_packet_count = 0;
828 int written_count = 0;
829 char *filename = NULL;
831 int secs_per_block = 0;
833 nstime_t block_start;
834 gchar *fprefix = NULL;
835 gchar *fsuffix = NULL;
838 char* init_progfile_dir_error;
842 * Get credential information for later use.
844 init_process_policies();
847 /* Register wiretap plugins */
848 if ((init_progfile_dir_error = init_progfile_dir(argv[0], main))) {
849 g_warning("capinfos: init_progfile_dir(): %s", init_progfile_dir_error);
850 g_free(init_progfile_dir_error);
852 init_report_err(failure_message,NULL,NULL,NULL);
857 /* Process the options */
858 while ((opt = getopt(argc, argv, "A:B:c:C:dD:E:F:hrs:i:t:S:T:vw:")) !=-1) {
863 err_prob = strtod(optarg, &p);
864 if (p == optarg || err_prob < 0.0 || err_prob > 1.0) {
865 fprintf(stderr, "editcap: probability \"%s\" must be between 0.0 and 1.0\n",
869 srand( (unsigned int) (time(NULL) + getpid()) );
873 out_file_type = wtap_short_string_to_file_type(optarg);
874 if (out_file_type < 0) {
875 fprintf(stderr, "editcap: \"%s\" isn't a valid capture file type\n\n",
877 list_capture_types();
883 split_packet_count = strtol(optarg, &p, 10);
884 if (p == optarg || *p != '\0') {
885 fprintf(stderr, "editcap: \"%s\" isn't a valid packet count\n",
889 if (split_packet_count <= 0) {
890 fprintf(stderr, "editcap: \"%d\" packet count must be larger than zero\n",
897 choplen = strtol(optarg, &p, 10);
898 if (p == optarg || *p != '\0') {
899 fprintf(stderr, "editcap: \"%s\" isn't a valid chop length\n",
907 dup_detect_by_time = FALSE;
908 dup_window = DEFAULT_DUP_DEPTH;
913 dup_detect_by_time = FALSE;
914 dup_window = strtol(optarg, &p, 10);
915 if (p == optarg || *p != '\0') {
916 fprintf(stderr, "editcap: \"%s\" isn't a valid dupicate window value\n",
920 if (dup_window < 0 || dup_window > MAX_DUP_DEPTH) {
921 fprintf(stderr, "editcap: \"%d\" duplicate window value must be between 0 and %d inclusive.\n",
922 dup_window, MAX_DUP_DEPTH);
929 dup_detect_by_time = TRUE;
930 dup_window = MAX_DUP_DEPTH;
931 set_rel_time(optarg);
934 case '?': /* Bad options if GNU getopt */
937 list_capture_types();
954 keep_em = !keep_em; /* Just invert */
958 snaplen = strtol(optarg, &p, 10);
959 if (p == optarg || *p != '\0') {
960 fprintf(stderr, "editcap: \"%s\" isn't a valid snapshot length\n",
967 set_time_adjustment(optarg);
971 set_strict_time_adj(optarg);
972 do_strict_time_adjustment = TRUE;
976 out_frame_type = wtap_short_string_to_encap(optarg);
977 if (out_frame_type < 0) {
978 fprintf(stderr, "editcap: \"%s\" isn't a valid encapsulation type\n\n",
986 verbose = !verbose; /* Just invert */
989 case 'i': /* break capture file based on time interval */
990 secs_per_block = atoi(optarg);
991 if(secs_per_block <= 0) {
992 fprintf(stderr, "editcap: \"%s\" isn't a valid time interval\n\n", optarg);
1001 memset(&starttm,0,sizeof(struct tm));
1003 if(!strptime(optarg,"%Y-%m-%d %T",&starttm)) {
1004 fprintf(stderr, "editcap: \"%s\" isn't a valid time format\n\n", optarg);
1008 check_startstop = TRUE;
1009 starttm.tm_isdst = -1;
1011 starttime = mktime(&starttm);
1019 memset(&stoptm,0,sizeof(struct tm));
1021 if(!strptime(optarg,"%Y-%m-%d %T",&stoptm)) {
1022 fprintf(stderr, "editcap: \"%s\" isn't a valid time format\n\n", optarg);
1025 check_startstop = TRUE;
1026 stoptm.tm_isdst = -1;
1027 stoptime = mktime(&stoptm);
1035 printf("Optind = %i, argc = %i\n", optind, argc);
1038 if ((argc - optind) < 1) {
1045 if (check_startstop && !stoptime) {
1047 /* XXX: will work until 2035 */
1048 memset(&stoptm,0,sizeof(struct tm));
1049 stoptm.tm_year = 135;
1050 stoptm.tm_mday = 31;
1053 stoptime = mktime(&stoptm);
1056 nstime_set_unset(&block_start);
1058 if (starttime > stoptime) {
1059 fprintf(stderr, "editcap: start time is after the stop time\n");
1063 if (split_packet_count > 0 && secs_per_block > 0) {
1064 fprintf(stderr, "editcap: can't split on both packet count and time interval\n");
1065 fprintf(stderr, "editcap: at the same time\n");
1069 wth = wtap_open_offline(argv[optind], &err, &err_info, FALSE);
1072 fprintf(stderr, "editcap: Can't open %s: %s\n", argv[optind],
1073 wtap_strerror(err));
1076 case WTAP_ERR_UNSUPPORTED:
1077 case WTAP_ERR_UNSUPPORTED_ENCAP:
1078 case WTAP_ERR_BAD_RECORD:
1079 fprintf(stderr, "(%s)\n", err_info);
1088 fprintf(stderr, "File %s is a %s capture file.\n", argv[optind],
1089 wtap_file_type_string(wtap_file_type(wth)));
1093 * Now, process the rest, if any ... we only write if there is an extra
1094 * argument or so ...
1097 if ((argc - optind) >= 2) {
1099 if (out_frame_type == -2)
1100 out_frame_type = wtap_file_encap(wth);
1102 for (i = optind + 2; i < argc; i++)
1103 if (add_selection(argv[i]) == FALSE)
1106 if (dup_detect || dup_detect_by_time) {
1107 for (i = 0; i < dup_window; i++) {
1108 memset(&fd_hash[i].digest, 0, 16);
1110 nstime_set_unset(&fd_hash[i].time);
1114 while (wtap_read(wth, &err, &err_info, &data_offset)) {
1115 phdr = wtap_phdr(wth);
1117 if (nstime_is_unset(&block_start)) { /* should only be the first packet */
1118 block_start.secs = phdr->ts.secs;
1119 block_start.nsecs = phdr->ts.nsecs;
1121 if (split_packet_count > 0 || secs_per_block > 0) {
1122 if (!fileset_extract_prefix_suffix(argv[optind+1], &fprefix, &fsuffix))
1125 filename = fileset_get_filename_by_pattern(block_cnt++, &phdr->ts, fprefix, fsuffix);
1127 filename = g_strdup(argv[optind+1]);
1129 pdh = wtap_dump_open(filename, out_file_type,
1130 out_frame_type, wtap_snapshot_length(wth),
1131 FALSE /* compressed */, &err);
1133 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1134 wtap_strerror(err));
1141 if (secs_per_block > 0) {
1142 while ((phdr->ts.secs - block_start.secs > secs_per_block) ||
1143 (phdr->ts.secs - block_start.secs == secs_per_block &&
1144 phdr->ts.nsecs >= block_start.nsecs )) { /* time for the next file */
1146 if (!wtap_dump_close(pdh, &err)) {
1147 fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
1148 wtap_strerror(err));
1151 block_start.secs = block_start.secs + secs_per_block; /* reset for next interval */
1153 filename = fileset_get_filename_by_pattern(block_cnt++, &phdr->ts, fprefix, fsuffix);
1157 fprintf(stderr, "Continuing writing in file %s\n", filename);
1160 pdh = wtap_dump_open(filename, out_file_type,
1161 out_frame_type, wtap_snapshot_length(wth), FALSE /* compressed */, &err);
1164 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1165 wtap_strerror(err));
1171 if (split_packet_count > 0) {
1173 /* time for the next file? */
1174 if (written_count > 0 &&
1175 written_count % split_packet_count == 0) {
1176 if (!wtap_dump_close(pdh, &err)) {
1177 fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
1178 wtap_strerror(err));
1183 filename = fileset_get_filename_by_pattern(block_cnt++, &phdr->ts, fprefix, fsuffix);
1187 fprintf(stderr, "Continuing writing in file %s\n", filename);
1190 pdh = wtap_dump_open(filename, out_file_type,
1191 out_frame_type, wtap_snapshot_length(wth), FALSE /* compressed */, &err);
1193 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1194 wtap_strerror(err));
1200 check_ts = check_timestamp(wth);
1202 if ( ((check_startstop && check_ts) || (!check_startstop && !check_ts)) && ((!selected(count) && !keep_em) ||
1203 (selected(count) && keep_em)) ) {
1205 if (verbose && !dup_detect && !dup_detect_by_time)
1206 printf("Packet: %u\n", count);
1208 /* We simply write it, perhaps after truncating it; we could do other
1209 things, like modify it. */
1211 phdr = wtap_phdr(wth);
1213 if (choplen != 0 && phdr->caplen > choplen) {
1215 snap_phdr.caplen -= choplen;
1219 if (snaplen != 0 && phdr->caplen > snaplen) {
1221 snap_phdr.caplen = snaplen;
1226 * Do we adjust timestamps to insure strict chronologically order?
1229 if (do_strict_time_adjustment) {
1230 if (previous_time.secs || previous_time.nsecs) {
1231 if (!strict_time_adj.is_negative) {
1235 current.secs = phdr->ts.secs;
1236 current.nsecs = phdr->ts.nsecs;
1238 nstime_delta(&delta, ¤t, &previous_time);
1240 if (delta.secs < 0 || delta.nsecs < 0)
1243 * A negative delta indicates that the current packet
1244 * has an absolute timestamp less than the previous packet
1245 * that it is being compared to. This is NOT a normal
1246 * situation since trace files usually have packets in
1247 * chronological order (oldest to newest).
1249 /* printf("++out of order, need to adjust this packet!\n"); */
1251 snap_phdr.ts.secs = previous_time.secs + strict_time_adj.tv.tv_sec;
1252 snap_phdr.ts.nsecs = previous_time.nsecs;
1253 if (snap_phdr.ts.nsecs + strict_time_adj.tv.tv_usec * 1000 > ONE_MILLION * 1000) {
1255 snap_phdr.ts.secs++;
1256 snap_phdr.ts.nsecs += (strict_time_adj.tv.tv_usec - ONE_MILLION) * 1000;
1258 snap_phdr.ts.nsecs += strict_time_adj.tv.tv_usec * 1000;
1264 * A negative strict time adjustment is requested.
1265 * Unconditionally set each timestamp to previous
1266 * packet's timestamp plus delta.
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;
1281 previous_time.secs = phdr->ts.secs;
1282 previous_time.nsecs = phdr->ts.nsecs;
1285 /* assume that if the frame's tv_sec is 0, then
1286 * the timestamp isn't supported */
1287 if (phdr->ts.secs > 0 && time_adj.tv.tv_sec != 0) {
1289 if (time_adj.is_negative)
1290 snap_phdr.ts.secs -= time_adj.tv.tv_sec;
1292 snap_phdr.ts.secs += time_adj.tv.tv_sec;
1296 /* assume that if the frame's tv_sec is 0, then
1297 * the timestamp isn't supported */
1298 if (phdr->ts.secs > 0 && time_adj.tv.tv_usec != 0) {
1300 if (time_adj.is_negative) { /* subtract */
1301 if (snap_phdr.ts.nsecs/1000 < time_adj.tv.tv_usec) { /* borrow */
1302 snap_phdr.ts.secs--;
1303 snap_phdr.ts.nsecs += ONE_MILLION * 1000;
1305 snap_phdr.ts.nsecs -= time_adj.tv.tv_usec * 1000;
1307 if (snap_phdr.ts.nsecs + time_adj.tv.tv_usec * 1000 > ONE_MILLION * 1000) {
1309 snap_phdr.ts.secs++;
1310 snap_phdr.ts.nsecs += (time_adj.tv.tv_usec - ONE_MILLION) * 1000;
1312 snap_phdr.ts.nsecs += time_adj.tv.tv_usec * 1000;
1318 /* suppress duplicates by packet window */
1320 buf = wtap_buf_ptr(wth);
1321 if (is_duplicate(buf, phdr->caplen)) {
1323 fprintf(stdout, "Skipped: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1324 for (i = 0; i < 16; i++) {
1325 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1327 fprintf(stdout, "\n");
1334 fprintf(stdout, "Packet: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1335 for (i = 0; i < 16; i++) {
1336 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1338 fprintf(stdout, "\n");
1343 /* suppress duplicates by time window */
1344 if (dup_detect_by_time) {
1347 current.secs = phdr->ts.secs;
1348 current.nsecs = phdr->ts.nsecs;
1350 buf = wtap_buf_ptr(wth);
1352 if (is_duplicate_rel_time(buf, phdr->caplen, ¤t)) {
1354 fprintf(stdout, "Skipped: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1355 for (i = 0; i < 16; i++) {
1356 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1358 fprintf(stdout, "\n");
1365 fprintf(stdout, "Packet: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1366 for (i = 0; i < 16; i++) {
1367 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1369 fprintf(stdout, "\n");
1374 /* Random error mutation */
1375 if (err_prob > 0.0) {
1376 int real_data_start = 0;
1377 buf = wtap_buf_ptr(wth);
1378 /* Protect non-protocol data */
1379 if (wtap_file_type(wth) == WTAP_FILE_CATAPULT_DCT2000) {
1380 real_data_start = find_dct2000_real_data(buf);
1382 for (i = real_data_start; i < (int) phdr->caplen; i++) {
1383 if (rand() <= err_prob * RAND_MAX) {
1384 err_type = rand() / (RAND_MAX / ERR_WT_TOTAL + 1);
1386 if (err_type < ERR_WT_BIT) {
1387 buf[i] ^= 1 << (rand() / (RAND_MAX / 8 + 1));
1388 err_type = ERR_WT_TOTAL;
1390 err_type -= ERR_WT_BYTE;
1393 if (err_type < ERR_WT_BYTE) {
1394 buf[i] = rand() / (RAND_MAX / 255 + 1);
1395 err_type = ERR_WT_TOTAL;
1397 err_type -= ERR_WT_BYTE;
1400 if (err_type < ERR_WT_ALNUM) {
1401 buf[i] = ALNUM_CHARS[rand() / (RAND_MAX / ALNUM_LEN + 1)];
1402 err_type = ERR_WT_TOTAL;
1404 err_type -= ERR_WT_ALNUM;
1407 if (err_type < ERR_WT_FMT) {
1408 if ((unsigned int)i < phdr->caplen - 2)
1409 strncpy((char*) &buf[i], "%s", 2);
1410 err_type = ERR_WT_TOTAL;
1412 err_type -= ERR_WT_FMT;
1415 if (err_type < ERR_WT_AA) {
1416 for (j = i; j < (int) phdr->caplen; j++) {
1425 if (!wtap_dump(pdh, phdr, wtap_pseudoheader(wth), wtap_buf_ptr(wth),
1427 fprintf(stderr, "editcap: Error writing to %s: %s\n",
1428 filename, wtap_strerror(err));
1440 /* Print a message noting that the read failed somewhere along the line. */
1442 "editcap: An error occurred while reading \"%s\": %s.\n",
1443 argv[optind], wtap_strerror(err));
1446 case WTAP_ERR_UNSUPPORTED:
1447 case WTAP_ERR_UNSUPPORTED_ENCAP:
1448 case WTAP_ERR_BAD_RECORD:
1449 fprintf(stderr, "(%s)\n", err_info);
1456 /* No valid packages found, open the outfile so we can write an empty header */
1458 filename = g_strdup(argv[optind+1]);
1460 pdh = wtap_dump_open(filename, out_file_type,
1461 out_frame_type, wtap_snapshot_length(wth), FALSE /* compressed */, &err);
1463 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1464 wtap_strerror(err));
1469 if (!wtap_dump_close(pdh, &err)) {
1471 fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
1472 wtap_strerror(err));
1480 fprintf(stdout, "%u packet%s seen, %u packet%s skipped with duplicate window of %u packets.\n",
1481 count - 1, plurality(count - 1, "", "s"),
1482 duplicate_count, plurality(duplicate_count, "", "s"), dup_window);
1483 } else if (dup_detect_by_time) {
1484 fprintf(stdout, "%u packet%s seen, %u packet%s skipped with duplicate time window equal to or less than %ld.%09ld seconds.\n",
1485 count - 1, plurality(count - 1, "", "s"),
1486 duplicate_count, plurality(duplicate_count, "", "s"),
1487 (long)relative_time_window.secs, (long int)relative_time_window.nsecs);
1493 /* Skip meta-information read from file to return offset of real
1495 static int find_dct2000_real_data(guint8 *buf)
1499 for (n=0; buf[n] != '\0'; n++); /* Context name */
1501 n++; /* Context port number */
1502 for (; buf[n] != '\0'; n++); /* Timestamp */
1504 for (; buf[n] != '\0'; n++); /* Protocol name */
1506 for (; buf[n] != '\0'; n++); /* Variant number (as string) */
1508 for (; buf[n] != '\0'; n++); /* Outhdr (as string) */
1510 n += 2; /* Direction & encap */