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