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