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(const struct string_elem *a, const struct string_elem *b)
744 return strcmp(a->sstr, b->sstr);
748 string_elem_print(const struct string_elem *data, void *not_used)
750 fprintf(stderr, " %s - %s\n", data->sstr, data->lstr);
754 list_capture_types(void) {
756 struct string_elem *captypes;
759 captypes = g_malloc(sizeof(struct string_elem) * WTAP_NUM_FILE_TYPES);
760 fprintf(stderr, "editcap: The available capture file types for the \"-F\" flag are:\n");
761 for (i = 0; i < WTAP_NUM_FILE_TYPES; i++) {
762 if (wtap_dump_can_open(i)) {
763 captypes[i].sstr = wtap_file_type_short_string(i);
764 captypes[i].lstr = wtap_file_type_string(i);
765 list = g_slist_insert_sorted(list, &captypes[i], string_compare);
768 g_slist_foreach(list, string_elem_print, NULL);
774 list_encap_types(void) {
776 struct string_elem *encaps;
779 encaps = g_malloc(sizeof(struct string_elem) * WTAP_NUM_ENCAP_TYPES);
780 fprintf(stderr, "editcap: The available encapsulation types for the \"-T\" flag are:\n");
781 for (i = 0; i < WTAP_NUM_ENCAP_TYPES; i++) {
782 encaps[i].sstr = wtap_encap_short_string(i);
783 if (encaps[i].sstr != NULL) {
784 encaps[i].lstr = wtap_encap_string(i);
785 list = g_slist_insert_sorted(list, &encaps[i], string_compare);
788 g_slist_foreach(list, string_elem_print, NULL);
795 * Don't report failures to load plugins because most (non-wiretap) plugins
796 * *should* fail to load (because we're not linked against libwireshark and
797 * dissector plugins need libwireshark).
800 failure_message(const char *msg_format _U_, va_list ap _U_)
807 main(int argc, char *argv[])
814 unsigned int snaplen = 0; /* No limit */
815 unsigned int choplen = 0; /* No chop */
816 wtap_dumper *pdh = NULL;
818 unsigned duplicate_count = 0;
820 struct wtap_pkthdr snap_phdr;
821 const struct wtap_pkthdr *phdr;
824 int split_packet_count = 0;
825 int written_count = 0;
826 char *filename = NULL;
828 int secs_per_block = 0;
830 nstime_t block_start;
831 gchar *fprefix = NULL;
832 gchar *fsuffix = NULL;
835 char* init_progfile_dir_error;
839 * Get credential information for later use.
841 init_process_policies();
844 /* Register wiretap plugins */
845 if ((init_progfile_dir_error = init_progfile_dir(argv[0], main))) {
846 g_warning("capinfos: init_progfile_dir(): %s", init_progfile_dir_error);
847 g_free(init_progfile_dir_error);
849 init_report_err(failure_message,NULL,NULL,NULL);
854 /* Process the options */
855 while ((opt = getopt(argc, argv, "A:B:c:C:dD:E:F:hrs:i:t:S:T:vw:")) !=-1) {
860 err_prob = strtod(optarg, &p);
861 if (p == optarg || err_prob < 0.0 || err_prob > 1.0) {
862 fprintf(stderr, "editcap: probability \"%s\" must be between 0.0 and 1.0\n",
866 srand( (unsigned int) (time(NULL) + getpid()) );
870 out_file_type = wtap_short_string_to_file_type(optarg);
871 if (out_file_type < 0) {
872 fprintf(stderr, "editcap: \"%s\" isn't a valid capture file type\n\n",
874 list_capture_types();
880 split_packet_count = strtol(optarg, &p, 10);
881 if (p == optarg || *p != '\0') {
882 fprintf(stderr, "editcap: \"%s\" isn't a valid packet count\n",
886 if (split_packet_count <= 0) {
887 fprintf(stderr, "editcap: \"%d\" packet count must be larger than zero\n",
894 choplen = strtol(optarg, &p, 10);
895 if (p == optarg || *p != '\0') {
896 fprintf(stderr, "editcap: \"%s\" isn't a valid chop length\n",
904 dup_detect_by_time = FALSE;
905 dup_window = DEFAULT_DUP_DEPTH;
910 dup_detect_by_time = FALSE;
911 dup_window = strtol(optarg, &p, 10);
912 if (p == optarg || *p != '\0') {
913 fprintf(stderr, "editcap: \"%s\" isn't a valid dupicate window value\n",
917 if (dup_window < 0 || dup_window > MAX_DUP_DEPTH) {
918 fprintf(stderr, "editcap: \"%d\" duplicate window value must be between 0 and %d inclusive.\n",
919 dup_window, MAX_DUP_DEPTH);
926 dup_detect_by_time = TRUE;
927 dup_window = MAX_DUP_DEPTH;
928 set_rel_time(optarg);
931 case '?': /* Bad options if GNU getopt */
934 list_capture_types();
951 keep_em = !keep_em; /* Just invert */
955 snaplen = strtol(optarg, &p, 10);
956 if (p == optarg || *p != '\0') {
957 fprintf(stderr, "editcap: \"%s\" isn't a valid snapshot length\n",
964 set_time_adjustment(optarg);
968 set_strict_time_adj(optarg);
969 do_strict_time_adjustment = TRUE;
973 out_frame_type = wtap_short_string_to_encap(optarg);
974 if (out_frame_type < 0) {
975 fprintf(stderr, "editcap: \"%s\" isn't a valid encapsulation type\n\n",
983 verbose = !verbose; /* Just invert */
986 case 'i': /* break capture file based on time interval */
987 secs_per_block = atoi(optarg);
988 if(secs_per_block <= 0) {
989 fprintf(stderr, "editcap: \"%s\" isn't a valid time interval\n\n", optarg);
998 memset(&starttm,0,sizeof(struct tm));
1000 if(!strptime(optarg,"%Y-%m-%d %T",&starttm)) {
1001 fprintf(stderr, "editcap: \"%s\" isn't a valid time format\n\n", optarg);
1005 check_startstop = TRUE;
1006 starttm.tm_isdst = -1;
1008 starttime = mktime(&starttm);
1016 memset(&stoptm,0,sizeof(struct tm));
1018 if(!strptime(optarg,"%Y-%m-%d %T",&stoptm)) {
1019 fprintf(stderr, "editcap: \"%s\" isn't a valid time format\n\n", optarg);
1022 check_startstop = TRUE;
1023 stoptm.tm_isdst = -1;
1024 stoptime = mktime(&stoptm);
1032 printf("Optind = %i, argc = %i\n", optind, argc);
1035 if ((argc - optind) < 1) {
1042 if (check_startstop && !stoptime) {
1044 /* XXX: will work until 2035 */
1045 memset(&stoptm,0,sizeof(struct tm));
1046 stoptm.tm_year = 135;
1047 stoptm.tm_mday = 31;
1050 stoptime = mktime(&stoptm);
1053 nstime_set_unset(&block_start);
1055 if (starttime > stoptime) {
1056 fprintf(stderr, "editcap: start time is after the stop time\n");
1060 if (split_packet_count > 0 && secs_per_block > 0) {
1061 fprintf(stderr, "editcap: can't split on both packet count and time interval\n");
1062 fprintf(stderr, "editcap: at the same time\n");
1066 wth = wtap_open_offline(argv[optind], &err, &err_info, FALSE);
1069 fprintf(stderr, "editcap: Can't open %s: %s\n", argv[optind],
1070 wtap_strerror(err));
1073 case WTAP_ERR_UNSUPPORTED:
1074 case WTAP_ERR_UNSUPPORTED_ENCAP:
1075 case WTAP_ERR_BAD_RECORD:
1076 fprintf(stderr, "(%s)\n", err_info);
1085 fprintf(stderr, "File %s is a %s capture file.\n", argv[optind],
1086 wtap_file_type_string(wtap_file_type(wth)));
1090 * Now, process the rest, if any ... we only write if there is an extra
1091 * argument or so ...
1094 if ((argc - optind) >= 2) {
1096 if (out_frame_type == -2)
1097 out_frame_type = wtap_file_encap(wth);
1099 for (i = optind + 2; i < argc; i++)
1100 if (add_selection(argv[i]) == FALSE)
1103 if (dup_detect || dup_detect_by_time) {
1104 for (i = 0; i < dup_window; i++) {
1105 memset(&fd_hash[i].digest, 0, 16);
1107 nstime_set_unset(&fd_hash[i].time);
1111 while (wtap_read(wth, &err, &err_info, &data_offset)) {
1112 phdr = wtap_phdr(wth);
1114 if (nstime_is_unset(&block_start)) { /* should only be the first packet */
1115 block_start.secs = phdr->ts.secs;
1116 block_start.nsecs = phdr->ts.nsecs;
1118 if (split_packet_count > 0 || secs_per_block > 0) {
1119 if (!fileset_extract_prefix_suffix(argv[optind+1], &fprefix, &fsuffix))
1122 filename = fileset_get_filename_by_pattern(block_cnt++, &phdr->ts, fprefix, fsuffix);
1124 filename = g_strdup(argv[optind+1]);
1126 pdh = wtap_dump_open(filename, out_file_type,
1127 out_frame_type, wtap_snapshot_length(wth),
1128 FALSE /* compressed */, &err);
1130 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1131 wtap_strerror(err));
1138 if (secs_per_block > 0) {
1139 while ((phdr->ts.secs - block_start.secs > secs_per_block) ||
1140 (phdr->ts.secs - block_start.secs == secs_per_block &&
1141 phdr->ts.nsecs >= block_start.nsecs )) { /* time for the next file */
1143 if (!wtap_dump_close(pdh, &err)) {
1144 fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
1145 wtap_strerror(err));
1148 block_start.secs = block_start.secs + secs_per_block; /* reset for next interval */
1150 filename = fileset_get_filename_by_pattern(block_cnt++, &phdr->ts, fprefix, fsuffix);
1154 fprintf(stderr, "Continuing writing in file %s\n", filename);
1157 pdh = wtap_dump_open(filename, out_file_type,
1158 out_frame_type, wtap_snapshot_length(wth), FALSE /* compressed */, &err);
1161 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1162 wtap_strerror(err));
1168 if (split_packet_count > 0) {
1170 /* time for the next file? */
1171 if (written_count > 0 &&
1172 written_count % split_packet_count == 0) {
1173 if (!wtap_dump_close(pdh, &err)) {
1174 fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
1175 wtap_strerror(err));
1180 filename = fileset_get_filename_by_pattern(block_cnt++, &phdr->ts, fprefix, fsuffix);
1184 fprintf(stderr, "Continuing writing in file %s\n", filename);
1187 pdh = wtap_dump_open(filename, out_file_type,
1188 out_frame_type, wtap_snapshot_length(wth), FALSE /* compressed */, &err);
1190 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1191 wtap_strerror(err));
1197 check_ts = check_timestamp(wth);
1199 if ( ((check_startstop && check_ts) || (!check_startstop && !check_ts)) && ((!selected(count) && !keep_em) ||
1200 (selected(count) && keep_em)) ) {
1202 if (verbose && !dup_detect && !dup_detect_by_time)
1203 printf("Packet: %u\n", count);
1205 /* We simply write it, perhaps after truncating it; we could do other
1206 things, like modify it. */
1208 phdr = wtap_phdr(wth);
1210 if (choplen != 0 && phdr->caplen > choplen) {
1212 snap_phdr.caplen -= choplen;
1216 if (snaplen != 0 && phdr->caplen > snaplen) {
1218 snap_phdr.caplen = snaplen;
1223 * Do we adjust timestamps to insure strict chronologically order?
1226 if (do_strict_time_adjustment) {
1227 if (previous_time.secs || previous_time.nsecs) {
1228 if (!strict_time_adj.is_negative) {
1232 current.secs = phdr->ts.secs;
1233 current.nsecs = phdr->ts.nsecs;
1235 nstime_delta(&delta, ¤t, &previous_time);
1237 if (delta.secs < 0 || delta.nsecs < 0)
1240 * A negative delta indicates that the current packet
1241 * has an absolute timestamp less than the previous packet
1242 * that it is being compared to. This is NOT a normal
1243 * situation since trace files usually have packets in
1244 * chronological order (oldest to newest).
1246 /* printf("++out of order, need to adjust this packet!\n"); */
1248 snap_phdr.ts.secs = previous_time.secs + strict_time_adj.tv.tv_sec;
1249 snap_phdr.ts.nsecs = previous_time.nsecs;
1250 if (snap_phdr.ts.nsecs + strict_time_adj.tv.tv_usec * 1000 > ONE_MILLION * 1000) {
1252 snap_phdr.ts.secs++;
1253 snap_phdr.ts.nsecs += (strict_time_adj.tv.tv_usec - ONE_MILLION) * 1000;
1255 snap_phdr.ts.nsecs += strict_time_adj.tv.tv_usec * 1000;
1261 * A negative strict time adjustment is requested.
1262 * Unconditionally set each timestamp to previous
1263 * packet's timestamp plus delta.
1266 snap_phdr.ts.secs = previous_time.secs + strict_time_adj.tv.tv_sec;
1267 snap_phdr.ts.nsecs = previous_time.nsecs;
1268 if (snap_phdr.ts.nsecs + strict_time_adj.tv.tv_usec * 1000 > ONE_MILLION * 1000) {
1270 snap_phdr.ts.secs++;
1271 snap_phdr.ts.nsecs += (strict_time_adj.tv.tv_usec - ONE_MILLION) * 1000;
1273 snap_phdr.ts.nsecs += strict_time_adj.tv.tv_usec * 1000;
1278 previous_time.secs = phdr->ts.secs;
1279 previous_time.nsecs = phdr->ts.nsecs;
1282 /* assume that if the frame's tv_sec is 0, then
1283 * the timestamp isn't supported */
1284 if (phdr->ts.secs > 0 && time_adj.tv.tv_sec != 0) {
1286 if (time_adj.is_negative)
1287 snap_phdr.ts.secs -= time_adj.tv.tv_sec;
1289 snap_phdr.ts.secs += time_adj.tv.tv_sec;
1293 /* assume that if the frame's tv_sec is 0, then
1294 * the timestamp isn't supported */
1295 if (phdr->ts.secs > 0 && time_adj.tv.tv_usec != 0) {
1297 if (time_adj.is_negative) { /* subtract */
1298 if (snap_phdr.ts.nsecs/1000 < time_adj.tv.tv_usec) { /* borrow */
1299 snap_phdr.ts.secs--;
1300 snap_phdr.ts.nsecs += ONE_MILLION * 1000;
1302 snap_phdr.ts.nsecs -= time_adj.tv.tv_usec * 1000;
1304 if (snap_phdr.ts.nsecs + time_adj.tv.tv_usec * 1000 > ONE_MILLION * 1000) {
1306 snap_phdr.ts.secs++;
1307 snap_phdr.ts.nsecs += (time_adj.tv.tv_usec - ONE_MILLION) * 1000;
1309 snap_phdr.ts.nsecs += time_adj.tv.tv_usec * 1000;
1315 /* suppress duplicates by packet window */
1317 buf = wtap_buf_ptr(wth);
1318 if (is_duplicate(buf, phdr->caplen)) {
1320 fprintf(stdout, "Skipped: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1321 for (i = 0; i < 16; i++) {
1322 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1324 fprintf(stdout, "\n");
1331 fprintf(stdout, "Packet: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1332 for (i = 0; i < 16; i++) {
1333 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1335 fprintf(stdout, "\n");
1340 /* suppress duplicates by time window */
1341 if (dup_detect_by_time) {
1344 current.secs = phdr->ts.secs;
1345 current.nsecs = phdr->ts.nsecs;
1347 buf = wtap_buf_ptr(wth);
1349 if (is_duplicate_rel_time(buf, phdr->caplen, ¤t)) {
1351 fprintf(stdout, "Skipped: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1352 for (i = 0; i < 16; i++) {
1353 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1355 fprintf(stdout, "\n");
1362 fprintf(stdout, "Packet: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1363 for (i = 0; i < 16; i++) {
1364 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1366 fprintf(stdout, "\n");
1371 /* Random error mutation */
1372 if (err_prob > 0.0) {
1373 int real_data_start = 0;
1374 buf = wtap_buf_ptr(wth);
1375 /* Protect non-protocol data */
1376 if (wtap_file_type(wth) == WTAP_FILE_CATAPULT_DCT2000) {
1377 real_data_start = find_dct2000_real_data(buf);
1379 for (i = real_data_start; i < (int) phdr->caplen; i++) {
1380 if (rand() <= err_prob * RAND_MAX) {
1381 err_type = rand() / (RAND_MAX / ERR_WT_TOTAL + 1);
1383 if (err_type < ERR_WT_BIT) {
1384 buf[i] ^= 1 << (rand() / (RAND_MAX / 8 + 1));
1385 err_type = ERR_WT_TOTAL;
1387 err_type -= ERR_WT_BYTE;
1390 if (err_type < ERR_WT_BYTE) {
1391 buf[i] = rand() / (RAND_MAX / 255 + 1);
1392 err_type = ERR_WT_TOTAL;
1394 err_type -= ERR_WT_BYTE;
1397 if (err_type < ERR_WT_ALNUM) {
1398 buf[i] = ALNUM_CHARS[rand() / (RAND_MAX / ALNUM_LEN + 1)];
1399 err_type = ERR_WT_TOTAL;
1401 err_type -= ERR_WT_ALNUM;
1404 if (err_type < ERR_WT_FMT) {
1405 if ((unsigned int)i < phdr->caplen - 2)
1406 strncpy((char*) &buf[i], "%s", 2);
1407 err_type = ERR_WT_TOTAL;
1409 err_type -= ERR_WT_FMT;
1412 if (err_type < ERR_WT_AA) {
1413 for (j = i; j < (int) phdr->caplen; j++) {
1422 if (!wtap_dump(pdh, phdr, wtap_pseudoheader(wth), wtap_buf_ptr(wth),
1424 fprintf(stderr, "editcap: Error writing to %s: %s\n",
1425 filename, wtap_strerror(err));
1437 /* Print a message noting that the read failed somewhere along the line. */
1439 "editcap: An error occurred while reading \"%s\": %s.\n",
1440 argv[optind], wtap_strerror(err));
1443 case WTAP_ERR_UNSUPPORTED:
1444 case WTAP_ERR_UNSUPPORTED_ENCAP:
1445 case WTAP_ERR_BAD_RECORD:
1446 fprintf(stderr, "(%s)\n", err_info);
1453 /* No valid packages found, open the outfile so we can write an empty header */
1455 filename = g_strdup(argv[optind+1]);
1457 pdh = wtap_dump_open(filename, out_file_type,
1458 out_frame_type, wtap_snapshot_length(wth), FALSE /* compressed */, &err);
1460 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1461 wtap_strerror(err));
1466 if (!wtap_dump_close(pdh, &err)) {
1468 fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
1469 wtap_strerror(err));
1477 fprintf(stdout, "%u packet%s seen, %u packet%s skipped with duplicate window of %u packets.\n",
1478 count - 1, plurality(count - 1, "", "s"),
1479 duplicate_count, plurality(duplicate_count, "", "s"), dup_window);
1480 } else if (dup_detect_by_time) {
1481 fprintf(stdout, "%u packet%s seen, %u packet%s skipped with duplicate time window equal to or less than %ld.%09ld seconds.\n",
1482 count - 1, plurality(count - 1, "", "s"),
1483 duplicate_count, plurality(duplicate_count, "", "s"),
1484 (long)relative_time_window.secs, (long int)relative_time_window.nsecs);
1490 /* Skip meta-information read from file to return offset of real
1492 static int find_dct2000_real_data(guint8 *buf)
1496 for (n=0; buf[n] != '\0'; n++); /* Context name */
1498 n++; /* Context port number */
1499 for (; buf[n] != '\0'; n++); /* Timestamp */
1501 for (; buf[n] != '\0'; n++); /* Protocol name */
1503 for (; buf[n] != '\0'; n++); /* Variant number (as string) */
1505 for (; buf[n] != '\0'; n++); /* Outhdr (as string) */
1507 n += 2; /* Direction & encap */