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