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
55 #ifdef HAVE_SYS_TIME_H
62 #include "wsutil/wsgetopt.h"
66 #include <wsutil/file_util.h>
67 #include <wsutil/unicode-utils.h>
68 #include <process.h> /* getpid */
69 #ifdef HAVE_WINSOCK2_H
74 #ifdef NEED_STRPTIME_H
75 # include "wsutil/strptime.h"
78 #include <wsutil/privileges.h>
81 * The symbols declared in the below are exported from libwireshark,
82 * but we don't want to link whole libwireshark to editcap.
83 * We link the object directly instead and this needs a little trick
84 * with the WS_BUILD_DLL #define.
87 #define RESET_SYMBOL_EXPORT /* wsutil/wsgetopt.h set export behavior above. */
88 #include "epan/crypt/md5.h"
89 #include "epan/plugins.h"
90 #include "epan/report_err.h"
91 #include "epan/filesystem.h"
92 #include "epan/nstime.h"
94 #define RESET_SYMBOL_EXPORT
96 #include "svnversion.h"
98 #include "ringbuffer.h" /* For RINGBUFFER_MAX_NUM_FILES */
101 * Some globals so we can pass things to various routines
113 * Duplicate frame detection
115 typedef struct _fd_hash_t {
116 md5_byte_t digest[16];
121 #define DEFAULT_DUP_DEPTH 5 /* Used with -d */
122 #define MAX_DUP_DEPTH 1000000 /* the maximum window (and actual size of fd_hash[]) for de-duplication */
124 fd_hash_t fd_hash[MAX_DUP_DEPTH];
125 int dup_window = DEFAULT_DUP_DEPTH;
126 int cur_dup_entry = 0;
128 #define ONE_MILLION 1000000
129 #define ONE_BILLION 1000000000
131 /* Weights of different errors we can introduce */
132 /* We should probably make these command-line arguments */
133 /* XXX - Should we add a bit-level error? */
134 #define ERR_WT_BIT 5 /* Flip a random bit */
135 #define ERR_WT_BYTE 5 /* Substitute a random byte */
136 #define ERR_WT_ALNUM 5 /* Substitute a random character in [A-Za-z0-9] */
137 #define ERR_WT_FMT 2 /* Substitute "%s" */
138 #define ERR_WT_AA 1 /* Fill the remainder of the buffer with 0xAA */
139 #define ERR_WT_TOTAL (ERR_WT_BIT + ERR_WT_BYTE + ERR_WT_ALNUM + ERR_WT_FMT + ERR_WT_AA)
141 #define ALNUM_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
142 #define ALNUM_LEN (sizeof(ALNUM_CHARS) - 1)
145 struct time_adjustment {
150 #define MAX_SELECTIONS 512
151 static struct select_item selectfrm[MAX_SELECTIONS];
152 static int max_selected = -1;
153 static int keep_em = 0;
154 #ifdef PCAP_NG_DEFAULT
155 static int out_file_type = WTAP_FILE_PCAPNG; /* default to pcapng */
157 static int out_file_type = WTAP_FILE_PCAP; /* default to pcap */
159 static int out_frame_type = -2; /* Leave frame type alone */
160 static int verbose = 0; /* Not so verbose */
161 static struct time_adjustment time_adj = {{0, 0}, 0}; /* no adjustment */
162 static nstime_t relative_time_window = {0, 0}; /* de-dup time window */
163 static double err_prob = 0.0;
164 static time_t starttime = 0;
165 static time_t stoptime = 0;
166 static gboolean check_startstop = FALSE;
167 static gboolean dup_detect = FALSE;
168 static gboolean dup_detect_by_time = FALSE;
170 static int do_strict_time_adjustment = FALSE;
171 static struct time_adjustment strict_time_adj = {{0, 0}, 0}; /* strict time adjustment */
172 static nstime_t previous_time = {0, 0}; /* previous time */
174 static int find_dct2000_real_data(guint8 *buf);
177 abs_time_to_str_with_sec_resolution(const struct wtap_nstime *abs_time)
180 gchar *buf = (gchar *)g_malloc(16);
182 #if (defined _WIN32) && (_MSC_VER < 1500)
183 /* calling localtime() on MSVC 2005 with huge values causes it to crash */
184 /* XXX - find the exact value that still does work */
185 /* XXX - using _USE_32BIT_TIME_T might be another way to circumvent this problem */
186 if(abs_time->secs > 2000000000) {
190 tmp = localtime(&abs_time->secs);
192 g_snprintf(buf, 16, "%d%02d%02d%02d%02d%02d",
206 fileset_get_filename_by_pattern(guint idx, const struct wtap_nstime *time_val,
207 gchar *fprefix, gchar *fsuffix)
213 timestr = abs_time_to_str_with_sec_resolution(time_val);
214 g_snprintf(filenum, sizeof(filenum), "%05u", idx % RINGBUFFER_MAX_NUM_FILES);
215 abs_str = g_strconcat(fprefix, "_", filenum, "_", timestr, fsuffix, NULL);
222 fileset_extract_prefix_suffix(const char *fname, gchar **fprefix, gchar **fsuffix)
224 char *pfx, *last_pathsep;
227 save_file = g_strdup(fname);
228 if (save_file == NULL) {
229 fprintf(stderr, "editcap: Out of memory\n");
233 last_pathsep = strrchr(save_file, G_DIR_SEPARATOR);
234 pfx = strrchr(save_file,'.');
235 if (pfx != NULL && (last_pathsep == NULL || pfx > last_pathsep)) {
236 /* The pathname has a "." in it, and it's in the last component
237 of the pathname (because there is either only one component,
238 i.e. last_pathsep is null as there are no path separators,
239 or the "." is after the path separator before the last
242 Treat it as a separator between the rest of the file name and
243 the file name suffix, and arrange that the names given to the
244 ring buffer files have the specified suffix, i.e. put the
245 changing part of the name *before* the suffix. */
247 *fprefix = g_strdup(save_file);
248 pfx[0] = '.'; /* restore capfile_name */
249 *fsuffix = g_strdup(pfx);
251 /* Either there's no "." in the pathname, or it's in a directory
252 component, so the last component has no suffix. */
253 *fprefix = g_strdup(save_file);
260 /* Add a selection item, a simple parser for now */
262 add_selection(char *sel)
267 if (++max_selected >= MAX_SELECTIONS) {
268 /* Let the user know we stopped selecting */
269 printf("Out of room for packet selections!\n");
273 printf("Add_Selected: %s\n", sel);
275 if ((locn = strchr(sel, '-')) == NULL) { /* No dash, so a single number? */
277 printf("Not inclusive ...");
279 selectfrm[max_selected].inclusive = 0;
280 selectfrm[max_selected].first = atoi(sel);
282 printf(" %i\n", selectfrm[max_selected].first);
287 printf("Inclusive ...");
290 selectfrm[max_selected].inclusive = 1;
291 selectfrm[max_selected].first = atoi(sel);
292 selectfrm[max_selected].second = atoi(next);
294 printf(" %i, %i\n", selectfrm[max_selected].first, selectfrm[max_selected].second);
301 /* Was the packet selected? */
308 for (i = 0; i<= max_selected; i++) {
310 if (selectfrm[i].inclusive) {
311 if (selectfrm[i].first <= recno && selectfrm[i].second >= recno)
315 if (recno == selectfrm[i].first)
324 /* is the packet in the selected timeframe */
326 check_timestamp(wtap *wth)
328 struct wtap_pkthdr* pkthdr = wtap_phdr(wth);
330 return ( pkthdr->ts.secs >= starttime ) && ( pkthdr->ts.secs < stoptime );
334 set_time_adjustment(char *optarg_str_p)
343 /* skip leading whitespace */
344 while (*optarg_str_p == ' ' || *optarg_str_p == '\t') {
348 /* check for a negative adjustment */
349 if (*optarg_str_p == '-') {
350 time_adj.is_negative = 1;
354 /* collect whole number of seconds, if any */
355 if (*optarg_str_p == '.') { /* only fractional (i.e., .5 is ok) */
359 val = strtol(optarg_str_p, &frac, 10);
360 if (frac == NULL || frac == optarg_str_p || val == LONG_MIN || val == LONG_MAX) {
361 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
365 if (val < 0) { /* implies '--' since we caught '-' above */
366 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
371 time_adj.tv.tv_sec = val;
373 /* now collect the partial seconds, if any */
374 if (*frac != '\0') { /* chars left, so get fractional part */
375 val = strtol(&(frac[1]), &end, 10);
376 /* if more than 6 fractional digits truncate to 6 */
377 if((end - &(frac[1])) > 6) {
378 frac[7] = 't'; /* 't' for truncate */
379 val = strtol(&(frac[1]), &end, 10);
381 if (*frac != '.' || end == NULL || end == frac
382 || val < 0 || val > ONE_MILLION || val == LONG_MIN || val == LONG_MAX) {
383 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
389 return; /* no fractional digits */
392 /* adjust fractional portion from fractional to numerator
393 * e.g., in "1.5" from 5 to 500000 since .5*10^6 = 500000 */
394 if (frac && end) { /* both are valid */
395 frac_digits = end - frac - 1; /* fractional digit count (remember '.') */
396 while(frac_digits < 6) { /* this is frac of 10^6 */
401 time_adj.tv.tv_usec = (int)val;
405 set_strict_time_adj(char *optarg_str_p)
414 /* skip leading whitespace */
415 while (*optarg_str_p == ' ' || *optarg_str_p == '\t') {
420 * check for a negative adjustment
421 * A negative strict adjustment value is a flag
422 * to adjust all frames by the specifed delta time.
424 if (*optarg_str_p == '-') {
425 strict_time_adj.is_negative = 1;
429 /* collect whole number of seconds, if any */
430 if (*optarg_str_p == '.') { /* only fractional (i.e., .5 is ok) */
434 val = strtol(optarg_str_p, &frac, 10);
435 if (frac == NULL || frac == optarg_str_p || val == LONG_MIN || val == LONG_MAX) {
436 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
440 if (val < 0) { /* implies '--' since we caught '-' above */
441 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
446 strict_time_adj.tv.tv_sec = val;
448 /* now collect the partial seconds, if any */
449 if (*frac != '\0') { /* chars left, so get fractional part */
450 val = strtol(&(frac[1]), &end, 10);
451 /* if more than 6 fractional digits truncate to 6 */
452 if((end - &(frac[1])) > 6) {
453 frac[7] = 't'; /* 't' for truncate */
454 val = strtol(&(frac[1]), &end, 10);
456 if (*frac != '.' || end == NULL || end == frac
457 || val < 0 || val > ONE_MILLION || val == LONG_MIN || val == LONG_MAX) {
458 fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
464 return; /* no fractional digits */
467 /* adjust fractional portion from fractional to numerator
468 * e.g., in "1.5" from 5 to 500000 since .5*10^6 = 500000 */
469 if (frac && end) { /* both are valid */
470 frac_digits = end - frac - 1; /* fractional digit count (remember '.') */
471 while(frac_digits < 6) { /* this is frac of 10^6 */
476 strict_time_adj.tv.tv_usec = (int)val;
480 set_rel_time(char *optarg_str_p)
489 /* skip leading whitespace */
490 while (*optarg_str_p == ' ' || *optarg_str_p == '\t') {
494 /* ignore negative adjustment */
495 if (*optarg_str_p == '-') {
499 /* collect whole number of seconds, if any */
500 if (*optarg_str_p == '.') { /* only fractional (i.e., .5 is ok) */
504 val = strtol(optarg_str_p, &frac, 10);
505 if (frac == NULL || frac == optarg_str_p || val == LONG_MIN || val == LONG_MAX) {
506 fprintf(stderr, "1: editcap: \"%s\" isn't a valid rel time value\n",
510 if (val < 0) { /* implies '--' since we caught '-' above */
511 fprintf(stderr, "2: editcap: \"%s\" isn't a valid rel time value\n",
516 relative_time_window.secs = val;
518 /* now collect the partial seconds, if any */
519 if (*frac != '\0') { /* chars left, so get fractional part */
520 val = strtol(&(frac[1]), &end, 10);
521 /* if more than 9 fractional digits truncate to 9 */
522 if((end - &(frac[1])) > 9) {
523 frac[10] = 't'; /* 't' for truncate */
524 val = strtol(&(frac[1]), &end, 10);
526 if (*frac != '.' || end == NULL || end == frac
527 || val < 0 || val > ONE_BILLION || val == LONG_MIN || val == LONG_MAX) {
528 fprintf(stderr, "3: editcap: \"%s\" isn't a valid rel time value\n",
534 return; /* no fractional digits */
537 /* adjust fractional portion from fractional to numerator
538 * e.g., in "1.5" from 5 to 500000000 since .5*10^9 = 500000000 */
539 if (frac && end) { /* both are valid */
540 frac_digits = end - frac - 1; /* fractional digit count (remember '.') */
541 while(frac_digits < 9) { /* this is frac of 10^9 */
546 relative_time_window.nsecs = (int)val;
550 is_duplicate(guint8* fd, guint32 len) {
555 if (cur_dup_entry >= dup_window)
558 /* Calculate our digest */
560 md5_append(&ms, fd, len);
561 md5_finish(&ms, fd_hash[cur_dup_entry].digest);
563 fd_hash[cur_dup_entry].len = len;
565 /* Look for duplicates */
566 for (i = 0; i < dup_window; i++) {
567 if (i == cur_dup_entry)
570 if (fd_hash[i].len == fd_hash[cur_dup_entry].len &&
571 memcmp(fd_hash[i].digest, fd_hash[cur_dup_entry].digest, 16) == 0) {
580 is_duplicate_rel_time(guint8* fd, guint32 len, const nstime_t *current) {
585 if (cur_dup_entry >= dup_window)
588 /* Calculate our digest */
590 md5_append(&ms, fd, len);
591 md5_finish(&ms, fd_hash[cur_dup_entry].digest);
593 fd_hash[cur_dup_entry].len = len;
594 fd_hash[cur_dup_entry].time.secs = current->secs;
595 fd_hash[cur_dup_entry].time.nsecs = current->nsecs;
598 * Look for relative time related duplicates.
599 * This is hopefully a reasonably efficient mechanism for
600 * finding duplicates by rel time in the fd_hash[] cache.
601 * We check starting from the most recently added hash
602 * entries and work backwards towards older packets.
603 * This approach allows the dup test to be terminated
604 * when the relative time of a cached entry is found to
605 * be beyond the dup time window.
607 * Of course this assumes that the input trace file is
608 * "well-formed" in the sense that the packet timestamps are
609 * in strict chronologically increasing order (which is NOT
610 * always the case!!).
612 * The fd_hash[] table was deliberatly created large (1,000,000).
613 * Looking for time related duplicates in large trace files with
614 * non-fractional dup time window values can potentially take
615 * a long time to complete.
618 for (i = cur_dup_entry - 1;; i--) {
626 if (i == cur_dup_entry) {
628 * We've decremented back to where we started.
634 if (nstime_is_unset(&(fd_hash[i].time))) {
636 * We've decremented to an unused fd_hash[] entry.
642 nstime_delta(&delta, current, &fd_hash[i].time);
644 if(delta.secs < 0 || delta.nsecs < 0)
647 * A negative delta implies that the current packet
648 * has an absolute timestamp less than the cached packet
649 * that it is being compared to. This is NOT a normal
650 * situation since trace files usually have packets in
651 * chronological order (oldest to newest).
653 * There are several possible ways to deal with this:
654 * 1. 'continue' dup checking with the next cached frame.
655 * 2. 'break' from looking for a duplicate of the current frame.
656 * 3. Take the absolute value of the delta and see if that
657 * falls within the specifed dup time window.
659 * Currently this code does option 1. But it would pretty
660 * easy to add yet-another-editcap-option to select one of
661 * the other behaviors for dealing with out-of-sequence
667 cmp = nstime_cmp(&delta, &relative_time_window);
671 * The delta time indicates that we are now looking at
672 * cached packets beyond the specified dup time window.
676 } else if (fd_hash[i].len == fd_hash[cur_dup_entry].len &&
677 memcmp(fd_hash[i].digest, fd_hash[cur_dup_entry].digest, 16) == 0) {
686 usage(gboolean is_error)
695 fprintf(output, "Editcap %s"
697 " (" SVNVERSION " from " SVNPATH ")"
700 fprintf(output, "Edit and/or translate the format of capture files.\n");
701 fprintf(output, "See http://www.wireshark.org for more information.\n");
702 fprintf(output, "\n");
703 fprintf(output, "Usage: editcap [options] ... <infile> <outfile> [ <packet#>[-<packet#>] ... ]\n");
704 fprintf(output, "\n");
705 fprintf(output, "<infile> and <outfile> must both be present.\n");
706 fprintf(output, "A single packet or a range of packets can be selected.\n");
707 fprintf(output, "\n");
708 fprintf(output, "Packet selection:\n");
709 fprintf(output, " -r keep the selected packets; default is to delete them.\n");
710 fprintf(output, " -A <start time> only output packets whose timestamp is after (or equal\n");
711 fprintf(output, " to) the given time (format as YYYY-MM-DD hh:mm:ss).\n");
712 fprintf(output, " -B <stop time> only output packets whose timestamp is before the\n");
713 fprintf(output, " given time (format as YYYY-MM-DD hh:mm:ss).\n");
714 fprintf(output, "\n");
715 fprintf(output, "Duplicate packet removal:\n");
716 fprintf(output, " -d remove packet if duplicate (window == %d).\n", DEFAULT_DUP_DEPTH);
717 fprintf(output, " -D <dup window> remove packet if duplicate; configurable <dup window>\n");
718 fprintf(output, " Valid <dup window> values are 0 to %d.\n", MAX_DUP_DEPTH);
719 fprintf(output, " NOTE: A <dup window> of 0 with -v (verbose option) is\n");
720 fprintf(output, " useful to print MD5 hashes.\n");
721 fprintf(output, " -w <dup time window> remove packet if duplicate packet is found EQUAL TO OR\n");
722 fprintf(output, " LESS THAN <dup time window> prior to current packet.\n");
723 fprintf(output, " A <dup time window> is specified in relative seconds\n");
724 fprintf(output, " (e.g. 0.000001).\n");
725 fprintf(output, "\n");
726 fprintf(output, " NOTE: The use of the 'Duplicate packet removal' options with\n");
727 fprintf(output, " other editcap options except -v may not always work as expected.\n");
728 fprintf(output, " Specifically the -r, -t or -S options will very likely NOT have the\n");
729 fprintf(output, " desired effect if combined with the -d, -D or -w.\n");
730 fprintf(output, "\n");
731 fprintf(output, "Packet manipulation:\n");
732 fprintf(output, " -s <snaplen> truncate each packet to max. <snaplen> bytes of data.\n");
733 fprintf(output, " -C <choplen> chop each packet by <choplen> bytes. Positive values\n");
734 fprintf(output, " chop at the packet beginning, negative values at the\n");
735 fprintf(output, " packet end.\n");
736 fprintf(output, " -t <time adjustment> adjust the timestamp of each packet;\n");
737 fprintf(output, " <time adjustment> is in relative seconds (e.g. -0.5).\n");
738 fprintf(output, " -S <strict adjustment> adjust timestamp of packets if necessary to insure\n");
739 fprintf(output, " strict chronological increasing order. The <strict\n");
740 fprintf(output, " adjustment> is specified in relative seconds with\n");
741 fprintf(output, " values of 0 or 0.000001 being the most reasonable.\n");
742 fprintf(output, " A negative adjustment value will modify timestamps so\n");
743 fprintf(output, " that each packet's delta time is the absolute value\n");
744 fprintf(output, " of the adjustment specified. A value of -0 will set\n");
745 fprintf(output, " all packets to the timestamp of the first packet.\n");
746 fprintf(output, " -E <error probability> set the probability (between 0.0 and 1.0 incl.)\n");
747 fprintf(output, " that a particular packet byte will be randomly changed.\n");
748 fprintf(output, "\n");
749 fprintf(output, "Output File(s):\n");
750 fprintf(output, " -c <packets per file> split the packet output to different files\n");
751 fprintf(output, " based on uniform packet counts\n");
752 fprintf(output, " with a maximum of <packets per file> each.\n");
753 fprintf(output, " -i <seconds per file> split the packet output to different files\n");
754 fprintf(output, " based on uniform time intervals\n");
755 fprintf(output, " with a maximum of <seconds per file> each.\n");
756 fprintf(output, " -F <capture type> set the output file type; default is pcapng.\n");
757 fprintf(output, " an empty \"-F\" option will list the file types.\n");
758 fprintf(output, " -T <encap type> set the output file encapsulation type;\n");
759 fprintf(output, " default is the same as the input file.\n");
760 fprintf(output, " an empty \"-T\" option will list the encapsulation types.\n");
761 fprintf(output, "\n");
762 fprintf(output, "Miscellaneous:\n");
763 fprintf(output, " -h display this help and exit.\n");
764 fprintf(output, " -v verbose output.\n");
765 fprintf(output, " If -v is used with any of the 'Duplicate Packet\n");
766 fprintf(output, " Removal' options (-d, -D or -w) then Packet lengths\n");
767 fprintf(output, " and MD5 hashes are printed to standard-out.\n");
768 fprintf(output, "\n");
772 const char *sstr; /* The short string */
773 const char *lstr; /* The long string */
777 string_compare(gconstpointer a, gconstpointer b)
779 return strcmp(((const struct string_elem *)a)->sstr,
780 ((const struct string_elem *)b)->sstr);
784 string_elem_print(gpointer data, gpointer not_used _U_)
786 fprintf(stderr, " %s - %s\n",
787 ((struct string_elem *)data)->sstr,
788 ((struct string_elem *)data)->lstr);
792 list_capture_types(void) {
794 struct string_elem *captypes;
797 captypes = g_new(struct string_elem,WTAP_NUM_FILE_TYPES);
798 fprintf(stderr, "editcap: The available capture file types for the \"-F\" flag are:\n");
799 for (i = 0; i < WTAP_NUM_FILE_TYPES; i++) {
800 if (wtap_dump_can_open(i)) {
801 captypes[i].sstr = wtap_file_type_short_string(i);
802 captypes[i].lstr = wtap_file_type_string(i);
803 list = g_slist_insert_sorted(list, &captypes[i], string_compare);
806 g_slist_foreach(list, string_elem_print, NULL);
812 list_encap_types(void) {
814 struct string_elem *encaps;
817 encaps = (struct string_elem *)g_malloc(sizeof(struct string_elem) * WTAP_NUM_ENCAP_TYPES);
818 fprintf(stderr, "editcap: The available encapsulation types for the \"-T\" flag are:\n");
819 for (i = 0; i < WTAP_NUM_ENCAP_TYPES; i++) {
820 encaps[i].sstr = wtap_encap_short_string(i);
821 if (encaps[i].sstr != NULL) {
822 encaps[i].lstr = wtap_encap_string(i);
823 list = g_slist_insert_sorted(list, &encaps[i], string_compare);
826 g_slist_foreach(list, string_elem_print, NULL);
833 * Don't report failures to load plugins because most (non-wiretap) plugins
834 * *should* fail to load (because we're not linked against libwireshark and
835 * dissector plugins need libwireshark).
838 failure_message(const char *msg_format _U_, va_list ap _U_)
845 main(int argc, char *argv[])
853 guint32 snaplen = 0; /* No limit */
854 int choplen = 0; /* No chop */
855 wtap_dumper *pdh = NULL;
856 unsigned int count = 1;
857 unsigned int duplicate_count = 0;
859 struct wtap_pkthdr snap_phdr;
860 const struct wtap_pkthdr *phdr;
862 wtapng_section_t *shb_hdr;
863 wtapng_iface_descriptions_t *idb_inf;
865 guint32 read_count = 0;
866 int split_packet_count = 0;
867 int written_count = 0;
868 char *filename = NULL;
869 gboolean ts_okay = TRUE;
870 int secs_per_block = 0;
872 nstime_t block_start;
873 gchar *fprefix = NULL;
874 gchar *fsuffix = NULL;
878 char* init_progfile_dir_error;
882 arg_list_utf_16to8(argc, argv);
883 create_app_running_mutex();
887 * Get credential information for later use.
889 init_process_policies();
892 /* Register wiretap plugins */
893 if ((init_progfile_dir_error = init_progfile_dir(argv[0], main))) {
894 g_warning("editcap: init_progfile_dir(): %s", init_progfile_dir_error);
895 g_free(init_progfile_dir_error);
897 init_report_err(failure_message,NULL,NULL,NULL);
902 /* Process the options */
903 while ((opt = getopt(argc, argv, "A:B:c:C:dD:E:F:hrs:i:t:S:T:vw:")) !=-1) {
908 err_prob = strtod(optarg, &p);
909 if (p == optarg || err_prob < 0.0 || err_prob > 1.0) {
910 fprintf(stderr, "editcap: probability \"%s\" must be between 0.0 and 1.0\n",
914 srand( (unsigned int) (time(NULL) + getpid()) );
918 out_file_type = wtap_short_string_to_file_type(optarg);
919 if (out_file_type < 0) {
920 fprintf(stderr, "editcap: \"%s\" isn't a valid capture file type\n\n",
922 list_capture_types();
928 split_packet_count = (int)strtol(optarg, &p, 10);
929 if (p == optarg || *p != '\0') {
930 fprintf(stderr, "editcap: \"%s\" isn't a valid packet count\n",
934 if (split_packet_count <= 0) {
935 fprintf(stderr, "editcap: \"%d\" packet count must be larger than zero\n",
942 choplen = (int)strtol(optarg, &p, 10);
943 if (p == optarg || *p != '\0') {
944 fprintf(stderr, "editcap: \"%s\" isn't a valid chop length\n",
952 dup_detect_by_time = FALSE;
953 dup_window = DEFAULT_DUP_DEPTH;
958 dup_detect_by_time = FALSE;
959 dup_window = (int)strtol(optarg, &p, 10);
960 if (p == optarg || *p != '\0') {
961 fprintf(stderr, "editcap: \"%s\" isn't a valid duplicate window value\n",
965 if (dup_window < 0 || dup_window > MAX_DUP_DEPTH) {
966 fprintf(stderr, "editcap: \"%d\" duplicate window value must be between 0 and %d inclusive.\n",
967 dup_window, MAX_DUP_DEPTH);
974 dup_detect_by_time = TRUE;
975 dup_window = MAX_DUP_DEPTH;
976 set_rel_time(optarg);
979 case '?': /* Bad options if GNU getopt */
982 list_capture_types();
999 keep_em = !keep_em; /* Just invert */
1003 snaplen = (guint32)strtol(optarg, &p, 10);
1004 if (p == optarg || *p != '\0') {
1005 fprintf(stderr, "editcap: \"%s\" isn't a valid snapshot length\n",
1012 set_time_adjustment(optarg);
1016 set_strict_time_adj(optarg);
1017 do_strict_time_adjustment = TRUE;
1021 out_frame_type = wtap_short_string_to_encap(optarg);
1022 if (out_frame_type < 0) {
1023 fprintf(stderr, "editcap: \"%s\" isn't a valid encapsulation type\n\n",
1031 verbose = !verbose; /* Just invert */
1034 case 'i': /* break capture file based on time interval */
1035 secs_per_block = atoi(optarg);
1036 if(secs_per_block <= 0) {
1037 fprintf(stderr, "editcap: \"%s\" isn't a valid time interval\n\n", optarg);
1046 memset(&starttm,0,sizeof(struct tm));
1048 if(!strptime(optarg,"%Y-%m-%d %T",&starttm)) {
1049 fprintf(stderr, "editcap: \"%s\" isn't a valid time format\n\n", optarg);
1053 check_startstop = TRUE;
1054 starttm.tm_isdst = -1;
1056 starttime = mktime(&starttm);
1064 memset(&stoptm,0,sizeof(struct tm));
1066 if(!strptime(optarg,"%Y-%m-%d %T",&stoptm)) {
1067 fprintf(stderr, "editcap: \"%s\" isn't a valid time format\n\n", optarg);
1070 check_startstop = TRUE;
1071 stoptm.tm_isdst = -1;
1072 stoptime = mktime(&stoptm);
1080 printf("Optind = %i, argc = %i\n", optind, argc);
1083 if ((argc - optind) < 1) {
1090 if (check_startstop && !stoptime) {
1092 /* XXX: will work until 2035 */
1093 memset(&stoptm,0,sizeof(struct tm));
1094 stoptm.tm_year = 135;
1095 stoptm.tm_mday = 31;
1098 stoptime = mktime(&stoptm);
1101 nstime_set_unset(&block_start);
1103 if (starttime > stoptime) {
1104 fprintf(stderr, "editcap: start time is after the stop time\n");
1108 if (split_packet_count > 0 && secs_per_block > 0) {
1109 fprintf(stderr, "editcap: can't split on both packet count and time interval\n");
1110 fprintf(stderr, "editcap: at the same time\n");
1114 wth = wtap_open_offline(argv[optind], &err, &err_info, FALSE);
1117 fprintf(stderr, "editcap: Can't open %s: %s\n", argv[optind],
1118 wtap_strerror(err));
1121 case WTAP_ERR_UNSUPPORTED:
1122 case WTAP_ERR_UNSUPPORTED_ENCAP:
1123 case WTAP_ERR_BAD_FILE:
1124 fprintf(stderr, "(%s)\n", err_info);
1133 fprintf(stderr, "File %s is a %s capture file.\n", argv[optind],
1134 wtap_file_type_string(wtap_file_type(wth)));
1137 shb_hdr = wtap_file_get_shb_info(wth);
1138 idb_inf = wtap_file_get_idb_info(wth);
1141 * Now, process the rest, if any ... we only write if there is an extra
1142 * argument or so ...
1145 if ((argc - optind) >= 2) {
1147 if (out_frame_type == -2)
1148 out_frame_type = wtap_file_encap(wth);
1150 for (i = optind + 2; i < argc; i++)
1151 if (add_selection(argv[i]) == FALSE)
1154 if (dup_detect || dup_detect_by_time) {
1155 for (i = 0; i < dup_window; i++) {
1156 memset(&fd_hash[i].digest, 0, 16);
1158 nstime_set_unset(&fd_hash[i].time);
1162 while (wtap_read(wth, &err, &err_info, &data_offset)) {
1165 phdr = wtap_phdr(wth);
1166 buf = wtap_buf_ptr(wth);
1168 if (nstime_is_unset(&block_start)) { /* should only be the first packet */
1169 block_start.secs = phdr->ts.secs;
1170 block_start.nsecs = phdr->ts.nsecs;
1172 if (split_packet_count > 0 || secs_per_block > 0) {
1173 if (!fileset_extract_prefix_suffix(argv[optind+1], &fprefix, &fsuffix))
1176 filename = fileset_get_filename_by_pattern(block_cnt++, &phdr->ts, fprefix, fsuffix);
1178 filename = g_strdup(argv[optind+1]);
1180 /* If we don't have an application name add Editcap */
1181 if(shb_hdr->shb_user_appl == NULL) {
1182 g_snprintf(appname, sizeof(appname), "Editcap " VERSION);
1183 shb_hdr->shb_user_appl = appname;
1186 pdh = wtap_dump_open_ng(filename, out_file_type, out_frame_type,
1187 snaplen ? MIN(snaplen, wtap_snapshot_length(wth)) : wtap_snapshot_length(wth),
1188 FALSE /* compressed */, shb_hdr, idb_inf, &err);
1191 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1192 wtap_strerror(err));
1199 if (secs_per_block > 0) {
1200 while ((phdr->ts.secs - block_start.secs > secs_per_block) ||
1201 (phdr->ts.secs - block_start.secs == secs_per_block &&
1202 phdr->ts.nsecs >= block_start.nsecs )) { /* time for the next file */
1204 if (!wtap_dump_close(pdh, &err)) {
1205 fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
1206 wtap_strerror(err));
1209 block_start.secs = block_start.secs + secs_per_block; /* reset for next interval */
1211 filename = fileset_get_filename_by_pattern(block_cnt++, &phdr->ts, fprefix, fsuffix);
1215 fprintf(stderr, "Continuing writing in file %s\n", filename);
1218 pdh = wtap_dump_open_ng(filename, out_file_type, out_frame_type,
1219 snaplen ? MIN(snaplen, wtap_snapshot_length(wth)) : wtap_snapshot_length(wth),
1220 FALSE /* compressed */, shb_hdr, idb_inf, &err);
1223 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1224 wtap_strerror(err));
1230 if (split_packet_count > 0) {
1232 /* time for the next file? */
1233 if (written_count > 0 &&
1234 written_count % split_packet_count == 0) {
1235 if (!wtap_dump_close(pdh, &err)) {
1236 fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
1237 wtap_strerror(err));
1242 filename = fileset_get_filename_by_pattern(block_cnt++, &phdr->ts, fprefix, fsuffix);
1246 fprintf(stderr, "Continuing writing in file %s\n", filename);
1249 pdh = wtap_dump_open_ng(filename, out_file_type, out_frame_type,
1250 snaplen ? MIN(snaplen, wtap_snapshot_length(wth)) : wtap_snapshot_length(wth),
1251 FALSE /* compressed */, shb_hdr, idb_inf, &err);
1253 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1254 wtap_strerror(err));
1260 if (check_startstop)
1261 ts_okay = check_timestamp(wth);
1263 if ( ts_okay && ((!selected(count) && !keep_em) || (selected(count) && keep_em)) ) {
1265 if (verbose && !dup_detect && !dup_detect_by_time)
1266 printf("Packet: %u\n", count);
1268 /* We simply write it, perhaps after truncating it; we could do other
1269 things, like modify it. */
1271 phdr = wtap_phdr(wth);
1273 if (snaplen != 0 && phdr->caplen > snaplen) {
1275 snap_phdr.caplen = snaplen;
1281 if (((signed int) phdr->caplen + choplen) > 0)
1282 snap_phdr.caplen += choplen;
1284 snap_phdr.caplen = 0;
1286 } else if (choplen > 0) {
1288 if (phdr->caplen > (unsigned int) choplen) {
1289 snap_phdr.caplen -= choplen;
1292 snap_phdr.caplen = 0;
1297 * Do we adjust timestamps to insure strict chronologically order?
1300 if (do_strict_time_adjustment) {
1301 if (previous_time.secs || previous_time.nsecs) {
1302 if (!strict_time_adj.is_negative) {
1306 current.secs = phdr->ts.secs;
1307 current.nsecs = phdr->ts.nsecs;
1309 nstime_delta(&delta, ¤t, &previous_time);
1311 if (delta.secs < 0 || delta.nsecs < 0)
1314 * A negative delta indicates that the current packet
1315 * has an absolute timestamp less than the previous packet
1316 * that it is being compared to. This is NOT a normal
1317 * situation since trace files usually have packets in
1318 * chronological order (oldest to newest).
1320 /* printf("++out of order, need to adjust this packet!\n"); */
1322 snap_phdr.ts.secs = previous_time.secs + strict_time_adj.tv.tv_sec;
1323 snap_phdr.ts.nsecs = previous_time.nsecs;
1324 if (snap_phdr.ts.nsecs + strict_time_adj.tv.tv_usec * 1000 > ONE_MILLION * 1000) {
1326 snap_phdr.ts.secs++;
1327 snap_phdr.ts.nsecs += (strict_time_adj.tv.tv_usec - ONE_MILLION) * 1000;
1329 snap_phdr.ts.nsecs += strict_time_adj.tv.tv_usec * 1000;
1335 * A negative strict time adjustment is requested.
1336 * Unconditionally set each timestamp to previous
1337 * packet's timestamp plus delta.
1340 snap_phdr.ts.secs = previous_time.secs + strict_time_adj.tv.tv_sec;
1341 snap_phdr.ts.nsecs = previous_time.nsecs;
1342 if (snap_phdr.ts.nsecs + strict_time_adj.tv.tv_usec * 1000 > ONE_MILLION * 1000) {
1344 snap_phdr.ts.secs++;
1345 snap_phdr.ts.nsecs += (strict_time_adj.tv.tv_usec - ONE_MILLION) * 1000;
1347 snap_phdr.ts.nsecs += strict_time_adj.tv.tv_usec * 1000;
1352 previous_time.secs = phdr->ts.secs;
1353 previous_time.nsecs = phdr->ts.nsecs;
1356 /* assume that if the frame's tv_sec is 0, then
1357 * the timestamp isn't supported */
1358 if (phdr->ts.secs > 0 && time_adj.tv.tv_sec != 0) {
1360 if (time_adj.is_negative)
1361 snap_phdr.ts.secs -= time_adj.tv.tv_sec;
1363 snap_phdr.ts.secs += time_adj.tv.tv_sec;
1367 /* assume that if the frame's tv_sec is 0, then
1368 * the timestamp isn't supported */
1369 if (phdr->ts.secs > 0 && time_adj.tv.tv_usec != 0) {
1371 if (time_adj.is_negative) { /* subtract */
1372 if (snap_phdr.ts.nsecs/1000 < time_adj.tv.tv_usec) { /* borrow */
1373 snap_phdr.ts.secs--;
1374 snap_phdr.ts.nsecs += ONE_MILLION * 1000;
1376 snap_phdr.ts.nsecs -= time_adj.tv.tv_usec * 1000;
1378 if (snap_phdr.ts.nsecs + time_adj.tv.tv_usec * 1000 > ONE_MILLION * 1000) {
1380 snap_phdr.ts.secs++;
1381 snap_phdr.ts.nsecs += (time_adj.tv.tv_usec - ONE_MILLION) * 1000;
1383 snap_phdr.ts.nsecs += time_adj.tv.tv_usec * 1000;
1389 /* suppress duplicates by packet window */
1391 if (is_duplicate(buf, phdr->caplen)) {
1393 fprintf(stdout, "Skipped: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1394 for (i = 0; i < 16; i++) {
1395 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1397 fprintf(stdout, "\n");
1404 fprintf(stdout, "Packet: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1405 for (i = 0; i < 16; i++) {
1406 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1408 fprintf(stdout, "\n");
1413 /* suppress duplicates by time window */
1414 if (dup_detect_by_time) {
1417 current.secs = phdr->ts.secs;
1418 current.nsecs = phdr->ts.nsecs;
1420 if (is_duplicate_rel_time(buf, phdr->caplen, ¤t)) {
1422 fprintf(stdout, "Skipped: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1423 for (i = 0; i < 16; i++) {
1424 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1426 fprintf(stdout, "\n");
1433 fprintf(stdout, "Packet: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1434 for (i = 0; i < 16; i++) {
1435 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1437 fprintf(stdout, "\n");
1442 /* Random error mutation */
1443 if (err_prob > 0.0) {
1444 int real_data_start = 0;
1445 /* Protect non-protocol data */
1446 if (wtap_file_type(wth) == WTAP_FILE_CATAPULT_DCT2000) {
1447 real_data_start = find_dct2000_real_data(buf);
1449 for (i = real_data_start; i < (int) phdr->caplen; i++) {
1450 if (rand() <= err_prob * RAND_MAX) {
1451 err_type = rand() / (RAND_MAX / ERR_WT_TOTAL + 1);
1453 if (err_type < ERR_WT_BIT) {
1454 buf[i] ^= 1 << (rand() / (RAND_MAX / 8 + 1));
1455 err_type = ERR_WT_TOTAL;
1457 err_type -= ERR_WT_BYTE;
1460 if (err_type < ERR_WT_BYTE) {
1461 buf[i] = rand() / (RAND_MAX / 255 + 1);
1462 err_type = ERR_WT_TOTAL;
1464 err_type -= ERR_WT_BYTE;
1467 if (err_type < ERR_WT_ALNUM) {
1468 buf[i] = ALNUM_CHARS[rand() / (RAND_MAX / ALNUM_LEN + 1)];
1469 err_type = ERR_WT_TOTAL;
1471 err_type -= ERR_WT_ALNUM;
1474 if (err_type < ERR_WT_FMT) {
1475 if ((unsigned int)i < phdr->caplen - 2)
1476 g_strlcpy((char*) &buf[i], "%s", 2);
1477 err_type = ERR_WT_TOTAL;
1479 err_type -= ERR_WT_FMT;
1482 if (err_type < ERR_WT_AA) {
1483 for (j = i; j < (int) phdr->caplen; j++) {
1492 if (!wtap_dump(pdh, phdr, buf, &err)) {
1495 case WTAP_ERR_UNSUPPORTED_ENCAP:
1497 * This is a problem with the particular frame we're writing;
1498 * note that, and give the frame number.
1500 fprintf(stderr, "editcap: Frame %u of \"%s\" has a network type that can't be saved in a file with that format\n.",
1501 read_count, argv[optind]);
1505 fprintf(stderr, "editcap: Error writing to %s: %s\n",
1506 filename, wtap_strerror(err));
1521 /* Print a message noting that the read failed somewhere along the line. */
1523 "editcap: An error occurred while reading \"%s\": %s.\n",
1524 argv[optind], wtap_strerror(err));
1527 case WTAP_ERR_UNSUPPORTED:
1528 case WTAP_ERR_UNSUPPORTED_ENCAP:
1529 case WTAP_ERR_BAD_FILE:
1530 fprintf(stderr, "(%s)\n", err_info);
1537 /* No valid packages found, open the outfile so we can write an empty header */
1539 filename = g_strdup(argv[optind+1]);
1541 pdh = wtap_dump_open_ng(filename, out_file_type, out_frame_type,
1542 snaplen ? MIN(snaplen, wtap_snapshot_length(wth)): wtap_snapshot_length(wth),
1543 FALSE /* compressed */, shb_hdr, idb_inf, &err);
1545 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1546 wtap_strerror(err));
1554 if (!wtap_dump_close(pdh, &err)) {
1556 fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
1557 wtap_strerror(err));
1566 fprintf(stdout, "%u packet%s seen, %u packet%s skipped with duplicate window of %u packets.\n",
1567 count - 1, plurality(count - 1, "", "s"),
1568 duplicate_count, plurality(duplicate_count, "", "s"), dup_window);
1569 } else if (dup_detect_by_time) {
1570 fprintf(stdout, "%u packet%s seen, %u packet%s skipped with duplicate time window equal to or less than %ld.%09ld seconds.\n",
1571 count - 1, plurality(count - 1, "", "s"),
1572 duplicate_count, plurality(duplicate_count, "", "s"),
1573 (long)relative_time_window.secs, (long int)relative_time_window.nsecs);
1579 /* Skip meta-information read from file to return offset of real
1581 static int find_dct2000_real_data(guint8 *buf)
1585 for (n=0; buf[n] != '\0'; n++); /* Context name */
1587 n++; /* Context port number */
1588 for (; buf[n] != '\0'; n++); /* Timestamp */
1590 for (; buf[n] != '\0'; n++); /* Protocol name */
1592 for (; buf[n] != '\0'; n++); /* Variant number (as string) */
1594 for (; buf[n] != '\0'; n++); /* Outhdr (as string) */
1596 n += 2; /* Direction & encap */