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