72099cf46774324c5ba5cf9392f18ef40e688582
[metze/wireshark/wip.git] / reordercap.c
1 /* Reorder the frames from an input dump file, and write to output dump file.
2  * Martin Mathieson and Jakub Jawadzki
3  *
4  * Wireshark - Network traffic analyzer
5  * By Gerald Combs <gerald@wireshark.org>
6  * Copyright 1998 Gerald Combs
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  *
22  */
23
24 #include <config.h>
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <glib.h>
30
31 #ifdef HAVE_UNISTD_H
32 #include <unistd.h>
33 #endif
34
35 #ifdef HAVE_GETOPT_H
36 #include <getopt.h>
37 #endif
38
39 #ifdef HAVE_LIBZ
40 #include <zlib.h>      /* to get the libz version number */
41 #endif
42
43 #include "wtap.h"
44
45 #ifndef HAVE_GETOPT
46 #include "wsutil/wsgetopt.h"
47 #endif
48
49 #include <wsutil/file_util.h>
50 #include <wsutil/crash_info.h>
51 #include <wsutil/copyright_info.h>
52 #include <wsutil/os_version_info.h>
53 #include <wsutil/ws_version_info.h>
54
55 #include "version_info.h"
56
57 static void
58 show_version(GString *comp_info_str, GString *runtime_info_str)
59 {
60     printf("Reordercap (Wireshark) %s\n"
61            "\n"
62            "%s"
63            "\n"
64            "%s"
65            "\n"
66            "%s",
67            get_ws_vcs_version_info(), get_copyright_info(),
68            comp_info_str->str, runtime_info_str->str);
69 }
70
71 /* Show command-line usage */
72 static void
73 print_usage(FILE *output)
74 {
75     fprintf(output, "\n");
76     fprintf(output, "Usage: reordercap [options] <infile> <outfile>\n");
77     fprintf(output, "\n");
78     fprintf(output, "Options:\n");
79     fprintf(output, "  -n        don't write to output file if the input file is ordered.\n");
80     fprintf(output, "  -h        display this help and exit.\n");
81 }
82
83 /* Remember where this frame was in the file */
84 typedef struct FrameRecord_t {
85     gint64       offset;
86     guint        num;
87
88     nstime_t     time;
89 } FrameRecord_t;
90
91
92 /**************************************************/
93 /* Debugging only                                 */
94
95 /* Enable this symbol to see debug output */
96 /* #define REORDER_DEBUG */
97
98 #ifdef REORDER_DEBUG
99 #define DEBUG_PRINT printf
100 #else
101 #define DEBUG_PRINT(...)
102 #endif
103 /**************************************************/
104
105
106 static void
107 frame_write(FrameRecord_t *frame, wtap *wth, wtap_dumper *pdh,
108             struct wtap_pkthdr *phdr, Buffer *buf, const char *infile)
109 {
110     int    err;
111     gchar  *err_info;
112
113     DEBUG_PRINT("\nDumping frame (offset=%" G_GINT64_MODIFIER "u)\n",
114                 frame->offset);
115
116
117     /* Re-read the frame from the stored location */
118     if (!wtap_seek_read(wth, frame->offset, phdr, buf, &err, &err_info)) {
119         if (err != 0) {
120             /* Print a message noting that the read failed somewhere along the line. */
121             fprintf(stderr,
122                     "reordercap: An error occurred while re-reading \"%s\": %s.\n",
123                     infile, wtap_strerror(err));
124             switch (err) {
125
126             case WTAP_ERR_UNSUPPORTED:
127             case WTAP_ERR_UNWRITABLE_ENCAP:
128             case WTAP_ERR_BAD_FILE:
129             case WTAP_ERR_UNWRITABLE_REC_DATA:
130                 fprintf(stderr, "(%s)\n", err_info);
131                 g_free(err_info);
132                 break;
133             }
134             exit(1);
135         }
136     }
137
138     /* Copy, and set length and timestamp from item. */
139     /* TODO: remove when wtap_seek_read() fills in phdr,
140        including time stamps, for all file types  */
141     phdr->ts = frame->time;
142
143     /* Dump frame to outfile */
144     if (!wtap_dump(pdh, phdr, ws_buffer_start_ptr(buf), &err, &err_info)) {
145         switch (err) {
146
147         case WTAP_ERR_UNWRITABLE_REC_DATA:
148             fprintf(stderr, "reordercap: Error (%s) writing frame to outfile (%s)\n",
149                     wtap_strerror(err), err_info);
150             g_free(err_info);
151             break;
152
153         default:
154             fprintf(stderr, "reordercap: Error (%s) writing frame to outfile\n",
155                     wtap_strerror(err));
156             break;
157         }
158         exit(1);
159     }
160 }
161
162 /* Comparing timestamps between 2 frames.
163    negative if (t1 < t2)
164    zero     if (t1 == t2)
165    positive if (t1 > t2)
166 */
167 static int
168 frames_compare(gconstpointer a, gconstpointer b)
169 {
170     const FrameRecord_t *frame1 = *(const FrameRecord_t *const *) a;
171     const FrameRecord_t *frame2 = *(const FrameRecord_t *const *) b;
172
173     const nstime_t *time1 = &frame1->time;
174     const nstime_t *time2 = &frame2->time;
175
176     return nstime_cmp(time1, time2);
177 }
178
179 static void
180 get_reordercap_compiled_info(GString *str)
181 {
182     /* LIBZ */
183     g_string_append(str, ", ");
184 #ifdef HAVE_LIBZ
185     g_string_append(str, "with libz ");
186 #ifdef ZLIB_VERSION
187     g_string_append(str, ZLIB_VERSION);
188 #else /* ZLIB_VERSION */
189     g_string_append(str, "(version unknown)");
190 #endif /* ZLIB_VERSION */
191 #else /* HAVE_LIBZ */
192     g_string_append(str, "without libz");
193 #endif /* HAVE_LIBZ */
194 }
195
196 static void
197 get_reordercap_runtime_info(GString *str)
198 {
199     /* zlib */
200 #if defined(HAVE_LIBZ) && !defined(_WIN32)
201     g_string_append_printf(str, ", with libz %s", zlibVersion());
202 #endif
203 }
204
205 /********************************************************************/
206 /* Main function.                                                   */
207 /********************************************************************/
208 int
209 main(int argc, char *argv[])
210 {
211     GString *comp_info_str;
212     GString *runtime_info_str;
213     wtap *wth = NULL;
214     wtap_dumper *pdh = NULL;
215     struct wtap_pkthdr dump_phdr;
216     Buffer buf;
217     int err;
218     gchar *err_info;
219     gint64 data_offset;
220     const struct wtap_pkthdr *phdr;
221     guint wrong_order_count = 0;
222     gboolean write_output_regardless = TRUE;
223     guint i;
224     wtapng_section_t            *shb_hdr;
225     wtapng_iface_descriptions_t *idb_inf;
226
227     GPtrArray *frames;
228     FrameRecord_t *prevFrame = NULL;
229
230     int opt;
231     static const struct option long_options[] = {
232         {(char *)"help", no_argument, NULL, 'h'},
233         {(char *)"version", no_argument, NULL, 'v'},
234         {0, 0, 0, 0 }
235     };
236     int file_count;
237     char *infile;
238     char *outfile;
239
240     /* Assemble the compile-time version information string */
241     comp_info_str = g_string_new("Compiled ");
242     get_compiled_version_info(comp_info_str, NULL, get_reordercap_compiled_info);
243
244     /* Assemble the run-time version information string */
245     runtime_info_str = g_string_new("Running ");
246     get_runtime_version_info(runtime_info_str, get_reordercap_runtime_info);
247
248     /* Add it to the information to be reported on a crash. */
249     ws_add_crash_info("Reordercap (Wireshark) %s\n"
250          "\n"
251          "%s"
252          "\n"
253          "%s",
254       get_ws_vcs_version_info(), comp_info_str->str, runtime_info_str->str);
255
256     /* Process the options first */
257     while ((opt = getopt_long(argc, argv, "hnv", long_options, NULL)) != -1) {
258         switch (opt) {
259             case 'n':
260                 write_output_regardless = FALSE;
261                 break;
262             case 'h':
263                 printf("Reordercap (Wireshark) %s\n"
264                        "Reorder timestamps of input file frames into output file.\n"
265                        "See http://www.wireshark.org for more information.\n",
266                        get_ws_vcs_version_info());
267                 print_usage(stdout);
268                 exit(0);
269             case 'v':
270                 show_version(comp_info_str, runtime_info_str);
271                 g_string_free(comp_info_str, TRUE);
272                 g_string_free(runtime_info_str, TRUE);
273                 exit(0);
274             case '?':
275                 print_usage(stderr);
276                 exit(1);
277         }
278     }
279
280     /* Remaining args are file names */
281     file_count = argc - optind;
282     if (file_count == 2) {
283         infile  = argv[optind];
284         outfile = argv[optind+1];
285     }
286     else {
287         print_usage(stderr);
288         exit(1);
289     }
290
291     /* Open infile */
292     /* TODO: if reordercap is ever changed to give the user a choice of which
293        open_routine reader to use, then the following needs to change. */
294     wth = wtap_open_offline(infile, WTAP_TYPE_AUTO, &err, &err_info, TRUE);
295     if (wth == NULL) {
296         fprintf(stderr, "reordercap: Can't open %s: %s\n", infile,
297                 wtap_strerror(err));
298         switch (err) {
299
300         case WTAP_ERR_UNSUPPORTED:
301         case WTAP_ERR_UNWRITABLE_ENCAP:
302         case WTAP_ERR_BAD_FILE:
303         case WTAP_ERR_UNWRITABLE_REC_DATA:
304             fprintf(stderr, "(%s)\n", err_info);
305             g_free(err_info);
306             break;
307         }
308         exit(1);
309     }
310     DEBUG_PRINT("file_type_subtype is %u\n", wtap_file_type_subtype(wth));
311
312     shb_hdr = wtap_file_get_shb_info(wth);
313     idb_inf = wtap_file_get_idb_info(wth);
314
315     /* Open outfile (same filetype/encap as input file) */
316     pdh = wtap_dump_open_ng(outfile, wtap_file_type_subtype(wth), wtap_file_encap(wth),
317                             65535, FALSE, shb_hdr, idb_inf, &err);
318     g_free(idb_inf);
319     if (pdh == NULL) {
320         fprintf(stderr, "reordercap: Failed to open output file: (%s) - error %s\n",
321                 outfile, wtap_strerror(err));
322         g_free(shb_hdr);
323         exit(1);
324     }
325
326     /* Allocate the array of frame pointers. */
327     frames = g_ptr_array_new();
328
329     /* Read each frame from infile */
330     while (wtap_read(wth, &err, &err_info, &data_offset)) {
331         FrameRecord_t *newFrameRecord;
332
333         phdr = wtap_phdr(wth);
334
335         newFrameRecord = g_slice_new(FrameRecord_t);
336         newFrameRecord->num = frames->len + 1;
337         newFrameRecord->offset = data_offset;
338         if (phdr->presence_flags & WTAP_HAS_TS) {
339             newFrameRecord->time = phdr->ts;
340         } else {
341             nstime_set_unset(&newFrameRecord->time);
342         }
343
344         if (prevFrame && frames_compare(&newFrameRecord, &prevFrame) < 0) {
345            wrong_order_count++;
346         }
347
348         g_ptr_array_add(frames, newFrameRecord);
349         prevFrame = newFrameRecord;
350     }
351     if (err != 0) {
352       /* Print a message noting that the read failed somewhere along the line. */
353       fprintf(stderr,
354               "reordercap: An error occurred while reading \"%s\": %s.\n",
355               infile, wtap_strerror(err));
356       switch (err) {
357
358       case WTAP_ERR_UNSUPPORTED:
359       case WTAP_ERR_UNWRITABLE_ENCAP:
360       case WTAP_ERR_BAD_FILE:
361       case WTAP_ERR_UNWRITABLE_REC_DATA:
362           fprintf(stderr, "(%s)\n", err_info);
363           g_free(err_info);
364           break;
365       }
366     }
367
368     printf("%u frames, %u out of order\n", frames->len, wrong_order_count);
369
370     /* Sort the frames */
371     if (wrong_order_count > 0) {
372         g_ptr_array_sort(frames, frames_compare);
373     }
374
375     /* Write out each sorted frame in turn */
376     wtap_phdr_init(&dump_phdr);
377     ws_buffer_init(&buf, 1500);
378     for (i = 0; i < frames->len; i++) {
379         FrameRecord_t *frame = (FrameRecord_t *)frames->pdata[i];
380
381         /* Avoid writing if already sorted and configured to */
382         if (write_output_regardless || (wrong_order_count > 0)) {
383             frame_write(frame, wth, pdh, &dump_phdr, &buf, infile);
384         }
385         g_slice_free(FrameRecord_t, frame);
386     }
387     wtap_phdr_cleanup(&dump_phdr);
388     ws_buffer_free(&buf);
389
390     if (!write_output_regardless && (wrong_order_count == 0)) {
391         printf("Not writing output file because input file is already in order!\n");
392     }
393
394     /* Free the whole array */
395     g_ptr_array_free(frames, TRUE);
396
397     /* Close outfile */
398     if (!wtap_dump_close(pdh, &err)) {
399         fprintf(stderr, "reordercap: Error closing %s: %s\n", outfile,
400                 wtap_strerror(err));
401         g_free(shb_hdr);
402         exit(1);
403     }
404     g_free(shb_hdr);
405
406     /* Finally, close infile */
407     wtap_fdclose(wth);
408
409     return 0;
410 }
411
412 /*
413  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
414  *
415  * Local variables:
416  * c-basic-offset: 4
417  * tab-width: 8
418  * indent-tabs-mode: nil
419  * End:
420  *
421  * vi: set shiftwidth=4 tabstop=8 expandtab:
422  * :indentSize=4:tabSize=8:noTabs=true:
423  */