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