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