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>
79 #include <wsutil/report_err.h>
80 #include <wsutil/strnatcmp.h>
83 * The symbols declared in the below are exported from libwireshark,
84 * but we don't want to link whole libwireshark to editcap.
85 * We link the object directly instead and this needs a little trick
86 * with the WS_BUILD_DLL #define.
89 #define RESET_SYMBOL_EXPORT /* wsutil/wsgetopt.h set export behavior above. */
90 #include "epan/crypt/md5.h"
91 #include "epan/plugins.h"
92 #include "epan/filesystem.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. You can use this option more than once.\n");
736 fprintf(output, " -L adjust the frame length when chopping and/or snapping\n");
737 fprintf(output, " -t <time adjustment> adjust the timestamp of each packet;\n");
738 fprintf(output, " <time adjustment> is in relative seconds (e.g. -0.5).\n");
739 fprintf(output, " -S <strict adjustment> adjust timestamp of packets if necessary to insure\n");
740 fprintf(output, " strict chronological increasing order. The <strict\n");
741 fprintf(output, " adjustment> is specified in relative seconds with\n");
742 fprintf(output, " values of 0 or 0.000001 being the most reasonable.\n");
743 fprintf(output, " A negative adjustment value will modify timestamps so\n");
744 fprintf(output, " that each packet's delta time is the absolute value\n");
745 fprintf(output, " of the adjustment specified. A value of -0 will set\n");
746 fprintf(output, " all packets to the timestamp of the first packet.\n");
747 fprintf(output, " -E <error probability> set the probability (between 0.0 and 1.0 incl.) that\n");
748 fprintf(output, " a particular packet byte will be randomly changed.\n");
749 fprintf(output, "\n");
750 fprintf(output, "Output File(s):\n");
751 fprintf(output, " -c <packets per file> split the packet output to different files based on\n");
752 fprintf(output, " uniform packet counts with a maximum of\n");
753 fprintf(output, " <packets per file> each.\n");
754 fprintf(output, " -i <seconds per file> split the packet output to different files based on\n");
755 fprintf(output, " uniform time intervals with a maximum of\n");
756 fprintf(output, " <seconds per file> each.\n");
757 fprintf(output, " -F <capture type> set the output file type; default is pcapng. An empty\n");
758 fprintf(output, " \"-F\" option will list the file types.\n");
759 fprintf(output, " -T <encap type> set the output file encapsulation type; default is the\n");
760 fprintf(output, " same as the input file. An empty \"-T\" option will\n");
761 fprintf(output, " list the encapsulation types.\n");
762 fprintf(output, "\n");
763 fprintf(output, "Miscellaneous:\n");
764 fprintf(output, " -h display this help and exit.\n");
765 fprintf(output, " -v verbose output.\n");
766 fprintf(output, " If -v is used with any of the 'Duplicate Packet\n");
767 fprintf(output, " Removal' options (-d, -D or -w) then Packet lengths\n");
768 fprintf(output, " and MD5 hashes are printed to standard-out.\n");
769 fprintf(output, "\n");
773 const char *sstr; /* The short string */
774 const char *lstr; /* The long string */
778 string_compare(gconstpointer a, gconstpointer b)
780 return strcmp(((const struct string_elem *)a)->sstr,
781 ((const struct string_elem *)b)->sstr);
785 string_nat_compare(gconstpointer a, gconstpointer b)
787 return strnatcmp(((const struct string_elem *)a)->sstr,
788 ((const struct string_elem *)b)->sstr);
792 string_elem_print(gpointer data, gpointer not_used _U_)
794 fprintf(stderr, " %s - %s\n",
795 ((struct string_elem *)data)->sstr,
796 ((struct string_elem *)data)->lstr);
800 list_capture_types(void) {
802 struct string_elem *captypes;
805 captypes = g_new(struct string_elem,WTAP_NUM_FILE_TYPES);
806 fprintf(stderr, "editcap: The available capture file types for the \"-F\" flag are:\n");
807 for (i = 0; i < WTAP_NUM_FILE_TYPES; i++) {
808 if (wtap_dump_can_open(i)) {
809 captypes[i].sstr = wtap_file_type_short_string(i);
810 captypes[i].lstr = wtap_file_type_string(i);
811 list = g_slist_insert_sorted(list, &captypes[i], string_compare);
814 g_slist_foreach(list, string_elem_print, NULL);
820 list_encap_types(void) {
822 struct string_elem *encaps;
825 encaps = (struct string_elem *)g_malloc(sizeof(struct string_elem) * WTAP_NUM_ENCAP_TYPES);
826 fprintf(stderr, "editcap: The available encapsulation types for the \"-T\" flag are:\n");
827 for (i = 0; i < WTAP_NUM_ENCAP_TYPES; i++) {
828 encaps[i].sstr = wtap_encap_short_string(i);
829 if (encaps[i].sstr != NULL) {
830 encaps[i].lstr = wtap_encap_string(i);
831 list = g_slist_insert_sorted(list, &encaps[i], string_nat_compare);
834 g_slist_foreach(list, string_elem_print, NULL);
841 * Don't report failures to load plugins because most (non-wiretap) plugins
842 * *should* fail to load (because we're not linked against libwireshark and
843 * dissector plugins need libwireshark).
846 failure_message(const char *msg_format _U_, va_list ap _U_)
853 main(int argc, char *argv[])
861 guint32 snaplen = 0; /* No limit */
862 int choplen_begin = 0; /* No chop at beginning */
863 int choplen_end = 0; /* No chop at end */
864 gboolean adjlen = FALSE;
865 wtap_dumper *pdh = NULL;
866 unsigned int count = 1;
867 unsigned int duplicate_count = 0;
869 struct wtap_pkthdr snap_phdr;
870 const struct wtap_pkthdr *phdr;
872 wtapng_section_t *shb_hdr;
873 wtapng_iface_descriptions_t *idb_inf;
875 guint32 read_count = 0;
876 int split_packet_count = 0;
877 int written_count = 0;
878 char *filename = NULL;
879 gboolean ts_okay = TRUE;
880 int secs_per_block = 0;
882 nstime_t block_start;
883 gchar *fprefix = NULL;
884 gchar *fsuffix = NULL;
888 char* init_progfile_dir_error;
892 arg_list_utf_16to8(argc, argv);
893 create_app_running_mutex();
897 * Get credential information for later use.
899 init_process_policies();
902 /* Register wiretap plugins */
903 if ((init_progfile_dir_error = init_progfile_dir(argv[0], main))) {
904 g_warning("editcap: init_progfile_dir(): %s", init_progfile_dir_error);
905 g_free(init_progfile_dir_error);
907 init_report_err(failure_message,NULL,NULL,NULL);
912 /* Process the options */
913 while ((opt = getopt(argc, argv, "A:B:c:C:dD:E:F:hi:Lrs:S:t:T:vw:")) !=-1) {
919 memset(&starttm,0,sizeof(struct tm));
921 if(!strptime(optarg,"%Y-%m-%d %T",&starttm)) {
922 fprintf(stderr, "editcap: \"%s\" isn't a valid time format\n\n", optarg);
926 check_startstop = TRUE;
927 starttm.tm_isdst = -1;
929 starttime = mktime(&starttm);
937 memset(&stoptm,0,sizeof(struct tm));
939 if(!strptime(optarg,"%Y-%m-%d %T",&stoptm)) {
940 fprintf(stderr, "editcap: \"%s\" isn't a valid time format\n\n", optarg);
943 check_startstop = TRUE;
944 stoptm.tm_isdst = -1;
945 stoptime = mktime(&stoptm);
950 split_packet_count = (int)strtol(optarg, &p, 10);
951 if (p == optarg || *p != '\0') {
952 fprintf(stderr, "editcap: \"%s\" isn't a valid packet count\n",
956 if (split_packet_count <= 0) {
957 fprintf(stderr, "editcap: \"%d\" packet count must be larger than zero\n",
967 choplen = (int)strtol(optarg, &p, 10);
968 if (p == optarg || *p != '\0') {
969 fprintf(stderr, "editcap: \"%s\" isn't a valid chop length\n",
974 choplen_begin += choplen;
975 else if (choplen < 0)
976 choplen_end += choplen;
982 dup_detect_by_time = FALSE;
983 dup_window = DEFAULT_DUP_DEPTH;
988 dup_detect_by_time = FALSE;
989 dup_window = (int)strtol(optarg, &p, 10);
990 if (p == optarg || *p != '\0') {
991 fprintf(stderr, "editcap: \"%s\" isn't a valid duplicate window value\n",
995 if (dup_window < 0 || dup_window > MAX_DUP_DEPTH) {
996 fprintf(stderr, "editcap: \"%d\" duplicate window value must be between 0 and %d inclusive.\n",
997 dup_window, MAX_DUP_DEPTH);
1003 err_prob = strtod(optarg, &p);
1004 if (p == optarg || err_prob < 0.0 || err_prob > 1.0) {
1005 fprintf(stderr, "editcap: probability \"%s\" must be between 0.0 and 1.0\n",
1009 srand( (unsigned int) (time(NULL) + getpid()) );
1013 out_file_type = wtap_short_string_to_file_type(optarg);
1014 if (out_file_type < 0) {
1015 fprintf(stderr, "editcap: \"%s\" isn't a valid capture file type\n\n",
1017 list_capture_types();
1027 case 'i': /* break capture file based on time interval */
1028 secs_per_block = atoi(optarg);
1029 if(secs_per_block <= 0) {
1030 fprintf(stderr, "editcap: \"%s\" isn't a valid time interval\n\n", optarg);
1040 keep_em = !keep_em; /* Just invert */
1044 snaplen = (guint32)strtol(optarg, &p, 10);
1045 if (p == optarg || *p != '\0') {
1046 fprintf(stderr, "editcap: \"%s\" isn't a valid snapshot length\n",
1053 set_strict_time_adj(optarg);
1054 do_strict_time_adjustment = TRUE;
1058 set_time_adjustment(optarg);
1062 out_frame_type = wtap_short_string_to_encap(optarg);
1063 if (out_frame_type < 0) {
1064 fprintf(stderr, "editcap: \"%s\" isn't a valid encapsulation type\n\n",
1072 verbose = !verbose; /* Just invert */
1077 dup_detect_by_time = TRUE;
1078 dup_window = MAX_DUP_DEPTH;
1079 set_rel_time(optarg);
1082 case '?': /* Bad options if GNU getopt */
1085 list_capture_types();
1099 printf("Optind = %i, argc = %i\n", optind, argc);
1102 if ((argc - optind) < 1) {
1109 if (check_startstop && !stoptime) {
1111 /* XXX: will work until 2035 */
1112 memset(&stoptm,0,sizeof(struct tm));
1113 stoptm.tm_year = 135;
1114 stoptm.tm_mday = 31;
1117 stoptime = mktime(&stoptm);
1120 nstime_set_unset(&block_start);
1122 if (starttime > stoptime) {
1123 fprintf(stderr, "editcap: start time is after the stop time\n");
1127 if (split_packet_count > 0 && secs_per_block > 0) {
1128 fprintf(stderr, "editcap: can't split on both packet count and time interval\n");
1129 fprintf(stderr, "editcap: at the same time\n");
1133 wth = wtap_open_offline(argv[optind], &err, &err_info, FALSE);
1136 fprintf(stderr, "editcap: Can't open %s: %s\n", argv[optind],
1137 wtap_strerror(err));
1140 case WTAP_ERR_UNSUPPORTED:
1141 case WTAP_ERR_UNSUPPORTED_ENCAP:
1142 case WTAP_ERR_BAD_FILE:
1143 fprintf(stderr, "(%s)\n", err_info);
1152 fprintf(stderr, "File %s is a %s capture file.\n", argv[optind],
1153 wtap_file_type_string(wtap_file_type(wth)));
1156 shb_hdr = wtap_file_get_shb_info(wth);
1157 idb_inf = wtap_file_get_idb_info(wth);
1160 * Now, process the rest, if any ... we only write if there is an extra
1161 * argument or so ...
1164 if ((argc - optind) >= 2) {
1166 if (out_frame_type == -2)
1167 out_frame_type = wtap_file_encap(wth);
1169 for (i = optind + 2; i < argc; i++)
1170 if (add_selection(argv[i]) == FALSE)
1173 if (dup_detect || dup_detect_by_time) {
1174 for (i = 0; i < dup_window; i++) {
1175 memset(&fd_hash[i].digest, 0, 16);
1177 nstime_set_unset(&fd_hash[i].time);
1181 while (wtap_read(wth, &err, &err_info, &data_offset)) {
1184 phdr = wtap_phdr(wth);
1185 buf = wtap_buf_ptr(wth);
1187 if (nstime_is_unset(&block_start)) { /* should only be the first packet */
1188 block_start.secs = phdr->ts.secs;
1189 block_start.nsecs = phdr->ts.nsecs;
1191 if (split_packet_count > 0 || secs_per_block > 0) {
1192 if (!fileset_extract_prefix_suffix(argv[optind+1], &fprefix, &fsuffix))
1195 filename = fileset_get_filename_by_pattern(block_cnt++, &phdr->ts, fprefix, fsuffix);
1197 filename = g_strdup(argv[optind+1]);
1199 /* If we don't have an application name add Editcap */
1200 if(shb_hdr->shb_user_appl == NULL) {
1201 g_snprintf(appname, sizeof(appname), "Editcap " VERSION);
1202 shb_hdr->shb_user_appl = appname;
1205 pdh = wtap_dump_open_ng(filename, out_file_type, out_frame_type,
1206 snaplen ? MIN(snaplen, wtap_snapshot_length(wth)) : wtap_snapshot_length(wth),
1207 FALSE /* compressed */, shb_hdr, idb_inf, &err);
1210 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1211 wtap_strerror(err));
1218 if (secs_per_block > 0) {
1219 while ((phdr->ts.secs - block_start.secs > secs_per_block) ||
1220 (phdr->ts.secs - block_start.secs == secs_per_block &&
1221 phdr->ts.nsecs >= block_start.nsecs )) { /* time for the next file */
1223 if (!wtap_dump_close(pdh, &err)) {
1224 fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
1225 wtap_strerror(err));
1228 block_start.secs = block_start.secs + secs_per_block; /* reset for next interval */
1230 filename = fileset_get_filename_by_pattern(block_cnt++, &phdr->ts, fprefix, fsuffix);
1234 fprintf(stderr, "Continuing writing in file %s\n", filename);
1237 pdh = wtap_dump_open_ng(filename, out_file_type, out_frame_type,
1238 snaplen ? MIN(snaplen, wtap_snapshot_length(wth)) : wtap_snapshot_length(wth),
1239 FALSE /* compressed */, shb_hdr, idb_inf, &err);
1242 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1243 wtap_strerror(err));
1249 if (split_packet_count > 0) {
1251 /* time for the next file? */
1252 if (written_count > 0 &&
1253 written_count % split_packet_count == 0) {
1254 if (!wtap_dump_close(pdh, &err)) {
1255 fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
1256 wtap_strerror(err));
1261 filename = fileset_get_filename_by_pattern(block_cnt++, &phdr->ts, fprefix, fsuffix);
1265 fprintf(stderr, "Continuing writing in file %s\n", filename);
1268 pdh = wtap_dump_open_ng(filename, out_file_type, out_frame_type,
1269 snaplen ? MIN(snaplen, wtap_snapshot_length(wth)) : wtap_snapshot_length(wth),
1270 FALSE /* compressed */, shb_hdr, idb_inf, &err);
1272 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1273 wtap_strerror(err));
1279 if (check_startstop)
1280 ts_okay = check_timestamp(wth);
1282 if ( ts_okay && ((!selected(count) && !keep_em) || (selected(count) && keep_em)) ) {
1284 if (verbose && !dup_detect && !dup_detect_by_time)
1285 printf("Packet: %u\n", count);
1287 /* We simply write it, perhaps after truncating it; we could do other
1288 things, like modify it. */
1290 phdr = wtap_phdr(wth);
1293 if (phdr->caplen > snaplen) {
1295 snap_phdr.caplen = snaplen;
1298 if (adjlen && phdr->len > snaplen) {
1300 snap_phdr.len = snaplen;
1305 if (choplen_end < 0) {
1307 if (((signed int) phdr->caplen + choplen_end) > 0)
1308 snap_phdr.caplen += choplen_end;
1310 snap_phdr.caplen = 0;
1312 if (((signed int) phdr->len + choplen_end) > 0)
1313 snap_phdr.len += choplen_end;
1320 if (choplen_begin > 0) {
1322 if (phdr->caplen > (unsigned int) choplen_begin) {
1323 snap_phdr.caplen -= choplen_begin;
1324 buf += choplen_begin;
1326 snap_phdr.caplen = 0;
1328 if (phdr->len > (unsigned int) choplen_begin) {
1329 snap_phdr.len -= choplen_begin;
1337 * Do we adjust timestamps to insure strict chronologically order?
1340 if (do_strict_time_adjustment) {
1341 if (previous_time.secs || previous_time.nsecs) {
1342 if (!strict_time_adj.is_negative) {
1346 current.secs = phdr->ts.secs;
1347 current.nsecs = phdr->ts.nsecs;
1349 nstime_delta(&delta, ¤t, &previous_time);
1351 if (delta.secs < 0 || delta.nsecs < 0)
1354 * A negative delta indicates that the current packet
1355 * has an absolute timestamp less than the previous packet
1356 * that it is being compared to. This is NOT a normal
1357 * situation since trace files usually have packets in
1358 * chronological order (oldest to newest).
1360 /* printf("++out of order, need to adjust this packet!\n"); */
1362 snap_phdr.ts.secs = previous_time.secs + strict_time_adj.tv.tv_sec;
1363 snap_phdr.ts.nsecs = previous_time.nsecs;
1364 if (snap_phdr.ts.nsecs + strict_time_adj.tv.tv_usec * 1000 > ONE_MILLION * 1000) {
1366 snap_phdr.ts.secs++;
1367 snap_phdr.ts.nsecs += (strict_time_adj.tv.tv_usec - ONE_MILLION) * 1000;
1369 snap_phdr.ts.nsecs += strict_time_adj.tv.tv_usec * 1000;
1375 * A negative strict time adjustment is requested.
1376 * Unconditionally set each timestamp to previous
1377 * packet's timestamp plus delta.
1380 snap_phdr.ts.secs = previous_time.secs + strict_time_adj.tv.tv_sec;
1381 snap_phdr.ts.nsecs = previous_time.nsecs;
1382 if (snap_phdr.ts.nsecs + strict_time_adj.tv.tv_usec * 1000 > ONE_MILLION * 1000) {
1384 snap_phdr.ts.secs++;
1385 snap_phdr.ts.nsecs += (strict_time_adj.tv.tv_usec - ONE_MILLION) * 1000;
1387 snap_phdr.ts.nsecs += strict_time_adj.tv.tv_usec * 1000;
1392 previous_time.secs = phdr->ts.secs;
1393 previous_time.nsecs = phdr->ts.nsecs;
1396 /* assume that if the frame's tv_sec is 0, then
1397 * the timestamp isn't supported */
1398 if (phdr->ts.secs > 0 && time_adj.tv.tv_sec != 0) {
1400 if (time_adj.is_negative)
1401 snap_phdr.ts.secs -= time_adj.tv.tv_sec;
1403 snap_phdr.ts.secs += time_adj.tv.tv_sec;
1407 /* assume that if the frame's tv_sec is 0, then
1408 * the timestamp isn't supported */
1409 if (phdr->ts.secs > 0 && time_adj.tv.tv_usec != 0) {
1411 if (time_adj.is_negative) { /* subtract */
1412 if (snap_phdr.ts.nsecs/1000 < time_adj.tv.tv_usec) { /* borrow */
1413 snap_phdr.ts.secs--;
1414 snap_phdr.ts.nsecs += ONE_MILLION * 1000;
1416 snap_phdr.ts.nsecs -= time_adj.tv.tv_usec * 1000;
1418 if (snap_phdr.ts.nsecs + time_adj.tv.tv_usec * 1000 > ONE_MILLION * 1000) {
1420 snap_phdr.ts.secs++;
1421 snap_phdr.ts.nsecs += (time_adj.tv.tv_usec - ONE_MILLION) * 1000;
1423 snap_phdr.ts.nsecs += time_adj.tv.tv_usec * 1000;
1429 /* suppress duplicates by packet window */
1431 if (is_duplicate(buf, phdr->caplen)) {
1433 fprintf(stdout, "Skipped: %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");
1444 fprintf(stdout, "Packet: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1445 for (i = 0; i < 16; i++) {
1446 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1448 fprintf(stdout, "\n");
1453 /* suppress duplicates by time window */
1454 if (dup_detect_by_time) {
1457 current.secs = phdr->ts.secs;
1458 current.nsecs = phdr->ts.nsecs;
1460 if (is_duplicate_rel_time(buf, phdr->caplen, ¤t)) {
1462 fprintf(stdout, "Skipped: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1463 for (i = 0; i < 16; i++) {
1464 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1466 fprintf(stdout, "\n");
1473 fprintf(stdout, "Packet: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1474 for (i = 0; i < 16; i++) {
1475 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1477 fprintf(stdout, "\n");
1482 /* Random error mutation */
1483 if (err_prob > 0.0) {
1484 int real_data_start = 0;
1485 /* Protect non-protocol data */
1486 if (wtap_file_type(wth) == WTAP_FILE_CATAPULT_DCT2000) {
1487 real_data_start = find_dct2000_real_data(buf);
1489 for (i = real_data_start; i < (int) phdr->caplen; i++) {
1490 if (rand() <= err_prob * RAND_MAX) {
1491 err_type = rand() / (RAND_MAX / ERR_WT_TOTAL + 1);
1493 if (err_type < ERR_WT_BIT) {
1494 buf[i] ^= 1 << (rand() / (RAND_MAX / 8 + 1));
1495 err_type = ERR_WT_TOTAL;
1497 err_type -= ERR_WT_BYTE;
1500 if (err_type < ERR_WT_BYTE) {
1501 buf[i] = rand() / (RAND_MAX / 255 + 1);
1502 err_type = ERR_WT_TOTAL;
1504 err_type -= ERR_WT_BYTE;
1507 if (err_type < ERR_WT_ALNUM) {
1508 buf[i] = ALNUM_CHARS[rand() / (RAND_MAX / ALNUM_LEN + 1)];
1509 err_type = ERR_WT_TOTAL;
1511 err_type -= ERR_WT_ALNUM;
1514 if (err_type < ERR_WT_FMT) {
1515 if ((unsigned int)i < phdr->caplen - 2)
1516 g_strlcpy((char*) &buf[i], "%s", 2);
1517 err_type = ERR_WT_TOTAL;
1519 err_type -= ERR_WT_FMT;
1522 if (err_type < ERR_WT_AA) {
1523 for (j = i; j < (int) phdr->caplen; j++) {
1532 if (!wtap_dump(pdh, phdr, buf, &err)) {
1535 case WTAP_ERR_UNSUPPORTED_ENCAP:
1537 * This is a problem with the particular frame we're writing;
1538 * note that, and give the frame number.
1540 fprintf(stderr, "editcap: Frame %u of \"%s\" has a network type that can't be saved in a file with that format\n.",
1541 read_count, argv[optind]);
1545 fprintf(stderr, "editcap: Error writing to %s: %s\n",
1546 filename, wtap_strerror(err));
1561 /* Print a message noting that the read failed somewhere along the line. */
1563 "editcap: An error occurred while reading \"%s\": %s.\n",
1564 argv[optind], wtap_strerror(err));
1567 case WTAP_ERR_UNSUPPORTED:
1568 case WTAP_ERR_UNSUPPORTED_ENCAP:
1569 case WTAP_ERR_BAD_FILE:
1570 fprintf(stderr, "(%s)\n", err_info);
1577 /* No valid packages found, open the outfile so we can write an empty header */
1579 filename = g_strdup(argv[optind+1]);
1581 pdh = wtap_dump_open_ng(filename, out_file_type, out_frame_type,
1582 snaplen ? MIN(snaplen, wtap_snapshot_length(wth)): wtap_snapshot_length(wth),
1583 FALSE /* compressed */, shb_hdr, idb_inf, &err);
1585 fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1586 wtap_strerror(err));
1594 if (!wtap_dump_close(pdh, &err)) {
1596 fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
1597 wtap_strerror(err));
1606 fprintf(stdout, "%u packet%s seen, %u packet%s skipped with duplicate window of %u packets.\n",
1607 count - 1, plurality(count - 1, "", "s"),
1608 duplicate_count, plurality(duplicate_count, "", "s"), dup_window);
1609 } else if (dup_detect_by_time) {
1610 fprintf(stdout, "%u packet%s seen, %u packet%s skipped with duplicate time window equal to or less than %ld.%09ld seconds.\n",
1611 count - 1, plurality(count - 1, "", "s"),
1612 duplicate_count, plurality(duplicate_count, "", "s"),
1613 (long)relative_time_window.secs, (long int)relative_time_window.nsecs);
1619 /* Skip meta-information read from file to return offset of real
1621 static int find_dct2000_real_data(guint8 *buf)
1625 for (n=0; buf[n] != '\0'; n++); /* Context name */
1627 n++; /* Context port number */
1628 for (; buf[n] != '\0'; n++); /* Timestamp */
1630 for (; buf[n] != '\0'; n++); /* Protocol name */
1632 for (; buf[n] != '\0'; n++); /* Variant number (as string) */
1634 for (; buf[n] != '\0'; n++); /* Outhdr (as string) */
1636 n += 2; /* Direction & encap */