Add -A <start time> and -B <stop time> options to editcap
[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 <glib.h>
18
19 #ifdef HAVE_UNISTD_H
20 #include <unistd.h>
21 #endif
22
23 #include <time.h>
24 #ifdef HAVE_SYS_TIME_H
25 #include <sys/time.h>
26 #endif
27
28 #include <string.h>
29 #include "wtap.h"
30
31 #ifdef NEED_GETOPT_H
32 #include "getopt.h"
33 #endif
34
35 #ifdef _WIN32
36 #include <process.h>    /* getpid */
37 #endif
38
39 #ifdef NEED_STRPTIME_H
40 # include "strptime.h"
41 #endif
42
43 #include "svnversion.h"
44
45 /*
46  * Some globals so we can pass things to various routines
47  */
48
49 struct select_item {
50
51   int inclusive;
52   int first, second;
53
54 };
55
56 #define ONE_MILLION 1000000
57
58 /* Weights of different errors we can introduce */
59 /* We should probably make these command-line arguments */
60 /* XXX - Should we add a bit-level error? */
61 #define ERR_WT_BIT   5  /* Flip a random bit */
62 #define ERR_WT_BYTE  5  /* Substitute a random byte */
63 #define ERR_WT_ALNUM 5  /* Substitute a random character in [A-Za-z0-9] */
64 #define ERR_WT_FMT   2  /* Substitute "%s" */
65 #define ERR_WT_AA    1  /* Fill the remainder of the buffer with 0xAA */
66 #define ERR_WT_TOTAL (ERR_WT_BIT + ERR_WT_BYTE + ERR_WT_ALNUM + ERR_WT_FMT + ERR_WT_AA)
67
68 #define ALNUM_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
69 #define ALNUM_LEN (sizeof(ALNUM_CHARS) - 1)
70
71
72 struct time_adjustment {
73   struct timeval tv;
74   int is_negative;
75 };
76
77 static struct select_item selectfrm[100];
78 static int max_selected = -1;
79 static int keep_em = 0;
80 static int out_file_type = WTAP_FILE_PCAP;   /* default to "libpcap"   */
81 static int out_frame_type = -2;              /* Leave frame type alone */
82 static int verbose = 0;                      /* Not so verbose         */
83 static struct time_adjustment time_adj = {{0, 0}, 0}; /* no adjustment */
84 static double err_prob = 0.0;
85 static guint32 starttime = 0;
86 static guint32 stoptime = 4294967295;
87 static gboolean check_startstop = FALSE;
88
89 /* Add a selection item, a simple parser for now */
90
91 static void add_selection(char *sel)
92 {
93   char *locn;
94   char *next;
95
96   if (max_selected == (sizeof(selectfrm)/sizeof(struct select_item)) - 1)
97     return;
98
99   printf("Add_Selected: %s\n", sel);
100
101   if ((locn = strchr(sel, '-')) == NULL) { /* No dash, so a single number? */
102
103     printf("Not inclusive ...");
104
105     max_selected++;
106     selectfrm[max_selected].inclusive = 0;
107     selectfrm[max_selected].first = atoi(sel);
108
109     printf(" %i\n", selectfrm[max_selected].first);
110
111   }
112   else {
113
114     printf("Inclusive ...");
115
116     next = locn + 1;
117     max_selected++;
118     selectfrm[max_selected].inclusive = 1;
119     selectfrm[max_selected].first = atoi(sel);
120     selectfrm[max_selected].second = atoi(next);
121
122     printf(" %i, %i\n", selectfrm[max_selected].first, selectfrm[max_selected].second);
123
124   }
125
126
127 }
128
129 /* Was the packet selected? */
130
131 static int selected(int recno)
132 {
133   int i = 0;
134
135   for (i = 0; i<= max_selected; i++) {
136
137     if (selectfrm[i].inclusive) {
138       if (selectfrm[i].first <= recno && selectfrm[i].second >= recno)
139         return 1;
140     }
141     else {
142       if (recno == selectfrm[i].first)
143         return 1;
144     }
145   }
146
147   return 0;
148
149 }
150
151 /* is the packet in the selected timeframe */
152 static gboolean check_timestamp(wtap *wth) {
153         struct wtap_pkthdr* pkthdr = wtap_phdr(wth);
154         return ((guint32) pkthdr->ts.secs >= starttime ) && ( (guint32) pkthdr->ts.secs <= stoptime );
155 }
156
157 static void
158 set_time_adjustment(char *optarg)
159 {
160   char *frac, *end;
161   long val;
162   int frac_digits;
163
164   if (!optarg)
165     return;
166
167   /* skip leading whitespace */
168   while (*optarg == ' ' || *optarg == '\t') {
169       optarg++;
170   }
171
172   /* check for a negative adjustment */
173   if (*optarg == '-') {
174       time_adj.is_negative = 1;
175       optarg++;
176   }
177
178   /* collect whole number of seconds, if any */
179   if (*optarg == '.') {         /* only fractional (i.e., .5 is ok) */
180       val  = 0;
181       frac = optarg;
182   } else {
183       val = strtol(optarg, &frac, 10);
184       if (frac == NULL || frac == optarg || val == LONG_MIN || val == LONG_MAX) {
185           fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
186                   optarg);
187           exit(1);
188       }
189       if (val < 0) {            /* implies '--' since we caught '-' above  */
190           fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
191                   optarg);
192           exit(1);
193       }
194   }
195   time_adj.tv.tv_sec = val;
196
197   /* now collect the partial seconds, if any */
198   if (*frac != '\0') {             /* chars left, so get fractional part */
199     val = strtol(&(frac[1]), &end, 10);
200     if (*frac != '.' || end == NULL || end == frac
201         || val < 0 || val > ONE_MILLION || val == LONG_MIN || val == LONG_MAX) {
202       fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
203               optarg);
204       exit(1);
205     }
206   }
207   else {
208     return;                     /* no fractional digits */
209   }
210
211   /* adjust fractional portion from fractional to numerator
212    * e.g., in "1.5" from 5 to 500000 since .5*10^6 = 500000 */
213   if (frac && end) {            /* both are valid */
214     frac_digits = end - frac - 1;   /* fractional digit count (remember '.') */
215     while(frac_digits < 6) {    /* this is frac of 10^6 */
216       val *= 10;
217       frac_digits++;
218     }
219   }
220   time_adj.tv.tv_usec = val;
221 }
222
223 static void usage(void)
224 {
225   fprintf(stderr, "Editcap %s"
226 #ifdef SVNVERSION
227           " (" SVNVERSION ")"
228 #endif
229           "\n", VERSION);
230   fprintf(stderr, "Edit and/or translate the format of capture files.\n");
231   fprintf(stderr, "See http://www.ethereal.com for more information.\n");
232   fprintf(stderr, "\n");
233   fprintf(stderr, "Usage: editcap [options] ... <infile> <outfile> [ <packet#>[-<packet#>] ... ]\n");
234   fprintf(stderr, "\n");
235   fprintf(stderr, "A single packet or a range of packets can be selected.\n");
236   fprintf(stderr, "\n");
237   fprintf(stderr, "Packets:\n");
238   fprintf(stderr, "  -C <choplen>           chop each packet at the end by <choplen> bytes\n");
239   fprintf(stderr, "  -E <error probability> set the probability (between 0.0 and 1.0 incl.)\n");
240   fprintf(stderr, "                         that a particular packet byte will be randomly changed\n");
241   fprintf(stderr, "  -r                     keep the selected packets, default is to delete them\n");
242   fprintf(stderr, "  -s <snaplen>           truncate packets to max. <snaplen> bytes of data\n");
243   fprintf(stderr, "  -t <time adjustment>   adjust the timestamp of selected packets,\n");
244   fprintf(stderr, "                         <time adjustment> is in relative seconds (e.g. -0.5)\n");
245   fprintf(stderr, "  -A <start time>        don't output packets whose timestamp is before the\n");
246   fprintf(stderr, "                         given time (format as YYYY-MM-DD hh-mm-ss)\n");
247   fprintf(stderr, "  -B <stop time>         don't output packets whose timestamp is after the\n");
248   fprintf(stderr, "                         given time (format as YYYY-MM-DD hh-mm-ss)\n");
249   fprintf(stderr, "\n");
250   fprintf(stderr, "Output File(s):\n");
251   fprintf(stderr, "  -c <packets per file>  split the packet output to different files,\n");
252   fprintf(stderr, "                         with a maximum of <packets per file> each\n");
253   fprintf(stderr, "  -F <capture type>      set the output file type, default is libpcap\n");
254   fprintf(stderr, "                         an empty \"-F\" option will list the file types\n");
255   fprintf(stderr, "  -T <encap type>        set the output file encapsulation type,\n");
256   fprintf(stderr, "                         default is the same as the input file\n");
257   fprintf(stderr, "                         an empty \"-T\" option will list the encapsulation types\n");
258   fprintf(stderr, "\n");
259   fprintf(stderr, "Miscellaneous:\n");
260   fprintf(stderr, "  -h                     display this help and exit\n");
261   fprintf(stderr, "  -v                     verbose output\n");
262   fprintf(stderr, "\n");
263 }
264
265 static void list_capture_types(void) {
266     int i;
267
268     fprintf(stderr, "editcap: The available capture file types for \"F\":\n");
269     for (i = 0; i < WTAP_NUM_FILE_TYPES; i++) {
270       if (wtap_dump_can_open(i))
271         fprintf(stderr, "    %s - %s\n",
272           wtap_file_type_short_string(i), wtap_file_type_string(i));
273     }
274 }
275
276 static void list_encap_types(void) {
277     int i;
278     const char *string;
279
280     fprintf(stderr, "editcap: The available encapsulation types for \"T\":\n");
281     for (i = 0; i < WTAP_NUM_ENCAP_TYPES; i++) {
282         string = wtap_encap_short_string(i);
283         if (string != NULL)
284           fprintf(stderr, "    %s - %s\n",
285             string, wtap_encap_string(i));
286     }
287 }
288
289 int main(int argc, char *argv[])
290
291 {
292   wtap *wth;
293   int i, j, err;
294   gchar *err_info;
295   extern char *optarg;
296   extern int optind;
297   int opt;
298   char *p;
299   unsigned int snaplen = 0;             /* No limit               */
300   unsigned int choplen = 0;             /* No chop                */
301   wtap_dumper *pdh;
302   int count = 1;
303   long data_offset;
304   struct wtap_pkthdr snap_phdr;
305   const struct wtap_pkthdr *phdr;
306   int err_type;
307   guint8 *buf;
308   int split_packet_count = 0;
309   int written_count = 0;
310   char *filename;
311
312   /* Process the options first */
313
314   while ((opt = getopt(argc, argv, "A:B:c:C:E:F:hrs:t:T:v")) !=-1) {
315
316     switch (opt) {
317
318     case 'E':
319       err_prob = strtod(optarg, &p);
320       if (p == optarg || err_prob < 0.0 || err_prob > 1.0) {
321         fprintf(stderr, "editcap: probability \"%s\" must be between 0.0 and 1.0\n",
322             optarg);
323         exit(1);
324       }
325       srand(time(NULL) + getpid());
326       break;
327
328     case 'F':
329       out_file_type = wtap_short_string_to_file_type(optarg);
330       if (out_file_type < 0) {
331         fprintf(stderr, "editcap: \"%s\" isn't a valid capture file type\n\n",
332             optarg);
333         list_capture_types();
334         exit(1);
335       }
336       break;
337
338     case 'c':
339       split_packet_count = strtol(optarg, &p, 10);
340       if (p == optarg || *p != '\0') {
341         fprintf(stderr, "editcap: \"%s\" isn't a valid packet count\n",
342             optarg);
343         exit(1);
344       }
345       if (split_packet_count <= 0) {
346         fprintf(stderr, "editcap: \"%d\" packet count must be larger than zero\n",
347                 split_packet_count);
348         exit(1);
349       }
350       break;
351
352     case 'C':
353       choplen = strtol(optarg, &p, 10);
354       if (p == optarg || *p != '\0') {
355         fprintf(stderr, "editcap: \"%s\" isn't a valid chop length\n",
356             optarg);
357         exit(1);
358       }
359       break;
360
361     case '?':              /* Bad options if GNU getopt */
362       switch(optopt) {
363       case'F':
364         list_capture_types();
365         break;
366       case'T':
367         list_encap_types();
368         break;
369       default:
370         usage();
371       }
372       exit(1);
373       break;
374
375     case 'h':
376       usage();
377       exit(1);
378       break;
379
380     case 'r':
381       keep_em = !keep_em;  /* Just invert */
382       break;
383
384     case 's':
385       snaplen = strtol(optarg, &p, 10);
386       if (p == optarg || *p != '\0') {
387         fprintf(stderr, "editcap: \"%s\" isn't a valid snapshot length\n",
388             optarg);
389         exit(1);
390       }
391       break;
392
393     case 't':
394       set_time_adjustment(optarg);
395       break;
396
397     case 'T':
398       out_frame_type = wtap_short_string_to_encap(optarg);
399       if (out_frame_type < 0) {
400         fprintf(stderr, "editcap: \"%s\" isn't a valid encapsulation type\n\n",
401             optarg);
402         list_encap_types();
403         exit(1);
404       }
405       break;
406
407     case 'v':
408       verbose = !verbose;  /* Just invert */
409       break;
410
411         case 'A':
412         {
413                 struct tm timecode;
414                 
415                 if(!strptime(optarg,"%F %T",&timecode)) {
416                         fprintf(stderr, "editcap: \"%s\" isn't a valid time format\n\n",
417                                         optarg);
418                         exit(1);
419                 }
420                 
421                 starttime = mktime(&timecode);
422                 check_startstop = TRUE;
423                 break;
424         }       
425         case 'B':
426         {
427                 struct tm timecode;
428
429                 if(!strptime(optarg,"%F %T",&timecode)) {
430                         fprintf(stderr, "editcap: \"%s\" isn't a valid time format\n\n",
431                                         optarg);
432                         exit(1);
433                 }
434                 check_startstop = TRUE;
435                 stoptime = mktime(&timecode);
436                 break;
437         }
438     }
439
440   }
441   
442 #ifdef DEBUG
443   printf("Optind = %i, argc = %i\n", optind, argc);
444 #endif
445
446   if ((argc - optind) < 1) {
447
448     usage();
449     exit(1);
450
451   }
452
453   if (starttime > stoptime) {
454           fprintf(stderr, "editcap: start time is after the stop time\n");
455           exit(1);
456   }
457   
458   wth = wtap_open_offline(argv[optind], &err, &err_info, FALSE);
459
460   if (!wth) {
461     fprintf(stderr, "editcap: Can't open %s: %s\n", argv[optind],
462         wtap_strerror(err));
463     switch (err) {
464
465     case WTAP_ERR_UNSUPPORTED:
466     case WTAP_ERR_UNSUPPORTED_ENCAP:
467     case WTAP_ERR_BAD_RECORD:
468       fprintf(stderr, "(%s)\n", err_info);
469       g_free(err_info);
470       break;
471     }
472     exit(1);
473
474   }
475
476   if (verbose) {
477
478     fprintf(stderr, "File %s is a %s capture file.\n", argv[optind],
479             wtap_file_type_string(wtap_file_type(wth)));
480
481   }
482
483   /*
484    * Now, process the rest, if any ... we only write if there is an extra
485    * argument or so ...
486    */
487
488   if ((argc - optind) >= 2) {
489
490     if (out_frame_type == -2)
491       out_frame_type = wtap_file_encap(wth);
492
493     if (split_packet_count > 0) {
494       filename = (char *) malloc(strlen(argv[optind+1]) + 20);
495       if (!filename) {
496         exit(5);
497       }
498       sprintf(filename, "%s-%05d", argv[optind+1], 0);
499     } else {
500       filename = argv[optind+1];
501     }
502   
503     pdh = wtap_dump_open(filename, out_file_type,
504                          out_frame_type, wtap_snapshot_length(wth), FALSE /* compressed */, &err);
505     if (pdh == NULL) {
506
507       fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
508               wtap_strerror(err));
509       exit(1);
510
511     }
512
513     for (i = optind + 2; i < argc; i++)
514       add_selection(argv[i]);
515
516     while (wtap_read(wth, &err, &err_info, &data_offset)) {
517
518       if (split_packet_count > 0 && (written_count % split_packet_count == 0)) {
519         if (!wtap_dump_close(pdh, &err)) {
520
521           fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
522                   wtap_strerror(err));
523           exit(1);
524         }
525
526         sprintf(filename, "%s-%05d",argv[optind+1], count / split_packet_count);
527
528         if (verbose) {
529           fprintf(stderr, "Continuing writing in file %s\n", filename);
530         }
531
532         pdh = wtap_dump_open(filename, out_file_type,
533                              out_frame_type, wtap_snapshot_length(wth), FALSE /* compressed */, &err);
534         if (pdh == NULL) {
535           
536           fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
537                   wtap_strerror(err));
538           exit(1);
539           
540         }
541       }
542
543       if ( ((check_startstop && check_timestamp(wth)) || (!check_startstop && !check_timestamp(wth))) && ((!selected(count) && !keep_em) ||
544           (selected(count) && keep_em)) ) {
545
546         if (verbose)
547           printf("Packet: %u\n", count);
548
549         /* We simply write it, perhaps after truncating it; we could do other
550            things, like modify it. */
551
552         phdr = wtap_phdr(wth);
553
554         if (choplen != 0 && phdr->caplen > choplen) {
555           snap_phdr = *phdr;
556           snap_phdr.caplen -= choplen;
557           phdr = &snap_phdr;
558         }
559
560         if (snaplen != 0 && phdr->caplen > snaplen) {
561           snap_phdr = *phdr;
562           snap_phdr.caplen = snaplen;
563           phdr = &snap_phdr;
564         }
565
566         /* assume that if the frame's tv_sec is 0, then
567          * the timestamp isn't supported */
568         if (phdr->ts.secs > 0 && time_adj.tv.tv_sec != 0) {
569           snap_phdr = *phdr;
570           if (time_adj.is_negative)
571             snap_phdr.ts.secs -= time_adj.tv.tv_sec;
572           else
573             snap_phdr.ts.secs += time_adj.tv.tv_sec;
574           phdr = &snap_phdr;
575         }
576
577         /* assume that if the frame's tv_sec is 0, then
578          * the timestamp isn't supported */
579         if (phdr->ts.secs > 0 && time_adj.tv.tv_usec != 0) {
580           snap_phdr = *phdr;
581           if (time_adj.is_negative) { /* subtract */
582             if (snap_phdr.ts.nsecs/1000 < time_adj.tv.tv_usec) { /* borrow */
583               snap_phdr.ts.secs--;
584               snap_phdr.ts.nsecs += ONE_MILLION * 1000;
585             }
586             snap_phdr.ts.nsecs -= time_adj.tv.tv_usec * 1000;
587           } else {                  /* add */
588             if (snap_phdr.ts.nsecs + time_adj.tv.tv_usec * 1000 > ONE_MILLION * 1000) {
589               /* carry */
590               snap_phdr.ts.secs++;
591               snap_phdr.ts.nsecs += (time_adj.tv.tv_usec - ONE_MILLION) * 1000;
592             } else {
593               snap_phdr.ts.nsecs += time_adj.tv.tv_usec * 1000;
594             }
595           }
596           phdr = &snap_phdr;
597         }
598
599         if (err_prob > 0.0) {
600           buf = wtap_buf_ptr(wth);
601           for (i = 0; i < (int) phdr->caplen; i++) {
602             if (rand() <= err_prob * RAND_MAX) {
603               err_type = rand() / (RAND_MAX / ERR_WT_TOTAL + 1);
604
605               if (err_type < ERR_WT_BIT) {
606                 buf[i] ^= 1 << (rand() / (RAND_MAX / 8 + 1));
607                 err_type = ERR_WT_TOTAL;
608               } else {
609                 err_type -= ERR_WT_BYTE;
610               }
611
612               if (err_type < ERR_WT_BYTE) {
613                 buf[i] = rand() / (RAND_MAX / 255 + 1);
614                 err_type = ERR_WT_TOTAL;
615               } else {
616                 err_type -= ERR_WT_BYTE;
617               }
618
619               if (err_type < ERR_WT_ALNUM) {
620                 buf[i] = ALNUM_CHARS[rand() / (RAND_MAX / ALNUM_LEN + 1)];
621                 err_type = ERR_WT_TOTAL;
622               } else {
623                 err_type -= ERR_WT_ALNUM;
624               }
625
626               if (err_type < ERR_WT_FMT) {
627                 if ((unsigned int)i < phdr->caplen - 2)
628                   strcpy((char*) &buf[i],  "%s");
629                 err_type = ERR_WT_TOTAL;
630               } else {
631                 err_type -= ERR_WT_FMT;
632               }
633
634               if (err_type < ERR_WT_AA) {
635                 for (j = i; j < (int) phdr->caplen; j++) {
636                   buf[j] = 0xAA;
637                 }
638                 i = phdr->caplen;
639               }
640             }
641           }
642         }
643
644         if (!wtap_dump(pdh, phdr, wtap_pseudoheader(wth), wtap_buf_ptr(wth),
645                        &err)) {
646
647           fprintf(stderr, "editcap: Error writing to %s: %s\n",
648                   filename, wtap_strerror(err));
649           exit(1);
650
651         }
652
653         written_count++;
654
655       }
656
657       count++;
658
659     }
660
661     if (err != 0) {
662       /* Print a message noting that the read failed somewhere along the line. */
663       fprintf(stderr,
664               "editcap: An error occurred while reading \"%s\": %s.\n",
665               argv[optind], wtap_strerror(err));
666       switch (err) {
667
668       case WTAP_ERR_UNSUPPORTED:
669       case WTAP_ERR_UNSUPPORTED_ENCAP:
670       case WTAP_ERR_BAD_RECORD:
671         fprintf(stderr, "(%s)\n", err_info);
672         break;
673       }
674     }
675
676     if (!wtap_dump_close(pdh, &err)) {
677
678       fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
679               wtap_strerror(err));
680       exit(1);
681
682     }
683   }
684
685   return 0;
686 }
687