1 /* Edit capture files. We can delete packets, adjust timestamps, or
2 * simply convert from one format to another format.
4 * Originally written by Richard Sharpe.
5 * Improved by Guy Harris.
6 * Further improved by Richard Sharpe.
8 * Copyright 2013, Richard Sharpe <realrichardsharpe[AT]gmail.com>
12 * Wireshark - Network traffic analyzer
13 * By Gerald Combs <gerald@wireshark.org>
14 * Copyright 1998 Gerald Combs
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
26 * You should have received a copy of the GNU General Public License along
27 * with this program; if not, write to the Free Software Foundation, Inc.,
28 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
39 * Just make sure we include the prototype for strptime as well
40 * (needed for glibc 2.2) but make sure we do this only if not
57 #ifdef HAVE_SYS_TIME_H
64 #include "wsutil/wsgetopt.h"
68 #include <wsutil/file_util.h>
69 #include <wsutil/unicode-utils.h>
70 #include <process.h> /* getpid */
71 #ifdef HAVE_WINSOCK2_H
76 #ifdef NEED_STRPTIME_H
77 # include "wsutil/strptime.h"
80 #include "epan/crypt/md5.h"
81 #include "epan/plugins.h"
82 #include "epan/report_err.h"
83 #include "epan/filesystem.h"
84 #include <wsutil/privileges.h>
85 #include "epan/nstime.h"
87 #include "svnversion.h"
89 #include "ringbuffer.h" /* For RINGBUFFER_MAX_NUM_FILES */
92 * Some globals so we can pass things to various routines
104 * Duplicate frame detection
106 typedef struct _fd_hash_t {
107 md5_byte_t digest[16];
112 #define DEFAULT_DUP_DEPTH 5 /* Used with -d */
113 #define MAX_DUP_DEPTH 1000000 /* the maximum window (and actual size of fd_hash[]) for de-duplication */
115 fd_hash_t fd_hash[MAX_DUP_DEPTH];
116 int dup_window = DEFAULT_DUP_DEPTH;
117 int cur_dup_entry = 0;
119 #define ONE_MILLION 1000000
120 #define ONE_BILLION 1000000000
122 /* Weights of different errors we can introduce */
123 /* We should probably make these command-line arguments */
124 /* XXX - Should we add a bit-level error? */
125 #define ERR_WT_BIT 5 /* Flip a random bit */
126 #define ERR_WT_BYTE 5 /* Substitute a random byte */
127 #define ERR_WT_ALNUM 5 /* Substitute a random character in [A-Za-z0-9] */
128 #define ERR_WT_FMT 2 /* Substitute "%s" */
129 #define ERR_WT_AA 1 /* Fill the remainder of the buffer with 0xAA */
130 #define ERR_WT_TOTAL (ERR_WT_BIT + ERR_WT_BYTE + ERR_WT_ALNUM + ERR_WT_FMT + ERR_WT_AA)
132 #define ALNUM_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
133 #define ALNUM_LEN (sizeof(ALNUM_CHARS) - 1)
136 struct time_adjustment {
141 #define MAX_SELECTIONS 512
142 static struct select_item selectfrm[MAX_SELECTIONS];
143 static int max_selected = -1;
144 static int keep_em = 0;
145 #ifdef PCAP_NG_DEFAULT
146 static int out_file_type = WTAP_FILE_PCAPNG; /* default to pcapng */
148 static int out_file_type = WTAP_FILE_PCAP; /* default to pcap */
150 static int out_frame_type = -2; /* Leave frame type alone */
151 static int verbose = 0; /* Not so verbose */
152 static struct time_adjustment time_adj = {{0, 0}, 0}; /* no adjustment */
153 static nstime_t relative_time_window = {0, 0}; /* de-dup time window */
154 static double err_prob = 0.0;
155 static time_t starttime = 0;
156 static time_t stoptime = 0;
157 static gboolean check_startstop = FALSE;
158 static gboolean dup_detect = FALSE;
159 static gboolean dup_detect_by_time = FALSE;
161 static int do_strict_time_adjustment = FALSE;
162 static struct time_adjustment strict_time_adj = {{0, 0}, 0}; /* strict time adjustment */
163 static nstime_t previous_time = {0, 0}; /* previous time */
165 static int find_dct2000_real_data(guint8 *buf);
168 abs_time_to_str_with_sec_resolution(const struct wtap_nstime *abs_time)
171 gchar *buf = g_malloc(16);
173 #if (defined _WIN32) && (_MSC_VER < 1500)
174 /* calling localtime() on MSVC 2005 with huge values causes it to crash */
175 /* XXX - find the exact value that still does work */
176 /* XXX - using _USE_32BIT_TIME_T might be another way to circumvent this problem */
177 if(abs_time->secs > 2000000000) {
181 tmp = localtime(&abs_time->secs);
183 g_snprintf(buf, 16, "%d%02d%02d%02d%02d%02d",
197 fileset_get_filename_by_pattern(guint idx, const struct wtap_nstime *time_val,
198 gchar *fprefix, gchar *fsuffix)
204 timestr = abs_time_to_str_with_sec_resolution(time_val);
205 g_snprintf(filenum, sizeof(filenum), "%05u", idx % RINGBUFFER_MAX_NUM_FILES);
206 abs_str = g_strconcat(fprefix, "_", filenum, "_", timestr, fsuffix, NULL);
213 fileset_extract_prefix_suffix(const char *fname, gchar **fprefix, gchar **fsuffix)
215 char *pfx, *last_pathsep;
218 save_file = g_strdup(fname);
219 if (save_file == NULL) {
220 fprintf(stderr, "editcap: Out of memory\n");
224 last_pathsep = strrchr(save_file, G_DIR_SEPARATOR);
225 pfx = strrchr(save_file,'.');
226 if (pfx != NULL && (last_pathsep == NULL || pfx > last_pathsep)) {
227 /* The pathname has a "." in it, and it's in the last component
228 of the pathname (because there is either only one component,
229 i.e. last_pathsep is null as there are no path separators,
230 or the "." is after the path separator before the last
233 Treat it as a separator between the rest of the file name and
234 the file name suffix, and arrange that the names given to the
235 ring buffer files have the specified suffix, i.e. put the
236 changing part of the name *before* the suffix. */
238 *fprefix = g_strdup(save_file);
239 pfx[0] = '.'; /* restore capfile_name */
240 *fsuffix = g_strdup(pfx);
242 /* Either there's no "." in the pathname, or it's in a directory
243 component, so the last component has no suffix. */
244 *fprefix = g_strdup(save_file);
251 /* Add a selection item, a simple parser for now */
253 add_selection(char *sel)
258 if (++max_selected >= MAX_SELECTIONS) {
259 /* Let the user know we stopped selecting */
260 printf("Out of room for packet selections!\n");
264 printf("Add_Selected: %s\n", sel);
266 if ((locn = strchr(sel, '-')) == NULL) { /* No dash, so a single number? */
268 printf("Not inclusive ...");
270 selectfrm[max_selected].inclusive = 0;
271 selectfrm[max_selected].first = atoi(sel);
273 printf(" %i\n", selectfrm[max_selected].first);
278 printf("Inclusive ...");
281 selectfrm[max_selected].inclusive = 1;
282 selectfrm[max_selected].first = atoi(sel);
283 selectfrm[max_selected].second = atoi(next);
285 printf(" %i, %i\n", selectfrm[max_selected].first, selectfrm[max_selected].second);
292 /* Was the packet selected? */
299 for (i = 0; i<= max_selected; i++) {
301 if (selectfrm[i].inclusive) {
302 if (selectfrm[i].first <= recno && selectfrm[i].second >= recno)
306 if (recno == selectfrm[i].first)
315 /* is the packet in the selected timeframe */
317 check_timestamp(wtap *wth)
319 struct wtap_pkthdr* pkthdr = wtap_phdr(wth);
321 return ( pkthdr->ts.secs >= starttime ) && ( pkthdr->ts.secs < stoptime );
325 set_time_adjustment(char *optarg_str_p)
334 /* skip leading whitespace */
335 while (*optarg_str_p == ' ' || *optarg_str_p == '\t') {
339 /* check for a negative adjustment */
340 if (*optarg_str_p == '-') {
341 time_adj.is_negative = 1;
345 /* collect whole number of seconds, if any */
346 if (*optarg_str_p == '.') { /* only fractional (i.e., .5 is ok) */
350 val = strtol(optarg_str_p, &frac, 10);
351 if (frac == NULL || frac == optarg_str_p || val == LONG_MIN || val == LONG_MAX) {
352 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
356 if (val < 0) { /* implies '--' since we caught '-' above */
357 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
362 time_adj.tv.tv_sec = val;
364 /* now collect the partial seconds, if any */
365 if (*frac != '\0') { /* chars left, so get fractional part */
366 val = strtol(&(frac[1]), &end, 10);
367 /* if more than 6 fractional digits truncate to 6 */
368 if((end - &(frac[1])) > 6) {
369 frac[7] = 't'; /* 't' for truncate */
370 val = strtol(&(frac[1]), &end, 10);
372 if (*frac != '.' || end == NULL || end == frac
373 || val < 0 || val > ONE_MILLION || val == LONG_MIN || val == LONG_MAX) {
374 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
380 return; /* no fractional digits */
383 /* adjust fractional portion from fractional to numerator
384 * e.g., in "1.5" from 5 to 500000 since .5*10^6 = 500000 */
385 if (frac && end) { /* both are valid */
386 frac_digits = end - frac - 1; /* fractional digit count (remember '.') */
387 while(frac_digits < 6) { /* this is frac of 10^6 */
392 time_adj.tv.tv_usec = (int)val;
396 set_strict_time_adj(char *optarg_str_p)
405 /* skip leading whitespace */
406 while (*optarg_str_p == ' ' || *optarg_str_p == '\t') {
411 * check for a negative adjustment
412 * A negative strict adjustment value is a flag
413 * to adjust all frames by the specifed delta time.
415 if (*optarg_str_p == '-') {
416 strict_time_adj.is_negative = 1;
420 /* collect whole number of seconds, if any */
421 if (*optarg_str_p == '.') { /* only fractional (i.e., .5 is ok) */
425 val = strtol(optarg_str_p, &frac, 10);
426 if (frac == NULL || frac == optarg_str_p || val == LONG_MIN || val == LONG_MAX) {
427 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
431 if (val < 0) { /* implies '--' since we caught '-' above */
432 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
437 strict_time_adj.tv.tv_sec = val;
439 /* now collect the partial seconds, if any */
440 if (*frac != '\0') { /* chars left, so get fractional part */
441 val = strtol(&(frac[1]), &end, 10);
442 /* if more than 6 fractional digits truncate to 6 */
443 if((end - &(frac[1])) > 6) {
444 frac[7] = 't'; /* 't' for truncate */
445 val = strtol(&(frac[1]), &end, 10);
447 if (*frac != '.' || end == NULL || end == frac
448 || val < 0 || val > ONE_MILLION || val == LONG_MIN || val == LONG_MAX) {
449 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
455 return; /* no fractional digits */
458 /* adjust fractional portion from fractional to numerator
459 * e.g., in "1.5" from 5 to 500000 since .5*10^6 = 500000 */
460 if (frac && end) { /* both are valid */
461 frac_digits = end - frac - 1; /* fractional digit count (remember '.') */
462 while(frac_digits < 6) { /* this is frac of 10^6 */
467 strict_time_adj.tv.tv_usec = (int)val;
471 set_rel_time(char *optarg_str_p)
480 /* skip leading whitespace */
481 while (*optarg_str_p == ' ' || *optarg_str_p == '\t') {
485 /* ignore negative adjustment */
486 if (*optarg_str_p == '-') {
490 /* collect whole number of seconds, if any */
491 if (*optarg_str_p == '.') { /* only fractional (i.e., .5 is ok) */
495 val = strtol(optarg_str_p, &frac, 10);
496 if (frac == NULL || frac == optarg_str_p || val == LONG_MIN || val == LONG_MAX) {
497 fprintf(stderr, "1: editcap: \"%s\" isn't a valid rel time value\n",
501 if (val < 0) { /* implies '--' since we caught '-' above */
502 fprintf(stderr, "2: editcap: \"%s\" isn't a valid rel time value\n",
507 relative_time_window.secs = val;
509 /* now collect the partial seconds, if any */
510 if (*frac != '\0') { /* chars left, so get fractional part */
511 val = strtol(&(frac[1]), &end, 10);
512 /* if more than 9 fractional digits truncate to 9 */
513 if((end - &(frac[1])) > 9) {
514 frac[10] = 't'; /* 't' for truncate */
515 val = strtol(&(frac[1]), &end, 10);
517 if (*frac != '.' || end == NULL || end == frac
518 || val < 0 || val > ONE_BILLION || val == LONG_MIN || val == LONG_MAX) {
519 fprintf(stderr, "3: editcap: \"%s\" isn't a valid rel time value\n",
525 return; /* no fractional digits */
528 /* adjust fractional portion from fractional to numerator
529 * e.g., in "1.5" from 5 to 500000000 since .5*10^9 = 500000000 */
530 if (frac && end) { /* both are valid */
531 frac_digits = end - frac - 1; /* fractional digit count (remember '.') */
532 while(frac_digits < 9) { /* this is frac of 10^9 */
537 relative_time_window.nsecs = (int)val;
541 is_duplicate(guint8* fd, guint32 len) {
546 if (cur_dup_entry >= dup_window)
549 /* Calculate our digest */
551 md5_append(&ms, fd, len);
552 md5_finish(&ms, fd_hash[cur_dup_entry].digest);
554 fd_hash[cur_dup_entry].len = len;
556 /* Look for duplicates */
557 for (i = 0; i < dup_window; i++) {
558 if (i == cur_dup_entry)
561 if (fd_hash[i].len == fd_hash[cur_dup_entry].len &&
562 memcmp(fd_hash[i].digest, fd_hash[cur_dup_entry].digest, 16) == 0) {
571 is_duplicate_rel_time(guint8* fd, guint32 len, const nstime_t *current) {
576 if (cur_dup_entry >= dup_window)
579 /* Calculate our digest */
581 md5_append(&ms, fd, len);
582 md5_finish(&ms, fd_hash[cur_dup_entry].digest);
584 fd_hash[cur_dup_entry].len = len;
585 fd_hash[cur_dup_entry].time.secs = current->secs;
586 fd_hash[cur_dup_entry].time.nsecs = current->nsecs;
589 * Look for relative time related duplicates.
590 * This is hopefully a reasonably efficient mechanism for
591 * finding duplicates by rel time in the fd_hash[] cache.
592 * We check starting from the most recently added hash
593 * entries and work backwards towards older packets.
594 * This approach allows the dup test to be terminated
595 * when the relative time of a cached entry is found to
596 * be beyond the dup time window.
598 * Of course this assumes that the input trace file is
599 * "well-formed" in the sense that the packet timestamps are
600 * in strict chronologically increasing order (which is NOT
601 * always the case!!).
603 * The fd_hash[] table was deliberatly created large (1,000,000).
604 * Looking for time related duplicates in large trace files with
605 * non-fractional dup time window values can potentially take
606 * a long time to complete.
609 for (i = cur_dup_entry - 1;; i--) {
617 if (i == cur_dup_entry) {
619 * We've decremented back to where we started.
625 if (nstime_is_unset(&(fd_hash[i].time))) {
627 * We've decremented to an unused fd_hash[] entry.
633 nstime_delta(&delta, current, &fd_hash[i].time);
635 if(delta.secs < 0 || delta.nsecs < 0)
638 * A negative delta implies that the current packet
639 * has an absolute timestamp less than the cached packet
640 * that it is being compared to. This is NOT a normal
641 * situation since trace files usually have packets in
642 * chronological order (oldest to newest).
644 * There are several possible ways to deal with this:
645 * 1. 'continue' dup checking with the next cached frame.
646 * 2. 'break' from looking for a duplicate of the current frame.
647 * 3. Take the absolute value of the delta and see if that
648 * falls within the specifed dup time window.
650 * Currently this code does option 1. But it would pretty
651 * easy to add yet-another-editcap-option to select one of
652 * the other behaviors for dealing with out-of-sequence
658 cmp = nstime_cmp(&delta, &relative_time_window);
662 * The delta time indicates that we are now looking at
663 * cached packets beyond the specified dup time window.
667 } else if (fd_hash[i].len == fd_hash[cur_dup_entry].len &&
668 memcmp(fd_hash[i].digest, fd_hash[cur_dup_entry].digest, 16) == 0) {
677 usage(gboolean is_error)
686 fprintf(output, "Editcap %s"
688 " (" SVNVERSION " from " SVNPATH ")"
691 fprintf(output, "Edit and/or translate the format of capture files.\n");
692 fprintf(output, "See http://www.wireshark.org for more information.\n");
693 fprintf(output, "\n");
694 fprintf(output, "Usage: editcap [options] ... <infile> <outfile> [ <packet#>[-<packet#>] ... ]\n");
695 fprintf(output, "\n");
696 fprintf(output, "<infile> and <outfile> must both be present.\n");
697 fprintf(output, "A single packet or a range of packets can be selected.\n");
698 fprintf(output, "\n");
699 fprintf(output, "Packet selection:\n");
700 fprintf(output, " -r keep the selected packets; default is to delete them.\n");
701 fprintf(output, " -A <start time> only output packets whose timestamp is after (or equal\n");
702 fprintf(output, " to) the given time (format as YYYY-MM-DD hh:mm:ss).\n");
703 fprintf(output, " -B <stop time> only output packets whose timestamp is before the\n");
704 fprintf(output, " given time (format as YYYY-MM-DD hh:mm:ss).\n");
705 fprintf(output, "\n");
706 fprintf(output, "Duplicate packet removal:\n");
707 fprintf(output, " -d remove packet if duplicate (window == %d).\n", DEFAULT_DUP_DEPTH);
708 fprintf(output, " -D <dup window> remove packet if duplicate; configurable <dup window>\n");
709 fprintf(output, " Valid <dup window> values are 0 to %d.\n", MAX_DUP_DEPTH);
710 fprintf(output, " NOTE: A <dup window> of 0 with -v (verbose option) is\n");
711 fprintf(output, " useful to print MD5 hashes.\n");
712 fprintf(output, " -w <dup time window> remove packet if duplicate packet is found EQUAL TO OR\n");
713 fprintf(output, " LESS THAN <dup time window> prior to current packet.\n");
714 fprintf(output, " A <dup time window> is specified in relative seconds\n");
715 fprintf(output, " (e.g. 0.000001).\n");
716 fprintf(output, "\n");
717 fprintf(output, " NOTE: The use of the 'Duplicate packet removal' options with\n");
718 fprintf(output, " other editcap options except -v may not always work as expected.\n");
719 fprintf(output, " Specifically the -r, -t or -S options will very likely NOT have the\n");
720 fprintf(output, " desired effect if combined with the -d, -D or -w.\n");
721 fprintf(output, "\n");
722 fprintf(output, "Packet manipulation:\n");
723 fprintf(output, " -s <snaplen> truncate each packet to max. <snaplen> bytes of data.\n");
724 fprintf(output, " -C <choplen> chop each packet by <choplen> bytes. Positive values\n");
725 fprintf(output, " chop at the packet beginning, negative values at the\n");
726 fprintf(output, " packet end.\n");
727 fprintf(output, " -t <time adjustment> adjust the timestamp of each packet;\n");
728 fprintf(output, " <time adjustment> is in relative seconds (e.g. -0.5).\n");
729 fprintf(output, " -S <strict adjustment> adjust timestamp of packets if necessary to insure\n");
730 fprintf(output, " strict chronological increasing order. The <strict\n");
731 fprintf(output, " adjustment> is specified in relative seconds with\n");
732 fprintf(output, " values of 0 or 0.000001 being the most reasonable.\n");
733 fprintf(output, " A negative adjustment value will modify timestamps so\n");
734 fprintf(output, " that each packet's delta time is the absolute value\n");
735 fprintf(output, " of the adjustment specified. A value of -0 will set\n");
736 fprintf(output, " all packets to the timestamp of the first packet.\n");
737 fprintf(output, " -E <error probability> set the probability (between 0.0 and 1.0 incl.)\n");
738 fprintf(output, " that a particular packet byte will be randomly changed.\n");
739 fprintf(output, "\n");
740 fprintf(output, "Output File(s):\n");
741 fprintf(output, " -c <packets per file> split the packet output to different files\n");
742 fprintf(output, " based on uniform packet counts\n");
743 fprintf(output, " with a maximum of <packets per file> each.\n");
744 fprintf(output, " -i <seconds per file> split the packet output to different files\n");
745 fprintf(output, " based on uniform time intervals\n");
746 fprintf(output, " with a maximum of <seconds per file> each.\n");
747 fprintf(output, " -F <capture type> set the output file type; default is pcapng.\n");
748 fprintf(output, " an empty \"-F\" option will list the file types.\n");
749 fprintf(output, " -T <encap type> set the output file encapsulation type;\n");
750 fprintf(output, " default is the same as the input file.\n");
751 fprintf(output, " an empty \"-T\" option will list the encapsulation types.\n");
752 fprintf(output, "\n");
753 fprintf(output, "Miscellaneous:\n");
754 fprintf(output, " -h display this help and exit.\n");
755 fprintf(output, " -v verbose output.\n");
756 fprintf(output, " If -v is used with any of the 'Duplicate Packet\n");
757 fprintf(output, " Removal' options (-d, -D or -w) then Packet lengths\n");
758 fprintf(output, " and MD5 hashes are printed to standard-out.\n");
759 fprintf(output, "\n");
763 const char *sstr; /* The short string */
764 const char *lstr; /* The long string */
768 string_compare(gconstpointer a, gconstpointer b)
770 return strcmp(((const struct string_elem *)a)->sstr,
771 ((const struct string_elem *)b)->sstr);
775 string_elem_print(gpointer data, gpointer not_used _U_)
777 fprintf(stderr, " %s - %s\n",
778 ((struct string_elem *)data)->sstr,
779 ((struct string_elem *)data)->lstr);
783 list_capture_types(void) {
785 struct string_elem *captypes;
788 captypes = g_malloc(sizeof(struct string_elem) * WTAP_NUM_FILE_TYPES);
789 fprintf(stderr, "editcap: The available capture file types for the \"-F\" flag are:\n");
790 for (i = 0; i < WTAP_NUM_FILE_TYPES; i++) {
791 if (wtap_dump_can_open(i)) {
792 captypes[i].sstr = wtap_file_type_short_string(i);
793 captypes[i].lstr = wtap_file_type_string(i);
794 list = g_slist_insert_sorted(list, &captypes[i], string_compare);
797 g_slist_foreach(list, string_elem_print, NULL);
803 list_encap_types(void) {
805 struct string_elem *encaps;
808 encaps = g_malloc(sizeof(struct string_elem) * WTAP_NUM_ENCAP_TYPES);
809 fprintf(stderr, "editcap: The available encapsulation types for the \"-T\" flag are:\n");
810 for (i = 0; i < WTAP_NUM_ENCAP_TYPES; i++) {
811 encaps[i].sstr = wtap_encap_short_string(i);
812 if (encaps[i].sstr != NULL) {
813 encaps[i].lstr = wtap_encap_string(i);
814 list = g_slist_insert_sorted(list, &encaps[i], string_compare);
817 g_slist_foreach(list, string_elem_print, NULL);
824 * Don't report failures to load plugins because most (non-wiretap) plugins
825 * *should* fail to load (because we're not linked against libwireshark and
826 * dissector plugins need libwireshark).
829 failure_message(const char *msg_format _U_, va_list ap _U_)
836 main(int argc, char *argv[])
844 guint32 snaplen = 0; /* No limit */
845 int choplen = 0; /* No chop */
846 wtap_dumper *pdh = NULL;
847 unsigned int count = 1;
848 unsigned int duplicate_count = 0;
850 struct wtap_pkthdr snap_phdr;
851 const struct wtap_pkthdr *phdr;
853 wtapng_section_t *shb_hdr;
854 wtapng_iface_descriptions_t *idb_inf;
856 guint32 read_count = 0;
857 int split_packet_count = 0;
858 int written_count = 0;
859 char *filename = NULL;
860 gboolean ts_okay = TRUE;
861 int secs_per_block = 0;
863 nstime_t block_start;
864 gchar *fprefix = NULL;
865 gchar *fsuffix = NULL;
869 char* init_progfile_dir_error;
873 arg_list_utf_16to8(argc, argv);
874 create_app_running_mutex();
878 * Get credential information for later use.
880 init_process_policies();
883 /* Register wiretap plugins */
884 if ((init_progfile_dir_error = init_progfile_dir(argv[0], main))) {
885 g_warning("editcap: init_progfile_dir(): %s", init_progfile_dir_error);
886 g_free(init_progfile_dir_error);
888 init_report_err(failure_message,NULL,NULL,NULL);
893 /* Process the options */
894 while ((opt = getopt(argc, argv, "A:B:c:C:dD:E:F:hrs:i:t:S:T:vw:")) !=-1) {
899 err_prob = strtod(optarg, &p);
900 if (p == optarg || err_prob < 0.0 || err_prob > 1.0) {
901 fprintf(stderr, "editcap: probability \"%s\" must be between 0.0 and 1.0\n",
905 srand( (unsigned int) (time(NULL) + getpid()) );
909 out_file_type = wtap_short_string_to_file_type(optarg);
910 if (out_file_type < 0) {
911 fprintf(stderr, "editcap: \"%s\" isn't a valid capture file type\n\n",
913 list_capture_types();
919 split_packet_count = (int)strtol(optarg, &p, 10);
920 if (p == optarg || *p != '\0') {
921 fprintf(stderr, "editcap: \"%s\" isn't a valid packet count\n",
925 if (split_packet_count <= 0) {
926 fprintf(stderr, "editcap: \"%d\" packet count must be larger than zero\n",
933 choplen = (int)strtol(optarg, &p, 10);
934 if (p == optarg || *p != '\0') {
935 fprintf(stderr, "editcap: \"%s\" isn't a valid chop length\n",
943 dup_detect_by_time = FALSE;
944 dup_window = DEFAULT_DUP_DEPTH;
949 dup_detect_by_time = FALSE;
950 dup_window = (int)strtol(optarg, &p, 10);
951 if (p == optarg || *p != '\0') {
952 fprintf(stderr, "editcap: \"%s\" isn't a valid duplicate window value\n",
956 if (dup_window < 0 || dup_window > MAX_DUP_DEPTH) {
957 fprintf(stderr, "editcap: \"%d\" duplicate window value must be between 0 and %d inclusive.\n",
958 dup_window, MAX_DUP_DEPTH);
965 dup_detect_by_time = TRUE;
966 dup_window = MAX_DUP_DEPTH;
967 set_rel_time(optarg);
970 case '?': /* Bad options if GNU getopt */
973 list_capture_types();
990 keep_em = !keep_em; /* Just invert */
994 snaplen = (guint32)strtol(optarg, &p, 10);
995 if (p == optarg || *p != '\0') {
996 fprintf(stderr, "editcap: \"%s\" isn't a valid snapshot length\n",
1003 set_time_adjustment(optarg);
1007 set_strict_time_adj(optarg);
1008 do_strict_time_adjustment = TRUE;
1012 out_frame_type = wtap_short_string_to_encap(optarg);
1013 if (out_frame_type < 0) {
1014 fprintf(stderr, "editcap: \"%s\" isn't a valid encapsulation type\n\n",
1022 verbose = !verbose; /* Just invert */
1025 case 'i': /* break capture file based on time interval */
1026 secs_per_block = atoi(optarg);
1027 if(secs_per_block <= 0) {
1028 fprintf(stderr, "editcap: \"%s\" isn't a valid time interval\n\n", optarg);
1037 memset(&starttm,0,sizeof(struct tm));
1039 if(!strptime(optarg,"%Y-%m-%d %T",&starttm)) {
1040 fprintf(stderr, "editcap: \"%s\" isn't a valid time format\n\n", optarg);
1044 check_startstop = TRUE;
1045 starttm.tm_isdst = -1;
1047 starttime = mktime(&starttm);
1055 memset(&stoptm,0,sizeof(struct tm));
1057 if(!strptime(optarg,"%Y-%m-%d %T",&stoptm)) {
1058 fprintf(stderr, "editcap: \"%s\" isn't a valid time format\n\n", optarg);
1061 check_startstop = TRUE;
1062 stoptm.tm_isdst = -1;
1063 stoptime = mktime(&stoptm);
1071 printf("Optind = %i, argc = %i\n", optind, argc);
1074 if ((argc - optind) < 1) {
1081 if (check_startstop && !stoptime) {
1083 /* XXX: will work until 2035 */
1084 memset(&stoptm,0,sizeof(struct tm));
1085 stoptm.tm_year = 135;
1086 stoptm.tm_mday = 31;
1089 stoptime = mktime(&stoptm);
1092 nstime_set_unset(&block_start);
1094 if (starttime > stoptime) {
1095 fprintf(stderr, "editcap: start time is after the stop time\n");
1099 if (split_packet_count > 0 && secs_per_block > 0) {
1100 fprintf(stderr, "editcap: can't split on both packet count and time interval\n");
1101 fprintf(stderr, "editcap: at the same time\n");
1105 wth = wtap_open_offline(argv[optind], &err, &err_info, FALSE);
1108 fprintf(stderr, "editcap: Can't open %s: %s\n", argv[optind],
1109 wtap_strerror(err));
1112 case WTAP_ERR_UNSUPPORTED:
1113 case WTAP_ERR_UNSUPPORTED_ENCAP:
1114 case WTAP_ERR_BAD_FILE:
1115 fprintf(stderr, "(%s)\n", err_info);
1124 fprintf(stderr, "File %s is a %s capture file.\n", argv[optind],
1125 wtap_file_type_string(wtap_file_type(wth)));
1128 shb_hdr = wtap_file_get_shb_info(wth);
1129 idb_inf = wtap_file_get_idb_info(wth);
1132 * Now, process the rest, if any ... we only write if there is an extra
1133 * argument or so ...
1136 if ((argc - optind) >= 2) {
1138 if (out_frame_type == -2)
1139 out_frame_type = wtap_file_encap(wth);
1141 for (i = optind + 2; i < argc; i++)
1142 if (add_selection(argv[i]) == FALSE)
1145 if (dup_detect || dup_detect_by_time) {
1146 for (i = 0; i < dup_window; i++) {
1147 memset(&fd_hash[i].digest, 0, 16);
1149 nstime_set_unset(&fd_hash[i].time);
1153 while (wtap_read(wth, &err, &err_info, &data_offset)) {
1156 phdr = wtap_phdr(wth);
1157 buf = wtap_buf_ptr(wth);
1159 if (nstime_is_unset(&block_start)) { /* should only be the first packet */
1160 block_start.secs = phdr->ts.secs;
1161 block_start.nsecs = phdr->ts.nsecs;
1163 if (split_packet_count > 0 || secs_per_block > 0) {
1164 if (!fileset_extract_prefix_suffix(argv[optind+1], &fprefix, &fsuffix))
1167 filename = fileset_get_filename_by_pattern(block_cnt++, &phdr->ts, fprefix, fsuffix);
1169 filename = g_strdup(argv[optind+1]);
1171 /* If we don't have an application name add Editcap */
1172 if(shb_hdr->shb_user_appl == NULL) {
1173 g_snprintf(appname, sizeof(appname), "Editcap " VERSION);
1174 shb_hdr->shb_user_appl = appname;
1177 pdh = wtap_dump_open_ng(filename, out_file_type, out_frame_type,
1178 snaplen ? MIN(snaplen, wtap_snapshot_length(wth)) : wtap_snapshot_length(wth),
1179 FALSE /* compressed */, shb_hdr, idb_inf, &err);
1182 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1183 wtap_strerror(err));
1190 if (secs_per_block > 0) {
1191 while ((phdr->ts.secs - block_start.secs > secs_per_block) ||
1192 (phdr->ts.secs - block_start.secs == secs_per_block &&
1193 phdr->ts.nsecs >= block_start.nsecs )) { /* time for the next file */
1195 if (!wtap_dump_close(pdh, &err)) {
1196 fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
1197 wtap_strerror(err));
1200 block_start.secs = block_start.secs + secs_per_block; /* reset for next interval */
1202 filename = fileset_get_filename_by_pattern(block_cnt++, &phdr->ts, fprefix, fsuffix);
1206 fprintf(stderr, "Continuing writing in file %s\n", filename);
1209 pdh = wtap_dump_open_ng(filename, out_file_type, out_frame_type,
1210 snaplen ? MIN(snaplen, wtap_snapshot_length(wth)) : wtap_snapshot_length(wth),
1211 FALSE /* compressed */, shb_hdr, idb_inf, &err);
1214 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1215 wtap_strerror(err));
1221 if (split_packet_count > 0) {
1223 /* time for the next file? */
1224 if (written_count > 0 &&
1225 written_count % split_packet_count == 0) {
1226 if (!wtap_dump_close(pdh, &err)) {
1227 fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
1228 wtap_strerror(err));
1233 filename = fileset_get_filename_by_pattern(block_cnt++, &phdr->ts, fprefix, fsuffix);
1237 fprintf(stderr, "Continuing writing in file %s\n", filename);
1240 pdh = wtap_dump_open_ng(filename, out_file_type, out_frame_type,
1241 snaplen ? MIN(snaplen, wtap_snapshot_length(wth)) : wtap_snapshot_length(wth),
1242 FALSE /* compressed */, shb_hdr, idb_inf, &err);
1244 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1245 wtap_strerror(err));
1251 if (check_startstop)
1252 ts_okay = check_timestamp(wth);
1254 if ( ts_okay && ((!selected(count) && !keep_em) || (selected(count) && keep_em)) ) {
1256 if (verbose && !dup_detect && !dup_detect_by_time)
1257 printf("Packet: %u\n", count);
1259 /* We simply write it, perhaps after truncating it; we could do other
1260 things, like modify it. */
1262 phdr = wtap_phdr(wth);
1264 if (snaplen != 0 && phdr->caplen > snaplen) {
1266 snap_phdr.caplen = snaplen;
1272 if (((signed int) phdr->caplen + choplen) > 0)
1273 snap_phdr.caplen += choplen;
1275 snap_phdr.caplen = 0;
1277 } else if (choplen > 0) {
1279 if (phdr->caplen > (unsigned int) choplen) {
1280 snap_phdr.caplen -= choplen;
1283 snap_phdr.caplen = 0;
1288 * Do we adjust timestamps to insure strict chronologically order?
1291 if (do_strict_time_adjustment) {
1292 if (previous_time.secs || previous_time.nsecs) {
1293 if (!strict_time_adj.is_negative) {
1297 current.secs = phdr->ts.secs;
1298 current.nsecs = phdr->ts.nsecs;
1300 nstime_delta(&delta, ¤t, &previous_time);
1302 if (delta.secs < 0 || delta.nsecs < 0)
1305 * A negative delta indicates that the current packet
1306 * has an absolute timestamp less than the previous packet
1307 * that it is being compared to. This is NOT a normal
1308 * situation since trace files usually have packets in
1309 * chronological order (oldest to newest).
1311 /* printf("++out of order, need to adjust this packet!\n"); */
1313 snap_phdr.ts.secs = previous_time.secs + strict_time_adj.tv.tv_sec;
1314 snap_phdr.ts.nsecs = previous_time.nsecs;
1315 if (snap_phdr.ts.nsecs + strict_time_adj.tv.tv_usec * 1000 > ONE_MILLION * 1000) {
1317 snap_phdr.ts.secs++;
1318 snap_phdr.ts.nsecs += (strict_time_adj.tv.tv_usec - ONE_MILLION) * 1000;
1320 snap_phdr.ts.nsecs += strict_time_adj.tv.tv_usec * 1000;
1326 * A negative strict time adjustment is requested.
1327 * Unconditionally set each timestamp to previous
1328 * packet's timestamp plus delta.
1331 snap_phdr.ts.secs = previous_time.secs + strict_time_adj.tv.tv_sec;
1332 snap_phdr.ts.nsecs = previous_time.nsecs;
1333 if (snap_phdr.ts.nsecs + strict_time_adj.tv.tv_usec * 1000 > ONE_MILLION * 1000) {
1335 snap_phdr.ts.secs++;
1336 snap_phdr.ts.nsecs += (strict_time_adj.tv.tv_usec - ONE_MILLION) * 1000;
1338 snap_phdr.ts.nsecs += strict_time_adj.tv.tv_usec * 1000;
1343 previous_time.secs = phdr->ts.secs;
1344 previous_time.nsecs = phdr->ts.nsecs;
1347 /* assume that if the frame's tv_sec is 0, then
1348 * the timestamp isn't supported */
1349 if (phdr->ts.secs > 0 && time_adj.tv.tv_sec != 0) {
1351 if (time_adj.is_negative)
1352 snap_phdr.ts.secs -= time_adj.tv.tv_sec;
1354 snap_phdr.ts.secs += time_adj.tv.tv_sec;
1358 /* assume that if the frame's tv_sec is 0, then
1359 * the timestamp isn't supported */
1360 if (phdr->ts.secs > 0 && time_adj.tv.tv_usec != 0) {
1362 if (time_adj.is_negative) { /* subtract */
1363 if (snap_phdr.ts.nsecs/1000 < time_adj.tv.tv_usec) { /* borrow */
1364 snap_phdr.ts.secs--;
1365 snap_phdr.ts.nsecs += ONE_MILLION * 1000;
1367 snap_phdr.ts.nsecs -= time_adj.tv.tv_usec * 1000;
1369 if (snap_phdr.ts.nsecs + time_adj.tv.tv_usec * 1000 > ONE_MILLION * 1000) {
1371 snap_phdr.ts.secs++;
1372 snap_phdr.ts.nsecs += (time_adj.tv.tv_usec - ONE_MILLION) * 1000;
1374 snap_phdr.ts.nsecs += time_adj.tv.tv_usec * 1000;
1380 /* suppress duplicates by packet window */
1382 if (is_duplicate(buf, phdr->caplen)) {
1384 fprintf(stdout, "Skipped: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1385 for (i = 0; i < 16; i++) {
1386 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1388 fprintf(stdout, "\n");
1395 fprintf(stdout, "Packet: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1396 for (i = 0; i < 16; i++) {
1397 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1399 fprintf(stdout, "\n");
1404 /* suppress duplicates by time window */
1405 if (dup_detect_by_time) {
1408 current.secs = phdr->ts.secs;
1409 current.nsecs = phdr->ts.nsecs;
1411 if (is_duplicate_rel_time(buf, phdr->caplen, ¤t)) {
1413 fprintf(stdout, "Skipped: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1414 for (i = 0; i < 16; i++) {
1415 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1417 fprintf(stdout, "\n");
1424 fprintf(stdout, "Packet: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1425 for (i = 0; i < 16; i++) {
1426 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1428 fprintf(stdout, "\n");
1433 /* Random error mutation */
1434 if (err_prob > 0.0) {
1435 int real_data_start = 0;
1436 /* Protect non-protocol data */
1437 if (wtap_file_type(wth) == WTAP_FILE_CATAPULT_DCT2000) {
1438 real_data_start = find_dct2000_real_data(buf);
1440 for (i = real_data_start; i < (int) phdr->caplen; i++) {
1441 if (rand() <= err_prob * RAND_MAX) {
1442 err_type = rand() / (RAND_MAX / ERR_WT_TOTAL + 1);
1444 if (err_type < ERR_WT_BIT) {
1445 buf[i] ^= 1 << (rand() / (RAND_MAX / 8 + 1));
1446 err_type = ERR_WT_TOTAL;
1448 err_type -= ERR_WT_BYTE;
1451 if (err_type < ERR_WT_BYTE) {
1452 buf[i] = rand() / (RAND_MAX / 255 + 1);
1453 err_type = ERR_WT_TOTAL;
1455 err_type -= ERR_WT_BYTE;
1458 if (err_type < ERR_WT_ALNUM) {
1459 buf[i] = ALNUM_CHARS[rand() / (RAND_MAX / ALNUM_LEN + 1)];
1460 err_type = ERR_WT_TOTAL;
1462 err_type -= ERR_WT_ALNUM;
1465 if (err_type < ERR_WT_FMT) {
1466 if ((unsigned int)i < phdr->caplen - 2)
1467 g_strlcpy((char*) &buf[i], "%s", 2);
1468 err_type = ERR_WT_TOTAL;
1470 err_type -= ERR_WT_FMT;
1473 if (err_type < ERR_WT_AA) {
1474 for (j = i; j < (int) phdr->caplen; j++) {
1483 if (!wtap_dump(pdh, phdr, buf, &err)) {
1486 case WTAP_ERR_UNSUPPORTED_ENCAP:
1488 * This is a problem with the particular frame we're writing;
1489 * note that, and give the frame number.
1491 fprintf(stderr, "editcap: Frame %u of \"%s\" has a network type that can't be saved in a file with that format\n.",
1492 read_count, argv[optind]);
1496 fprintf(stderr, "editcap: Error writing to %s: %s\n",
1497 filename, wtap_strerror(err));
1512 /* Print a message noting that the read failed somewhere along the line. */
1514 "editcap: An error occurred while reading \"%s\": %s.\n",
1515 argv[optind], wtap_strerror(err));
1518 case WTAP_ERR_UNSUPPORTED:
1519 case WTAP_ERR_UNSUPPORTED_ENCAP:
1520 case WTAP_ERR_BAD_FILE:
1521 fprintf(stderr, "(%s)\n", err_info);
1528 /* No valid packages found, open the outfile so we can write an empty header */
1530 filename = g_strdup(argv[optind+1]);
1532 pdh = wtap_dump_open_ng(filename, out_file_type, out_frame_type,
1533 snaplen ? MIN(snaplen, wtap_snapshot_length(wth)): wtap_snapshot_length(wth),
1534 FALSE /* compressed */, shb_hdr, idb_inf, &err);
1536 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1537 wtap_strerror(err));
1545 if (!wtap_dump_close(pdh, &err)) {
1547 fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
1548 wtap_strerror(err));
1557 fprintf(stdout, "%u packet%s seen, %u packet%s skipped with duplicate window of %u packets.\n",
1558 count - 1, plurality(count - 1, "", "s"),
1559 duplicate_count, plurality(duplicate_count, "", "s"), dup_window);
1560 } else if (dup_detect_by_time) {
1561 fprintf(stdout, "%u packet%s seen, %u packet%s skipped with duplicate time window equal to or less than %ld.%09ld seconds.\n",
1562 count - 1, plurality(count - 1, "", "s"),
1563 duplicate_count, plurality(duplicate_count, "", "s"),
1564 (long)relative_time_window.secs, (long int)relative_time_window.nsecs);
1570 /* Skip meta-information read from file to return offset of real
1572 static int find_dct2000_real_data(guint8 *buf)
1576 for (n=0; buf[n] != '\0'; n++); /* Context name */
1578 n++; /* Context port number */
1579 for (; buf[n] != '\0'; n++); /* Timestamp */
1581 for (; buf[n] != '\0'; n++); /* Protocol name */
1583 for (; buf[n] != '\0'; n++); /* Variant number (as string) */
1585 for (; buf[n] != '\0'; n++); /* Outhdr (as string) */
1587 n += 2; /* Direction & encap */