From Kari Vatjus-Anttila via https://bugs.wireshark.org/bugzilla/show_bug.cgi?id...
[obnox/wireshark/wip.git] / editcap.c
1 /* Edit capture files.  We can delete packets, adjust timestamps, or
2  * simply convert from one format to another format.
3  *
4  * $Id$
5  *
6  * Originally written by Richard Sharpe.
7  * Improved by Guy Harris.
8  * Further improved by Richard Sharpe.
9  */
10
11 #ifdef HAVE_CONFIG_H
12 #include "config.h"
13 #endif
14
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <stdarg.h>
19
20 /*
21  * Just make sure we include the prototype for strptime as well
22  * (needed for glibc 2.2) but make sure we do this only if not
23  * yet defined.
24  */
25
26 #ifndef __USE_XOPEN
27 #  define __USE_XOPEN
28 #endif
29
30 #include <time.h>
31 #include <glib.h>
32
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36
37
38
39 #ifdef HAVE_SYS_TIME_H
40 #include <sys/time.h>
41 #endif
42
43 #include "wtap.h"
44
45 #ifndef HAVE_GETOPT
46 #include "wsutil/wsgetopt.h"
47 #endif
48
49 #ifdef _WIN32
50 #include <wsutil/unicode-utils.h>
51 #include <process.h>    /* getpid */
52 #ifdef HAVE_WINSOCK2_H
53 #include <winsock2.h>
54 #endif
55 #endif
56
57 #ifdef NEED_STRPTIME_H
58 # include "wsutil/strptime.h"
59 #endif
60
61 #include "epan/crypt/crypt-md5.h"
62 #include "epan/plugins.h"
63 #include "epan/report_err.h"
64 #include "epan/filesystem.h"
65 #include <wsutil/privileges.h>
66 #include "epan/nstime.h"
67
68 #include "svnversion.h"
69
70 /*
71  * Some globals so we can pass things to various routines
72  */
73
74 struct select_item {
75
76   int inclusive;
77   int first, second;
78
79 };
80
81
82 /*
83  * Duplicate frame detection
84  */
85 typedef struct _fd_hash_t {
86   md5_byte_t digest[16];
87   guint32 len;
88   nstime_t time;
89 } fd_hash_t;
90
91 #define DEFAULT_DUP_DEPTH 5     /* Used with -d */
92 #define MAX_DUP_DEPTH 1000000   /* the maximum window (and actual size of fd_hash[]) for de-duplication */
93
94 fd_hash_t fd_hash[MAX_DUP_DEPTH];
95 int dup_window = DEFAULT_DUP_DEPTH;
96 int cur_dup_entry = 0;
97
98 #define ONE_MILLION 1000000
99 #define ONE_BILLION 1000000000
100
101 /* Weights of different errors we can introduce */
102 /* We should probably make these command-line arguments */
103 /* XXX - Should we add a bit-level error? */
104 #define ERR_WT_BIT   5  /* Flip a random bit */
105 #define ERR_WT_BYTE  5  /* Substitute a random byte */
106 #define ERR_WT_ALNUM 5  /* Substitute a random character in [A-Za-z0-9] */
107 #define ERR_WT_FMT   2  /* Substitute "%s" */
108 #define ERR_WT_AA    1  /* Fill the remainder of the buffer with 0xAA */
109 #define ERR_WT_TOTAL (ERR_WT_BIT + ERR_WT_BYTE + ERR_WT_ALNUM + ERR_WT_FMT + ERR_WT_AA)
110
111 #define ALNUM_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
112 #define ALNUM_LEN (sizeof(ALNUM_CHARS) - 1)
113
114
115 struct time_adjustment {
116   struct timeval tv;
117   int is_negative;
118 };
119
120 #define MAX_SELECTIONS 512
121 static struct select_item selectfrm[MAX_SELECTIONS];
122 static int max_selected = -1;
123 static int keep_em = 0;
124 #ifdef PCAP_NG_DEFAULT
125 static int out_file_type = WTAP_FILE_PCAPNG; /* default to pcapng   */
126 #else
127 static int out_file_type = WTAP_FILE_PCAP;   /* default to pcap     */
128 #endif
129 static int out_frame_type = -2;              /* Leave frame type alone */
130 static int verbose = 0;                      /* Not so verbose         */
131 static struct time_adjustment time_adj = {{0, 0}, 0}; /* no adjustment */
132 static nstime_t relative_time_window = {0, 0}; /* de-dup time window */
133 static double err_prob = 0.0;
134 static time_t starttime = 0;
135 static time_t stoptime = 0;
136 static gboolean check_startstop = FALSE;
137 static gboolean dup_detect = FALSE;
138 static gboolean dup_detect_by_time = FALSE;
139
140 static int do_strict_time_adjustment = FALSE;
141 static struct time_adjustment strict_time_adj = {{0, 0}, 0}; /* strict time adjustment */
142 static nstime_t previous_time = {0, 0}; /* previous time */
143
144 static int find_dct2000_real_data(guint8 *buf);
145
146 static gchar *
147 abs_time_to_str_with_sec_resolution(const struct wtap_nstime *abs_time)
148 {
149     struct tm *tmp;
150     gchar *buf = g_malloc(16);
151
152 #ifdef _MSC_VER
153     /* calling localtime() on MSVC 2005 with huge values causes it to crash */
154     /* XXX - find the exact value that still does work */
155     /* XXX - using _USE_32BIT_TIME_T might be another way to circumvent this problem */
156     if(abs_time->secs > 2000000000) {
157         tmp = NULL;
158     } else
159 #endif
160     tmp = localtime(&abs_time->secs);
161     if (tmp) {
162         g_snprintf(buf, 16, "%d%02d%02d%02d%02d%02d",
163             tmp->tm_year + 1900,
164             tmp->tm_mon+1,
165             tmp->tm_mday,
166             tmp->tm_hour,
167             tmp->tm_min,
168             tmp->tm_sec);
169     } else
170         buf[0] = '\0';
171
172     return buf;
173 }
174
175 static gchar*
176 fileset_get_filename_by_pattern(guint idx,    const struct wtap_nstime *time_val,
177                                 gchar *fprefix, gchar *fsuffix)
178 {
179     gchar filenum[5+1];
180     gchar *timestr;
181     gchar *abs_str;
182
183     timestr = abs_time_to_str_with_sec_resolution(time_val);
184     g_snprintf(filenum, sizeof(filenum), "%05u", idx);
185     abs_str = g_strconcat(fprefix, "_", filenum, "_", timestr, fsuffix, NULL);
186     g_free(timestr);
187
188     return abs_str;
189 }
190
191 static gboolean
192 fileset_extract_prefix_suffix(const char *fname, gchar **fprefix, gchar **fsuffix)
193 {
194     char  *pfx, *last_pathsep;
195     gchar *save_file;
196
197     save_file = g_strdup(fname);
198     if (save_file == NULL) {
199       fprintf(stderr, "editcap: Out of memory\n");
200       return FALSE;
201     }
202
203     last_pathsep = strrchr(save_file, G_DIR_SEPARATOR);
204     pfx = strrchr(save_file,'.');
205     if (pfx != NULL && (last_pathsep == NULL || pfx > last_pathsep)) {
206       /* The pathname has a "." in it, and it's in the last component
207          of the pathname (because there is either only one component,
208          i.e. last_pathsep is null as there are no path separators,
209          or the "." is after the path separator before the last
210          component.
211
212          Treat it as a separator between the rest of the file name and
213          the file name suffix, and arrange that the names given to the
214          ring buffer files have the specified suffix, i.e. put the
215          changing part of the name *before* the suffix. */
216       pfx[0] = '\0';
217       *fprefix = g_strdup(save_file);
218       pfx[0] = '.'; /* restore capfile_name */
219       *fsuffix = g_strdup(pfx);
220     } else {
221       /* Either there's no "." in the pathname, or it's in a directory
222          component, so the last component has no suffix. */
223       *fprefix = g_strdup(save_file);
224       *fsuffix = NULL;
225     }
226     g_free(save_file);
227     return TRUE;
228 }
229
230 /* Add a selection item, a simple parser for now */
231 static gboolean
232 add_selection(char *sel)
233 {
234   char *locn;
235   char *next;
236
237   if (++max_selected >= MAX_SELECTIONS) {
238     /* Let the user know we stopped selecting */
239     printf("Out of room for packet selections!\n");
240     return(FALSE);
241   }
242
243   printf("Add_Selected: %s\n", sel);
244
245   if ((locn = strchr(sel, '-')) == NULL) { /* No dash, so a single number? */
246
247     printf("Not inclusive ...");
248
249     selectfrm[max_selected].inclusive = 0;
250     selectfrm[max_selected].first = atoi(sel);
251
252     printf(" %i\n", selectfrm[max_selected].first);
253
254   }
255   else {
256
257     printf("Inclusive ...");
258
259     next = locn + 1;
260     selectfrm[max_selected].inclusive = 1;
261     selectfrm[max_selected].first = atoi(sel);
262     selectfrm[max_selected].second = atoi(next);
263
264     printf(" %i, %i\n", selectfrm[max_selected].first, selectfrm[max_selected].second);
265
266   }
267
268   return(TRUE);
269 }
270
271 /* Was the packet selected? */
272
273 static int
274 selected(int recno)
275 {
276   int i = 0;
277
278   for (i = 0; i<= max_selected; i++) {
279
280     if (selectfrm[i].inclusive) {
281       if (selectfrm[i].first <= recno && selectfrm[i].second >= recno)
282         return 1;
283     }
284     else {
285       if (recno == selectfrm[i].first)
286         return 1;
287     }
288   }
289
290   return 0;
291
292 }
293
294 /* is the packet in the selected timeframe */
295 static gboolean
296 check_timestamp(wtap *wth)
297 {
298   struct wtap_pkthdr* pkthdr = wtap_phdr(wth);
299
300   return ( pkthdr->ts.secs >= starttime ) && ( pkthdr->ts.secs < stoptime );
301 }
302
303 static void
304 set_time_adjustment(char *optarg_str_p)
305 {
306   char *frac, *end;
307   long val;
308   size_t frac_digits;
309
310   if (!optarg_str_p)
311     return;
312
313   /* skip leading whitespace */
314   while (*optarg_str_p == ' ' || *optarg_str_p == '\t') {
315       optarg_str_p++;
316   }
317
318   /* check for a negative adjustment */
319   if (*optarg_str_p == '-') {
320       time_adj.is_negative = 1;
321       optarg_str_p++;
322   }
323
324   /* collect whole number of seconds, if any */
325   if (*optarg_str_p == '.') {         /* only fractional (i.e., .5 is ok) */
326       val  = 0;
327       frac = optarg_str_p;
328   } else {
329       val = strtol(optarg_str_p, &frac, 10);
330       if (frac == NULL || frac == optarg_str_p || val == LONG_MIN || val == LONG_MAX) {
331           fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
332                   optarg_str_p);
333           exit(1);
334       }
335       if (val < 0) {            /* implies '--' since we caught '-' above  */
336           fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
337                   optarg_str_p);
338           exit(1);
339       }
340   }
341   time_adj.tv.tv_sec = val;
342
343   /* now collect the partial seconds, if any */
344   if (*frac != '\0') {             /* chars left, so get fractional part */
345     val = strtol(&(frac[1]), &end, 10);
346     /* if more than 6 fractional digits truncate to 6 */
347     if((end - &(frac[1])) > 6) {
348         frac[7] = 't'; /* 't' for truncate */
349         val = strtol(&(frac[1]), &end, 10);
350     }
351     if (*frac != '.' || end == NULL || end == frac
352         || val < 0 || val > ONE_MILLION || val == LONG_MIN || val == LONG_MAX) {
353       fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
354               optarg_str_p);
355       exit(1);
356     }
357   }
358   else {
359     return;                     /* no fractional digits */
360   }
361
362   /* adjust fractional portion from fractional to numerator
363    * e.g., in "1.5" from 5 to 500000 since .5*10^6 = 500000 */
364   if (frac && end) {            /* both are valid */
365     frac_digits = end - frac - 1;   /* fractional digit count (remember '.') */
366     while(frac_digits < 6) {    /* this is frac of 10^6 */
367       val *= 10;
368       frac_digits++;
369     }
370   }
371   time_adj.tv.tv_usec = val;
372 }
373
374 static void
375 set_strict_time_adj(char *optarg_str_p)
376 {
377   char *frac, *end;
378   long val;
379   size_t frac_digits;
380
381   if (!optarg_str_p)
382     return;
383
384   /* skip leading whitespace */
385   while (*optarg_str_p == ' ' || *optarg_str_p == '\t') {
386       optarg_str_p++;
387   }
388
389   /*
390    * check for a negative adjustment
391    * A negative strict adjustment value is a flag
392    * to adjust all frames by the specifed delta time.
393    */
394   if (*optarg_str_p == '-') {
395       strict_time_adj.is_negative = 1;
396       optarg_str_p++;
397   }
398
399   /* collect whole number of seconds, if any */
400   if (*optarg_str_p == '.') {         /* only fractional (i.e., .5 is ok) */
401       val  = 0;
402       frac = optarg_str_p;
403   } else {
404       val = strtol(optarg_str_p, &frac, 10);
405       if (frac == NULL || frac == optarg_str_p || val == LONG_MIN || val == LONG_MAX) {
406           fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
407                   optarg_str_p);
408           exit(1);
409       }
410       if (val < 0) {            /* implies '--' since we caught '-' above  */
411           fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
412                   optarg_str_p);
413           exit(1);
414       }
415   }
416   strict_time_adj.tv.tv_sec = val;
417
418   /* now collect the partial seconds, if any */
419   if (*frac != '\0') {             /* chars left, so get fractional part */
420     val = strtol(&(frac[1]), &end, 10);
421     /* if more than 6 fractional digits truncate to 6 */
422     if((end - &(frac[1])) > 6) {
423         frac[7] = 't'; /* 't' for truncate */
424         val = strtol(&(frac[1]), &end, 10);
425     }
426     if (*frac != '.' || end == NULL || end == frac
427         || val < 0 || val > ONE_MILLION || val == LONG_MIN || val == LONG_MAX) {
428       fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
429               optarg_str_p);
430       exit(1);
431     }
432   }
433   else {
434     return;                     /* no fractional digits */
435   }
436
437   /* adjust fractional portion from fractional to numerator
438    * e.g., in "1.5" from 5 to 500000 since .5*10^6 = 500000 */
439   if (frac && end) {            /* both are valid */
440     frac_digits = end - frac - 1;   /* fractional digit count (remember '.') */
441     while(frac_digits < 6) {    /* this is frac of 10^6 */
442       val *= 10;
443       frac_digits++;
444     }
445   }
446   strict_time_adj.tv.tv_usec = val;
447 }
448
449 static void
450 set_rel_time(char *optarg_str_p)
451 {
452   char *frac, *end;
453   long val;
454   size_t frac_digits;
455
456   if (!optarg_str_p)
457     return;
458
459   /* skip leading whitespace */
460   while (*optarg_str_p == ' ' || *optarg_str_p == '\t') {
461       optarg_str_p++;
462   }
463
464   /* ignore negative adjustment  */
465   if (*optarg_str_p == '-') {
466       optarg_str_p++;
467   }
468
469   /* collect whole number of seconds, if any */
470   if (*optarg_str_p == '.') {         /* only fractional (i.e., .5 is ok) */
471       val  = 0;
472       frac = optarg_str_p;
473   } else {
474       val = strtol(optarg_str_p, &frac, 10);
475       if (frac == NULL || frac == optarg_str_p || val == LONG_MIN || val == LONG_MAX) {
476           fprintf(stderr, "1: editcap: \"%s\" isn't a valid rel time value\n",
477                   optarg_str_p);
478           exit(1);
479       }
480       if (val < 0) {            /* implies '--' since we caught '-' above  */
481           fprintf(stderr, "2: editcap: \"%s\" isn't a valid rel time value\n",
482                   optarg_str_p);
483           exit(1);
484       }
485   }
486   relative_time_window.secs = val;
487
488   /* now collect the partial seconds, if any */
489   if (*frac != '\0') {             /* chars left, so get fractional part */
490     val = strtol(&(frac[1]), &end, 10);
491     /* if more than 9 fractional digits truncate to 9 */
492     if((end - &(frac[1])) > 9) {
493         frac[10] = 't'; /* 't' for truncate */
494         val = strtol(&(frac[1]), &end, 10);
495     }
496     if (*frac != '.' || end == NULL || end == frac
497         || val < 0 || val > ONE_BILLION || val == LONG_MIN || val == LONG_MAX) {
498       fprintf(stderr, "3: editcap: \"%s\" isn't a valid rel time value\n",
499               optarg_str_p);
500       exit(1);
501     }
502   }
503   else {
504     return;                     /* no fractional digits */
505   }
506
507   /* adjust fractional portion from fractional to numerator
508    * e.g., in "1.5" from 5 to 500000000 since .5*10^9 = 500000000 */
509   if (frac && end) {            /* both are valid */
510     frac_digits = end - frac - 1;   /* fractional digit count (remember '.') */
511     while(frac_digits < 9) {    /* this is frac of 10^9 */
512       val *= 10;
513       frac_digits++;
514     }
515   }
516   relative_time_window.nsecs = val;
517 }
518
519 static gboolean
520 is_duplicate(guint8* fd, guint32 len) {
521   int i;
522   md5_state_t ms;
523
524   cur_dup_entry++;
525   if (cur_dup_entry >= dup_window)
526     cur_dup_entry = 0;
527
528   /* Calculate our digest */
529   md5_init(&ms);
530   md5_append(&ms, fd, len);
531   md5_finish(&ms, fd_hash[cur_dup_entry].digest);
532
533   fd_hash[cur_dup_entry].len = len;
534
535   /* Look for duplicates */
536   for (i = 0; i < dup_window; i++) {
537     if (i == cur_dup_entry)
538       continue;
539
540     if (fd_hash[i].len == fd_hash[cur_dup_entry].len &&
541         memcmp(fd_hash[i].digest, fd_hash[cur_dup_entry].digest, 16) == 0) {
542       return TRUE;
543     }
544   }
545
546   return FALSE;
547 }
548
549 static gboolean
550 is_duplicate_rel_time(guint8* fd, guint32 len, const nstime_t *current) {
551   int i;
552   md5_state_t ms;
553
554   cur_dup_entry++;
555   if (cur_dup_entry >= dup_window)
556     cur_dup_entry = 0;
557
558   /* Calculate our digest */
559   md5_init(&ms);
560   md5_append(&ms, fd, len);
561   md5_finish(&ms, fd_hash[cur_dup_entry].digest);
562
563   fd_hash[cur_dup_entry].len = len;
564   fd_hash[cur_dup_entry].time.secs = current->secs;
565   fd_hash[cur_dup_entry].time.nsecs = current->nsecs;
566
567   /*
568    * Look for relative time related duplicates.
569    * This is hopefully a reasonably efficient mechanism for
570    * finding duplicates by rel time in the fd_hash[] cache.
571    * We check starting from the most recently added hash
572    * entries and work backwards towards older packets.
573    * This approach allows the dup test to be terminated
574    * when the relative time of a cached entry is found to
575    * be beyond the dup time window.
576    *
577    * Of course this assumes that the input trace file is
578    * "well-formed" in the sense that the packet timestamps are
579    * in strict chronologically increasing order (which is NOT
580    * always the case!!).
581    *
582    * The fd_hash[] table was deliberatly created large (1,000,000).
583    * Looking for time related duplicates in large trace files with
584    * non-fractional dup time window values can potentially take
585    * a long time to complete.
586    */
587
588   for (i = cur_dup_entry - 1;; i--) {
589     nstime_t delta;
590     int cmp;
591
592     if (i < 0) {
593       i = dup_window - 1;
594     }
595
596     if (i == cur_dup_entry) {
597       /*
598        * We've decremented back to where we started.
599        * Check no more!
600        */
601       break;
602     }
603
604     if (nstime_is_unset(&(fd_hash[i].time))) {
605       /*
606        * We've decremented to an unused fd_hash[] entry.
607        * Check no more!
608        */
609       break;
610     }
611
612     nstime_delta(&delta, current, &fd_hash[i].time);
613
614     if(delta.secs < 0 || delta.nsecs < 0)
615     {
616       /*
617        * A negative delta implies that the current packet
618        * has an absolute timestamp less than the cached packet
619        * that it is being compared to.  This is NOT a normal
620        * situation since trace files usually have packets in
621        * chronological order (oldest to newest).
622        *
623        * There are several possible ways to deal with this:
624        * 1. 'continue' dup checking with the next cached frame.
625        * 2. 'break' from looking for a duplicate of the current frame.
626        * 3. Take the absolute value of the delta and see if that
627        * falls within the specifed dup time window.
628        *
629        * Currently this code does option 1.  But it would pretty
630        * easy to add yet-another-editcap-option to select one of
631        * the other behaviors for dealing with out-of-sequence
632        * packets.
633        */
634       continue;
635     }
636
637     cmp = nstime_cmp(&delta, &relative_time_window);
638
639     if(cmp > 0) {
640       /*
641        * The delta time indicates that we are now looking at
642        * cached packets beyond the specified dup time window.
643        * Check no more!
644        */
645       break;
646     } else if (fd_hash[i].len == fd_hash[cur_dup_entry].len &&
647           memcmp(fd_hash[i].digest, fd_hash[cur_dup_entry].digest, 16) == 0) {
648       return TRUE;
649     }
650   }
651
652   return FALSE;
653 }
654
655 static void
656 usage(gboolean is_error)
657 {
658   FILE *output;
659
660   if (!is_error)
661     output = stdout;
662   else
663     output = stderr;
664
665   fprintf(output, "Editcap %s"
666 #ifdef SVNVERSION
667     " (" SVNVERSION " from " SVNPATH ")"
668 #endif
669     "\n", VERSION);
670   fprintf(output, "Edit and/or translate the format of capture files.\n");
671   fprintf(output, "See http://www.wireshark.org for more information.\n");
672   fprintf(output, "\n");
673   fprintf(output, "Usage: editcap [options] ... <infile> <outfile> [ <packet#>[-<packet#>] ... ]\n");
674   fprintf(output, "\n");
675   fprintf(output, "<infile> and <outfile> must both be present.\n");
676   fprintf(output, "A single packet or a range of packets can be selected.\n");
677   fprintf(output, "\n");
678   fprintf(output, "Packet selection:\n");
679   fprintf(output, "  -r                     keep the selected packets; default is to delete them.\n");
680   fprintf(output, "  -A <start time>        only output packets whose timestamp is after (or equal\n");
681   fprintf(output, "                         to) the given time (format as YYYY-MM-DD hh:mm:ss).\n");
682   fprintf(output, "  -B <stop time>         only output packets whose timestamp is before the\n");
683   fprintf(output, "                         given time (format as YYYY-MM-DD hh:mm:ss).\n");
684   fprintf(output, "\n");
685   fprintf(output, "Duplicate packet removal:\n");
686   fprintf(output, "  -d                     remove packet if duplicate (window == %d).\n", DEFAULT_DUP_DEPTH);
687   fprintf(output, "  -D <dup window>        remove packet if duplicate; configurable <dup window>\n");
688   fprintf(output, "                         Valid <dup window> values are 0 to %d.\n", MAX_DUP_DEPTH);
689   fprintf(output, "                         NOTE: A <dup window> of 0 with -v (verbose option) is\n");
690   fprintf(output, "                         useful to print MD5 hashes.\n");
691   fprintf(output, "  -w <dup time window>   remove packet if duplicate packet is found EQUAL TO OR\n");
692   fprintf(output, "                         LESS THAN <dup time window> prior to current packet.\n");
693   fprintf(output, "                         A <dup time window> is specified in relative seconds\n");
694   fprintf(output, "                         (e.g. 0.000001).\n");
695   fprintf(output, "\n");
696   fprintf(output, "           NOTE: The use of the 'Duplicate packet removal' options with\n");
697   fprintf(output, "           other editcap options except -v may not always work as expected.\n");
698   fprintf(output, "           Specifically the -r, -t or -S options will very likely NOT have the\n");
699   fprintf(output, "           desired effect if combined with the -d, -D or -w.\n");
700   fprintf(output, "\n");
701   fprintf(output, "Packet manipulation:\n");
702   fprintf(output, "  -s <snaplen>           truncate each packet to max. <snaplen> bytes of data.\n");
703   fprintf(output, "  -C <choplen>           chop each packet by <choplen> bytes. Positive values\n");
704   fprintf(output, "                         chop at the packet beginning, negative values at the\n");
705   fprintf(output, "                         packet end.\n");
706   fprintf(output, "  -t <time adjustment>   adjust the timestamp of each packet;\n");
707   fprintf(output, "                         <time adjustment> is in relative seconds (e.g. -0.5).\n");
708   fprintf(output, "  -S <strict adjustment> adjust timestamp of packets if necessary to insure\n");
709   fprintf(output, "                         strict chronological increasing order. The <strict\n");
710   fprintf(output, "                         adjustment> is specified in relative seconds with\n");
711   fprintf(output, "                         values of 0 or 0.000001 being the most reasonable.\n");
712   fprintf(output, "                         A negative adjustment value will modify timestamps so\n");
713   fprintf(output, "                         that each packet's delta time is the absolute value\n");
714   fprintf(output, "                         of the adjustment specified. A value of -0 will set\n");
715   fprintf(output, "                         all packets to the timestamp of the first packet.\n");
716   fprintf(output, "  -E <error probability> set the probability (between 0.0 and 1.0 incl.)\n");
717   fprintf(output, "                         that a particular packet byte will be randomly changed.\n");
718   fprintf(output, "\n");
719   fprintf(output, "Output File(s):\n");
720   fprintf(output, "  -c <packets per file>  split the packet output to different files\n");
721   fprintf(output, "                         based on uniform packet counts\n");
722   fprintf(output, "                         with a maximum of <packets per file> each.\n");
723   fprintf(output, "  -i <seconds per file>  split the packet output to different files\n");
724   fprintf(output, "                         based on uniform time intervals\n");
725   fprintf(output, "                         with a maximum of <seconds per file> each.\n");
726   fprintf(output, "  -F <capture type>      set the output file type; default is libpcap.\n");
727   fprintf(output, "                         an empty \"-F\" option will list the file types.\n");
728   fprintf(output, "  -T <encap type>        set the output file encapsulation type;\n");
729   fprintf(output, "                         default is the same as the input file.\n");
730   fprintf(output, "                         an empty \"-T\" option will list the encapsulation types.\n");
731   fprintf(output, "\n");
732   fprintf(output, "Miscellaneous:\n");
733   fprintf(output, "  -h                     display this help and exit.\n");
734   fprintf(output, "  -v                     verbose output.\n");
735   fprintf(output, "                         If -v is used with any of the 'Duplicate Packet\n");
736   fprintf(output, "                         Removal' options (-d, -D or -w) then Packet lengths\n");
737   fprintf(output, "                         and MD5 hashes are printed to standard-out.\n");
738   fprintf(output, "\n");
739 }
740
741 struct string_elem {
742     const char *sstr;   /* The short string */
743     const char *lstr;   /* The long string */
744 };
745
746 static gint
747 string_compare(gconstpointer a, gconstpointer b)
748 {
749     return strcmp(((const struct string_elem *)a)->sstr,
750         ((const struct string_elem *)b)->sstr);
751 }
752
753 static void
754 string_elem_print(gpointer data, gpointer not_used _U_)
755 {
756     fprintf(stderr, "    %s - %s\n",
757         ((struct string_elem *)data)->sstr,
758         ((struct string_elem *)data)->lstr);
759 }
760
761 static void
762 list_capture_types(void) {
763     int i;
764     struct string_elem *captypes;
765     GSList *list = NULL;
766
767     captypes = g_malloc(sizeof(struct string_elem) * WTAP_NUM_FILE_TYPES);
768     fprintf(stderr, "editcap: The available capture file types for the \"-F\" flag are:\n");
769     for (i = 0; i < WTAP_NUM_FILE_TYPES; i++) {
770       if (wtap_dump_can_open(i)) {
771         captypes[i].sstr = wtap_file_type_short_string(i);
772         captypes[i].lstr = wtap_file_type_string(i);
773         list = g_slist_insert_sorted(list, &captypes[i], string_compare);
774       }
775     }
776     g_slist_foreach(list, string_elem_print, NULL);
777     g_slist_free(list);
778     g_free(captypes);
779 }
780
781 static void
782 list_encap_types(void) {
783     int i;
784     struct string_elem *encaps;
785     GSList *list = NULL;
786
787     encaps = g_malloc(sizeof(struct string_elem) * WTAP_NUM_ENCAP_TYPES);
788     fprintf(stderr, "editcap: The available encapsulation types for the \"-T\" flag are:\n");
789     for (i = 0; i < WTAP_NUM_ENCAP_TYPES; i++) {
790         encaps[i].sstr = wtap_encap_short_string(i);
791         if (encaps[i].sstr != NULL) {
792             encaps[i].lstr = wtap_encap_string(i);
793             list = g_slist_insert_sorted(list, &encaps[i], string_compare);
794         }
795     }
796     g_slist_foreach(list, string_elem_print, NULL);
797     g_slist_free(list);
798     g_free(encaps);
799 }
800
801 #ifdef HAVE_PLUGINS
802 /*
803  *  Don't report failures to load plugins because most (non-wiretap) plugins
804  *  *should* fail to load (because we're not linked against libwireshark and
805  *  dissector plugins need libwireshark).
806  */
807 static void
808 failure_message(const char *msg_format _U_, va_list ap _U_)
809 {
810     return;
811 }
812 #endif
813
814 int
815 main(int argc, char *argv[])
816 {
817   wtap *wth;
818   int i, j, err;
819   gchar *err_info;
820   int opt;
821
822   char *p;
823   guint32 snaplen = 0;                  /* No limit               */
824   int choplen = 0;                      /* No chop                */
825   wtap_dumper *pdh = NULL;
826   int count = 1;
827   unsigned duplicate_count = 0;
828   gint64 data_offset;
829   struct wtap_pkthdr snap_phdr;
830   const struct wtap_pkthdr *phdr;
831   int err_type;
832   guint8 *buf;
833   guint32 read_count = 0;
834   int split_packet_count = 0;
835   int written_count = 0;
836   char *filename = NULL;
837   gboolean ts_okay = TRUE;
838   int secs_per_block = 0;
839   int block_cnt = 0;
840   nstime_t block_start;
841   gchar *fprefix = NULL;
842   gchar *fsuffix = NULL;
843
844 #ifdef HAVE_PLUGINS
845   char* init_progfile_dir_error;
846 #endif
847
848 #ifdef _WIN32
849   arg_list_utf_16to8(argc, argv);
850 #endif /* _WIN32 */
851
852   /*
853    * Get credential information for later use.
854    */
855   init_process_policies();
856
857 #ifdef HAVE_PLUGINS
858   /* Register wiretap plugins */
859   if ((init_progfile_dir_error = init_progfile_dir(argv[0], main))) {
860     g_warning("capinfos: init_progfile_dir(): %s", init_progfile_dir_error);
861     g_free(init_progfile_dir_error);
862   } else {
863     init_report_err(failure_message,NULL,NULL,NULL);
864     init_plugins();
865   }
866 #endif
867
868   /* Process the options */
869   while ((opt = getopt(argc, argv, "A:B:c:C:dD:E:F:hrs:i:t:S:T:vw:")) !=-1) {
870
871     switch (opt) {
872
873     case 'E':
874       err_prob = strtod(optarg, &p);
875       if (p == optarg || err_prob < 0.0 || err_prob > 1.0) {
876         fprintf(stderr, "editcap: probability \"%s\" must be between 0.0 and 1.0\n",
877             optarg);
878         exit(1);
879       }
880       srand( (unsigned int) (time(NULL) + getpid()) );
881       break;
882
883     case 'F':
884       out_file_type = wtap_short_string_to_file_type(optarg);
885       if (out_file_type < 0) {
886         fprintf(stderr, "editcap: \"%s\" isn't a valid capture file type\n\n",
887             optarg);
888         list_capture_types();
889         exit(1);
890       }
891       break;
892
893     case 'c':
894       split_packet_count = strtol(optarg, &p, 10);
895       if (p == optarg || *p != '\0') {
896         fprintf(stderr, "editcap: \"%s\" isn't a valid packet count\n",
897             optarg);
898         exit(1);
899       }
900       if (split_packet_count <= 0) {
901         fprintf(stderr, "editcap: \"%d\" packet count must be larger than zero\n",
902             split_packet_count);
903         exit(1);
904       }
905       break;
906
907     case 'C':
908       choplen = strtol(optarg, &p, 10);
909       if (p == optarg || *p != '\0') {
910         fprintf(stderr, "editcap: \"%s\" isn't a valid chop length\n",
911             optarg);
912         exit(1);
913       }
914       break;
915
916     case 'd':
917       dup_detect = TRUE;
918       dup_detect_by_time = FALSE;
919       dup_window = DEFAULT_DUP_DEPTH;
920       break;
921
922     case 'D':
923       dup_detect = TRUE;
924       dup_detect_by_time = FALSE;
925       dup_window = strtol(optarg, &p, 10);
926       if (p == optarg || *p != '\0') {
927         fprintf(stderr, "editcap: \"%s\" isn't a valid duplicate window value\n",
928             optarg);
929         exit(1);
930       }
931       if (dup_window < 0 || dup_window > MAX_DUP_DEPTH) {
932         fprintf(stderr, "editcap: \"%d\" duplicate window value must be between 0 and %d inclusive.\n",
933             dup_window, MAX_DUP_DEPTH);
934         exit(1);
935       }
936       break;
937
938     case 'w':
939       dup_detect = FALSE;
940       dup_detect_by_time = TRUE;
941       dup_window = MAX_DUP_DEPTH;
942       set_rel_time(optarg);
943       break;
944
945     case '?':              /* Bad options if GNU getopt */
946       switch(optopt) {
947       case'F':
948         list_capture_types();
949         break;
950       case'T':
951         list_encap_types();
952         break;
953       default:
954         usage(TRUE);
955       }
956       exit(1);
957       break;
958
959     case 'h':
960       usage(FALSE);
961       exit(1);
962       break;
963
964     case 'r':
965       keep_em = !keep_em;  /* Just invert */
966       break;
967
968     case 's':
969       snaplen = strtol(optarg, &p, 10);
970       if (p == optarg || *p != '\0') {
971         fprintf(stderr, "editcap: \"%s\" isn't a valid snapshot length\n",
972                 optarg);
973         exit(1);
974       }
975       break;
976
977     case 't':
978       set_time_adjustment(optarg);
979       break;
980
981     case 'S':
982       set_strict_time_adj(optarg);
983       do_strict_time_adjustment = TRUE;
984       break;
985
986     case 'T':
987       out_frame_type = wtap_short_string_to_encap(optarg);
988       if (out_frame_type < 0) {
989         fprintf(stderr, "editcap: \"%s\" isn't a valid encapsulation type\n\n",
990           optarg);
991         list_encap_types();
992         exit(1);
993       }
994       break;
995
996     case 'v':
997       verbose = !verbose;  /* Just invert */
998       break;
999
1000     case 'i': /* break capture file based on time interval */
1001       secs_per_block = atoi(optarg);
1002       if(secs_per_block <= 0) {
1003         fprintf(stderr, "editcap: \"%s\" isn't a valid time interval\n\n", optarg);
1004         exit(1);
1005         }
1006       break;
1007
1008     case 'A':
1009     {
1010       struct tm starttm;
1011
1012       memset(&starttm,0,sizeof(struct tm));
1013
1014       if(!strptime(optarg,"%Y-%m-%d %T",&starttm)) {
1015         fprintf(stderr, "editcap: \"%s\" isn't a valid time format\n\n", optarg);
1016         exit(1);
1017       }
1018
1019       check_startstop = TRUE;
1020       starttm.tm_isdst = -1;
1021
1022       starttime = mktime(&starttm);
1023       break;
1024     }
1025
1026     case 'B':
1027     {
1028       struct tm stoptm;
1029
1030       memset(&stoptm,0,sizeof(struct tm));
1031
1032       if(!strptime(optarg,"%Y-%m-%d %T",&stoptm)) {
1033         fprintf(stderr, "editcap: \"%s\" isn't a valid time format\n\n", optarg);
1034         exit(1);
1035       }
1036       check_startstop = TRUE;
1037       stoptm.tm_isdst = -1;
1038       stoptime = mktime(&stoptm);
1039       break;
1040     }
1041     }
1042
1043   }
1044
1045 #ifdef DEBUG
1046   printf("Optind = %i, argc = %i\n", optind, argc);
1047 #endif
1048
1049   if ((argc - optind) < 1) {
1050
1051     usage(TRUE);
1052     exit(1);
1053
1054   }
1055
1056   if (check_startstop && !stoptime) {
1057     struct tm stoptm;
1058     /* XXX: will work until 2035 */
1059     memset(&stoptm,0,sizeof(struct tm));
1060     stoptm.tm_year = 135;
1061     stoptm.tm_mday = 31;
1062     stoptm.tm_mon = 11;
1063
1064     stoptime = mktime(&stoptm);
1065   }
1066
1067   nstime_set_unset(&block_start);
1068
1069   if (starttime > stoptime) {
1070     fprintf(stderr, "editcap: start time is after the stop time\n");
1071     exit(1);
1072   }
1073
1074   if (split_packet_count > 0 && secs_per_block > 0) {
1075     fprintf(stderr, "editcap: can't split on both packet count and time interval\n");
1076     fprintf(stderr, "editcap: at the same time\n");
1077     exit(1);
1078   }
1079
1080   wth = wtap_open_offline(argv[optind], &err, &err_info, FALSE);
1081
1082   if (!wth) {
1083     fprintf(stderr, "editcap: Can't open %s: %s\n", argv[optind],
1084         wtap_strerror(err));
1085     switch (err) {
1086
1087     case WTAP_ERR_UNSUPPORTED:
1088     case WTAP_ERR_UNSUPPORTED_ENCAP:
1089     case WTAP_ERR_BAD_RECORD:
1090       fprintf(stderr, "(%s)\n", err_info);
1091       g_free(err_info);
1092       break;
1093     }
1094     exit(2);
1095
1096   }
1097
1098   if (verbose) {
1099     fprintf(stderr, "File %s is a %s capture file.\n", argv[optind],
1100             wtap_file_type_string(wtap_file_type(wth)));
1101   }
1102
1103   /*
1104    * Now, process the rest, if any ... we only write if there is an extra
1105    * argument or so ...
1106    */
1107
1108   if ((argc - optind) >= 2) {
1109
1110     if (out_frame_type == -2)
1111       out_frame_type = wtap_file_encap(wth);
1112
1113     for (i = optind + 2; i < argc; i++)
1114       if (add_selection(argv[i]) == FALSE)
1115         break;
1116
1117     if (dup_detect || dup_detect_by_time) {
1118       for (i = 0; i < dup_window; i++) {
1119         memset(&fd_hash[i].digest, 0, 16);
1120         fd_hash[i].len = 0;
1121         nstime_set_unset(&fd_hash[i].time);
1122       }
1123     }
1124
1125     while (wtap_read(wth, &err, &err_info, &data_offset)) {
1126       read_count++;
1127
1128       phdr = wtap_phdr(wth);
1129       buf = wtap_buf_ptr(wth);
1130
1131       if (nstime_is_unset(&block_start)) {  /* should only be the first packet */
1132         block_start.secs = phdr->ts.secs;
1133         block_start.nsecs = phdr->ts.nsecs;
1134
1135         if (split_packet_count > 0 || secs_per_block > 0) {
1136           if (!fileset_extract_prefix_suffix(argv[optind+1], &fprefix, &fsuffix))
1137               exit(2);
1138
1139           filename = fileset_get_filename_by_pattern(block_cnt++, &phdr->ts, fprefix, fsuffix);
1140         } else
1141           filename = g_strdup(argv[optind+1]);
1142
1143         pdh = wtap_dump_open(filename, out_file_type, out_frame_type,
1144           snaplen ? MIN(snaplen, wtap_snapshot_length(wth)) : wtap_snapshot_length(wth),
1145           FALSE /* compressed */, &err);
1146         if (pdh == NULL) {
1147           fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1148                   wtap_strerror(err));
1149           exit(2);
1150         }
1151       }
1152
1153       g_assert(filename);
1154
1155       if (secs_per_block > 0) {
1156         while ((phdr->ts.secs - block_start.secs >  secs_per_block) ||
1157                (phdr->ts.secs - block_start.secs == secs_per_block &&
1158                 phdr->ts.nsecs >= block_start.nsecs )) { /* time for the next file */
1159
1160           if (!wtap_dump_close(pdh, &err)) {
1161             fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
1162                 wtap_strerror(err));
1163             exit(2);
1164           }
1165           block_start.secs = block_start.secs +  secs_per_block; /* reset for next interval */
1166           g_free(filename);
1167           filename = fileset_get_filename_by_pattern(block_cnt++, &phdr->ts, fprefix, fsuffix);
1168           g_assert(filename);
1169
1170           if (verbose) {
1171             fprintf(stderr, "Continuing writing in file %s\n", filename);
1172           }
1173
1174           pdh = wtap_dump_open(filename, out_file_type, out_frame_type,
1175             snaplen ? MIN(snaplen, wtap_snapshot_length(wth)) : wtap_snapshot_length(wth),
1176             FALSE /* compressed */, &err);
1177
1178           if (pdh == NULL) {
1179             fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1180               wtap_strerror(err));
1181             exit(2);
1182           }
1183         }
1184       }
1185
1186       if (split_packet_count > 0) {
1187
1188         /* time for the next file? */
1189         if (written_count > 0 &&
1190             written_count % split_packet_count == 0) {
1191           if (!wtap_dump_close(pdh, &err)) {
1192             fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
1193                 wtap_strerror(err));
1194             exit(2);
1195           }
1196
1197           g_free(filename);
1198           filename = fileset_get_filename_by_pattern(block_cnt++, &phdr->ts, fprefix, fsuffix);
1199           g_assert(filename);
1200
1201           if (verbose) {
1202             fprintf(stderr, "Continuing writing in file %s\n", filename);
1203           }
1204
1205           pdh = wtap_dump_open(filename, out_file_type, out_frame_type,
1206             snaplen ? MIN(snaplen, wtap_snapshot_length(wth)) : wtap_snapshot_length(wth),
1207             FALSE /* compressed */, &err);
1208           if (pdh == NULL) {
1209             fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1210                 wtap_strerror(err));
1211             exit(2);
1212           }
1213         }
1214       }
1215
1216       if (check_startstop)
1217         ts_okay = check_timestamp(wth);
1218
1219       if ( ts_okay && ((!selected(count) && !keep_em) || (selected(count) && keep_em)) ) {
1220
1221         if (verbose && !dup_detect && !dup_detect_by_time)
1222           printf("Packet: %u\n", count);
1223
1224         /* We simply write it, perhaps after truncating it; we could do other
1225            things, like modify it. */
1226
1227         phdr = wtap_phdr(wth);
1228
1229         if (snaplen != 0 && phdr->caplen > snaplen) {
1230           snap_phdr = *phdr;
1231           snap_phdr.caplen = snaplen;
1232           phdr = &snap_phdr;
1233         }
1234
1235         if (choplen < 0) {
1236           snap_phdr = *phdr;
1237           if (((signed int) phdr->caplen + choplen) > 0)
1238             snap_phdr.caplen += choplen;
1239           else
1240             snap_phdr.caplen = 0;
1241           phdr = &snap_phdr;
1242         } else if (choplen > 0) {
1243           snap_phdr = *phdr;
1244           if (phdr->caplen > (unsigned int) choplen) {
1245             snap_phdr.caplen -= choplen;
1246             buf += choplen;
1247           } else
1248             snap_phdr.caplen = 0;
1249           phdr = &snap_phdr;
1250         }
1251
1252         /*
1253          *  Do we adjust timestamps to insure strict chronologically order?
1254          */
1255
1256         if (do_strict_time_adjustment) {
1257           if (previous_time.secs || previous_time.nsecs) {
1258             if (!strict_time_adj.is_negative) {
1259               nstime_t current;
1260               nstime_t delta;
1261
1262               current.secs = phdr->ts.secs;
1263               current.nsecs = phdr->ts.nsecs;
1264
1265               nstime_delta(&delta, &current, &previous_time);
1266
1267               if (delta.secs < 0 || delta.nsecs < 0)
1268               {
1269                 /*
1270                  * A negative delta indicates that the current packet
1271                  * has an absolute timestamp less than the previous packet
1272                  * that it is being compared to.  This is NOT a normal
1273                  * situation since trace files usually have packets in
1274                  * chronological order (oldest to newest).
1275                  */
1276                 /* printf("++out of order, need to adjust this packet!\n"); */
1277                 snap_phdr = *phdr;
1278                 snap_phdr.ts.secs = previous_time.secs + strict_time_adj.tv.tv_sec;
1279                 snap_phdr.ts.nsecs = previous_time.nsecs;
1280                 if (snap_phdr.ts.nsecs + strict_time_adj.tv.tv_usec * 1000 > ONE_MILLION * 1000) {
1281                   /* carry */
1282                   snap_phdr.ts.secs++;
1283                   snap_phdr.ts.nsecs += (strict_time_adj.tv.tv_usec - ONE_MILLION) * 1000;
1284                 } else {
1285                   snap_phdr.ts.nsecs += strict_time_adj.tv.tv_usec * 1000;
1286                 }
1287                 phdr = &snap_phdr;
1288               }
1289             } else {
1290               /*
1291                * A negative strict time adjustment is requested.
1292                * Unconditionally set each timestamp to previous
1293                * packet's timestamp plus delta.
1294                */
1295               snap_phdr = *phdr;
1296               snap_phdr.ts.secs = previous_time.secs + strict_time_adj.tv.tv_sec;
1297               snap_phdr.ts.nsecs = previous_time.nsecs;
1298               if (snap_phdr.ts.nsecs + strict_time_adj.tv.tv_usec * 1000 > ONE_MILLION * 1000) {
1299                 /* carry */
1300                 snap_phdr.ts.secs++;
1301                 snap_phdr.ts.nsecs += (strict_time_adj.tv.tv_usec - ONE_MILLION) * 1000;
1302               } else {
1303                 snap_phdr.ts.nsecs += strict_time_adj.tv.tv_usec * 1000;
1304               }
1305               phdr = &snap_phdr;
1306             }
1307           }
1308           previous_time.secs = phdr->ts.secs;
1309           previous_time.nsecs = phdr->ts.nsecs;
1310         }
1311
1312         /* assume that if the frame's tv_sec is 0, then
1313          * the timestamp isn't supported */
1314         if (phdr->ts.secs > 0 && time_adj.tv.tv_sec != 0) {
1315           snap_phdr = *phdr;
1316           if (time_adj.is_negative)
1317             snap_phdr.ts.secs -= time_adj.tv.tv_sec;
1318           else
1319             snap_phdr.ts.secs += time_adj.tv.tv_sec;
1320           phdr = &snap_phdr;
1321         }
1322
1323         /* assume that if the frame's tv_sec is 0, then
1324          * the timestamp isn't supported */
1325         if (phdr->ts.secs > 0 && time_adj.tv.tv_usec != 0) {
1326           snap_phdr = *phdr;
1327           if (time_adj.is_negative) { /* subtract */
1328             if (snap_phdr.ts.nsecs/1000 < time_adj.tv.tv_usec) { /* borrow */
1329               snap_phdr.ts.secs--;
1330               snap_phdr.ts.nsecs += ONE_MILLION * 1000;
1331             }
1332             snap_phdr.ts.nsecs -= time_adj.tv.tv_usec * 1000;
1333           } else {                  /* add */
1334             if (snap_phdr.ts.nsecs + time_adj.tv.tv_usec * 1000 > ONE_MILLION * 1000) {
1335               /* carry */
1336               snap_phdr.ts.secs++;
1337               snap_phdr.ts.nsecs += (time_adj.tv.tv_usec - ONE_MILLION) * 1000;
1338             } else {
1339               snap_phdr.ts.nsecs += time_adj.tv.tv_usec * 1000;
1340             }
1341           }
1342           phdr = &snap_phdr;
1343         }
1344
1345         /* suppress duplicates by packet window */
1346         if (dup_detect) {
1347           if (is_duplicate(buf, phdr->caplen)) {
1348             if (verbose) {
1349               fprintf(stdout, "Skipped: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1350               for (i = 0; i < 16; i++) {
1351                 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1352               }
1353               fprintf(stdout, "\n");
1354             }
1355             duplicate_count++;
1356             count++;
1357             continue;
1358           } else {
1359             if (verbose) {
1360               fprintf(stdout, "Packet: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1361               for (i = 0; i < 16; i++) {
1362                 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1363               }
1364               fprintf(stdout, "\n");
1365             }
1366           }
1367         }
1368
1369         /* suppress duplicates by time window */
1370         if (dup_detect_by_time) {
1371           nstime_t current;
1372
1373           current.secs = phdr->ts.secs;
1374           current.nsecs = phdr->ts.nsecs;
1375
1376           if (is_duplicate_rel_time(buf, phdr->caplen, &current)) {
1377             if (verbose) {
1378               fprintf(stdout, "Skipped: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1379               for (i = 0; i < 16; i++) {
1380                 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1381               }
1382               fprintf(stdout, "\n");
1383             }
1384             duplicate_count++;
1385             count++;
1386             continue;
1387           } else {
1388             if (verbose) {
1389               fprintf(stdout, "Packet: %u, Len: %u, MD5 Hash: ", count, phdr->caplen);
1390               for (i = 0; i < 16; i++) {
1391                 fprintf(stdout, "%02x", (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1392               }
1393               fprintf(stdout, "\n");
1394             }
1395           }
1396         }
1397
1398         /* Random error mutation */
1399         if (err_prob > 0.0) {
1400           int real_data_start = 0;
1401           /* Protect non-protocol data */
1402           if (wtap_file_type(wth) == WTAP_FILE_CATAPULT_DCT2000) {
1403             real_data_start = find_dct2000_real_data(buf);
1404           }
1405           for (i = real_data_start; i < (int) phdr->caplen; i++) {
1406             if (rand() <= err_prob * RAND_MAX) {
1407               err_type = rand() / (RAND_MAX / ERR_WT_TOTAL + 1);
1408
1409               if (err_type < ERR_WT_BIT) {
1410                 buf[i] ^= 1 << (rand() / (RAND_MAX / 8 + 1));
1411                 err_type = ERR_WT_TOTAL;
1412               } else {
1413                 err_type -= ERR_WT_BYTE;
1414               }
1415
1416               if (err_type < ERR_WT_BYTE) {
1417                 buf[i] = rand() / (RAND_MAX / 255 + 1);
1418                 err_type = ERR_WT_TOTAL;
1419               } else {
1420                 err_type -= ERR_WT_BYTE;
1421               }
1422
1423               if (err_type < ERR_WT_ALNUM) {
1424                 buf[i] = ALNUM_CHARS[rand() / (RAND_MAX / ALNUM_LEN + 1)];
1425                 err_type = ERR_WT_TOTAL;
1426               } else {
1427                 err_type -= ERR_WT_ALNUM;
1428               }
1429
1430               if (err_type < ERR_WT_FMT) {
1431                 if ((unsigned int)i < phdr->caplen - 2)
1432                   g_strlcpy((char*) &buf[i], "%s", 2);
1433                 err_type = ERR_WT_TOTAL;
1434               } else {
1435                 err_type -= ERR_WT_FMT;
1436               }
1437
1438               if (err_type < ERR_WT_AA) {
1439                 for (j = i; j < (int) phdr->caplen; j++) {
1440                   buf[j] = 0xAA;
1441                 }
1442                 i = phdr->caplen;
1443               }
1444             }
1445           }
1446         }
1447
1448         if (!wtap_dump(pdh, phdr, wtap_pseudoheader(wth), buf, &err)) {
1449           switch (err) {
1450
1451           case WTAP_ERR_UNSUPPORTED_ENCAP:
1452             /*
1453              * This is a problem with the particular frame we're writing;
1454              * note that, and give the frame number.
1455              */
1456             fprintf(stderr, "editcap: Frame %u of \"%s\" has a network type that can't be saved in a file with that format\n.",
1457                     read_count, argv[optind]);
1458             break;
1459
1460           default:
1461             fprintf(stderr, "editcap: Error writing to %s: %s\n",
1462                     filename, wtap_strerror(err));
1463             break;
1464           }
1465           exit(2);
1466         }
1467         written_count++;
1468       }
1469       count++;
1470     }
1471
1472     g_free(fprefix);
1473     g_free(fsuffix);
1474
1475     if (err != 0) {
1476       /* Print a message noting that the read failed somewhere along the line. */
1477       fprintf(stderr,
1478               "editcap: An error occurred while reading \"%s\": %s.\n",
1479               argv[optind], wtap_strerror(err));
1480       switch (err) {
1481
1482       case WTAP_ERR_UNSUPPORTED:
1483       case WTAP_ERR_UNSUPPORTED_ENCAP:
1484       case WTAP_ERR_BAD_RECORD:
1485         fprintf(stderr, "(%s)\n", err_info);
1486         g_free(err_info);
1487         break;
1488       }
1489     }
1490
1491     if (!pdh) {
1492       /* No valid packages found, open the outfile so we can write an empty header */
1493       g_free (filename);
1494       filename = g_strdup(argv[optind+1]);
1495
1496       pdh = wtap_dump_open(filename, out_file_type, out_frame_type,
1497         snaplen ? MIN(snaplen, wtap_snapshot_length(wth)): wtap_snapshot_length(wth),
1498         FALSE /* compressed */, &err);
1499       if (pdh == NULL) {
1500         fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
1501         wtap_strerror(err));
1502         exit(2);
1503       }
1504     }
1505
1506     if (!wtap_dump_close(pdh, &err)) {
1507
1508       fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
1509           wtap_strerror(err));
1510       exit(2);
1511
1512     }
1513     g_free(filename);
1514   }
1515
1516   if (dup_detect) {
1517     fprintf(stdout, "%u packet%s seen, %u packet%s skipped with duplicate window of %u packets.\n",
1518                 count - 1, plurality(count - 1, "", "s"),
1519                 duplicate_count, plurality(duplicate_count, "", "s"), dup_window);
1520   } else if (dup_detect_by_time) {
1521     fprintf(stdout, "%u packet%s seen, %u packet%s skipped with duplicate time window equal to or less than %ld.%09ld seconds.\n",
1522                 count - 1, plurality(count - 1, "", "s"),
1523                 duplicate_count, plurality(duplicate_count, "", "s"),
1524                 (long)relative_time_window.secs, (long int)relative_time_window.nsecs);
1525   }
1526
1527   return 0;
1528 }
1529
1530 /* Skip meta-information read from file to return offset of real
1531    protocol data */
1532 static int find_dct2000_real_data(guint8 *buf)
1533 {
1534   int n=0;
1535
1536   for (n=0; buf[n] != '\0'; n++);   /* Context name */
1537   n++;
1538   n++;                              /* Context port number */
1539   for (; buf[n] != '\0'; n++);      /* Timestamp */
1540   n++;
1541   for (; buf[n] != '\0'; n++);      /* Protocol name */
1542   n++;
1543   for (; buf[n] != '\0'; n++);      /* Variant number (as string) */
1544   n++;
1545   for (; buf[n] != '\0'; n++);      /* Outhdr (as string) */
1546   n++;
1547   n += 2;                           /* Direction & encap */
1548
1549   return n;
1550 }