Nb is an unsigned value; treat it as such.
[metze/wireshark/wip.git] / editcap.c
1 /* editcap.c
2  * Edit capture files.  We can delete packets, adjust timestamps, or
3  * simply convert from one format to another format.
4  *
5  * Originally written by Richard Sharpe.
6  * Improved by Guy Harris.
7  * Further improved by Richard Sharpe.
8  *
9  * Copyright 2013, Richard Sharpe <realrichardsharpe[AT]gmail.com>
10  *
11  * Wireshark - Network traffic analyzer
12  * By Gerald Combs <gerald@wireshark.org>
13  * Copyright 1998 Gerald Combs
14  *
15  * SPDX-License-Identifier: GPL-2.0-or-later
16  */
17
18 #include <config.h>
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <stdarg.h>
24
25 /*
26  * Just make sure we include the prototype for strptime as well
27  * (needed for glibc 2.2) but make sure we do this only if not
28  * yet defined.
29  */
30
31 #ifndef __USE_XOPEN
32 #  define __USE_XOPEN
33 #endif
34
35 #include <time.h>
36 #include <glib.h>
37
38 #ifdef HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41
42 #ifdef HAVE_GETOPT_H
43 #include <getopt.h>
44 #endif
45
46 #include <wiretap/wtap.h>
47
48 #include "epan/etypes.h"
49
50 #ifndef HAVE_GETOPT_LONG
51 #include "wsutil/wsgetopt.h"
52 #endif
53
54 #ifdef _WIN32
55 #include <wsutil/unicode-utils.h>
56 #include <process.h>    /* getpid */
57 #include <winsock2.h>
58 #endif
59
60 #ifndef HAVE_STRPTIME
61 # include "wsutil/strptime.h"
62 #endif
63
64 #include <wsutil/crash_info.h>
65 #include <wsutil/clopts_common.h>
66 #include <wsutil/cmdarg_err.h>
67 #include <wsutil/filesystem.h>
68 #include <wsutil/file_util.h>
69 #include <wsutil/wsgcrypt.h>
70 #include <wsutil/plugins.h>
71 #include <wsutil/privileges.h>
72 #include <wsutil/report_message.h>
73 #include <wsutil/strnatcmp.h>
74 #include <wsutil/str_util.h>
75 #include <version_info.h>
76 #include <wsutil/pint.h>
77 #include <wsutil/strtoi.h>
78 #include <wiretap/wtap_opttypes.h>
79 #include <wiretap/pcapng.h>
80
81 #include "ui/failure_message.h"
82
83 #include "ringbuffer.h" /* For RINGBUFFER_MAX_NUM_FILES */
84
85 #define INVALID_OPTION 1
86 #define INVALID_FILE 2
87 #define CANT_EXTRACT_PREFIX 2
88 #define WRITE_ERROR 2
89 #define DUMP_ERROR 2
90
91 /*
92  * Some globals so we can pass things to various routines
93  */
94
95 struct select_item {
96     gboolean inclusive;
97     guint first, second;
98 };
99
100 /*
101  * Duplicate frame detection
102  */
103 typedef struct _fd_hash_t {
104     guint8     digest[16];
105     guint32    len;
106     nstime_t   frame_time;
107 } fd_hash_t;
108
109 #define DEFAULT_DUP_DEPTH       5   /* Used with -d */
110 #define MAX_DUP_DEPTH     1000000   /* the maximum window (and actual size of fd_hash[]) for de-duplication */
111
112 static fd_hash_t fd_hash[MAX_DUP_DEPTH];
113 static int       dup_window    = DEFAULT_DUP_DEPTH;
114 static int       cur_dup_entry = 0;
115
116 static guint32   ignored_bytes  = 0;  /* Used with -I */
117
118 #define ONE_BILLION 1000000000
119
120 /* Weights of different errors we can introduce */
121 /* We should probably make these command-line arguments */
122 /* XXX - Should we add a bit-level error? */
123 #define ERR_WT_BIT      5   /* Flip a random bit */
124 #define ERR_WT_BYTE     5   /* Substitute a random byte */
125 #define ERR_WT_ALNUM    5   /* Substitute a random character in [A-Za-z0-9] */
126 #define ERR_WT_FMT      2   /* Substitute "%s" */
127 #define ERR_WT_AA       1   /* Fill the remainder of the buffer with 0xAA */
128 #define ERR_WT_TOTAL    (ERR_WT_BIT + ERR_WT_BYTE + ERR_WT_ALNUM + ERR_WT_FMT + ERR_WT_AA)
129
130 #define ALNUM_CHARS     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
131 #define ALNUM_LEN       (sizeof(ALNUM_CHARS) - 1)
132
133 struct time_adjustment {
134     nstime_t tv;
135     int is_negative;
136 };
137
138 typedef struct _chop_t {
139     int len_begin;
140     int off_begin_pos;
141     int off_begin_neg;
142     int len_end;
143     int off_end_pos;
144     int off_end_neg;
145 } chop_t;
146
147
148 /* Table of user comments */
149 GTree *frames_user_comments = NULL;
150
151 #define MAX_SELECTIONS 512
152 static struct select_item     selectfrm[MAX_SELECTIONS];
153 static guint                  max_selected              = 0;
154 static int                    keep_em                   = 0;
155 #ifdef PCAP_NG_DEFAULT
156 static int                    out_file_type_subtype     = WTAP_FILE_TYPE_SUBTYPE_PCAPNG; /* default to pcapng   */
157 #else
158 static int                    out_file_type_subtype     = WTAP_FILE_TYPE_SUBTYPE_PCAP; /* default to pcap     */
159 #endif
160 static int                    out_frame_type            = -2; /* Leave frame type alone */
161 static int                    verbose                   = 0;  /* Not so verbose         */
162 static struct time_adjustment time_adj                  = {{0, 0}, 0}; /* no adjustment */
163 static nstime_t               relative_time_window      = {0, 0}; /* de-dup time window */
164 static double                 err_prob                  = 0.0;
165 static time_t                 starttime                 = 0;
166 static time_t                 stoptime                  = 0;
167 static gboolean               check_startstop           = FALSE;
168 static gboolean               rem_vlan                  = FALSE;
169 static gboolean               dup_detect                = FALSE;
170 static gboolean               dup_detect_by_time        = FALSE;
171
172 static int                    do_strict_time_adjustment = FALSE;
173 static struct time_adjustment strict_time_adj           = {{0, 0}, 0}; /* strict time adjustment */
174 static nstime_t               previous_time             = {0, 0}; /* previous time */
175
176 static int find_dct2000_real_data(guint8 *buf);
177 static void handle_chopping(chop_t chop, wtap_packet_header *out_phdr,
178                             const wtap_packet_header *in_phdr, guint8 **buf,
179                             gboolean adjlen);
180
181 static gchar *
182 abs_time_to_str_with_sec_resolution(const nstime_t *abs_time)
183 {
184     struct tm *tmp;
185     gchar     *buf = (gchar *)g_malloc(16);
186
187     tmp = localtime(&abs_time->secs);
188
189     if (tmp) {
190         g_snprintf(buf, 16, "%d%02d%02d%02d%02d%02d",
191             tmp->tm_year + 1900,
192             tmp->tm_mon+1,
193             tmp->tm_mday,
194             tmp->tm_hour,
195             tmp->tm_min,
196             tmp->tm_sec);
197     } else {
198         buf[0] = '\0';
199     }
200
201     return buf;
202 }
203
204 static gchar *
205 fileset_get_filename_by_pattern(guint idx, const wtap_rec *rec,
206                                 gchar *fprefix, gchar *fsuffix)
207 {
208     gchar  filenum[5+1];
209     gchar *timestr;
210     gchar *abs_str;
211
212     g_snprintf(filenum, sizeof(filenum), "%05u", idx % RINGBUFFER_MAX_NUM_FILES);
213     if (rec->presence_flags & WTAP_HAS_TS) {
214         timestr = abs_time_to_str_with_sec_resolution(&rec->ts);
215         abs_str = g_strconcat(fprefix, "_", filenum, "_", timestr, fsuffix, NULL);
216         g_free(timestr);
217     } else
218         abs_str = g_strconcat(fprefix, "_", filenum, fsuffix, NULL);
219
220     return abs_str;
221 }
222
223 static gboolean
224 fileset_extract_prefix_suffix(const char *fname, gchar **fprefix, gchar **fsuffix)
225 {
226     char  *pfx, *last_pathsep;
227     gchar *save_file;
228
229     save_file = g_strdup(fname);
230     if (save_file == NULL) {
231         fprintf(stderr, "editcap: Out of memory\n");
232         return FALSE;
233     }
234
235     last_pathsep = strrchr(save_file, G_DIR_SEPARATOR);
236     pfx = strrchr(save_file,'.');
237     if (pfx != NULL && (last_pathsep == NULL || pfx > last_pathsep)) {
238         /* The pathname has a "." in it, and it's in the last component
239          * of the pathname (because there is either only one component,
240          * i.e. last_pathsep is null as there are no path separators,
241          * or the "." is after the path separator before the last
242          * component.
243
244          * Treat it as a separator between the rest of the file name and
245          * the file name suffix, and arrange that the names given to the
246          * ring buffer files have the specified suffix, i.e. put the
247          * changing part of the name *before* the suffix. */
248         pfx[0] = '\0';
249         *fprefix = g_strdup(save_file);
250         pfx[0] = '.'; /* restore capfile_name */
251         *fsuffix = g_strdup(pfx);
252     } else {
253         /* Either there's no "." in the pathname, or it's in a directory
254          * component, so the last component has no suffix. */
255         *fprefix = g_strdup(save_file);
256         *fsuffix = NULL;
257     }
258     g_free(save_file);
259     return TRUE;
260 }
261
262 /* Add a selection item, a simple parser for now */
263 static gboolean
264 add_selection(char *sel, guint* max_selection)
265 {
266     char *locn;
267     char *next;
268
269     if (max_selected >= MAX_SELECTIONS) {
270         /* Let the user know we stopped selecting */
271         fprintf(stderr, "Out of room for packet selections.\n");
272         return(FALSE);
273     }
274
275     if (verbose)
276         fprintf(stderr, "Add_Selected: %s\n", sel);
277
278     if ((locn = strchr(sel, '-')) == NULL) { /* No dash, so a single number? */
279         if (verbose)
280             fprintf(stderr, "Not inclusive ...");
281
282         selectfrm[max_selected].inclusive = FALSE;
283         selectfrm[max_selected].first = get_guint32(sel, "packet number");
284         if (selectfrm[max_selected].first > *max_selection)
285             *max_selection = selectfrm[max_selected].first;
286
287         if (verbose)
288             fprintf(stderr, " %u\n", selectfrm[max_selected].first);
289     } else {
290         if (verbose)
291             fprintf(stderr, "Inclusive ...");
292
293         *locn = '\0';    /* split the range */
294         next = locn + 1;
295         selectfrm[max_selected].inclusive = TRUE;
296         selectfrm[max_selected].first = get_guint32(sel, "beginning of packet range");
297         selectfrm[max_selected].second = get_guint32(next, "end of packet range");
298
299         if (selectfrm[max_selected].second == 0)
300         {
301             /* Not a valid number, presume all */
302             selectfrm[max_selected].second = *max_selection = G_MAXUINT;
303         }
304         else if (selectfrm[max_selected].second > *max_selection)
305             *max_selection = selectfrm[max_selected].second;
306
307         if (verbose)
308             fprintf(stderr, " %u, %u\n", selectfrm[max_selected].first,
309                    selectfrm[max_selected].second);
310     }
311
312     max_selected++;
313     return(TRUE);
314 }
315
316 /* Was the packet selected? */
317
318 static int
319 selected(guint recno)
320 {
321     guint i;
322
323     for (i = 0; i < max_selected; i++) {
324         if (selectfrm[i].inclusive) {
325             if (selectfrm[i].first <= recno && selectfrm[i].second >= recno)
326                 return 1;
327         } else {
328             if (recno == selectfrm[i].first)
329                 return 1;
330         }
331     }
332
333   return 0;
334 }
335
336 static gboolean
337 set_time_adjustment(char *optarg_str_p)
338 {
339     char   *frac, *end;
340     long    val;
341     size_t  frac_digits;
342
343     if (!optarg_str_p)
344         return TRUE;
345
346     /* skip leading whitespace */
347     while (*optarg_str_p == ' ' || *optarg_str_p == '\t')
348         optarg_str_p++;
349
350     /* check for a negative adjustment */
351     if (*optarg_str_p == '-') {
352         time_adj.is_negative = 1;
353         optarg_str_p++;
354     }
355
356     /* collect whole number of seconds, if any */
357     if (*optarg_str_p == '.') {         /* only fractional (i.e., .5 is ok) */
358         val  = 0;
359         frac = optarg_str_p;
360     } else {
361         val = strtol(optarg_str_p, &frac, 10);
362         if (frac == NULL || frac == optarg_str_p
363             || val == LONG_MIN || val == LONG_MAX) {
364             fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
365                     optarg_str_p);
366             return FALSE;
367         }
368         if (val < 0) {            /* implies '--' since we caught '-' above  */
369             fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
370                     optarg_str_p);
371             return FALSE;
372         }
373     }
374     time_adj.tv.secs = val;
375
376     /* now collect the partial seconds, if any */
377     if (*frac != '\0') {             /* chars left, so get fractional part */
378         val = strtol(&(frac[1]), &end, 10);
379         /* if more than 9 fractional digits truncate to 9 */
380         if ((end - &(frac[1])) > 9) {
381             frac[10] = 't'; /* 't' for truncate */
382             val = strtol(&(frac[1]), &end, 10);
383         }
384         if (*frac != '.' || end == NULL || end == frac || val < 0
385             || val > ONE_BILLION || val == LONG_MIN || val == LONG_MAX) {
386             fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
387                     optarg_str_p);
388             return FALSE;
389         }
390     } else {
391         return TRUE;                     /* no fractional digits */
392     }
393
394     /* adjust fractional portion from fractional to numerator
395      * e.g., in "1.5" from 5 to 500000000 since .5*10^9 = 500000000 */
396     frac_digits = end - frac - 1;   /* fractional digit count (remember '.') */
397     while(frac_digits < 9) {    /* this is frac of 10^9 */
398         val *= 10;
399         frac_digits++;
400     }
401
402     time_adj.tv.nsecs = (int)val;
403     return TRUE;
404 }
405
406 static gboolean
407 set_strict_time_adj(char *optarg_str_p)
408 {
409     char   *frac, *end;
410     long    val;
411     size_t  frac_digits;
412
413     if (!optarg_str_p)
414         return TRUE;
415
416     /* skip leading whitespace */
417     while (*optarg_str_p == ' ' || *optarg_str_p == '\t')
418         optarg_str_p++;
419
420     /*
421      * check for a negative adjustment
422      * A negative strict adjustment value is a flag
423      * to adjust all frames by the specifed delta time.
424      */
425     if (*optarg_str_p == '-') {
426         strict_time_adj.is_negative = 1;
427         optarg_str_p++;
428     }
429
430     /* collect whole number of seconds, if any */
431     if (*optarg_str_p == '.') {         /* only fractional (i.e., .5 is ok) */
432         val  = 0;
433         frac = optarg_str_p;
434     } else {
435         val = strtol(optarg_str_p, &frac, 10);
436         if (frac == NULL || frac == optarg_str_p
437             || val == LONG_MIN || val == LONG_MAX) {
438             fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
439                     optarg_str_p);
440             return FALSE;
441         }
442         if (val < 0) {            /* implies '--' since we caught '-' above  */
443             fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
444                     optarg_str_p);
445             return FALSE;
446         }
447     }
448     strict_time_adj.tv.secs = val;
449
450     /* now collect the partial seconds, if any */
451     if (*frac != '\0') {             /* chars left, so get fractional part */
452         val = strtol(&(frac[1]), &end, 10);
453         /* if more than 9 fractional digits truncate to 9 */
454         if ((end - &(frac[1])) > 9) {
455             frac[10] = 't'; /* 't' for truncate */
456             val = strtol(&(frac[1]), &end, 10);
457         }
458         if (*frac != '.' || end == NULL || end == frac || val < 0
459             || val > ONE_BILLION || val == LONG_MIN || val == LONG_MAX) {
460             fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
461                     optarg_str_p);
462             return FALSE;
463         }
464     } else {
465         return TRUE;                     /* no fractional digits */
466     }
467
468     /* adjust fractional portion from fractional to numerator
469      * e.g., in "1.5" from 5 to 500000000 since .5*10^9 = 500000000 */
470     frac_digits = end - frac - 1;   /* fractional digit count (remember '.') */
471     while(frac_digits < 9) {    /* this is frac of 10^9 */
472         val *= 10;
473         frac_digits++;
474     }
475
476     strict_time_adj.tv.nsecs = (int)val;
477     return TRUE;
478 }
479
480 static gboolean
481 set_rel_time(char *optarg_str_p)
482 {
483     char   *frac, *end;
484     long    val;
485     size_t  frac_digits;
486
487     if (!optarg_str_p)
488         return TRUE;
489
490     /* skip leading whitespace */
491     while (*optarg_str_p == ' ' || *optarg_str_p == '\t')
492         optarg_str_p++;
493
494     /* ignore negative adjustment  */
495     if (*optarg_str_p == '-')
496         optarg_str_p++;
497
498     /* collect whole number of seconds, if any */
499     if (*optarg_str_p == '.') {         /* only fractional (i.e., .5 is ok) */
500         val  = 0;
501         frac = optarg_str_p;
502     } else {
503         val = strtol(optarg_str_p, &frac, 10);
504         if (frac == NULL || frac == optarg_str_p
505             || val == LONG_MIN || val == LONG_MAX) {
506             fprintf(stderr, "1: editcap: \"%s\" isn't a valid rel time value\n",
507                     optarg_str_p);
508             return FALSE;
509         }
510         if (val < 0) {            /* implies '--' since we caught '-' above  */
511             fprintf(stderr, "2: editcap: \"%s\" isn't a valid rel time value\n",
512                     optarg_str_p);
513             return FALSE;
514         }
515     }
516     relative_time_window.secs = val;
517
518     /* now collect the partial seconds, if any */
519     if (*frac != '\0') {             /* chars left, so get fractional part */
520         val = strtol(&(frac[1]), &end, 10);
521         /* if more than 9 fractional digits truncate to 9 */
522         if ((end - &(frac[1])) > 9) {
523             frac[10] = 't'; /* 't' for truncate */
524             val = strtol(&(frac[1]), &end, 10);
525         }
526         if (*frac != '.' || end == NULL || end == frac || val < 0
527             || val > ONE_BILLION || val == LONG_MIN || val == LONG_MAX) {
528             fprintf(stderr, "3: editcap: \"%s\" isn't a valid rel time value\n",
529                     optarg_str_p);
530             return FALSE;
531         }
532     } else {
533         return TRUE;                     /* no fractional digits */
534     }
535
536     /* adjust fractional portion from fractional to numerator
537      * e.g., in "1.5" from 5 to 500000000 since .5*10^9 = 500000000 */
538     frac_digits = end - frac - 1;   /* fractional digit count (remember '.') */
539     while(frac_digits < 9) {    /* this is frac of 10^9 */
540         val *= 10;
541         frac_digits++;
542     }
543
544     relative_time_window.nsecs = (int)val;
545     return TRUE;
546 }
547
548 #define LINUX_SLL_OFFSETP 14
549 #define VLAN_SIZE 4
550 static void
551 sll_remove_vlan_info(guint8* fd, guint32* len) {
552     if (pntoh16(fd + LINUX_SLL_OFFSETP) == ETHERTYPE_VLAN) {
553         int rest_len;
554         /* point to start of vlan */
555         fd = fd + LINUX_SLL_OFFSETP;
556         /* bytes to read after vlan info */
557         rest_len = *len - (LINUX_SLL_OFFSETP + VLAN_SIZE);
558         /* remove vlan info from packet */
559         memmove(fd, fd + VLAN_SIZE, rest_len);
560         *len -= 4;
561     }
562 }
563
564 static void
565 remove_vlan_info(const wtap_packet_header *phdr, guint8* fd, guint32* len) {
566     switch (phdr->pkt_encap) {
567         case WTAP_ENCAP_SLL:
568             sll_remove_vlan_info(fd, len);
569             break;
570         default:
571             /* no support for current pkt_encap */
572             break;
573     }
574 }
575
576 static gboolean
577 is_duplicate(guint8* fd, guint32 len) {
578     int i;
579
580     /*Hint to ignore some bytes at the start of the frame for the digest calculation(-I option) */
581     guint32 offset = ignored_bytes;
582     guint32 new_len;
583     guint8 *new_fd;
584
585     if (len <= ignored_bytes) {
586         offset = 0;
587     }
588
589     new_fd  = &fd[offset];
590     new_len = len - (offset);
591
592     cur_dup_entry++;
593     if (cur_dup_entry >= dup_window)
594         cur_dup_entry = 0;
595
596     /* Calculate our digest */
597     gcry_md_hash_buffer(GCRY_MD_MD5, fd_hash[cur_dup_entry].digest, new_fd, new_len);
598
599     fd_hash[cur_dup_entry].len = len;
600
601     /* Look for duplicates */
602     for (i = 0; i < dup_window; i++) {
603         if (i == cur_dup_entry)
604             continue;
605
606         if (fd_hash[i].len == fd_hash[cur_dup_entry].len
607             && memcmp(fd_hash[i].digest, fd_hash[cur_dup_entry].digest, 16) == 0) {
608             return TRUE;
609         }
610     }
611
612     return FALSE;
613 }
614
615 static gboolean
616 is_duplicate_rel_time(guint8* fd, guint32 len, const nstime_t *current) {
617     int i;
618
619     /*Hint to ignore some bytes at the start of the frame for the digest calculation(-I option) */
620     guint32 offset = ignored_bytes;
621     guint32 new_len;
622     guint8 *new_fd;
623
624     if (len <= ignored_bytes) {
625         offset = 0;
626     }
627
628     new_fd  = &fd[offset];
629     new_len = len - (offset);
630
631     cur_dup_entry++;
632     if (cur_dup_entry >= dup_window)
633         cur_dup_entry = 0;
634
635     /* Calculate our digest */
636     gcry_md_hash_buffer(GCRY_MD_MD5, fd_hash[cur_dup_entry].digest, new_fd, new_len);
637
638     fd_hash[cur_dup_entry].len = len;
639     fd_hash[cur_dup_entry].frame_time.secs = current->secs;
640     fd_hash[cur_dup_entry].frame_time.nsecs = current->nsecs;
641
642     /*
643      * Look for relative time related duplicates.
644      * This is hopefully a reasonably efficient mechanism for
645      * finding duplicates by rel time in the fd_hash[] cache.
646      * We check starting from the most recently added hash
647      * entries and work backwards towards older packets.
648      * This approach allows the dup test to be terminated
649      * when the relative time of a cached entry is found to
650      * be beyond the dup time window.
651      *
652      * Of course this assumes that the input trace file is
653      * "well-formed" in the sense that the packet timestamps are
654      * in strict chronologically increasing order (which is NOT
655      * always the case!!).
656      *
657      * The fd_hash[] table was deliberately created large (1,000,000).
658      * Looking for time related duplicates in large trace files with
659      * non-fractional dup time window values can potentially take
660      * a long time to complete.
661      */
662
663     for (i = cur_dup_entry - 1;; i--) {
664         nstime_t delta;
665         int cmp;
666
667         if (i < 0)
668             i = dup_window - 1;
669
670         if (i == cur_dup_entry) {
671             /*
672              * We've decremented back to where we started.
673              * Check no more!
674              */
675             break;
676         }
677
678         if (nstime_is_unset(&(fd_hash[i].frame_time))) {
679             /*
680              * We've decremented to an unused fd_hash[] entry.
681              * Check no more!
682              */
683             break;
684         }
685
686         nstime_delta(&delta, current, &fd_hash[i].frame_time);
687
688         if (delta.secs < 0 || delta.nsecs < 0) {
689             /*
690              * A negative delta implies that the current packet
691              * has an absolute timestamp less than the cached packet
692              * that it is being compared to.  This is NOT a normal
693              * situation since trace files usually have packets in
694              * chronological order (oldest to newest).
695              *
696              * There are several possible ways to deal with this:
697              * 1. 'continue' dup checking with the next cached frame.
698              * 2. 'break' from looking for a duplicate of the current frame.
699              * 3. Take the absolute value of the delta and see if that
700              * falls within the specifed dup time window.
701              *
702              * Currently this code does option 1.  But it would pretty
703              * easy to add yet-another-editcap-option to select one of
704              * the other behaviors for dealing with out-of-sequence
705              * packets.
706              */
707             continue;
708         }
709
710         cmp = nstime_cmp(&delta, &relative_time_window);
711
712         if (cmp > 0) {
713             /*
714              * The delta time indicates that we are now looking at
715              * cached packets beyond the specified dup time window.
716              * Check no more!
717              */
718             break;
719         } else if (fd_hash[i].len == fd_hash[cur_dup_entry].len
720                    && memcmp(fd_hash[i].digest, fd_hash[cur_dup_entry].digest, 16) == 0) {
721             return TRUE;
722         }
723     }
724
725     return FALSE;
726 }
727
728 static void
729 print_usage(FILE *output)
730 {
731     fprintf(output, "\n");
732     fprintf(output, "Usage: editcap [options] ... <infile> <outfile> [ <packet#>[-<packet#>] ... ]\n");
733     fprintf(output, "\n");
734     fprintf(output, "<infile> and <outfile> must both be present.\n");
735     fprintf(output, "A single packet or a range of packets can be selected.\n");
736     fprintf(output, "\n");
737     fprintf(output, "Packet selection:\n");
738     fprintf(output, "  -r                     keep the selected packets; default is to delete them.\n");
739     fprintf(output, "  -A <start time>        only output packets whose timestamp is after (or equal\n");
740     fprintf(output, "                         to) the given time (format as YYYY-MM-DD hh:mm:ss).\n");
741     fprintf(output, "  -B <stop time>         only output packets whose timestamp is before the\n");
742     fprintf(output, "                         given time (format as YYYY-MM-DD hh:mm:ss).\n");
743     fprintf(output, "\n");
744     fprintf(output, "Duplicate packet removal:\n");
745     fprintf(output, "  --novlan               remove vlan info from packets before checking for duplicates.\n");
746     fprintf(output, "  -d                     remove packet if duplicate (window == %d).\n", DEFAULT_DUP_DEPTH);
747     fprintf(output, "  -D <dup window>        remove packet if duplicate; configurable <dup window>.\n");
748     fprintf(output, "                         Valid <dup window> values are 0 to %d.\n", MAX_DUP_DEPTH);
749     fprintf(output, "                         NOTE: A <dup window> of 0 with -v (verbose option) is\n");
750     fprintf(output, "                         useful to print MD5 hashes.\n");
751     fprintf(output, "  -w <dup time window>   remove packet if duplicate packet is found EQUAL TO OR\n");
752     fprintf(output, "                         LESS THAN <dup time window> prior to current packet.\n");
753     fprintf(output, "                         A <dup time window> is specified in relative seconds\n");
754     fprintf(output, "                         (e.g. 0.000001).\n");
755     fprintf(output, "  -a <framenum>:<comment> Add or replace comment for given frame number\n");
756     fprintf(output, "\n");
757     fprintf(output, "  -I <bytes to ignore>   ignore the specified number of bytes at the beginning\n");
758     fprintf(output, "                         of the frame during MD5 hash calculation, unless the\n");
759     fprintf(output, "                         frame is too short, then the full frame is used.\n");
760     fprintf(output, "                         Useful to remove duplicated packets taken on\n");
761     fprintf(output, "                         several routers (different mac addresses for\n");
762     fprintf(output, "                         example).\n");
763     fprintf(output, "                         e.g. -I 26 in case of Ether/IP will ignore\n");
764     fprintf(output, "                         ether(14) and IP header(20 - 4(src ip) - 4(dst ip)).\n");
765     fprintf(output, "\n");
766     fprintf(output, "           NOTE: The use of the 'Duplicate packet removal' options with\n");
767     fprintf(output, "           other editcap options except -v may not always work as expected.\n");
768     fprintf(output, "           Specifically the -r, -t or -S options will very likely NOT have the\n");
769     fprintf(output, "           desired effect if combined with the -d, -D or -w.\n");
770     fprintf(output, "\n");
771     fprintf(output, "Packet manipulation:\n");
772     fprintf(output, "  -s <snaplen>           truncate each packet to max. <snaplen> bytes of data.\n");
773     fprintf(output, "  -C [offset:]<choplen>  chop each packet by <choplen> bytes. Positive values\n");
774     fprintf(output, "                         chop at the packet beginning, negative values at the\n");
775     fprintf(output, "                         packet end. If an optional offset precedes the length,\n");
776     fprintf(output, "                         then the bytes chopped will be offset from that value.\n");
777     fprintf(output, "                         Positive offsets are from the packet beginning,\n");
778     fprintf(output, "                         negative offsets are from the packet end. You can use\n");
779     fprintf(output, "                         this option more than once, allowing up to 2 chopping\n");
780     fprintf(output, "                         regions within a packet provided that at least 1\n");
781     fprintf(output, "                         choplen is positive and at least 1 is negative.\n");
782     fprintf(output, "  -L                     adjust the frame (i.e. reported) length when chopping\n");
783     fprintf(output, "                         and/or snapping.\n");
784     fprintf(output, "  -t <time adjustment>   adjust the timestamp of each packet.\n");
785     fprintf(output, "                         <time adjustment> is in relative seconds (e.g. -0.5).\n");
786     fprintf(output, "  -S <strict adjustment> adjust timestamp of packets if necessary to ensure\n");
787     fprintf(output, "                         strict chronological increasing order. The <strict\n");
788     fprintf(output, "                         adjustment> is specified in relative seconds with\n");
789     fprintf(output, "                         values of 0 or 0.000001 being the most reasonable.\n");
790     fprintf(output, "                         A negative adjustment value will modify timestamps so\n");
791     fprintf(output, "                         that each packet's delta time is the absolute value\n");
792     fprintf(output, "                         of the adjustment specified. A value of -0 will set\n");
793     fprintf(output, "                         all packets to the timestamp of the first packet.\n");
794     fprintf(output, "  -E <error probability> set the probability (between 0.0 and 1.0 incl.) that\n");
795     fprintf(output, "                         a particular packet byte will be randomly changed.\n");
796     fprintf(output, "  -o <change offset>     When used in conjunction with -E, skip some bytes from the\n");
797     fprintf(output, "                         beginning of the packet. This allows one to preserve some\n");
798     fprintf(output, "                         bytes, in order to have some headers untouched.\n");
799     fprintf(output, "\n");
800     fprintf(output, "Output File(s):\n");
801     fprintf(output, "  -c <packets per file>  split the packet output to different files based on\n");
802     fprintf(output, "                         uniform packet counts with a maximum of\n");
803     fprintf(output, "                         <packets per file> each.\n");
804     fprintf(output, "  -i <seconds per file>  split the packet output to different files based on\n");
805     fprintf(output, "                         uniform time intervals with a maximum of\n");
806     fprintf(output, "                         <seconds per file> each.\n");
807     fprintf(output, "  -F <capture type>      set the output file type; default is pcapng. An empty\n");
808     fprintf(output, "                         \"-F\" option will list the file types.\n");
809     fprintf(output, "  -T <encap type>        set the output file encapsulation type; default is the\n");
810     fprintf(output, "                         same as the input file. An empty \"-T\" option will\n");
811     fprintf(output, "                         list the encapsulation types.\n");
812     fprintf(output, "\n");
813     fprintf(output, "Miscellaneous:\n");
814     fprintf(output, "  -h                     display this help and exit.\n");
815     fprintf(output, "  -v                     verbose output.\n");
816     fprintf(output, "                         If -v is used with any of the 'Duplicate Packet\n");
817     fprintf(output, "                         Removal' options (-d, -D or -w) then Packet lengths\n");
818     fprintf(output, "                         and MD5 hashes are printed to standard-error.\n");
819 }
820
821 struct string_elem {
822     const char *sstr;   /* The short string */
823     const char *lstr;   /* The long string */
824 };
825
826 static gint
827 string_compare(gconstpointer a, gconstpointer b)
828 {
829     return strcmp(((const struct string_elem *)a)->sstr,
830         ((const struct string_elem *)b)->sstr);
831 }
832
833 static gint
834 string_nat_compare(gconstpointer a, gconstpointer b)
835 {
836     return ws_ascii_strnatcmp(((const struct string_elem *)a)->sstr,
837         ((const struct string_elem *)b)->sstr);
838 }
839
840 static void
841 string_elem_print(gpointer data, gpointer stream_ptr)
842 {
843     fprintf((FILE *) stream_ptr, "    %s - %s\n",
844         ((struct string_elem *)data)->sstr,
845         ((struct string_elem *)data)->lstr);
846 }
847
848 static void
849 list_capture_types(FILE *stream) {
850     int i;
851     struct string_elem *captypes;
852     GSList *list = NULL;
853
854     captypes = g_new(struct string_elem,WTAP_NUM_FILE_TYPES_SUBTYPES);
855     fprintf(stream, "editcap: The available capture file types for the \"-F\" flag are:\n");
856     for (i = 0; i < WTAP_NUM_FILE_TYPES_SUBTYPES; i++) {
857         if (wtap_dump_can_open(i)) {
858             captypes[i].sstr = wtap_file_type_subtype_short_string(i);
859             captypes[i].lstr = wtap_file_type_subtype_string(i);
860             list = g_slist_insert_sorted(list, &captypes[i], string_compare);
861         }
862     }
863     g_slist_foreach(list, string_elem_print, stream);
864     g_slist_free(list);
865     g_free(captypes);
866 }
867
868 static void
869 list_encap_types(FILE *stream) {
870     int i;
871     struct string_elem *encaps;
872     GSList *list = NULL;
873
874     encaps = (struct string_elem *)g_malloc(sizeof(struct string_elem) * WTAP_NUM_ENCAP_TYPES);
875     fprintf(stream, "editcap: The available encapsulation types for the \"-T\" flag are:\n");
876     for (i = 0; i < WTAP_NUM_ENCAP_TYPES; i++) {
877         encaps[i].sstr = wtap_encap_short_string(i);
878         if (encaps[i].sstr != NULL) {
879             encaps[i].lstr = wtap_encap_string(i);
880             list = g_slist_insert_sorted(list, &encaps[i], string_nat_compare);
881         }
882     }
883     g_slist_foreach(list, string_elem_print, stream);
884     g_slist_free(list);
885     g_free(encaps);
886 }
887
888 static int
889 framenum_compare(gconstpointer a, gconstpointer b, gpointer user_data _U_)
890 {
891     if (GPOINTER_TO_UINT(a) < GPOINTER_TO_UINT(b))
892         return -1;
893
894     if (GPOINTER_TO_UINT(a) > GPOINTER_TO_UINT(b))
895         return 1;
896
897     return 0;
898 }
899
900 /*
901  * General errors and warnings are reported with an console message
902  * in editcap.
903  */
904 static void
905 failure_warning_message(const char *msg_format, va_list ap)
906 {
907   fprintf(stderr, "editcap: ");
908   vfprintf(stderr, msg_format, ap);
909   fprintf(stderr, "\n");
910 }
911
912 /*
913  * Report additional information for an error in command-line arguments.
914  */
915 static void
916 failure_message_cont(const char *msg_format, va_list ap)
917 {
918   vfprintf(stderr, msg_format, ap);
919   fprintf(stderr, "\n");
920 }
921
922 static wtap_dumper *
923 editcap_dump_open(const char *filename, guint32 snaplen,
924                   GArray* shb_hdrs,
925                   wtapng_iface_descriptions_t *idb_inf,
926                   GArray* nrb_hdrs, int *write_err)
927 {
928   wtap_dumper *pdh;
929
930   if (strcmp(filename, "-") == 0) {
931     /* Write to the standard output. */
932     pdh = wtap_dump_open_stdout_ng(out_file_type_subtype, out_frame_type,
933                                    snaplen, FALSE /* compressed */,
934                                    shb_hdrs, idb_inf, nrb_hdrs, write_err);
935   } else {
936     pdh = wtap_dump_open_ng(filename, out_file_type_subtype, out_frame_type,
937                             snaplen, FALSE /* compressed */,
938                             shb_hdrs, idb_inf, nrb_hdrs, write_err);
939   }
940   return pdh;
941 }
942
943 int
944 main(int argc, char *argv[])
945 {
946     GString      *comp_info_str;
947     GString      *runtime_info_str;
948     char         *init_progfile_dir_error;
949     wtap         *wth = NULL;
950     int           i, j, read_err, write_err;
951     gchar        *read_err_info, *write_err_info;
952     int           opt;
953     static const struct option long_options[] = {
954         {"novlan", no_argument, NULL, 0x8100},
955         {"help", no_argument, NULL, 'h'},
956         {"version", no_argument, NULL, 'V'},
957         {0, 0, 0, 0 }
958     };
959
960     char         *p;
961     guint32       snaplen            = 0; /* No limit               */
962     chop_t        chop               = {0, 0, 0, 0, 0, 0}; /* No chop */
963     gboolean      adjlen             = FALSE;
964     wtap_dumper  *pdh                = NULL;
965     unsigned int  count              = 1;
966     unsigned int  duplicate_count    = 0;
967     gint64        data_offset;
968     int           err_type;
969     guint8       *buf;
970     guint32       read_count         = 0;
971     guint32       split_packet_count = 0;
972     int           written_count      = 0;
973     char         *filename           = NULL;
974     gboolean      ts_okay;
975     guint32       secs_per_block     = 0;
976     int           block_cnt          = 0;
977     nstime_t      block_start;
978     gchar        *fprefix            = NULL;
979     gchar        *fsuffix            = NULL;
980     guint32       change_offset      = 0;
981     guint         max_packet_number  = 0;
982     const wtap_rec              *rec;
983     wtap_rec                     temp_rec;
984     wtapng_iface_descriptions_t *idb_inf = NULL;
985     GArray                      *shb_hdrs = NULL;
986     GArray                      *nrb_hdrs = NULL;
987     char                        *shb_user_appl;
988     gboolean                     do_mutation;
989     guint32                      caplen;
990     int                          ret = EXIT_SUCCESS;
991
992     cmdarg_err_init(failure_warning_message, failure_message_cont);
993
994 #ifdef _WIN32
995     arg_list_utf_16to8(argc, argv);
996     create_app_running_mutex();
997 #endif /* _WIN32 */
998
999     /* Get the compile-time version information string */
1000     comp_info_str = get_compiled_version_info(NULL, NULL);
1001
1002     /* Get the run-time version information string */
1003     runtime_info_str = get_runtime_version_info(NULL);
1004
1005     /* Add it to the information to be reported on a crash. */
1006     ws_add_crash_info("Editcap (Wireshark) %s\n"
1007          "\n"
1008          "%s"
1009          "\n"
1010          "%s",
1011       get_ws_vcs_version_info(), comp_info_str->str, runtime_info_str->str);
1012     g_string_free(comp_info_str, TRUE);
1013     g_string_free(runtime_info_str, TRUE);
1014
1015     /*
1016      * Get credential information for later use.
1017      */
1018     init_process_policies();
1019
1020     /*
1021      * Attempt to get the pathname of the directory containing the
1022      * executable file.
1023      */
1024     init_progfile_dir_error = init_progfile_dir(argv[0], main);
1025     if (init_progfile_dir_error != NULL) {
1026         fprintf(stderr,
1027                 "editcap: Can't get pathname of directory containing the editcap program: %s.\n",
1028                 init_progfile_dir_error);
1029         g_free(init_progfile_dir_error);
1030     }
1031
1032     init_report_message(failure_warning_message, failure_warning_message,
1033                         NULL, NULL, NULL);
1034
1035     wtap_init(TRUE);
1036
1037     /* Process the options */
1038     while ((opt = getopt_long(argc, argv, ":a:A:B:c:C:dD:E:F:hi:I:Lo:rs:S:t:T:vVw:", long_options, NULL)) != -1) {
1039         switch (opt) {
1040         case 0x8100:
1041         {
1042             rem_vlan = TRUE;
1043             break;
1044         }
1045
1046         case 'a':
1047         {
1048             guint frame_number;
1049             gint string_start_index = 0;
1050
1051             if ((sscanf(optarg, "%u:%n", &frame_number, &string_start_index) < 1) || (string_start_index == 0)) {
1052                 fprintf(stderr, "editcap: \"%s\" isn't a valid <frame>:<comment>\n\n",
1053                         optarg);
1054                 ret = INVALID_OPTION;
1055                 goto clean_exit;
1056             }
1057
1058             /* Lazily create the table */
1059             if (!frames_user_comments) {
1060                 frames_user_comments = g_tree_new_full(framenum_compare, NULL, NULL, g_free);
1061             }
1062
1063             /* Insert this entry (framenum -> comment) */
1064             g_tree_replace(frames_user_comments, GUINT_TO_POINTER(frame_number), g_strdup(optarg+string_start_index));
1065             break;
1066         }
1067
1068         case 'A':
1069         {
1070             struct tm starttm;
1071
1072             memset(&starttm,0,sizeof(struct tm));
1073
1074             if (!strptime(optarg,"%Y-%m-%d %T", &starttm)) {
1075                 fprintf(stderr, "editcap: \"%s\" isn't a valid time format\n\n",
1076                         optarg);
1077                 ret = INVALID_OPTION;
1078                 goto clean_exit;
1079             }
1080
1081             check_startstop = TRUE;
1082             starttm.tm_isdst = -1;
1083
1084             starttime = mktime(&starttm);
1085             break;
1086         }
1087
1088         case 'B':
1089         {
1090             struct tm stoptm;
1091
1092             memset(&stoptm,0,sizeof(struct tm));
1093
1094             if (!strptime(optarg,"%Y-%m-%d %T", &stoptm)) {
1095                 fprintf(stderr, "editcap: \"%s\" isn't a valid time format\n\n",
1096                         optarg);
1097                 ret = INVALID_OPTION;
1098                 goto clean_exit;
1099             }
1100             check_startstop = TRUE;
1101             stoptm.tm_isdst = -1;
1102             stoptime = mktime(&stoptm);
1103             break;
1104         }
1105
1106         case 'c':
1107             split_packet_count = get_nonzero_guint32(optarg, "packet count");
1108             break;
1109
1110         case 'C':
1111         {
1112             int choplen = 0, chopoff = 0;
1113
1114             switch (sscanf(optarg, "%d:%d", &chopoff, &choplen)) {
1115             case 1: /* only the chop length was specififed */
1116                 choplen = chopoff;
1117                 chopoff = 0;
1118                 break;
1119
1120             case 2: /* both an offset and chop length was specified */
1121                 break;
1122
1123             default:
1124                 fprintf(stderr, "editcap: \"%s\" isn't a valid chop length or offset:length\n",
1125                         optarg);
1126                 ret = INVALID_OPTION;
1127                 goto clean_exit;
1128                 break;
1129             }
1130
1131             if (choplen > 0) {
1132                 chop.len_begin += choplen;
1133                 if (chopoff > 0)
1134                     chop.off_begin_pos += chopoff;
1135                 else
1136                     chop.off_begin_neg += chopoff;
1137             } else if (choplen < 0) {
1138                 chop.len_end += choplen;
1139                 if (chopoff > 0)
1140                     chop.off_end_pos += chopoff;
1141                 else
1142                     chop.off_end_neg += chopoff;
1143             }
1144             break;
1145         }
1146
1147         case 'd':
1148             dup_detect = TRUE;
1149             dup_detect_by_time = FALSE;
1150             dup_window = DEFAULT_DUP_DEPTH;
1151             break;
1152
1153         case 'D':
1154             dup_detect = TRUE;
1155             dup_detect_by_time = FALSE;
1156             dup_window = get_guint32(optarg, "duplicate window");
1157             if (dup_window > MAX_DUP_DEPTH) {
1158                 fprintf(stderr, "editcap: \"%d\" duplicate window value must be between 0 and %d inclusive.\n",
1159                         dup_window, MAX_DUP_DEPTH);
1160                 ret = INVALID_OPTION;
1161                 goto clean_exit;
1162             }
1163             break;
1164
1165         case 'E':
1166             err_prob = g_ascii_strtod(optarg, &p);
1167             if (p == optarg || err_prob < 0.0 || err_prob > 1.0) {
1168                 fprintf(stderr, "editcap: probability \"%s\" must be between 0.0 and 1.0\n",
1169                         optarg);
1170                 ret = INVALID_OPTION;
1171                 goto clean_exit;
1172             }
1173             srand( (unsigned int) (time(NULL) + ws_getpid()) );
1174             break;
1175
1176         case 'F':
1177             out_file_type_subtype = wtap_short_string_to_file_type_subtype(optarg);
1178             if (out_file_type_subtype < 0) {
1179                 fprintf(stderr, "editcap: \"%s\" isn't a valid capture file type\n\n",
1180                         optarg);
1181                 list_capture_types(stderr);
1182                 ret = INVALID_OPTION;
1183                 goto clean_exit;
1184             }
1185             break;
1186
1187         case 'h':
1188             printf("Editcap (Wireshark) %s\n"
1189                    "Edit and/or translate the format of capture files.\n"
1190                    "See https://www.wireshark.org for more information.\n",
1191                get_ws_vcs_version_info());
1192             print_usage(stdout);
1193             goto clean_exit;
1194             break;
1195
1196         case 'i': /* break capture file based on time interval */
1197             secs_per_block = get_nonzero_guint32(optarg, "time interval");
1198             break;
1199
1200         case 'I': /* ignored_bytes at the beginning of the frame for duplications removal */
1201             ignored_bytes = get_guint32(optarg, "number of bytes to ignore");
1202             break;
1203
1204         case 'L':
1205             adjlen = TRUE;
1206             break;
1207
1208         case 'o':
1209             change_offset = get_guint32(optarg, "change offset");
1210             break;
1211
1212         case 'r':
1213             keep_em = !keep_em;  /* Just invert */
1214             break;
1215
1216         case 's':
1217             snaplen = get_nonzero_guint32(optarg, "snapshot length");
1218             break;
1219
1220         case 'S':
1221             if (!set_strict_time_adj(optarg)) {
1222                 ret = INVALID_OPTION;
1223                 goto clean_exit;
1224             }
1225             do_strict_time_adjustment = TRUE;
1226             break;
1227
1228         case 't':
1229             if (!set_time_adjustment(optarg)) {
1230                 ret = INVALID_OPTION;
1231                 goto clean_exit;
1232             }
1233             break;
1234
1235         case 'T':
1236             out_frame_type = wtap_short_string_to_encap(optarg);
1237             if (out_frame_type < 0) {
1238                 fprintf(stderr, "editcap: \"%s\" isn't a valid encapsulation type\n\n",
1239                         optarg);
1240                 list_encap_types(stderr);
1241                 ret = INVALID_OPTION;
1242                 goto clean_exit;
1243             }
1244             break;
1245
1246         case 'v':
1247             verbose = !verbose;  /* Just invert */
1248             break;
1249
1250         case 'V':
1251             comp_info_str = get_compiled_version_info(NULL, NULL);
1252             runtime_info_str = get_runtime_version_info(NULL);
1253             show_version("Editcap (Wireshark)", comp_info_str, runtime_info_str);
1254             g_string_free(comp_info_str, TRUE);
1255             g_string_free(runtime_info_str, TRUE);
1256             goto clean_exit;
1257             break;
1258
1259         case 'w':
1260             dup_detect = FALSE;
1261             dup_detect_by_time = TRUE;
1262             dup_window = MAX_DUP_DEPTH;
1263             if (!set_rel_time(optarg)) {
1264                 ret = INVALID_OPTION;
1265                 goto clean_exit;
1266             }
1267             break;
1268
1269         case '?':              /* Bad options if GNU getopt */
1270         case ':':              /* missing option argument */
1271             switch(optopt) {
1272             case'F':
1273                 list_capture_types(stdout);
1274                 break;
1275             case'T':
1276                 list_encap_types(stdout);
1277                 break;
1278             default:
1279                 if (opt == '?') {
1280                     fprintf(stderr, "editcap: invalid option -- '%c'\n", optopt);
1281                 } else {
1282                     fprintf(stderr, "editcap: option requires an argument -- '%c'\n", optopt);
1283                 }
1284                 print_usage(stderr);
1285                 ret = INVALID_OPTION;
1286                 break;
1287             }
1288             goto clean_exit;
1289             break;
1290         }
1291     } /* processing commmand-line options */
1292
1293 #ifdef DEBUG
1294     fprintf(stderr, "Optind = %i, argc = %i\n", optind, argc);
1295 #endif
1296
1297     if ((argc - optind) < 1) {
1298         print_usage(stderr);
1299         ret = INVALID_OPTION;
1300         goto clean_exit;
1301     }
1302
1303     if (check_startstop && !stoptime) {
1304         struct tm stoptm;
1305
1306         /* XXX: will work until 2035 */
1307         memset(&stoptm,0,sizeof(struct tm));
1308         stoptm.tm_year = 135;
1309         stoptm.tm_mday = 31;
1310         stoptm.tm_mon = 11;
1311
1312         stoptime = mktime(&stoptm);
1313     }
1314
1315     nstime_set_unset(&block_start);
1316
1317     if (starttime > stoptime) {
1318         fprintf(stderr, "editcap: start time is after the stop time\n");
1319         ret = INVALID_OPTION;
1320         goto clean_exit;
1321     }
1322
1323     if (split_packet_count != 0 && secs_per_block != 0) {
1324         fprintf(stderr, "editcap: can't split on both packet count and time interval\n");
1325         fprintf(stderr, "editcap: at the same time\n");
1326         ret = INVALID_OPTION;
1327         goto clean_exit;
1328     }
1329
1330     wth = wtap_open_offline(argv[optind], WTAP_TYPE_AUTO, &read_err, &read_err_info, FALSE);
1331
1332     if (!wth) {
1333         cfile_open_failure_message("editcap", argv[optind], read_err,
1334                                    read_err_info);
1335         ret = INVALID_FILE;
1336         goto clean_exit;
1337     }
1338
1339     if (verbose) {
1340         fprintf(stderr, "File %s is a %s capture file.\n", argv[optind],
1341                 wtap_file_type_subtype_string(wtap_file_type_subtype(wth)));
1342     }
1343
1344     shb_hdrs = wtap_file_get_shb_for_new_file(wth);
1345     idb_inf = wtap_file_get_idb_info(wth);
1346     nrb_hdrs = wtap_file_get_nrb_for_new_file(wth);
1347
1348     /*
1349      * Now, process the rest, if any ... we only write if there is an extra
1350      * argument or so ...
1351      */
1352
1353     if ((argc - optind) >= 2) {
1354         if (out_frame_type == -2)
1355             out_frame_type = wtap_file_encap(wth);
1356
1357         for (i = optind + 2; i < argc; i++)
1358             if (add_selection(argv[i], &max_packet_number) == FALSE)
1359                 break;
1360
1361         if (keep_em == FALSE)
1362             max_packet_number = G_MAXUINT;
1363
1364         if (dup_detect || dup_detect_by_time) {
1365             for (i = 0; i < dup_window; i++) {
1366                 memset(&fd_hash[i].digest, 0, 16);
1367                 fd_hash[i].len = 0;
1368                 nstime_set_unset(&fd_hash[i].frame_time);
1369             }
1370         }
1371
1372         /* Read all of the packets in turn */
1373         while (wtap_read(wth, &read_err, &read_err_info, &data_offset)) {
1374             if (max_packet_number <= read_count)
1375                 break;
1376
1377             read_count++;
1378
1379             rec = wtap_get_rec(wth);
1380
1381             /* Extra actions for the first packet */
1382             if (read_count == 1) {
1383                 if (split_packet_count != 0 || secs_per_block != 0) {
1384                     if (!fileset_extract_prefix_suffix(argv[optind+1], &fprefix, &fsuffix)) {
1385                         ret = CANT_EXTRACT_PREFIX;
1386                         goto clean_exit;
1387                     }
1388
1389                     filename = fileset_get_filename_by_pattern(block_cnt++, rec, fprefix, fsuffix);
1390                 } else {
1391                     filename = g_strdup(argv[optind+1]);
1392                 }
1393                 g_assert(filename);
1394
1395                 /* If we don't have an application name add Editcap */
1396                 if (wtap_block_get_string_option_value(g_array_index(shb_hdrs, wtap_block_t, 0), OPT_SHB_USERAPPL, &shb_user_appl) != WTAP_OPTTYPE_SUCCESS) {
1397                     wtap_block_add_string_option_format(g_array_index(shb_hdrs, wtap_block_t, 0), OPT_SHB_USERAPPL, "Editcap " VERSION);
1398                 }
1399
1400                 pdh = editcap_dump_open(filename,
1401                                         snaplen ? MIN(snaplen, wtap_snapshot_length(wth)) : wtap_snapshot_length(wth),
1402                                         shb_hdrs, idb_inf, nrb_hdrs, &write_err);
1403
1404                 if (pdh == NULL) {
1405                     cfile_dump_open_failure_message("editcap", filename,
1406                                                     write_err,
1407                                                     out_file_type_subtype);
1408                     ret = INVALID_FILE;
1409                     goto clean_exit;
1410                 }
1411             } /* first packet only handling */
1412
1413
1414             buf = wtap_get_buf_ptr(wth);
1415
1416             /*
1417              * Not all packets have time stamps. Only process the time
1418              * stamp if we have one.
1419              */
1420             if (rec->presence_flags & WTAP_HAS_TS) {
1421                 if (nstime_is_unset(&block_start)) {
1422                     block_start = rec->ts;
1423                 }
1424                 if (secs_per_block != 0) {
1425                     while (((guint32)(rec->ts.secs - block_start.secs) >  secs_per_block)
1426                            || ((guint32)(rec->ts.secs - block_start.secs) == secs_per_block
1427                                && rec->ts.nsecs >= block_start.nsecs )) { /* time for the next file */
1428
1429                         if (!wtap_dump_close(pdh, &write_err)) {
1430                             cfile_close_failure_message(filename, write_err);
1431                             ret = WRITE_ERROR;
1432                             goto clean_exit;
1433                         }
1434                         block_start.secs = block_start.secs +  secs_per_block; /* reset for next interval */
1435                         g_free(filename);
1436                         filename = fileset_get_filename_by_pattern(block_cnt++, rec, fprefix, fsuffix);
1437                         g_assert(filename);
1438
1439                         if (verbose)
1440                             fprintf(stderr, "Continuing writing in file %s\n", filename);
1441
1442                         pdh = editcap_dump_open(filename,
1443                                                 snaplen ? MIN(snaplen, wtap_snapshot_length(wth)) : wtap_snapshot_length(wth),
1444                                                 shb_hdrs, idb_inf, nrb_hdrs, &write_err);
1445
1446                         if (pdh == NULL) {
1447                             cfile_dump_open_failure_message("editcap", filename,
1448                                                             write_err,
1449                                                             out_file_type_subtype);
1450                             ret = INVALID_FILE;
1451                             goto clean_exit;
1452                         }
1453                     }
1454                 }
1455             }  /* time stamp handling */
1456
1457             if (split_packet_count != 0) {
1458                 /* time for the next file? */
1459                 if (written_count > 0 && (written_count % split_packet_count) == 0) {
1460                     if (!wtap_dump_close(pdh, &write_err)) {
1461                         cfile_close_failure_message(filename, write_err);
1462                         ret = WRITE_ERROR;
1463                         goto clean_exit;
1464                     }
1465
1466                     g_free(filename);
1467                     filename = fileset_get_filename_by_pattern(block_cnt++, rec, fprefix, fsuffix);
1468                     g_assert(filename);
1469
1470                     if (verbose)
1471                         fprintf(stderr, "Continuing writing in file %s\n", filename);
1472
1473                     pdh = editcap_dump_open(filename,
1474                                             snaplen ? MIN(snaplen, wtap_snapshot_length(wth)) : wtap_snapshot_length(wth),
1475                                             shb_hdrs, idb_inf, nrb_hdrs, &write_err);
1476                     if (pdh == NULL) {
1477                         cfile_dump_open_failure_message("editcap", filename,
1478                                                         write_err,
1479                                                         out_file_type_subtype);
1480                         ret = INVALID_FILE;
1481                         goto clean_exit;
1482                     }
1483                 }
1484             } /* split packet handling */
1485
1486             if (check_startstop) {
1487                 /*
1488                  * Is the packet in the selected timeframe?
1489                  * If the packet has no time stamp, the answer is "no".
1490                  */
1491                 if (rec->presence_flags & WTAP_HAS_TS)
1492                     ts_okay = (rec->ts.secs >= starttime) && (rec->ts.secs < stoptime);
1493                 else
1494                     ts_okay = FALSE;
1495             } else {
1496                 /*
1497                  * No selected timeframe, so all packets are "in the
1498                  * selected timeframe".
1499                  */
1500                 ts_okay = TRUE;
1501             }
1502
1503             if (ts_okay && ((!selected(count) && !keep_em)
1504                             || (selected(count) && keep_em))) {
1505
1506                 if (verbose && !dup_detect && !dup_detect_by_time)
1507                     fprintf(stderr, "Packet: %u\n", count);
1508
1509                 /* We simply write it, perhaps after truncating it; we could
1510                  * do other things, like modify it. */
1511
1512                 rec = wtap_get_rec(wth);
1513
1514                 if (rec->presence_flags & WTAP_HAS_TS) {
1515                     /* Do we adjust timestamps to ensure strict chronological
1516                      * order? */
1517                     if (do_strict_time_adjustment) {
1518                         if (previous_time.secs || previous_time.nsecs) {
1519                             if (!strict_time_adj.is_negative) {
1520                                 nstime_t current;
1521                                 nstime_t delta;
1522
1523                                 current = rec->ts;
1524
1525                                 nstime_delta(&delta, &current, &previous_time);
1526
1527                                 if (delta.secs < 0 || delta.nsecs < 0) {
1528                                     /*
1529                                      * A negative delta indicates that the current packet
1530                                      * has an absolute timestamp less than the previous packet
1531                                      * that it is being compared to.  This is NOT a normal
1532                                      * situation since trace files usually have packets in
1533                                      * chronological order (oldest to newest).
1534                                      * Copy and change rather than modify
1535                                      * returned rec.
1536                                      */
1537                                     /* fprintf(stderr, "++out of order, need to adjust this packet!\n"); */
1538                                     temp_rec = *rec;
1539                                     temp_rec.ts.secs = previous_time.secs + strict_time_adj.tv.secs;
1540                                     temp_rec.ts.nsecs = previous_time.nsecs;
1541                                     if (temp_rec.ts.nsecs + strict_time_adj.tv.nsecs > ONE_BILLION) {
1542                                         /* carry */
1543                                         temp_rec.ts.secs++;
1544                                         temp_rec.ts.nsecs += strict_time_adj.tv.nsecs - ONE_BILLION;
1545                                     } else {
1546                                         temp_rec.ts.nsecs += strict_time_adj.tv.nsecs;
1547                                     }
1548                                     rec = &temp_rec;
1549                                 }
1550                             } else {
1551                                 /*
1552                                  * A negative strict time adjustment is requested.
1553                                  * Unconditionally set each timestamp to previous
1554                                  * packet's timestamp plus delta.
1555                                  * Copy and change rather than modify returned
1556                                  * rec.
1557                                  */
1558                                 temp_rec = *rec;
1559                                 temp_rec.ts.secs = previous_time.secs + strict_time_adj.tv.secs;
1560                                 temp_rec.ts.nsecs = previous_time.nsecs;
1561                                 if (temp_rec.ts.nsecs + strict_time_adj.tv.nsecs > ONE_BILLION) {
1562                                     /* carry */
1563                                     temp_rec.ts.secs++;
1564                                     temp_rec.ts.nsecs += strict_time_adj.tv.nsecs - ONE_BILLION;
1565                                 } else {
1566                                     temp_rec.ts.nsecs += strict_time_adj.tv.nsecs;
1567                                 }
1568                                 rec = &temp_rec;
1569                             }
1570                         }
1571                         previous_time = rec->ts;
1572                     }
1573
1574                     if (time_adj.tv.secs != 0) {
1575                         /* Copy and change rather than modify returned rec */
1576                         temp_rec = *rec;
1577                         if (time_adj.is_negative)
1578                             temp_rec.ts.secs -= time_adj.tv.secs;
1579                         else
1580                             temp_rec.ts.secs += time_adj.tv.secs;
1581                         rec = &temp_rec;
1582                     }
1583
1584                     if (time_adj.tv.nsecs != 0) {
1585                         /* Copy and change rather than modify returned rec */
1586                         temp_rec = *rec;
1587                         if (time_adj.is_negative) { /* subtract */
1588                             if (temp_rec.ts.nsecs < time_adj.tv.nsecs) { /* borrow */
1589                                 temp_rec.ts.secs--;
1590                                 temp_rec.ts.nsecs += ONE_BILLION;
1591                             }
1592                             temp_rec.ts.nsecs -= time_adj.tv.nsecs;
1593                         } else {                  /* add */
1594                             if (temp_rec.ts.nsecs + time_adj.tv.nsecs > ONE_BILLION) {
1595                                 /* carry */
1596                                 temp_rec.ts.secs++;
1597                                 temp_rec.ts.nsecs += time_adj.tv.nsecs - ONE_BILLION;
1598                             } else {
1599                                 temp_rec.ts.nsecs += time_adj.tv.nsecs;
1600                             }
1601                         }
1602                         rec = &temp_rec;
1603                     }
1604                 } /* time stamp adjustment */
1605
1606                 if (rec->rec_type == REC_TYPE_PACKET) {
1607                     if (snaplen != 0) {
1608                         /* Limit capture length to snaplen */
1609                         if (rec->rec_header.packet_header.caplen > snaplen) {
1610                             /* Copy and change rather than modify returned wtap_rec */
1611                             temp_rec = *rec;
1612                             temp_rec.rec_header.packet_header.caplen = snaplen;
1613                             rec = &temp_rec;
1614                         }
1615                         /* If -L, also set reported length to snaplen */
1616                         if (adjlen && rec->rec_header.packet_header.len > snaplen) {
1617                             /* Copy and change rather than modify returned phdr */
1618                             temp_rec = *rec;
1619                             temp_rec.rec_header.packet_header.len = snaplen;
1620                             rec = &temp_rec;
1621                         }
1622                     }
1623
1624                     /*
1625                      * CHOP
1626                      * Copy and change rather than modify returned phdr.
1627                      */
1628                     temp_rec = *rec;
1629                     handle_chopping(chop, &temp_rec.rec_header.packet_header,
1630                                     &rec->rec_header.packet_header, &buf,
1631                                     adjlen);
1632                     rec = &temp_rec;
1633
1634                     /* remove vlan info */
1635                     if (rem_vlan) {
1636                         /* Copy and change rather than modify returned rec */
1637                         temp_rec = *rec;
1638                         remove_vlan_info(&rec->rec_header.packet_header, buf,
1639                                          &temp_rec.rec_header.packet_header.caplen);
1640                         rec = &temp_rec;
1641                     }
1642
1643                     /* suppress duplicates by packet window */
1644                     if (dup_detect) {
1645                         if (is_duplicate(buf, rec->rec_header.packet_header.caplen)) {
1646                             if (verbose) {
1647                                 fprintf(stderr, "Skipped: %u, Len: %u, MD5 Hash: ",
1648                                         count,
1649                                         rec->rec_header.packet_header.caplen);
1650                                 for (i = 0; i < 16; i++)
1651                                     fprintf(stderr, "%02x",
1652                                             (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1653                                 fprintf(stderr, "\n");
1654                             }
1655                             duplicate_count++;
1656                             count++;
1657                             continue;
1658                         } else {
1659                             if (verbose) {
1660                                 fprintf(stderr, "Packet: %u, Len: %u, MD5 Hash: ",
1661                                         count,
1662                                         rec->rec_header.packet_header.caplen);
1663                                 for (i = 0; i < 16; i++)
1664                                     fprintf(stderr, "%02x",
1665                                             (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1666                                 fprintf(stderr, "\n");
1667                             }
1668                         }
1669                     } /* suppression of duplicates */
1670
1671                     if (rec->presence_flags & WTAP_HAS_TS) {
1672                         /* suppress duplicates by time window */
1673                         if (dup_detect_by_time) {
1674                             nstime_t current;
1675
1676                             current.secs  = rec->ts.secs;
1677                             current.nsecs = rec->ts.nsecs;
1678
1679                             if (is_duplicate_rel_time(buf,
1680                                                       rec->rec_header.packet_header.caplen,
1681                                                       &current)) {
1682                                 if (verbose) {
1683                                     fprintf(stderr, "Skipped: %u, Len: %u, MD5 Hash: ",
1684                                             count,
1685                                             rec->rec_header.packet_header.caplen);
1686                                     for (i = 0; i < 16; i++)
1687                                         fprintf(stderr, "%02x",
1688                                                 (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1689                                     fprintf(stderr, "\n");
1690                                 }
1691                                 duplicate_count++;
1692                                 count++;
1693                                 continue;
1694                             } else {
1695                                 if (verbose) {
1696                                     fprintf(stderr, "Packet: %u, Len: %u, MD5 Hash: ",
1697                                             count,
1698                                             rec->rec_header.packet_header.caplen);
1699                                     for (i = 0; i < 16; i++)
1700                                         fprintf(stderr, "%02x",
1701                                                 (unsigned char)fd_hash[cur_dup_entry].digest[i]);
1702                                     fprintf(stderr, "\n");
1703                                 }
1704                             }
1705                         }
1706                     } /* suppress duplicates by time window */
1707                 }
1708
1709                 /* Random error mutation */
1710                 do_mutation = FALSE;
1711                 caplen = 0;
1712                 if (err_prob > 0.0) {
1713                     switch (rec->rec_type) {
1714
1715                     case REC_TYPE_PACKET:
1716                         caplen = rec->rec_header.packet_header.caplen;
1717                         do_mutation = TRUE;
1718                         break;
1719
1720                     case REC_TYPE_SYSCALL:
1721                         caplen = rec->rec_header.syscall_header.event_filelen;
1722                         do_mutation = TRUE;
1723                         break;
1724                     }
1725
1726                     if (change_offset > caplen) {
1727                         fprintf(stderr, "change offset %u is longer than caplen %u in packet %u\n",
1728                             change_offset, caplen, count);
1729                         do_mutation = FALSE;
1730                     }
1731                 }
1732
1733                 if (do_mutation) {
1734                     int real_data_start = 0;
1735
1736                     /* Protect non-protocol data */
1737                     switch (rec->rec_type) {
1738
1739                     case REC_TYPE_PACKET:
1740                         if (wtap_file_type_subtype(wth) == WTAP_FILE_TYPE_SUBTYPE_CATAPULT_DCT2000)
1741                             real_data_start = find_dct2000_real_data(buf);
1742                         break;
1743                     }
1744
1745                     real_data_start += change_offset;
1746
1747                     for (i = real_data_start; i < (int) caplen; i++) {
1748                         if (rand() <= err_prob * RAND_MAX) {
1749                             err_type = rand() / (RAND_MAX / ERR_WT_TOTAL + 1);
1750
1751                             if (err_type < ERR_WT_BIT) {
1752                                 buf[i] ^= 1 << (rand() / (RAND_MAX / 8 + 1));
1753                                 err_type = ERR_WT_TOTAL;
1754                             } else {
1755                                 err_type -= ERR_WT_BYTE;
1756                             }
1757
1758                             if (err_type < ERR_WT_BYTE) {
1759                                 buf[i] = rand() / (RAND_MAX / 255 + 1);
1760                                 err_type = ERR_WT_TOTAL;
1761                             } else {
1762                                 err_type -= ERR_WT_BYTE;
1763                             }
1764
1765                             if (err_type < ERR_WT_ALNUM) {
1766                                 buf[i] = ALNUM_CHARS[rand() / (RAND_MAX / ALNUM_LEN + 1)];
1767                                 err_type = ERR_WT_TOTAL;
1768                             } else {
1769                                 err_type -= ERR_WT_ALNUM;
1770                             }
1771
1772                             if (err_type < ERR_WT_FMT) {
1773                                 if ((unsigned int)i < caplen - 2)
1774                                     g_strlcpy((char*) &buf[i], "%s", 2);
1775                                 err_type = ERR_WT_TOTAL;
1776                             } else {
1777                                 err_type -= ERR_WT_FMT;
1778                             }
1779
1780                             if (err_type < ERR_WT_AA) {
1781                                 for (j = i; j < (int) caplen; j++)
1782                                     buf[j] = 0xAA;
1783                                 i = caplen;
1784                             }
1785                         }
1786                     }
1787                 } /* random error mutation */
1788
1789                 /* Find a packet comment we may need to write */
1790                 if (frames_user_comments) {
1791                     const char *comment =
1792                         (const char*)g_tree_lookup(frames_user_comments, GUINT_TO_POINTER(read_count));
1793                     /* XXX: What about comment changed to no comment? */
1794                     if (comment != NULL) {
1795                         /* Copy and change rather than modify returned rec */
1796                         temp_rec = *rec;
1797                         temp_rec.opt_comment = g_strdup(comment);
1798                         temp_rec.has_comment_changed = TRUE;
1799                         rec = &temp_rec;
1800                     } else {
1801                         /* Copy and change rather than modify returned rec */
1802                         temp_rec = *rec;
1803                         temp_rec.has_comment_changed = FALSE;
1804                         rec = &temp_rec;
1805                     }
1806                 }
1807
1808                 /* Attempt to dump out current frame to the output file */
1809                 if (!wtap_dump(pdh, rec, buf, &write_err, &write_err_info)) {
1810                     cfile_write_failure_message("editcap", argv[optind],
1811                                                 filename,
1812                                                 write_err, write_err_info,
1813                                                 read_count,
1814                                                 out_file_type_subtype);
1815                     ret = DUMP_ERROR;
1816                     goto clean_exit;
1817                 }
1818                 written_count++;
1819             }
1820             count++;
1821         }
1822
1823         g_free(fprefix);
1824         g_free(fsuffix);
1825
1826         if (read_err != 0) {
1827             /* Print a message noting that the read failed somewhere along the
1828              * line. */
1829             cfile_read_failure_message("editcap", argv[optind], read_err,
1830                                        read_err_info);
1831         }
1832
1833         if (!pdh) {
1834             /* No valid packages found, open the outfile so we can write an
1835              * empty header */
1836             g_free (filename);
1837             filename = g_strdup(argv[optind+1]);
1838
1839             pdh = editcap_dump_open(filename,
1840                                     snaplen ? MIN(snaplen, wtap_snapshot_length(wth)): wtap_snapshot_length(wth),
1841                                     shb_hdrs, idb_inf, nrb_hdrs, &write_err);
1842             if (pdh == NULL) {
1843                 cfile_dump_open_failure_message("editcap", filename,
1844                                                 write_err,
1845                                                 out_file_type_subtype);
1846                 ret = INVALID_FILE;
1847                 goto clean_exit;
1848             }
1849         }
1850
1851         if (!wtap_dump_close(pdh, &write_err)) {
1852             cfile_close_failure_message(filename, write_err);
1853             ret = WRITE_ERROR;
1854             goto clean_exit;
1855         }
1856         g_free(filename);
1857
1858         if (frames_user_comments) {
1859             g_tree_destroy(frames_user_comments);
1860         }
1861     }
1862
1863     if (dup_detect) {
1864         fprintf(stderr, "%u packet%s seen, %u packet%s skipped with duplicate window of %i packets.\n",
1865                 count - 1, plurality(count - 1, "", "s"), duplicate_count,
1866                 plurality(duplicate_count, "", "s"), dup_window);
1867     } else if (dup_detect_by_time) {
1868         fprintf(stderr, "%u packet%s seen, %u packet%s skipped with duplicate time window equal to or less than %ld.%09ld seconds.\n",
1869                 count - 1, plurality(count - 1, "", "s"), duplicate_count,
1870                 plurality(duplicate_count, "", "s"),
1871                 (long)relative_time_window.secs,
1872                 (long int)relative_time_window.nsecs);
1873     }
1874
1875 clean_exit:
1876     wtap_block_array_free(shb_hdrs);
1877     wtap_block_array_free(nrb_hdrs);
1878     g_free(idb_inf);
1879     if (wth != NULL)
1880         wtap_close(wth);
1881     wtap_cleanup();
1882     free_progdirs();
1883     return ret;
1884 }
1885
1886 /* Skip meta-information read from file to return offset of real
1887  * protocol data */
1888 static int
1889 find_dct2000_real_data(guint8 *buf)
1890 {
1891     int n = 0;
1892
1893     for (n = 0; buf[n] != '\0'; n++);   /* Context name */
1894     n++;
1895     n++;                                /* Context port number */
1896     for (; buf[n] != '\0'; n++);        /* Timestamp */
1897     n++;
1898     for (; buf[n] != '\0'; n++);        /* Protocol name */
1899     n++;
1900     for (; buf[n] != '\0'; n++);        /* Variant number (as string) */
1901     n++;
1902     for (; buf[n] != '\0'; n++);        /* Outhdr (as string) */
1903     n++;
1904     n += 2;                             /* Direction & encap */
1905
1906     return n;
1907 }
1908
1909 /*
1910  * We support up to 2 chopping regions in a single pass: one specified by the
1911  * positive chop length, and one by the negative chop length.
1912  */
1913 static void
1914 handle_chopping(chop_t chop, wtap_packet_header *out_phdr,
1915                 const wtap_packet_header *in_phdr, guint8 **buf,
1916                 gboolean adjlen)
1917 {
1918     /* If we're not chopping anything from one side, then the offset for that
1919      * side is meaningless. */
1920     if (chop.len_begin == 0)
1921         chop.off_begin_pos = chop.off_begin_neg = 0;
1922     if (chop.len_end == 0)
1923         chop.off_end_pos = chop.off_end_neg = 0;
1924
1925     if (chop.off_begin_neg < 0) {
1926         chop.off_begin_pos += in_phdr->caplen + chop.off_begin_neg;
1927         chop.off_begin_neg = 0;
1928     }
1929     if (chop.off_end_pos > 0) {
1930         chop.off_end_neg += chop.off_end_pos - in_phdr->caplen;
1931         chop.off_end_pos = 0;
1932     }
1933
1934     /* If we've crossed chopping regions, swap them */
1935     if (chop.len_begin && chop.len_end) {
1936         if (chop.off_begin_pos > ((int)in_phdr->caplen + chop.off_end_neg)) {
1937             int tmp_len, tmp_off;
1938
1939             tmp_off = in_phdr->caplen + chop.off_end_neg + chop.len_end;
1940             tmp_len = -chop.len_end;
1941
1942             chop.off_end_neg = chop.len_begin + chop.off_begin_pos - in_phdr->caplen;
1943             chop.len_end = -chop.len_begin;
1944
1945             chop.len_begin = tmp_len;
1946             chop.off_begin_pos = tmp_off;
1947         }
1948     }
1949
1950     /* Make sure we don't chop off more than we have available */
1951     if (in_phdr->caplen < (guint32)(chop.off_begin_pos - chop.off_end_neg)) {
1952         chop.len_begin = 0;
1953         chop.len_end = 0;
1954     }
1955     if ((guint32)(chop.len_begin - chop.len_end) >
1956         (in_phdr->caplen - (guint32)(chop.off_begin_pos - chop.off_end_neg))) {
1957         chop.len_begin = in_phdr->caplen - (chop.off_begin_pos - chop.off_end_neg);
1958         chop.len_end = 0;
1959     }
1960
1961     /* Handle chopping from the beginning.  Note that if a beginning offset
1962      * was specified, we need to keep that piece */
1963     if (chop.len_begin > 0) {
1964         *out_phdr = *in_phdr;
1965
1966         if (chop.off_begin_pos > 0) {
1967             memmove(*buf + chop.off_begin_pos,
1968                     *buf + chop.off_begin_pos + chop.len_begin,
1969                     out_phdr->caplen - chop.len_begin);
1970         } else {
1971             *buf += chop.len_begin;
1972         }
1973         out_phdr->caplen -= chop.len_begin;
1974
1975         if (adjlen) {
1976             if (in_phdr->len > (guint32)chop.len_begin)
1977                 out_phdr->len -= chop.len_begin;
1978             else
1979                 out_phdr->len = 0;
1980         }
1981         in_phdr = out_phdr;
1982     }
1983
1984     /* Handle chopping from the end.  Note that if an ending offset was
1985      * specified, we need to keep that piece */
1986     if (chop.len_end < 0) {
1987         *out_phdr = *in_phdr;
1988
1989         if (chop.off_end_neg < 0) {
1990             memmove(*buf + (gint)out_phdr->caplen + (chop.len_end + chop.off_end_neg),
1991                     *buf + (gint)out_phdr->caplen + chop.off_end_neg,
1992                     -chop.off_end_neg);
1993         }
1994         out_phdr->caplen += chop.len_end;
1995
1996         if (adjlen) {
1997             if (((signed int) in_phdr->len + chop.len_end) > 0)
1998                 out_phdr->len += chop.len_end;
1999             else
2000                 out_phdr->len = 0;
2001         }
2002         /*in_phdr = out_phdr;*/
2003     }
2004 }
2005
2006 /*
2007  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
2008  *
2009  * Local variables:
2010  * c-basic-offset: 4
2011  * tab-width: 8
2012  * indent-tabs-mode: nil
2013  * End:
2014  *
2015  * vi: set shiftwidth=4 tabstop=8 expandtab:
2016  * :indentSize=4:tabSize=8:noTabs=true:
2017  */