File: | builds/wireshark/wireshark/editcap.c |
Warning: | line 827, column 5 Value stored to 'caplen' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 <[email protected]> |
13 | * Copyright 1998 Gerald Combs |
14 | * |
15 | * SPDX-License-Identifier: GPL-2.0-or-later |
16 | */ |
17 | |
18 | #include <config.h> |
19 | #define WS_LOG_DOMAIN"Main" LOG_DOMAIN_MAIN"Main" |
20 | |
21 | #include <stdio.h> |
22 | #include <stdlib.h> |
23 | #include <string.h> |
24 | #include <stdarg.h> |
25 | #include <math.h> |
26 | #include <stddef.h> |
27 | |
28 | #include <time.h> |
29 | #include <glib.h> |
30 | #include <gcrypt.h> |
31 | |
32 | #ifdef HAVE_UNISTD_H1 |
33 | #include <unistd.h> |
34 | #endif |
35 | |
36 | #include <ws_exit_codes.h> |
37 | #include <wsutil/ws_getopt.h> |
38 | |
39 | #include <wiretap/secrets-types.h> |
40 | #include <wiretap/wtap.h> |
41 | |
42 | #include "epan/etypes.h" |
43 | #include "epan/dissectors/packet-ieee80211-radiotap-defs.h" |
44 | |
45 | #ifdef _WIN32 |
46 | #include <process.h> /* getpid */ |
47 | #include <winsock2.h> |
48 | #endif |
49 | |
50 | #include <wsutil/clopts_common.h> |
51 | #include <wsutil/cmdarg_err.h> |
52 | #include <wsutil/filesystem.h> |
53 | #include <wsutil/file_util.h> |
54 | #include <wsutil/plugins.h> |
55 | #include <wsutil/privileges.h> |
56 | #include <wsutil/strnatcmp.h> |
57 | #include <wsutil/str_util.h> |
58 | #include <cli_main.h> |
59 | #include <wsutil/version_info.h> |
60 | #include <wsutil/pint.h> |
61 | #include <wsutil/strtoi.h> |
62 | #include <wsutil/ws_assert.h> |
63 | #include <wsutil/wslog.h> |
64 | #include <wiretap/wtap_opttypes.h> |
65 | |
66 | #include "ui/failure_message.h" |
67 | |
68 | #include "ringbuffer.h" /* For RINGBUFFER_MAX_NUM_FILES */ |
69 | |
70 | /* Additional exit codes */ |
71 | #define CANT_EXTRACT_PREFIX2 2 |
72 | #define WRITE_ERROR2 2 |
73 | #define DUMP_ERROR2 2 |
74 | |
75 | #define NANOSECS_PER_SEC1000000000 1000000000 |
76 | |
77 | /* |
78 | * Some globals so we can pass things to various routines |
79 | */ |
80 | |
81 | struct select_item { |
82 | bool_Bool inclusive; |
83 | uint64_t first, second; |
84 | }; |
85 | |
86 | /* |
87 | * Duplicate frame detection |
88 | */ |
89 | typedef struct _fd_hash_t { |
90 | uint8_t digest[16]; |
91 | uint32_t len; |
92 | nstime_t frame_time; |
93 | } fd_hash_t; |
94 | |
95 | #define DEFAULT_DUP_DEPTH5 5 /* Used with -d */ |
96 | #define MAX_DUP_DEPTH1000000 1000000 /* the maximum window (and actual size of fd_hash[]) for de-duplication */ |
97 | |
98 | static fd_hash_t fd_hash[MAX_DUP_DEPTH1000000]; |
99 | static int dup_window = DEFAULT_DUP_DEPTH5; |
100 | static int cur_dup_entry; |
101 | |
102 | static uint32_t ignored_bytes; /* Used with -I */ |
103 | |
104 | #define ONE_BILLION1000000000 1000000000 |
105 | |
106 | /* Weights of different errors we can introduce */ |
107 | /* We should probably make these command-line arguments */ |
108 | /* XXX - Should we add a bit-level error? */ |
109 | #define ERR_WT_BIT5 5 /* Flip a random bit */ |
110 | #define ERR_WT_BYTE5 5 /* Substitute a random byte */ |
111 | #define ERR_WT_ALNUM5 5 /* Substitute a random character in [A-Za-z0-9] */ |
112 | #define ERR_WT_FMT2 2 /* Substitute "%s" */ |
113 | #define ERR_WT_AA1 1 /* Fill the remainder of the buffer with 0xAA */ |
114 | #define ERR_WT_TOTAL(5 + 5 + 5 + 2 + 1) (ERR_WT_BIT5 + ERR_WT_BYTE5 + ERR_WT_ALNUM5 + ERR_WT_FMT2 + ERR_WT_AA1) |
115 | |
116 | #define ALNUM_CHARS"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" |
117 | #define ALNUM_LEN(sizeof("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" ) - 1) (sizeof(ALNUM_CHARS"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") - 1) |
118 | |
119 | struct time_adjustment { |
120 | nstime_t tv; |
121 | int is_negative; |
122 | }; |
123 | |
124 | typedef struct _chop_t { |
125 | int len_begin; |
126 | int off_begin_pos; |
127 | int off_begin_neg; |
128 | int len_end; |
129 | int off_end_pos; |
130 | int off_end_neg; |
131 | } chop_t; |
132 | |
133 | |
134 | /* Table of user comments */ |
135 | GTree *frames_user_comments; |
136 | GPtrArray *capture_comments; |
137 | |
138 | #define MAX_SELECTIONS512 512 |
139 | static struct select_item selectfrm[MAX_SELECTIONS512]; |
140 | static unsigned max_selected; |
141 | static bool_Bool keep_em; |
142 | static int out_file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_UNKNOWN-1; |
143 | static int out_frame_type = -2; /* Leave frame type alone */ |
144 | static bool_Bool verbose; /* Not so verbose */ |
145 | static struct time_adjustment time_adj; /* no adjustment */ |
146 | static nstime_t relative_time_window; /* de-dup time window */ |
147 | static double err_prob = -1.0; |
148 | static nstime_t starttime; |
149 | static bool_Bool have_starttime; |
150 | static nstime_t stoptime; |
151 | static bool_Bool have_stoptime; |
152 | static bool_Bool check_startstop; |
153 | static bool_Bool rem_vlan; |
154 | static bool_Bool dup_detect; |
155 | static bool_Bool dup_detect_by_time; |
156 | static bool_Bool skip_radiotap; |
157 | static bool_Bool discard_all_secrets; |
158 | static bool_Bool discard_cap_comments; |
159 | static bool_Bool set_unused; |
160 | static bool_Bool discard_pkt_comments; |
161 | static bool_Bool do_extract_secrets; |
162 | |
163 | static int do_strict_time_adjustment; |
164 | static struct time_adjustment strict_time_adj; /* strict time adjustment */ |
165 | static nstime_t previous_time; /* previous time */ |
166 | |
167 | static const struct { |
168 | const char *str; |
169 | uint32_t id; |
170 | } secrets_types[] = { |
171 | { "tls", SECRETS_TYPE_TLS0x544c534b }, |
172 | { "ssh", SECRETS_TYPE_SSH0x5353484b }, |
173 | { "wg", SECRETS_TYPE_WIREGUARD0x57474b4c }, |
174 | { "opcua", SECRETS_TYPE_OPCUA0x55414b4c }, |
175 | }; |
176 | |
177 | static unsigned find_dct2000_real_data(const uint8_t *buf); |
178 | static void handle_chopping(chop_t chop, wtap_packet_header *phdr, |
179 | uint8_t **buf, bool_Bool adjlen); |
180 | |
181 | static char * |
182 | abs_time_to_str_with_sec_resolution(const nstime_t *abs_time) |
183 | { |
184 | struct tm *tmp; |
185 | char *buf = (char *)g_malloc(16); |
186 | |
187 | tmp = localtime(&abs_time->secs); |
188 | |
189 | if (tmp) { |
190 | 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 char * |
205 | fileset_get_filename_by_pattern(unsigned idx, const nstime_t *ts, |
206 | char *fprefix, char *fsuffix) |
207 | { |
208 | char filenum[5+1]; |
209 | char *timestr; |
210 | char *abs_str; |
211 | |
212 | snprintf(filenum, sizeof(filenum), "%05u", idx % RINGBUFFER_MAX_NUM_FILES100000); |
213 | if (ts) { |
214 | timestr = abs_time_to_str_with_sec_resolution(ts); |
215 | abs_str = g_strconcat(fprefix, "_", filenum, "_", timestr, fsuffix, NULL((void*)0)); |
216 | g_free(timestr); |
217 | } else |
218 | abs_str = g_strconcat(fprefix, "_", filenum, fsuffix, NULL((void*)0)); |
219 | |
220 | return abs_str; |
221 | } |
222 | |
223 | static bool_Bool |
224 | fileset_extract_prefix_suffix(const char *fname, char **fprefix, char **fsuffix, wtap_compression_type *compression_typep) |
225 | { |
226 | char *pfx, *last_pathsep; |
227 | char *save_file; |
228 | wtap_compression_type compression_type; |
229 | |
230 | save_file = g_strdup(fname)g_strdup_inline (fname); |
231 | if (save_file == NULL((void*)0)) { |
232 | fprintf(stderrstderr, "editcap: Out of memory\n"); |
233 | return false0; |
234 | } |
235 | |
236 | last_pathsep = strrchr(save_file, G_DIR_SEPARATOR'/'); |
237 | if (last_pathsep == NULL((void*)0)) { |
238 | last_pathsep = save_file; |
239 | } |
240 | pfx = strrchr(last_pathsep, '.'); |
241 | if (pfx != NULL((void*)0)) { |
242 | /* The pathname has a "." in it, and it's in the last component |
243 | * of the pathname (because there is either only one component, |
244 | * i.e. last_pathsep is null as there are no path separators, |
245 | * or the "." is after the path separator before the last |
246 | * component. |
247 | |
248 | * Treat it as a separator between the rest of the file name and |
249 | * the file name suffix, and arrange that the names given to the |
250 | * ring buffer files have the specified suffix, i.e. put the |
251 | * changing part of the name *before* the suffix. */ |
252 | pfx[0] = '\0'; |
253 | compression_type = wtap_extension_to_compression_type(pfx + 1); |
254 | if (compression_type != WTAP_UNKNOWN_COMPRESSION) { |
255 | char *pfx2 = strrchr(last_pathsep, '.'); |
256 | if (pfx2 != NULL((void*)0)) { |
257 | pfx[0] = '.'; |
258 | pfx = pfx2; |
259 | pfx[0] = '\0'; |
260 | } |
261 | if (compression_typep && *compression_typep == WTAP_UNKNOWN_COMPRESSION) { |
262 | *compression_typep = compression_type; |
263 | } |
264 | /* XXX - What if there's an extension matching a compression type |
265 | * and the passed in compression type is known but something else? |
266 | */ |
267 | } |
268 | *fprefix = g_strdup(save_file)g_strdup_inline (save_file); |
269 | pfx[0] = '.'; /* restore capfile_name */ |
270 | *fsuffix = g_strdup(pfx)g_strdup_inline (pfx); |
271 | } else { |
272 | /* Either there's no "." in the pathname, or it's in a directory |
273 | * component, so the last component has no suffix. */ |
274 | *fprefix = g_strdup(save_file)g_strdup_inline (save_file); |
275 | *fsuffix = NULL((void*)0); |
276 | } |
277 | g_free(save_file); |
278 | return true1; |
279 | } |
280 | |
281 | /* Add a selection item, a simple parser for now */ |
282 | static bool_Bool |
283 | add_selection(char *sel, uint64_t* max_selection) |
284 | { |
285 | char *locn; |
286 | char *next; |
287 | |
288 | if (max_selected >= MAX_SELECTIONS512) { |
289 | /* Let the user know we stopped selecting */ |
290 | fprintf(stderrstderr, "Out of room for packet selections.\n"); |
291 | return false0; |
292 | } |
293 | |
294 | if (verbose) |
295 | fprintf(stderrstderr, "Add_Selected: %s\n", sel); |
296 | |
297 | if ((locn = strchr(sel, '-')) == NULL((void*)0)) { /* No dash, so a single number? */ |
298 | if (verbose) |
299 | fprintf(stderrstderr, "Not inclusive ..."); |
300 | |
301 | selectfrm[max_selected].inclusive = false0; |
302 | selectfrm[max_selected].first = get_uint64(sel, "packet number"); |
303 | if (selectfrm[max_selected].first > *max_selection) |
304 | *max_selection = selectfrm[max_selected].first; |
305 | |
306 | if (verbose) |
307 | fprintf(stderrstderr, " %" PRIu64"l" "u" "\n", selectfrm[max_selected].first); |
308 | } else { |
309 | if (verbose) |
310 | fprintf(stderrstderr, "Inclusive ..."); |
311 | |
312 | *locn = '\0'; /* split the range */ |
313 | next = locn + 1; |
314 | selectfrm[max_selected].inclusive = true1; |
315 | selectfrm[max_selected].first = get_uint64(sel, "beginning of packet range"); |
316 | selectfrm[max_selected].second = get_uint64(next, "end of packet range"); |
317 | |
318 | if (selectfrm[max_selected].second == 0) |
319 | { |
320 | /* Not a valid number, presume all */ |
321 | selectfrm[max_selected].second = *max_selection = UINT64_MAX(18446744073709551615UL); |
322 | } |
323 | else if (selectfrm[max_selected].second > *max_selection) |
324 | *max_selection = selectfrm[max_selected].second; |
325 | |
326 | if (verbose) |
327 | fprintf(stderrstderr, " %" PRIu64"l" "u" ", %" PRIu64"l" "u" "\n", selectfrm[max_selected].first, |
328 | selectfrm[max_selected].second); |
329 | } |
330 | |
331 | max_selected++; |
332 | return true1; |
333 | } |
334 | |
335 | /* Was the packet selected? */ |
336 | |
337 | static bool_Bool |
338 | selected(uint64_t recno) |
339 | { |
340 | unsigned i; |
341 | |
342 | for (i = 0; i < max_selected; i++) { |
343 | if (selectfrm[i].inclusive) { |
344 | if (selectfrm[i].first <= recno && selectfrm[i].second >= recno) |
345 | return true1; |
346 | } else { |
347 | if (recno == selectfrm[i].first) |
348 | return true1; |
349 | } |
350 | } |
351 | |
352 | return false0; |
353 | } |
354 | |
355 | static bool_Bool |
356 | set_time_adjustment(char *optarg_str_p) |
357 | { |
358 | char *frac, *end; |
359 | long val; |
360 | size_t frac_digits; |
361 | |
362 | if (!optarg_str_p) |
363 | return true1; |
364 | |
365 | /* skip leading whitespace */ |
366 | while (*optarg_str_p == ' ' || *optarg_str_p == '\t') |
367 | optarg_str_p++; |
368 | |
369 | /* check for a negative adjustment */ |
370 | if (*optarg_str_p == '-') { |
371 | time_adj.is_negative = 1; |
372 | optarg_str_p++; |
373 | } |
374 | |
375 | /* collect whole number of seconds, if any */ |
376 | if (*optarg_str_p == '.') { /* only fractional (i.e., .5 is ok) */ |
377 | val = 0; |
378 | frac = optarg_str_p; |
379 | } else { |
380 | val = strtol(optarg_str_p, &frac, 10); |
381 | if (frac == NULL((void*)0) || frac == optarg_str_p |
382 | || val == LONG_MIN(-9223372036854775807L -1L) || val == LONG_MAX9223372036854775807L) { |
383 | fprintf(stderrstderr, "editcap: \"%s\" isn't a valid time adjustment\n", |
384 | optarg_str_p); |
385 | return false0; |
386 | } |
387 | if (val < 0) { /* implies '--' since we caught '-' above */ |
388 | fprintf(stderrstderr, "editcap: \"%s\" isn't a valid time adjustment\n", |
389 | optarg_str_p); |
390 | return false0; |
391 | } |
392 | } |
393 | time_adj.tv.secs = val; |
394 | |
395 | /* now collect the partial seconds, if any */ |
396 | if (*frac != '\0') { /* chars left, so get fractional part */ |
397 | val = strtol(&(frac[1]), &end, 10); |
398 | /* if more than 9 fractional digits truncate to 9 */ |
399 | if ((end - &(frac[1])) > 9) { |
400 | frac[10] = 't'; /* 't' for truncate */ |
401 | val = strtol(&(frac[1]), &end, 10); |
402 | } |
403 | if (*frac != '.' || end == NULL((void*)0) || end == frac || val < 0 |
404 | || val >= ONE_BILLION1000000000 || val == LONG_MIN(-9223372036854775807L -1L) || val == LONG_MAX9223372036854775807L) { |
405 | fprintf(stderrstderr, "editcap: \"%s\" isn't a valid time adjustment\n", |
406 | optarg_str_p); |
407 | return false0; |
408 | } |
409 | } else { |
410 | return true1; /* no fractional digits */ |
411 | } |
412 | |
413 | /* adjust fractional portion from fractional to numerator |
414 | * e.g., in "1.5" from 5 to 500000000 since .5*10^9 = 500000000 */ |
415 | frac_digits = end - frac - 1; /* fractional digit count (remember '.') */ |
416 | while(frac_digits < 9) { /* this is frac of 10^9 */ |
417 | val *= 10; |
418 | frac_digits++; |
419 | } |
420 | |
421 | time_adj.tv.nsecs = (int)val; |
422 | return true1; |
423 | } |
424 | |
425 | static bool_Bool |
426 | set_strict_time_adj(char *optarg_str_p) |
427 | { |
428 | char *frac, *end; |
429 | long val; |
430 | size_t frac_digits; |
431 | |
432 | if (!optarg_str_p) |
433 | return true1; |
434 | |
435 | /* skip leading whitespace */ |
436 | while (*optarg_str_p == ' ' || *optarg_str_p == '\t') |
437 | optarg_str_p++; |
438 | |
439 | /* |
440 | * check for a negative adjustment |
441 | * A negative strict adjustment value is a flag |
442 | * to adjust all frames by the specified delta time. |
443 | */ |
444 | if (*optarg_str_p == '-') { |
445 | strict_time_adj.is_negative = 1; |
446 | optarg_str_p++; |
447 | } |
448 | |
449 | /* collect whole number of seconds, if any */ |
450 | if (*optarg_str_p == '.') { /* only fractional (i.e., .5 is ok) */ |
451 | val = 0; |
452 | frac = optarg_str_p; |
453 | } else { |
454 | val = strtol(optarg_str_p, &frac, 10); |
455 | if (frac == NULL((void*)0) || frac == optarg_str_p |
456 | || val == LONG_MIN(-9223372036854775807L -1L) || val == LONG_MAX9223372036854775807L) { |
457 | fprintf(stderrstderr, "editcap: \"%s\" isn't a valid time adjustment\n", |
458 | optarg_str_p); |
459 | return false0; |
460 | } |
461 | if (val < 0) { /* implies '--' since we caught '-' above */ |
462 | fprintf(stderrstderr, "editcap: \"%s\" isn't a valid time adjustment\n", |
463 | optarg_str_p); |
464 | return false0; |
465 | } |
466 | } |
467 | strict_time_adj.tv.secs = val; |
468 | |
469 | /* now collect the partial seconds, if any */ |
470 | if (*frac != '\0') { /* chars left, so get fractional part */ |
471 | val = strtol(&(frac[1]), &end, 10); |
472 | /* if more than 9 fractional digits truncate to 9 */ |
473 | if ((end - &(frac[1])) > 9) { |
474 | frac[10] = 't'; /* 't' for truncate */ |
475 | val = strtol(&(frac[1]), &end, 10); |
476 | } |
477 | if (*frac != '.' || end == NULL((void*)0) || end == frac || val < 0 |
478 | || val >= ONE_BILLION1000000000 || val == LONG_MIN(-9223372036854775807L -1L) || val == LONG_MAX9223372036854775807L) { |
479 | fprintf(stderrstderr, "editcap: \"%s\" isn't a valid time adjustment\n", |
480 | optarg_str_p); |
481 | return false0; |
482 | } |
483 | } else { |
484 | return true1; /* no fractional digits */ |
485 | } |
486 | |
487 | /* adjust fractional portion from fractional to numerator |
488 | * e.g., in "1.5" from 5 to 500000000 since .5*10^9 = 500000000 */ |
489 | frac_digits = end - frac - 1; /* fractional digit count (remember '.') */ |
490 | while(frac_digits < 9) { /* this is frac of 10^9 */ |
491 | val *= 10; |
492 | frac_digits++; |
493 | } |
494 | |
495 | strict_time_adj.tv.nsecs = (int)val; |
496 | return true1; |
497 | } |
498 | |
499 | static bool_Bool |
500 | set_rel_time(char *optarg_str_p) |
501 | { |
502 | char *frac, *end; |
503 | long val; |
504 | size_t frac_digits; |
505 | |
506 | if (!optarg_str_p) |
507 | return true1; |
508 | |
509 | /* skip leading whitespace */ |
510 | while (*optarg_str_p == ' ' || *optarg_str_p == '\t') |
511 | optarg_str_p++; |
512 | |
513 | /* ignore negative adjustment */ |
514 | if (*optarg_str_p == '-') |
515 | optarg_str_p++; |
516 | |
517 | /* collect whole number of seconds, if any */ |
518 | if (*optarg_str_p == '.') { /* only fractional (i.e., .5 is ok) */ |
519 | val = 0; |
520 | frac = optarg_str_p; |
521 | } else { |
522 | val = strtol(optarg_str_p, &frac, 10); |
523 | if (frac == NULL((void*)0) || frac == optarg_str_p |
524 | || val == LONG_MIN(-9223372036854775807L -1L) || val == LONG_MAX9223372036854775807L) { |
525 | fprintf(stderrstderr, "1: editcap: \"%s\" isn't a valid rel time value\n", |
526 | optarg_str_p); |
527 | return false0; |
528 | } |
529 | if (val < 0) { /* implies '--' since we caught '-' above */ |
530 | fprintf(stderrstderr, "2: editcap: \"%s\" isn't a valid rel time value\n", |
531 | optarg_str_p); |
532 | return false0; |
533 | } |
534 | } |
535 | relative_time_window.secs = val; |
536 | |
537 | /* now collect the partial seconds, if any */ |
538 | if (*frac != '\0') { /* chars left, so get fractional part */ |
539 | val = strtol(&(frac[1]), &end, 10); |
540 | /* if more than 9 fractional digits truncate to 9 */ |
541 | if ((end - &(frac[1])) > 9) { |
542 | frac[10] = 't'; /* 't' for truncate */ |
543 | val = strtol(&(frac[1]), &end, 10); |
544 | } |
545 | if (*frac != '.' || end == NULL((void*)0) || end == frac || val < 0 |
546 | || val >= ONE_BILLION1000000000 || val == LONG_MIN(-9223372036854775807L -1L) || val == LONG_MAX9223372036854775807L) { |
547 | fprintf(stderrstderr, "3: editcap: \"%s\" isn't a valid rel time value\n", |
548 | optarg_str_p); |
549 | return false0; |
550 | } |
551 | } else { |
552 | return true1; /* no fractional digits */ |
553 | } |
554 | |
555 | /* adjust fractional portion from fractional to numerator |
556 | * e.g., in "1.5" from 5 to 500000000 since .5*10^9 = 500000000 */ |
557 | frac_digits = end - frac - 1; /* fractional digit count (remember '.') */ |
558 | while(frac_digits < 9) { /* this is frac of 10^9 */ |
559 | val *= 10; |
560 | frac_digits++; |
561 | } |
562 | |
563 | relative_time_window.nsecs = (int)val; |
564 | return true1; |
565 | } |
566 | |
567 | #define SLL_ADDRLEN8 8 /* length of address field */ |
568 | struct sll_header { |
569 | uint16_t sll_pkttype; /* packet type */ |
570 | uint16_t sll_hatype; /* link-layer address type */ |
571 | uint16_t sll_halen; /* link-layer address length */ |
572 | uint8_t sll_addr[SLL_ADDRLEN8]; /* link-layer address */ |
573 | uint16_t sll_protocol; /* protocol */ |
574 | }; |
575 | |
576 | struct sll2_header { |
577 | uint16_t sll2_protocol; /* protocol */ |
578 | uint16_t sll2_reserved_mbz; /* reserved - must be zero */ |
579 | uint32_t sll2_if_index; /* 1-based interface index */ |
580 | uint16_t sll2_hatype; /* link-layer address type */ |
581 | uint8_t sll2_pkttype; /* packet type */ |
582 | uint8_t sll2_halen; /* link-layer address length */ |
583 | uint8_t sll2_addr[SLL_ADDRLEN8]; /* link-layer address */ |
584 | }; |
585 | |
586 | #define VLAN_SIZE4 4 |
587 | static void |
588 | sll_remove_vlan_info(uint8_t* fd, uint32_t* len) { |
589 | if (pntoh16(fd + offsetof(struct sll_header, sll_protocol)__builtin_offsetof(struct sll_header, sll_protocol)) == ETHERTYPE_VLAN0x8100) { |
590 | int rest_len; |
591 | /* point to start of vlan */ |
592 | fd = fd + offsetof(struct sll_header, sll_protocol)__builtin_offsetof(struct sll_header, sll_protocol); |
593 | /* bytes to read after vlan info */ |
594 | rest_len = *len - (offsetof(struct sll_header, sll_protocol)__builtin_offsetof(struct sll_header, sll_protocol) + VLAN_SIZE4); |
595 | /* remove vlan info from packet */ |
596 | memmove(fd, fd + VLAN_SIZE4, rest_len); |
597 | *len -= 4; |
598 | } |
599 | } |
600 | |
601 | |
602 | |
603 | static void |
604 | sll_set_unused_info(uint8_t* fd) { |
605 | uint32_t ha_len; |
606 | ha_len = pntoh16(fd + offsetof(struct sll_header, sll_halen)__builtin_offsetof(struct sll_header, sll_halen)); |
607 | |
608 | if (ha_len < SLL_ADDRLEN8) { |
609 | int unused; |
610 | unused = SLL_ADDRLEN8 - ha_len; |
611 | /* point to end of sll_ddr */ |
612 | fd = fd + offsetof(struct sll_header, sll_addr)__builtin_offsetof(struct sll_header, sll_addr) + ha_len; |
613 | /* set zeros in the unused data */ |
614 | memset(fd, 0, unused); |
615 | } |
616 | } |
617 | |
618 | static void |
619 | sll2_set_unused_info(uint8_t* fd) { |
620 | uint32_t ha_len; |
621 | ha_len = *(fd + offsetof(struct sll2_header, sll2_halen)__builtin_offsetof(struct sll2_header, sll2_halen)); |
622 | |
623 | if (ha_len < SLL_ADDRLEN8) { |
624 | int unused; |
625 | unused = SLL_ADDRLEN8 - ha_len; |
626 | /* point to end of sll2_addr */ |
627 | fd = fd + offsetof(struct sll2_header, sll2_addr)__builtin_offsetof(struct sll2_header, sll2_addr) + ha_len; |
628 | /* set zeros in the unused data */ |
629 | memset(fd, 0, unused); |
630 | } |
631 | } |
632 | |
633 | static void |
634 | remove_vlan_info(wtap_packet_header *phdr, uint8_t* fd) { |
635 | switch (phdr->pkt_encap) { |
636 | case WTAP_ENCAP_SLL25: |
637 | sll_remove_vlan_info(fd, &phdr->caplen); |
638 | break; |
639 | default: |
640 | /* no support for current pkt_encap */ |
641 | break; |
642 | } |
643 | } |
644 | |
645 | static void |
646 | set_unused_info(const wtap_packet_header *phdr, uint8_t* fd) { |
647 | switch (phdr->pkt_encap) { |
648 | case WTAP_ENCAP_SLL25: |
649 | sll_set_unused_info(fd); |
650 | break; |
651 | case WTAP_ENCAP_SLL2210: |
652 | sll2_set_unused_info(fd); |
653 | break; |
654 | default: |
655 | /* no support for current pkt_encap */ |
656 | break; |
657 | } |
658 | } |
659 | |
660 | static bool_Bool |
661 | is_duplicate(uint8_t* fd, uint32_t len) { |
662 | int i; |
663 | const struct ieee80211_radiotap_header* tap_header; |
664 | |
665 | /*Hint to ignore some bytes at the start of the frame for the digest calculation(-I option) */ |
666 | uint32_t offset = ignored_bytes; |
667 | uint32_t new_len; |
668 | uint8_t *new_fd; |
669 | |
670 | if (len <= ignored_bytes) { |
671 | offset = 0; |
672 | } |
673 | |
674 | /* Get the size of radiotap header and use that as offset (-p option) */ |
675 | if (skip_radiotap == true1) { |
676 | tap_header = (const struct ieee80211_radiotap_header*)fd; |
677 | offset = pletoh16(&tap_header->it_len); |
678 | if (offset >= len) |
679 | offset = 0; |
680 | } |
681 | |
682 | new_fd = &fd[offset]; |
683 | new_len = len - (offset); |
684 | |
685 | cur_dup_entry++; |
686 | if (cur_dup_entry >= dup_window) |
687 | cur_dup_entry = 0; |
688 | |
689 | /* Calculate our digest */ |
690 | gcry_md_hash_buffer(GCRY_MD_MD5, fd_hash[cur_dup_entry].digest, new_fd, new_len); |
691 | |
692 | fd_hash[cur_dup_entry].len = len; |
693 | |
694 | /* Look for duplicates */ |
695 | for (i = 0; i < dup_window; i++) { |
696 | if (i == cur_dup_entry) |
697 | continue; |
698 | |
699 | if (fd_hash[i].len == fd_hash[cur_dup_entry].len |
700 | && memcmp(fd_hash[i].digest, fd_hash[cur_dup_entry].digest, 16) == 0) { |
701 | return true1; |
702 | } |
703 | } |
704 | |
705 | return false0; |
706 | } |
707 | |
708 | static bool_Bool |
709 | is_duplicate_rel_time(uint8_t* fd, uint32_t len, const nstime_t *current) { |
710 | int i; |
711 | |
712 | /*Hint to ignore some bytes at the start of the frame for the digest calculation(-I option) */ |
713 | uint32_t offset = ignored_bytes; |
714 | uint32_t new_len; |
715 | uint8_t *new_fd; |
716 | |
717 | if (len <= ignored_bytes) { |
718 | offset = 0; |
719 | } |
720 | |
721 | new_fd = &fd[offset]; |
722 | new_len = len - (offset); |
723 | |
724 | cur_dup_entry++; |
725 | if (cur_dup_entry >= dup_window) |
726 | cur_dup_entry = 0; |
727 | |
728 | /* Calculate our digest */ |
729 | gcry_md_hash_buffer(GCRY_MD_MD5, fd_hash[cur_dup_entry].digest, new_fd, new_len); |
730 | |
731 | fd_hash[cur_dup_entry].len = len; |
732 | fd_hash[cur_dup_entry].frame_time.secs = current->secs; |
733 | fd_hash[cur_dup_entry].frame_time.nsecs = current->nsecs; |
734 | |
735 | /* |
736 | * Look for relative time related duplicates. |
737 | * This is hopefully a reasonably efficient mechanism for |
738 | * finding duplicates by rel time in the fd_hash[] cache. |
739 | * We check starting from the most recently added hash |
740 | * entries and work backwards towards older packets. |
741 | * This approach allows the dup test to be terminated |
742 | * when the relative time of a cached entry is found to |
743 | * be beyond the dup time window. |
744 | * |
745 | * Of course this assumes that the input trace file is |
746 | * "well-formed" in the sense that the packet timestamps are |
747 | * in strict chronologically increasing order (which is NOT |
748 | * always the case!!). |
749 | * |
750 | * The fd_hash[] table was deliberately created large (1,000,000). |
751 | * Looking for time related duplicates in large trace files with |
752 | * non-fractional dup time window values can potentially take |
753 | * a long time to complete. |
754 | */ |
755 | |
756 | for (i = cur_dup_entry - 1;; i--) { |
757 | nstime_t delta; |
758 | int cmp; |
759 | |
760 | if (i < 0) |
761 | i = dup_window - 1; |
762 | |
763 | if (i == cur_dup_entry) { |
764 | /* |
765 | * We've decremented back to where we started. |
766 | * Check no more! |
767 | */ |
768 | break; |
769 | } |
770 | |
771 | if (nstime_is_unset(&(fd_hash[i].frame_time))) { |
772 | /* |
773 | * We've decremented to an unused fd_hash[] entry. |
774 | * Check no more! |
775 | */ |
776 | break; |
777 | } |
778 | |
779 | nstime_delta(&delta, current, &fd_hash[i].frame_time); |
780 | |
781 | if (delta.secs < 0 || delta.nsecs < 0) { |
782 | /* |
783 | * A negative delta implies that the current packet |
784 | * has an absolute timestamp less than the cached packet |
785 | * that it is being compared to. This is NOT a normal |
786 | * situation since trace files usually have packets in |
787 | * chronological order (oldest to newest). |
788 | * |
789 | * There are several possible ways to deal with this: |
790 | * 1. 'continue' dup checking with the next cached frame. |
791 | * 2. 'break' from looking for a duplicate of the current frame. |
792 | * 3. Take the absolute value of the delta and see if that |
793 | * falls within the specified dup time window. |
794 | * |
795 | * Currently this code does option 1. But it would pretty |
796 | * easy to add yet-another-editcap-option to select one of |
797 | * the other behaviors for dealing with out-of-sequence |
798 | * packets. |
799 | */ |
800 | continue; |
801 | } |
802 | |
803 | cmp = nstime_cmp(&delta, &relative_time_window); |
804 | |
805 | if (cmp > 0) { |
806 | /* |
807 | * The delta time indicates that we are now looking at |
808 | * cached packets beyond the specified dup time window. |
809 | * Check no more! |
810 | */ |
811 | break; |
812 | } else if (fd_hash[i].len == fd_hash[cur_dup_entry].len |
813 | && memcmp(fd_hash[i].digest, fd_hash[cur_dup_entry].digest, 16) == 0) { |
814 | return true1; |
815 | } |
816 | } |
817 | |
818 | return false0; |
819 | } |
820 | |
821 | static void |
822 | mutate_packet_data(wtap_rec *rec, uint8_t *buf, uint32_t change_offset, uint64_t count) |
823 | { |
824 | uint32_t caplen; |
825 | unsigned real_data_start = 0; |
826 | |
827 | caplen = 0; |
Value stored to 'caplen' is never read | |
828 | |
829 | switch (rec->rec_type) { |
830 | |
831 | case REC_TYPE_PACKET0: |
832 | caplen = rec->rec_header.packet_header.caplen; |
833 | |
834 | /* |
835 | * Protect some non-protocol data. |
836 | * XXX - any reason not to fuzz this part? |
837 | */ |
838 | if (rec->rec_header.packet_header.pkt_encap == WTAP_ENCAP_CATAPULT_DCT200089) |
839 | real_data_start = find_dct2000_real_data(buf); |
840 | break; |
841 | |
842 | case REC_TYPE_FT_SPECIFIC_EVENT1: |
843 | case REC_TYPE_FT_SPECIFIC_REPORT2: |
844 | caplen = rec->rec_header.ft_specific_header.record_len; |
845 | break; |
846 | |
847 | case REC_TYPE_SYSCALL3: |
848 | caplen = rec->rec_header.syscall_header.event_filelen; |
849 | break; |
850 | |
851 | case REC_TYPE_SYSTEMD_JOURNAL_EXPORT4: |
852 | caplen = rec->rec_header.systemd_journal_export_header.record_len; |
853 | break; |
854 | |
855 | default: |
856 | /* We don't mutate anything else. */ |
857 | return; |
858 | } |
859 | |
860 | if (change_offset > caplen) { |
861 | fprintf(stderrstderr, "change offset %u is longer than caplen %u in packet %" PRIu64"l" "u" "\n", |
862 | change_offset, caplen, count); |
863 | return; |
864 | } |
865 | |
866 | real_data_start += change_offset; |
867 | |
868 | for (unsigned i = real_data_start; i < caplen; i++) { |
869 | if (rand() <= err_prob * RAND_MAX2147483647) { |
870 | int err_type = rand() / (RAND_MAX2147483647 / ERR_WT_TOTAL(5 + 5 + 5 + 2 + 1) + 1); |
871 | |
872 | if (err_type < ERR_WT_BIT5) { |
873 | buf[i] ^= 1 << (rand() / (RAND_MAX2147483647 / 8 + 1)); |
874 | err_type = ERR_WT_TOTAL(5 + 5 + 5 + 2 + 1); |
875 | } else { |
876 | err_type -= ERR_WT_BYTE5; |
877 | } |
878 | |
879 | if (err_type < ERR_WT_BYTE5) { |
880 | buf[i] = rand() / (RAND_MAX2147483647 / 255 + 1); |
881 | err_type = ERR_WT_TOTAL(5 + 5 + 5 + 2 + 1); |
882 | } else { |
883 | err_type -= ERR_WT_BYTE5; |
884 | } |
885 | |
886 | if (err_type < ERR_WT_ALNUM5) { |
887 | buf[i] = ALNUM_CHARS"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"[rand() / (RAND_MAX2147483647 / ALNUM_LEN(sizeof("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" ) - 1) + 1)]; |
888 | err_type = ERR_WT_TOTAL(5 + 5 + 5 + 2 + 1); |
889 | } else { |
890 | err_type -= ERR_WT_ALNUM5; |
891 | } |
892 | |
893 | if (err_type < ERR_WT_FMT2) { |
894 | if (i < caplen - 2) |
895 | (void) g_strlcpy((char*) &buf[i], "%s", 2); |
896 | err_type = ERR_WT_TOTAL(5 + 5 + 5 + 2 + 1); |
897 | } else { |
898 | err_type -= ERR_WT_FMT2; |
899 | } |
900 | |
901 | if (err_type < ERR_WT_AA1) { |
902 | for (unsigned j = i; j < caplen; j++) |
903 | buf[j] = 0xAA; |
904 | i = caplen; |
905 | } |
906 | } |
907 | } |
908 | } |
909 | |
910 | static void |
911 | print_usage(FILE *output) |
912 | { |
913 | fprintf(output, "\n"); |
914 | fprintf(output, "Usage: editcap [options] ... <infile> <outfile> [ <packet#>[-<packet#>] ... ]\n"); |
915 | fprintf(output, "\n"); |
916 | fprintf(output, "<infile> and <outfile> must both be present; use '-' for stdin or stdout.\n"); |
917 | fprintf(output, "A single packet or a range of packets can be selected.\n"); |
918 | fprintf(output, "\n"); |
919 | fprintf(output, "Packet selection:\n"); |
920 | fprintf(output, " -r keep the selected packets; default is to delete them.\n"); |
921 | fprintf(output, " -A <start time> only read packets whose timestamp is after (or equal\n"); |
922 | fprintf(output, " to) the given time.\n"); |
923 | fprintf(output, " -B <stop time> only read packets whose timestamp is before the\n"); |
924 | fprintf(output, " given time.\n"); |
925 | fprintf(output, " Time format for -A/-B options is\n"); |
926 | fprintf(output, " YYYY-MM-DDThh:mm:ss[.nnnnnnnnn][Z|+-hh:mm]\n"); |
927 | fprintf(output, " Unix epoch timestamps are also supported.\n"); |
928 | fprintf(output, "\n"); |
929 | fprintf(output, "Duplicate packet removal:\n"); |
930 | fprintf(output, " --novlan remove vlan info from packets before checking for duplicates.\n"); |
931 | fprintf(output, " -d remove packet if duplicate (window == %d).\n", DEFAULT_DUP_DEPTH5); |
932 | fprintf(output, " -D <dup window> remove packet if duplicate; configurable <dup window>.\n"); |
933 | fprintf(output, " Valid <dup window> values are 0 to %d.\n", MAX_DUP_DEPTH1000000); |
934 | fprintf(output, " NOTE: A <dup window> of 0 with -V (verbose option) is\n"); |
935 | fprintf(output, " useful to print MD5 hashes.\n"); |
936 | fprintf(output, " -w <dup time window> remove packet if duplicate packet is found EQUAL TO OR\n"); |
937 | fprintf(output, " LESS THAN <dup time window> prior to current packet.\n"); |
938 | fprintf(output, " A <dup time window> is specified in relative seconds\n"); |
939 | fprintf(output, " (e.g. 0.000001).\n"); |
940 | fprintf(output, " NOTE: The use of the 'Duplicate packet removal' options with\n"); |
941 | fprintf(output, " other editcap options except -V may not always work as expected.\n"); |
942 | fprintf(output, " Specifically the -r, -t or -S options will very likely NOT have the\n"); |
943 | fprintf(output, " desired effect if combined with the -d, -D or -w.\n"); |
944 | fprintf(output, " --skip-radiotap-header skip radiotap header when checking for packet duplicates.\n"); |
945 | fprintf(output, " Useful when processing packets captured by multiple radios\n"); |
946 | fprintf(output, " on the same channel in the vicinity of each other.\n"); |
947 | fprintf(output, " --set-unused set unused byts to zero in sll link addr.\n"); |
948 | fprintf(output, "\n"); |
949 | fprintf(output, "Packet manipulation:\n"); |
950 | fprintf(output, " -s <snaplen> truncate each packet to max. <snaplen> bytes of data.\n"); |
951 | fprintf(output, " -C [offset:]<choplen> chop each packet by <choplen> bytes. Positive values\n"); |
952 | fprintf(output, " chop at the packet beginning, negative values at the\n"); |
953 | fprintf(output, " packet end. If an optional offset precedes the length,\n"); |
954 | fprintf(output, " then the bytes chopped will be offset from that value.\n"); |
955 | fprintf(output, " Positive offsets are from the packet beginning,\n"); |
956 | fprintf(output, " negative offsets are from the packet end. You can use\n"); |
957 | fprintf(output, " this option more than once, allowing up to 2 chopping\n"); |
958 | fprintf(output, " regions within a packet provided that at least 1\n"); |
959 | fprintf(output, " choplen is positive and at least 1 is negative.\n"); |
960 | fprintf(output, " -L adjust the frame (i.e. reported) length when chopping\n"); |
961 | fprintf(output, " and/or snapping.\n"); |
962 | fprintf(output, " -t <time adjustment> adjust the timestamp of each packet.\n"); |
963 | fprintf(output, " <time adjustment> is in relative seconds (e.g. -0.5).\n"); |
964 | fprintf(output, " -S <strict adjustment> adjust timestamp of packets if necessary to ensure\n"); |
965 | fprintf(output, " strict chronological increasing order. The <strict\n"); |
966 | fprintf(output, " adjustment> is specified in relative seconds with\n"); |
967 | fprintf(output, " values of 0 or 0.000001 being the most reasonable.\n"); |
968 | fprintf(output, " A negative adjustment value will modify timestamps so\n"); |
969 | fprintf(output, " that each packet's delta time is the absolute value\n"); |
970 | fprintf(output, " of the adjustment specified. A value of -0 will set\n"); |
971 | fprintf(output, " all packets to the timestamp of the first packet.\n"); |
972 | fprintf(output, " -E <error probability> set the probability (between 0.0 and 1.0 incl.) that\n"); |
973 | fprintf(output, " a particular packet byte will be randomly changed.\n"); |
974 | fprintf(output, " -o <change offset> When used in conjunction with -E, skip some bytes from the\n"); |
975 | fprintf(output, " beginning of the packet. This allows one to preserve some\n"); |
976 | fprintf(output, " bytes, in order to have some headers untouched.\n"); |
977 | fprintf(output, " --seed <seed> When used in conjunction with -E, set the seed to use for\n"); |
978 | fprintf(output, " the pseudo-random number generator. This allows one to\n"); |
979 | fprintf(output, " repeat a particular sequence of errors.\n"); |
980 | fprintf(output, " -I <bytes to ignore> ignore the specified number of bytes at the beginning\n"); |
981 | fprintf(output, " of the frame during MD5 hash calculation, unless the\n"); |
982 | fprintf(output, " frame is too short, then the full frame is used.\n"); |
983 | fprintf(output, " Useful to remove duplicated packets taken on\n"); |
984 | fprintf(output, " several routers (different mac addresses for\n"); |
985 | fprintf(output, " example).\n"); |
986 | fprintf(output, " e.g. -I 26 in case of Ether/IP will ignore\n"); |
987 | fprintf(output, " ether(14) and IP header(20 - 4(src ip) - 4(dst ip)).\n"); |
988 | fprintf(output, " -a <framenum>:<comment> Add or replace comment for given frame number\n"); |
989 | fprintf(output, "\n"); |
990 | fprintf(output, "Output File(s):\n"); |
991 | fprintf(output, " if the output file(s) have the .gz extension, then\n"); |
992 | fprintf(output, " gzip compression will be used\n"); |
993 | fprintf(output, " -c <packets per file> split the packet output to different files based on\n"); |
994 | fprintf(output, " uniform packet counts with a maximum of\n"); |
995 | fprintf(output, " <packets per file> each.\n"); |
996 | fprintf(output, " -i <seconds per file> split the packet output to different files based on\n"); |
997 | fprintf(output, " uniform time intervals with a maximum of\n"); |
998 | fprintf(output, " <seconds per file> each.\n"); |
999 | fprintf(output, " -F <capture type> set the output file type; default is pcapng.\n"); |
1000 | fprintf(output, " An empty \"-F\" option will list the file types.\n"); |
1001 | fprintf(output, " -T <encap type> set the output file encapsulation type; default is the\n"); |
1002 | fprintf(output, " same as the input file. An empty \"-T\" option will\n"); |
1003 | fprintf(output, " list the encapsulation types.\n"); |
1004 | fprintf(output, " --inject-secrets <type>,<file> Insert decryption secrets from <file>. List\n"); |
1005 | fprintf(output, " supported secret types with \"--inject-secrets help\".\n"); |
1006 | fprintf(output, " --extract-secrets Extract decryption secrets into the output file instead.\n"); |
1007 | fprintf(output, " Incompatible with other options besides -V.\n"); |
1008 | fprintf(output, " --discard-all-secrets Discard all decryption secrets from the input file\n"); |
1009 | fprintf(output, " when writing the output file. Does not discard\n"); |
1010 | fprintf(output, " secrets added by \"--inject-secrets\" in the same\n"); |
1011 | fprintf(output, " command line.\n"); |
1012 | fprintf(output, " --capture-comment <comment>\n"); |
1013 | fprintf(output, " Add a capture file comment, if supported.\n"); |
1014 | fprintf(output, " --discard-capture-comment\n"); |
1015 | fprintf(output, " Discard capture file comments from the input file\n"); |
1016 | fprintf(output, " when writing the output file. Does not discard\n"); |
1017 | fprintf(output, " comments added by \"--capture-comment\" in the same\n"); |
1018 | fprintf(output, " command line.\n"); |
1019 | fprintf(output, " --discard-packet-comments\n"); |
1020 | fprintf(output, " Discard all packet comments from the input file\n"); |
1021 | fprintf(output, " when writing the output file. Does not discard\n"); |
1022 | fprintf(output, " comments added by \"-a\" in the same command line.\n"); |
1023 | fprintf(output, " --compress <type> Compress the output file using the type compression format.\n"); |
1024 | fprintf(output, "\n"); |
1025 | fprintf(output, "Miscellaneous:\n"); |
1026 | fprintf(output, " -h, --help display this help and exit.\n"); |
1027 | fprintf(output, " -V verbose output.\n"); |
1028 | fprintf(output, " If -V is used with any of the 'Duplicate Packet\n"); |
1029 | fprintf(output, " Removal' options (-d, -D or -w) then Packet lengths\n"); |
1030 | fprintf(output, " and MD5 hashes are printed to standard-error.\n"); |
1031 | fprintf(output, " -v, --version print version information and exit.\n"); |
1032 | } |
1033 | |
1034 | struct string_elem { |
1035 | const char *sstr; /* The short string */ |
1036 | const char *lstr; /* The long string */ |
1037 | }; |
1038 | |
1039 | static int |
1040 | string_nat_compare(const void *a, const void *b) |
1041 | { |
1042 | return ws_ascii_strnatcmp(((const struct string_elem *)a)->sstr, |
1043 | ((const struct string_elem *)b)->sstr); |
1044 | } |
1045 | |
1046 | static void |
1047 | string_elem_print(void *data, void *stream_ptr) |
1048 | { |
1049 | fprintf((FILE *) stream_ptr, " %s - %s\n", |
1050 | ((struct string_elem *)data)->sstr, |
1051 | ((struct string_elem *)data)->lstr); |
1052 | } |
1053 | |
1054 | static void |
1055 | list_capture_types(FILE *stream) { |
1056 | GArray *writable_type_subtypes; |
1057 | |
1058 | fprintf(stream, "editcap: The available capture file types for the \"-F\" flag are:\n"); |
1059 | writable_type_subtypes = wtap_get_writable_file_types_subtypes(FT_SORT_BY_NAME); |
1060 | for (unsigned i = 0; i < writable_type_subtypes->len; i++) { |
1061 | int ft = g_array_index(writable_type_subtypes, int, i)(((int*) (void *) (writable_type_subtypes)->data) [(i)]); |
1062 | fprintf(stream, " %s - %s\n", wtap_file_type_subtype_name(ft), |
1063 | wtap_file_type_subtype_description(ft)); |
1064 | } |
1065 | g_array_free(writable_type_subtypes, TRUE(!(0))); |
1066 | } |
1067 | |
1068 | static void |
1069 | list_encap_types(FILE *stream) { |
1070 | int i; |
1071 | struct string_elem *encaps; |
1072 | GSList *list = NULL((void*)0); |
1073 | |
1074 | encaps = g_new(struct string_elem, WTAP_NUM_ENCAP_TYPES)((struct string_elem *) g_malloc_n ((wtap_get_num_encap_types ()), sizeof (struct string_elem))); |
1075 | fprintf(stream, "editcap: The available encapsulation types for the \"-T\" flag are:\n"); |
1076 | for (i = 0; i < WTAP_NUM_ENCAP_TYPESwtap_get_num_encap_types(); i++) { |
1077 | encaps[i].sstr = wtap_encap_name(i); |
1078 | if (encaps[i].sstr != NULL((void*)0)) { |
1079 | encaps[i].lstr = wtap_encap_description(i); |
1080 | list = g_slist_insert_sorted(list, &encaps[i], string_nat_compare); |
1081 | } |
1082 | } |
1083 | g_slist_foreach(list, string_elem_print, stream); |
1084 | g_slist_free(list); |
1085 | g_free(encaps); |
1086 | } |
1087 | |
1088 | static void |
1089 | list_output_compression_types(void) { |
1090 | GSList *output_compression_types; |
1091 | |
1092 | fprintf(stderrstderr, "editcap: The available output compress type(s) for the \"--compress\" flag are:\n"); |
1093 | output_compression_types = wtap_get_all_output_compression_type_names_list(); |
1094 | for (GSList *compression_type = output_compression_types; |
1095 | compression_type != NULL((void*)0); |
1096 | compression_type = g_slist_next(compression_type)((compression_type) ? (((GSList *)(compression_type))->next ) : ((void*)0))) { |
1097 | fprintf(stderrstderr, " %s\n", (const char *)compression_type->data); |
1098 | } |
1099 | |
1100 | g_slist_free(output_compression_types); |
1101 | } |
1102 | |
1103 | static void |
1104 | list_secrets_types(FILE *stream) |
1105 | { |
1106 | for (unsigned i = 0; i < G_N_ELEMENTS(secrets_types)(sizeof (secrets_types) / sizeof ((secrets_types)[0])); i++) { |
1107 | fprintf(stream, " %s\n", secrets_types[i].str); |
1108 | } |
1109 | } |
1110 | |
1111 | static uint32_t |
1112 | lookup_secrets_type(const char *type) |
1113 | { |
1114 | for (unsigned i = 0; i < G_N_ELEMENTS(secrets_types)(sizeof (secrets_types) / sizeof ((secrets_types)[0])); i++) { |
1115 | if (!strcmp(secrets_types[i].str, type)) { |
1116 | return secrets_types[i].id; |
1117 | } |
1118 | } |
1119 | return 0; |
1120 | } |
1121 | |
1122 | static void |
1123 | validate_secrets_file(const char *filename, uint32_t secrets_type, const char *data) |
1124 | { |
1125 | if (secrets_type == SECRETS_TYPE_TLS0x544c534b) { |
1126 | /* |
1127 | * A key log file is unlikely going to look like either: |
1128 | * - a PEM-encoded private key file. |
1129 | * - a BER-encoded PKCS #12 file ("PFX file"). (Look for a Constructed |
1130 | * SEQUENCE tag, e.g. bytes 0x30 which happens to be ASCII '0'.) |
1131 | */ |
1132 | if (g_str_has_prefix(data, "-----BEGIN ")(__builtin_constant_p ("-----BEGIN ")? __extension__ ({ const char * const __str = (data); const char * const __prefix = ( "-----BEGIN "); gboolean __result = (0); if (__str == ((void* )0) || __prefix == ((void*)0)) __result = (g_str_has_prefix) ( __str, __prefix); else { const size_t __str_len = strlen (((__str ) + !(__str))); const size_t __prefix_len = strlen (((__prefix ) + !(__prefix))); if (__str_len >= __prefix_len) __result = memcmp (((__str) + !(__str)), ((__prefix) + !(__prefix)), __prefix_len ) == 0; } __result; }) : (g_str_has_prefix) (data, "-----BEGIN " ) ) || data[0] == 0x30) { |
1133 | fprintf(stderrstderr, |
1134 | "editcap: Warning: \"%s\" is not a key log file, but an unsupported private key file. Decryption will not work.\n", |
1135 | filename); |
1136 | } |
1137 | } |
1138 | } |
1139 | |
1140 | static int |
1141 | framenum_compare(const void *a, const void *b, void *user_data _U___attribute__((unused))) |
1142 | { |
1143 | uint64_t *frame_a = (uint64_t*)a; |
1144 | uint64_t *frame_b = (uint64_t*)b; |
1145 | if (*frame_a < *frame_b) |
1146 | return -1; |
1147 | |
1148 | if (*frame_a > *frame_b) |
1149 | return 1; |
1150 | |
1151 | return 0; |
1152 | } |
1153 | |
1154 | static wtap_dumper * |
1155 | editcap_dump_open(const char *filename, const wtap_dump_params *params, |
1156 | GArray *idbs_seen, int *err, char **err_info, |
1157 | wtap_compression_type compression_type) |
1158 | { |
1159 | wtap_dumper *pdh; |
1160 | |
1161 | if (strcmp(filename, "-") == 0) { |
1162 | /* Write to the standard output. */ |
1163 | pdh = wtap_dump_open_stdout(out_file_type_subtype, compression_type, |
1164 | params, err, err_info); |
1165 | } else { |
1166 | pdh = wtap_dump_open(filename, out_file_type_subtype, compression_type, |
1167 | params, err, err_info); |
1168 | } |
1169 | if (pdh == NULL((void*)0)) |
1170 | return NULL((void*)0); |
1171 | |
1172 | /* |
1173 | * If the output file supports identifying the interfaces on which |
1174 | * packets arrive, add all the IDBs we've seen so far. |
1175 | * |
1176 | * That mean that the abstract interface provided by libwiretap |
1177 | * involves WTAP_BLOCK_IF_ID_AND_INFO blocks. |
1178 | */ |
1179 | if (wtap_file_type_subtype_supports_block(wtap_dump_file_type_subtype(pdh), |
1180 | WTAP_BLOCK_IF_ID_AND_INFO) != BLOCK_NOT_SUPPORTED) { |
1181 | for (unsigned i = 0; i < idbs_seen->len; i++) { |
1182 | wtap_block_t if_data = g_array_index(idbs_seen, wtap_block_t, i)(((wtap_block_t*) (void *) (idbs_seen)->data) [(i)]); |
1183 | wtap_block_t if_data_copy; |
1184 | |
1185 | /* |
1186 | * Make a copy of this IDB, so that we can change the |
1187 | * encapsulation type without trashing the original. |
1188 | */ |
1189 | if_data_copy = wtap_block_make_copy(if_data); |
1190 | |
1191 | /* |
1192 | * If an encapsulation type was specified, override the |
1193 | * encapsulation type of the interface. |
1194 | */ |
1195 | if (out_frame_type != -2) { |
1196 | wtapng_if_descr_mandatory_t *if_mand; |
1197 | |
1198 | if_mand = (wtapng_if_descr_mandatory_t *)wtap_block_get_mandatory_data(if_data_copy); |
1199 | if_mand->wtap_encap = out_frame_type; |
1200 | } |
1201 | |
1202 | /* |
1203 | * Add this possibly-modified IDB to the file to which |
1204 | * we're currently writing. |
1205 | */ |
1206 | if (!wtap_dump_add_idb(pdh, if_data_copy, err, err_info)) { |
1207 | int close_err; |
1208 | char *close_err_info; |
1209 | |
1210 | wtap_dump_close(pdh, NULL((void*)0), &close_err, &close_err_info); |
1211 | g_free(close_err_info); |
1212 | wtap_block_unref(if_data_copy); |
1213 | return NULL((void*)0); |
1214 | } |
1215 | |
1216 | /* |
1217 | * Release the copy - wtap_dump_add_idb() makes its own copy. |
1218 | */ |
1219 | wtap_block_unref(if_data_copy); |
1220 | } |
1221 | } |
1222 | |
1223 | return pdh; |
1224 | } |
1225 | |
1226 | static bool_Bool |
1227 | process_new_idbs(wtap *wth, wtap_dumper *pdh, GArray *idbs_seen, |
1228 | int *err, char **err_info) |
1229 | { |
1230 | wtap_block_t if_data; |
1231 | |
1232 | while ((if_data = wtap_get_next_interface_description(wth)) != NULL((void*)0)) { |
1233 | /* |
1234 | * Only add interface blocks if the output file supports (meaning |
1235 | * *requires*) them. |
1236 | * |
1237 | * That mean that the abstract interface provided by libwiretap |
1238 | * involves WTAP_BLOCK_IF_ID_AND_INFO blocks. |
1239 | */ |
1240 | if (pdh != NULL((void*)0) && wtap_file_type_subtype_supports_block(wtap_dump_file_type_subtype(pdh), |
1241 | WTAP_BLOCK_IF_ID_AND_INFO) != BLOCK_NOT_SUPPORTED) { |
1242 | wtap_block_t if_data_copy; |
1243 | |
1244 | /* |
1245 | * Make a copy of this IDB, so that we can change the |
1246 | * encapsulation type without trashing the original. |
1247 | */ |
1248 | if_data_copy = wtap_block_make_copy(if_data); |
1249 | |
1250 | /* |
1251 | * If an encapsulation type was specified, override the |
1252 | * encapsulation type of the interface. |
1253 | */ |
1254 | if (out_frame_type != -2) { |
1255 | wtapng_if_descr_mandatory_t *if_mand; |
1256 | |
1257 | if_mand = (wtapng_if_descr_mandatory_t *)wtap_block_get_mandatory_data(if_data_copy); |
1258 | if_mand->wtap_encap = out_frame_type; |
1259 | } |
1260 | |
1261 | /* |
1262 | * Add this possibly-modified IDB to the file to which |
1263 | * we're currently writing. |
1264 | */ |
1265 | if (!wtap_dump_add_idb(pdh, if_data_copy, err, err_info)) |
1266 | return false0; |
1267 | |
1268 | /* |
1269 | * Release the copy - wtap_dump_add_idb() makes its own copy. |
1270 | */ |
1271 | wtap_block_unref(if_data_copy); |
1272 | |
1273 | /* |
1274 | * Also add an unmodified copy to the set of IDBs we've seen, |
1275 | * in case we start writing to another file (which would be |
1276 | * of the same type as the current file, and thus will also |
1277 | * require interface IDs). |
1278 | */ |
1279 | if_data_copy = wtap_block_make_copy(if_data); |
1280 | g_array_append_val(idbs_seen, if_data_copy)g_array_append_vals (idbs_seen, &(if_data_copy), 1); |
1281 | } |
1282 | } |
1283 | return true1; |
1284 | } |
1285 | |
1286 | static int |
1287 | extract_secrets(wtap *wth, char* filename, int *err, char **err_info) |
1288 | { |
1289 | wtap_rec read_rec; |
1290 | int64_t offset; |
1291 | char *fprefix = NULL((void*)0); |
1292 | char *fsuffix = NULL((void*)0); |
1293 | |
1294 | /* Read all of the packets in turn */ |
1295 | wtap_rec_init(&read_rec, 1514); |
1296 | while (wtap_read(wth, &read_rec, err, err_info, &offset)) { |
1297 | /* Do we want to respect the max packet number on the command line? |
1298 | * Probably more confusing than it's worth, because a user might |
1299 | * not know if a DSB is at the end of the file. |
1300 | */ |
1301 | wtap_rec_reset(&read_rec); |
1302 | } |
1303 | wtap_rec_cleanup(&read_rec); |
1304 | |
1305 | wtapng_dsb_mandatory_t *dsb; |
1306 | if (strcmp(filename, "-") == 0) { |
1307 | /* Sure. Why not. */ |
1308 | for (unsigned dsb_num = 0; dsb_num < wtap_file_get_num_dsbs(wth); ++dsb_num) { |
1309 | dsb = (wtapng_dsb_mandatory_t *)wtap_block_get_mandatory_data(wtap_file_get_dsb(wth, dsb_num)); |
1310 | if (verbose) { |
1311 | fprintf(stderrstderr, "Writing secrets type \"%s\" (0x%08x) to standard out.\n", |
1312 | secrets_type_description(dsb->secrets_type), dsb->secrets_type); |
1313 | } |
1314 | if (fwrite(dsb->secrets_data, 1, dsb->secrets_len, stdoutstdout) != dsb->secrets_len) { |
1315 | return WRITE_ERROR2; |
1316 | } |
1317 | } |
1318 | } else if (wtap_file_get_num_dsbs(wth) == 1) { |
1319 | dsb = (wtapng_dsb_mandatory_t *)wtap_block_get_mandatory_data(wtap_file_get_dsb(wth, 0)); |
1320 | if (verbose) { |
1321 | fprintf(stderrstderr, "Writing secrets type \"%s\" (0x%08x) to \"%s\".\n", |
1322 | secrets_type_description(dsb->secrets_type), dsb->secrets_type, |
1323 | filename); |
1324 | } |
1325 | if (!write_file_binary_mode(filename, dsb->secrets_data, dsb->secrets_len)) { |
1326 | return WRITE_ERROR2; |
1327 | } |
1328 | } else { |
1329 | /* We have more than one DSB, so write multiple files. While for some |
1330 | * types, we could combine the information from different DSBs togther |
1331 | * (and most of those are text-based, so we'd want to write in text |
1332 | * mode so that the line endings are uniform (which makes testing |
1333 | * harder), we don't know that for every type. |
1334 | */ |
1335 | if (!fileset_extract_prefix_suffix(filename, &fprefix, &fsuffix, NULL((void*)0))) { |
1336 | return CANT_EXTRACT_PREFIX2; |
1337 | } |
1338 | char *extract_filename; |
1339 | for (unsigned dsb_num = 0; dsb_num < wtap_file_get_num_dsbs(wth); ++dsb_num) { |
1340 | dsb = (wtapng_dsb_mandatory_t *)wtap_block_get_mandatory_data(wtap_file_get_dsb(wth, dsb_num)); |
1341 | extract_filename = fileset_get_filename_by_pattern(dsb_num, NULL((void*)0), fprefix, fsuffix); |
1342 | if (verbose) { |
1343 | fprintf(stderrstderr, "Writing secrets type \"%s\" (0x%08x) to \"%s\".\n", |
1344 | secrets_type_description(dsb->secrets_type), dsb->secrets_type, |
1345 | extract_filename); |
1346 | } |
1347 | if (!write_file_binary_mode(extract_filename, dsb->secrets_data, dsb->secrets_len)) { |
1348 | /* write_file_binary_mode already reports failures */ |
1349 | g_free(extract_filename); |
1350 | g_free(fprefix); |
1351 | g_free(fsuffix); |
1352 | |
1353 | return WRITE_ERROR2; |
1354 | } |
1355 | g_free(extract_filename); |
1356 | } |
1357 | g_free(fprefix); |
1358 | g_free(fsuffix); |
1359 | } |
1360 | return EXIT_SUCCESS0; |
1361 | } |
1362 | |
1363 | int |
1364 | main(int argc, char *argv[]) |
1365 | { |
1366 | char *configuration_init_error; |
1367 | wtap *wth = NULL((void*)0); |
1368 | int i, read_err, write_err; |
1369 | char *read_err_info, *write_err_info; |
1370 | int opt; |
1371 | |
1372 | #define LONGOPT_NO_VLAN3000 +1 LONGOPT_BASE_APPLICATION3000+1 |
1373 | #define LONGOPT_SKIP_RADIOTAP_HEADER3000 +2 LONGOPT_BASE_APPLICATION3000+2 |
1374 | #define LONGOPT_SEED3000 +3 LONGOPT_BASE_APPLICATION3000+3 |
1375 | #define LONGOPT_INJECT_SECRETS3000 +4 LONGOPT_BASE_APPLICATION3000+4 |
1376 | #define LONGOPT_DISCARD_ALL_SECRETS3000 +5 LONGOPT_BASE_APPLICATION3000+5 |
1377 | #define LONGOPT_CAPTURE_COMMENT3000 +6 LONGOPT_BASE_APPLICATION3000+6 |
1378 | #define LONGOPT_DISCARD_CAPTURE_COMMENT3000 +7 LONGOPT_BASE_APPLICATION3000+7 |
1379 | #define LONGOPT_SET_UNUSED3000 +8 LONGOPT_BASE_APPLICATION3000+8 |
1380 | #define LONGOPT_DISCARD_PACKET_COMMENTS3000 +9 LONGOPT_BASE_APPLICATION3000+9 |
1381 | #define LONGOPT_EXTRACT_SECRETS3000 +10 LONGOPT_BASE_APPLICATION3000+10 |
1382 | #define LONGOPT_COMPRESS3000 +11 LONGOPT_BASE_APPLICATION3000+11 |
1383 | |
1384 | static const struct ws_option long_options[] = { |
1385 | {"novlan", ws_no_argument0, NULL((void*)0), LONGOPT_NO_VLAN3000 +1}, |
1386 | {"skip-radiotap-header", ws_no_argument0, NULL((void*)0), LONGOPT_SKIP_RADIOTAP_HEADER3000 +2}, |
1387 | {"seed", ws_required_argument1, NULL((void*)0), LONGOPT_SEED3000 +3}, |
1388 | {"inject-secrets", ws_required_argument1, NULL((void*)0), LONGOPT_INJECT_SECRETS3000 +4}, |
1389 | {"discard-all-secrets", ws_no_argument0, NULL((void*)0), LONGOPT_DISCARD_ALL_SECRETS3000 +5}, |
1390 | {"help", ws_no_argument0, NULL((void*)0), 'h'}, |
1391 | {"version", ws_no_argument0, NULL((void*)0), 'v'}, |
1392 | {"capture-comment", ws_required_argument1, NULL((void*)0), LONGOPT_CAPTURE_COMMENT3000 +6}, |
1393 | {"discard-capture-comment", ws_no_argument0, NULL((void*)0), LONGOPT_DISCARD_CAPTURE_COMMENT3000 +7}, |
1394 | {"set-unused", ws_no_argument0, NULL((void*)0), LONGOPT_SET_UNUSED3000 +8}, |
1395 | {"discard-packet-comments", ws_no_argument0, NULL((void*)0), LONGOPT_DISCARD_PACKET_COMMENTS3000 +9}, |
1396 | {"extract-secrets", ws_no_argument0, NULL((void*)0), LONGOPT_EXTRACT_SECRETS3000 +10}, |
1397 | {"compress", ws_required_argument1, NULL((void*)0), LONGOPT_COMPRESS3000 +11}, |
1398 | {0, 0, 0, 0 } |
1399 | }; |
1400 | |
1401 | char *p; |
1402 | uint32_t snaplen = 0; /* No limit */ |
1403 | chop_t chop = {0, 0, 0, 0, 0, 0}; /* No chop */ |
1404 | bool_Bool adjlen = false0; |
1405 | wtap_dumper *pdh = NULL((void*)0); |
1406 | GArray *idbs_seen = NULL((void*)0); |
1407 | uint64_t count = 1; |
1408 | uint64_t duplicate_count = 0; |
1409 | int64_t data_offset; |
1410 | uint8_t *buf; |
1411 | uint64_t read_count = 0; |
1412 | uint64_t split_packet_count = 0; |
1413 | uint64_t written_count = 0; |
1414 | char *filename = NULL((void*)0); |
1415 | bool_Bool ts_okay; |
1416 | nstime_t secs_per_block = NSTIME_INIT_UNSET{0, 2147483647}; |
1417 | int block_cnt = 0; |
1418 | nstime_t block_next = NSTIME_INIT_UNSET{0, 2147483647}; |
1419 | char *fprefix = NULL((void*)0); |
1420 | char *fsuffix = NULL((void*)0); |
1421 | uint32_t change_offset = 0; |
1422 | uint64_t max_packet_number = 0; |
1423 | GArray *dsb_types = NULL((void*)0); |
1424 | GPtrArray *dsb_filenames = NULL((void*)0); |
1425 | wtap_rec read_rec; |
1426 | wtap_dump_params params = WTAP_DUMP_PARAMS_INIT{.snaplen=0}; |
1427 | char *shb_user_appl; |
1428 | int ret = EXIT_SUCCESS0; |
1429 | bool_Bool valid_seed = false0; |
1430 | unsigned int seed = 0; |
1431 | bool_Bool edit_option_specified = false0; |
1432 | wtap_compression_type compression_type = WTAP_UNKNOWN_COMPRESSION; |
1433 | |
1434 | /* Set the program name. */ |
1435 | g_set_prgname("editcap"); |
1436 | |
1437 | cmdarg_err_init(stderr_cmdarg_err, stderr_cmdarg_err_cont); |
1438 | memset(&read_rec, 0, sizeof read_rec); |
1439 | |
1440 | /* Initialize log handler early so we can have proper logging during startup. */ |
1441 | ws_log_init(vcmdarg_err); |
1442 | |
1443 | /* Early logging command-line initialization. */ |
1444 | ws_log_parse_args(&argc, argv, vcmdarg_err, WS_EXIT_INVALID_OPTION1); |
1445 | |
1446 | ws_noisy("Finished log init and parsing command line log arguments")do { if (1) { ws_log_full("Main", LOG_LEVEL_NOISY, "editcap.c" , 1446, __func__, "Finished log init and parsing command line log arguments" ); } } while (0); |
1447 | |
1448 | #ifdef _WIN32 |
1449 | create_app_running_mutex(); |
1450 | #endif /* _WIN32 */ |
1451 | |
1452 | /* |
1453 | * Get credential information for later use. |
1454 | */ |
1455 | init_process_policies(); |
1456 | |
1457 | /* |
1458 | * Attempt to get the pathname of the directory containing the |
1459 | * executable file. |
1460 | */ |
1461 | configuration_init_error = configuration_init(argv[0]); |
1462 | if (configuration_init_error != NULL((void*)0)) { |
1463 | cmdarg_err("Can't get pathname of directory containing the editcap program: %s.", |
1464 | configuration_init_error); |
1465 | g_free(configuration_init_error); |
1466 | } |
1467 | |
1468 | /* Initialize the version information. */ |
1469 | ws_init_version_info("Editcap", NULL((void*)0), NULL((void*)0)); |
1470 | |
1471 | init_report_failure_message("editcap"); |
1472 | |
1473 | wtap_init(true1); |
1474 | |
1475 | /* Process the options */ |
1476 | while ((opt = ws_getopt_long(argc, argv, "a:A:B:c:C:dD:E:F:hi:I:Lo:rs:S:t:T:vVw:", long_options, NULL((void*)0))) != -1) { |
1477 | if (opt != LONGOPT_EXTRACT_SECRETS3000 +10 && opt != 'V') { |
1478 | edit_option_specified = true1; |
1479 | } |
1480 | switch (opt) { |
1481 | case LONGOPT_NO_VLAN3000 +1: |
1482 | { |
1483 | rem_vlan = true1; |
1484 | break; |
1485 | } |
1486 | |
1487 | case LONGOPT_SKIP_RADIOTAP_HEADER3000 +2: |
1488 | { |
1489 | skip_radiotap = true1; |
1490 | break; |
1491 | } |
1492 | |
1493 | case LONGOPT_SEED3000 +3: |
1494 | { |
1495 | if (sscanf(ws_optarg, "%u", &seed) != 1) { |
1496 | cmdarg_err("\"%s\" isn't a valid seed", ws_optarg); |
1497 | ret = WS_EXIT_INVALID_OPTION1; |
1498 | goto clean_exit; |
1499 | } |
1500 | valid_seed = true1; |
1501 | break; |
1502 | } |
1503 | |
1504 | case LONGOPT_INJECT_SECRETS3000 +4: |
1505 | { |
1506 | uint32_t secrets_type_id = 0; |
1507 | const char *secrets_filename = NULL((void*)0); |
1508 | if (strcmp("help", ws_optarg) == 0) { |
1509 | list_secrets_types(stdoutstdout); |
1510 | goto clean_exit; |
1511 | } |
1512 | char **splitted = g_strsplit(ws_optarg, ",", 2); |
1513 | if (splitted[0] && splitted[0][0] != '\0') { |
1514 | secrets_type_id = lookup_secrets_type(splitted[0]); |
1515 | if (secrets_type_id == 0) { |
1516 | cmdarg_err("\"%s\" isn't a valid secrets type", splitted[0]); |
1517 | g_strfreev(splitted); |
1518 | ret = WS_EXIT_INVALID_OPTION1; |
1519 | goto clean_exit; |
1520 | } |
1521 | secrets_filename = splitted[1]; |
1522 | } else { |
1523 | cmdarg_err("no secrets type was specified for --inject-secrets"); |
1524 | g_strfreev(splitted); |
1525 | ret = WS_EXIT_INVALID_OPTION1; |
1526 | goto clean_exit; |
1527 | } |
1528 | if (!dsb_filenames) { |
1529 | dsb_types = g_array_new(FALSE(0), FALSE(0), sizeof(uint32_t)); |
1530 | dsb_filenames = g_ptr_array_new_with_free_func(g_free); |
1531 | } |
1532 | g_array_append_val(dsb_types, secrets_type_id)g_array_append_vals (dsb_types, &(secrets_type_id), 1); |
1533 | g_ptr_array_add(dsb_filenames, g_strdup(secrets_filename)g_strdup_inline (secrets_filename)); |
1534 | g_strfreev(splitted); |
1535 | break; |
1536 | } |
1537 | |
1538 | case LONGOPT_DISCARD_ALL_SECRETS3000 +5: |
1539 | { |
1540 | discard_all_secrets = true1; |
1541 | break; |
1542 | } |
1543 | |
1544 | case LONGOPT_CAPTURE_COMMENT3000 +6: |
1545 | { |
1546 | /* |
1547 | * Make sure this would fit in a pcapng option. |
1548 | * |
1549 | * XXX - 65535 is the maximum size for an option in pcapng; |
1550 | * what if another capture file format supports larger |
1551 | * comments? |
1552 | */ |
1553 | if (strlen(ws_optarg) > 65535) { |
1554 | /* It doesn't fit. Tell the user and give up. */ |
1555 | cmdarg_err("Capture comment %u is too large to save in a capture file.", |
1556 | capture_comments->len + 1); |
1557 | ret = WS_EXIT_INVALID_OPTION1; |
1558 | goto clean_exit; |
1559 | } |
1560 | |
1561 | /* pcapng supports multiple comments, so support them here too. |
1562 | */ |
1563 | if (!capture_comments) { |
1564 | capture_comments = g_ptr_array_new_with_free_func(g_free); |
1565 | } |
1566 | g_ptr_array_add(capture_comments, g_strdup(ws_optarg)g_strdup_inline (ws_optarg)); |
1567 | break; |
1568 | } |
1569 | |
1570 | case LONGOPT_DISCARD_CAPTURE_COMMENT3000 +7: |
1571 | { |
1572 | discard_cap_comments = true1; |
1573 | break; |
1574 | } |
1575 | |
1576 | case LONGOPT_SET_UNUSED3000 +8: |
1577 | { |
1578 | set_unused = true1; |
1579 | break; |
1580 | } |
1581 | |
1582 | case LONGOPT_DISCARD_PACKET_COMMENTS3000 +9: |
1583 | { |
1584 | discard_pkt_comments = true1; |
1585 | break; |
1586 | } |
1587 | |
1588 | case LONGOPT_EXTRACT_SECRETS3000 +10: |
1589 | { |
1590 | do_extract_secrets = true1; |
1591 | /* XXX - Would it make sense to specify what types of secrets |
1592 | * to extract (or any)? |
1593 | */ |
1594 | break; |
1595 | } |
1596 | |
1597 | case LONGOPT_COMPRESS3000 +11: |
1598 | { |
1599 | compression_type = wtap_name_to_compression_type(ws_optarg); |
1600 | if (compression_type == WTAP_UNKNOWN_COMPRESSION) { |
1601 | cmdarg_err("\"%s\" isn't a valid output compression mode", |
1602 | ws_optarg); |
1603 | list_output_compression_types(); |
1604 | goto clean_exit; |
1605 | } |
1606 | break; |
1607 | } |
1608 | |
1609 | case 'a': |
1610 | { |
1611 | uint64_t frame_number; |
1612 | int string_start_index = 0; |
1613 | |
1614 | if ((sscanf(ws_optarg, "%" SCNu64"l" "u" ":%n", &frame_number, &string_start_index) < 1) || (string_start_index == 0)) { |
1615 | cmdarg_err("\"%s\" isn't a valid <frame>:<comment>", ws_optarg); |
1616 | ret = WS_EXIT_INVALID_OPTION1; |
1617 | goto clean_exit; |
1618 | } |
1619 | |
1620 | /* |
1621 | * Make sure this would fit in a pcapng option. |
1622 | * |
1623 | * XXX - 65535 is the maximum size for an option in pcapng; |
1624 | * what if another capture file format supports larger |
1625 | * comments? |
1626 | */ |
1627 | if (strlen(ws_optarg+string_start_index) > 65535) { |
1628 | /* It doesn't fit. Tell the user and give up. */ |
1629 | cmdarg_err("A comment for frame %" PRIu64"l" "u" " is too large to save in a capture file.", |
1630 | frame_number); |
1631 | ret = WS_EXIT_INVALID_OPTION1; |
1632 | goto clean_exit; |
1633 | } |
1634 | |
1635 | /* Lazily create the table */ |
1636 | if (!frames_user_comments) { |
1637 | frames_user_comments = g_tree_new_full(framenum_compare, NULL((void*)0), g_free, g_free); |
1638 | } |
1639 | |
1640 | /* Insert this entry (framenum -> comment) */ |
1641 | uint64_t *frame_p = g_new(uint64_t, 1)((uint64_t *) g_malloc_n ((1), sizeof (uint64_t))); |
1642 | *frame_p = frame_number; |
1643 | g_tree_replace(frames_user_comments, frame_p, g_strdup(ws_optarg+string_start_index)g_strdup_inline (ws_optarg+string_start_index)); |
1644 | break; |
1645 | } |
1646 | |
1647 | case 'A': |
1648 | case 'B': |
1649 | { |
1650 | nstime_t in_time; |
1651 | |
1652 | check_startstop = true1; |
1653 | if ((NULL((void*)0) != iso8601_to_nstime(&in_time, ws_optarg, ISO8601_DATETIME)) || (NULL((void*)0) != unix_epoch_to_nstime(&in_time, ws_optarg))) { |
1654 | if (opt == 'A') { |
1655 | nstime_copy(&starttime, &in_time); |
1656 | have_starttime = true1; |
1657 | } else { |
1658 | nstime_copy(&stoptime, &in_time); |
1659 | have_stoptime = true1; |
1660 | } |
1661 | break; |
1662 | } |
1663 | else { |
1664 | cmdarg_err("\"%s\" isn't a valid date and time", ws_optarg); |
1665 | ret = WS_EXIT_INVALID_OPTION1; |
1666 | goto clean_exit; |
1667 | } |
1668 | } |
1669 | |
1670 | case 'c': |
1671 | split_packet_count = get_nonzero_uint64(ws_optarg, "packet count"); |
1672 | break; |
1673 | |
1674 | case 'C': |
1675 | { |
1676 | int choplen = 0, chopoff = 0; |
1677 | |
1678 | switch (sscanf(ws_optarg, "%d:%d", &chopoff, &choplen)) { |
1679 | case 1: /* only the chop length was specified */ |
1680 | choplen = chopoff; |
1681 | chopoff = 0; |
1682 | break; |
1683 | |
1684 | case 2: /* both an offset and chop length was specified */ |
1685 | break; |
1686 | |
1687 | default: |
1688 | cmdarg_err("\"%s\" isn't a valid chop length or offset:length", ws_optarg); |
1689 | ret = WS_EXIT_INVALID_OPTION1; |
1690 | goto clean_exit; |
1691 | break; |
1692 | } |
1693 | |
1694 | if (choplen > 0) { |
1695 | chop.len_begin += choplen; |
1696 | if (chopoff > 0) |
1697 | chop.off_begin_pos += chopoff; |
1698 | else |
1699 | chop.off_begin_neg += chopoff; |
1700 | } else if (choplen < 0) { |
1701 | chop.len_end += choplen; |
1702 | if (chopoff > 0) |
1703 | chop.off_end_pos += chopoff; |
1704 | else |
1705 | chop.off_end_neg += chopoff; |
1706 | } |
1707 | break; |
1708 | } |
1709 | |
1710 | case 'd': |
1711 | dup_detect = true1; |
1712 | dup_detect_by_time = false0; |
1713 | dup_window = DEFAULT_DUP_DEPTH5; |
1714 | break; |
1715 | |
1716 | case 'D': |
1717 | dup_detect = true1; |
1718 | dup_detect_by_time = false0; |
1719 | dup_window = get_uint32(ws_optarg, "duplicate window"); |
1720 | if (dup_window > MAX_DUP_DEPTH1000000) { |
1721 | cmdarg_err("\"%d\" duplicate window value must be between 0 and %d inclusive.", |
1722 | dup_window, MAX_DUP_DEPTH1000000); |
1723 | ret = WS_EXIT_INVALID_OPTION1; |
1724 | goto clean_exit; |
1725 | } |
1726 | break; |
1727 | |
1728 | case 'E': |
1729 | err_prob = g_ascii_strtod(ws_optarg, &p); |
1730 | if (p == ws_optarg || err_prob < 0.0 || err_prob > 1.0) { |
1731 | cmdarg_err("probability \"%s\" must be between 0.0 and 1.0", ws_optarg); |
1732 | ret = WS_EXIT_INVALID_OPTION1; |
1733 | goto clean_exit; |
1734 | } |
1735 | break; |
1736 | |
1737 | case 'F': |
1738 | out_file_type_subtype = wtap_name_to_file_type_subtype(ws_optarg); |
1739 | if (out_file_type_subtype < 0) { |
1740 | cmdarg_err("\"%s\" isn't a valid capture file type\n", ws_optarg); |
1741 | list_capture_types(stderrstderr); |
1742 | ret = WS_EXIT_INVALID_OPTION1; |
1743 | goto clean_exit; |
1744 | } |
1745 | break; |
1746 | |
1747 | case 'h': |
1748 | show_help_header("Edit and/or translate the format of capture files."); |
1749 | print_usage(stdoutstdout); |
1750 | goto clean_exit; |
1751 | break; |
1752 | |
1753 | case 'i': /* break capture file based on time interval */ |
1754 | { |
1755 | double spb = get_positive_double(ws_optarg, "time interval"); |
1756 | if (spb == 0.0) { |
1757 | cmdarg_err("The specified interval is zero"); |
1758 | ret = WS_EXIT_INVALID_OPTION1; |
1759 | goto clean_exit; |
1760 | } |
1761 | |
1762 | double spb_int, spb_frac; |
1763 | spb_frac = modf(spb, &spb_int); |
1764 | secs_per_block.secs = (time_t) spb_int; |
1765 | secs_per_block.nsecs = (int) (NANOSECS_PER_SEC1000000000 * spb_frac); |
1766 | } |
1767 | break; |
1768 | |
1769 | case 'I': /* ignored_bytes at the beginning of the frame for duplications removal */ |
1770 | ignored_bytes = get_uint32(ws_optarg, "number of bytes to ignore"); |
1771 | break; |
1772 | |
1773 | case 'L': |
1774 | adjlen = true1; |
1775 | break; |
1776 | |
1777 | case 'o': |
1778 | change_offset = get_uint32(ws_optarg, "change offset"); |
1779 | break; |
1780 | |
1781 | case 'r': |
1782 | if (keep_em) { |
1783 | cmdarg_err("-r was specified twice"); |
1784 | ret = WS_EXIT_INVALID_OPTION1; |
1785 | goto clean_exit; |
1786 | } |
1787 | keep_em = true1; |
1788 | break; |
1789 | |
1790 | case 's': |
1791 | snaplen = get_nonzero_uint32(ws_optarg, "snapshot length"); |
1792 | break; |
1793 | |
1794 | case 'S': |
1795 | if (!set_strict_time_adj(ws_optarg)) { |
1796 | ret = WS_EXIT_INVALID_OPTION1; |
1797 | goto clean_exit; |
1798 | } |
1799 | do_strict_time_adjustment = true1; |
1800 | break; |
1801 | |
1802 | case 't': |
1803 | if (!set_time_adjustment(ws_optarg)) { |
1804 | ret = WS_EXIT_INVALID_OPTION1; |
1805 | goto clean_exit; |
1806 | } |
1807 | break; |
1808 | |
1809 | case 'T': |
1810 | out_frame_type = wtap_name_to_encap(ws_optarg); |
1811 | if (out_frame_type < 0) { |
1812 | cmdarg_err("\"%s\" isn't a valid encapsulation type\n", ws_optarg); |
1813 | list_encap_types(stderrstderr); |
1814 | ret = WS_EXIT_INVALID_OPTION1; |
1815 | goto clean_exit; |
1816 | } |
1817 | break; |
1818 | |
1819 | case 'V': |
1820 | if (verbose) { |
1821 | cmdarg_err("-V was specified twice"); |
1822 | ret = WS_EXIT_INVALID_OPTION1; |
1823 | goto clean_exit; |
1824 | } |
1825 | verbose = true1; |
1826 | break; |
1827 | |
1828 | case 'v': |
1829 | show_version(); |
1830 | goto clean_exit; |
1831 | break; |
1832 | |
1833 | case 'w': |
1834 | dup_detect = false0; |
1835 | dup_detect_by_time = true1; |
1836 | dup_window = MAX_DUP_DEPTH1000000; |
1837 | if (!set_rel_time(ws_optarg)) { |
1838 | ret = WS_EXIT_INVALID_OPTION1; |
1839 | goto clean_exit; |
1840 | } |
1841 | break; |
1842 | |
1843 | case '?': /* Bad options - print usage */ |
1844 | default: |
1845 | switch(ws_optopt) { |
1846 | case'F': |
1847 | list_capture_types(stdoutstdout); |
1848 | break; |
1849 | case'T': |
1850 | list_encap_types(stdoutstdout); |
1851 | break; |
1852 | case LONGOPT_COMPRESS3000 +11: |
1853 | list_output_compression_types(); |
1854 | break; |
1855 | default: |
1856 | print_usage(stderrstderr); |
1857 | ret = WS_EXIT_INVALID_OPTION1; |
1858 | break; |
1859 | } |
1860 | goto clean_exit; |
1861 | break; |
1862 | } |
1863 | } /* processing command-line options */ |
1864 | |
1865 | #ifdef DEBUG |
1866 | fprintf(stderrstderr, "Optind = %i, argc = %i\n", ws_optind, argc); |
1867 | #endif |
1868 | |
1869 | if ((argc - ws_optind) < 2) { |
1870 | print_usage(stderrstderr); |
1871 | ret = WS_EXIT_INVALID_OPTION1; |
1872 | goto clean_exit; |
1873 | } |
1874 | |
1875 | if (out_file_type_subtype == WTAP_FILE_TYPE_SUBTYPE_UNKNOWN-1) { |
1876 | /* default to pcapng */ |
1877 | out_file_type_subtype = wtap_pcapng_file_type_subtype(); |
1878 | } |
1879 | |
1880 | if (split_packet_count != 0 || !nstime_is_unset(&secs_per_block)) { |
1881 | if (!fileset_extract_prefix_suffix(argv[ws_optind+1], &fprefix, &fsuffix, &compression_type)) { |
1882 | ret = CANT_EXTRACT_PREFIX2; |
1883 | goto clean_exit; |
1884 | } |
1885 | } else if (compression_type == WTAP_UNKNOWN_COMPRESSION) { |
1886 | /* An explicitly specified compression type overrides filename |
1887 | * magic. (Should we allow specifying "no" compression with, e.g. |
1888 | * a ".gz" extension?) */ |
1889 | const char *sfx = strrchr(argv[ws_optind+1], '.'); |
1890 | if (sfx) { |
1891 | compression_type = wtap_extension_to_compression_type(sfx + 1); |
1892 | } |
1893 | } |
1894 | |
1895 | if (compression_type == WTAP_UNKNOWN_COMPRESSION) { |
1896 | compression_type = WTAP_UNCOMPRESSED; |
1897 | } |
1898 | |
1899 | if (!wtap_can_write_compression_type(compression_type)) { |
1900 | cmdarg_err("Output files can't be written as %s", |
1901 | wtap_compression_type_description(compression_type)); |
1902 | ret = WS_EXIT_INVALID_OPTION1; |
1903 | goto clean_exit; |
1904 | } |
1905 | |
1906 | if (compression_type != WTAP_UNCOMPRESSED && !wtap_dump_can_compress(out_file_type_subtype)) { |
1907 | cmdarg_err("The file format %s can't be written to output compressed format", |
1908 | wtap_file_type_subtype_name(out_file_type_subtype)); |
1909 | ret = WS_EXIT_INVALID_OPTION1; |
1910 | goto clean_exit; |
1911 | } |
1912 | |
1913 | if (err_prob >= 0.0) { |
1914 | if (!valid_seed) { |
1915 | seed = (unsigned int) (time(NULL((void*)0)) + ws_getpidgetpid()); |
1916 | } |
1917 | if (verbose) { |
1918 | fprintf(stderrstderr, "Using seed %u\n", seed); |
1919 | } |
1920 | srand(seed); |
1921 | } |
1922 | |
1923 | if (have_starttime && have_stoptime && |
1924 | nstime_cmp(&starttime, &stoptime) > 0) { |
1925 | cmdarg_err("start time is after the stop time"); |
1926 | ret = WS_EXIT_INVALID_OPTION1; |
1927 | goto clean_exit; |
1928 | } |
1929 | |
1930 | if (split_packet_count != 0 && !nstime_is_unset(&secs_per_block)) { |
1931 | cmdarg_err("can't split on both packet count and time interval"); |
1932 | cmdarg_err_cont("at the same time"); |
1933 | ret = WS_EXIT_INVALID_OPTION1; |
1934 | goto clean_exit; |
1935 | } |
1936 | |
1937 | wth = wtap_open_offline(argv[ws_optind], WTAP_TYPE_AUTO0, &read_err, &read_err_info, false0); |
1938 | |
1939 | if (!wth) { |
1940 | cfile_open_failure_message(argv[ws_optind], read_err, read_err_info); |
1941 | ret = WS_EXIT_INVALID_FILE3; |
1942 | goto clean_exit; |
1943 | } |
1944 | |
1945 | if (verbose) { |
1946 | fprintf(stderrstderr, "File %s is a %s capture file.\n", argv[ws_optind], |
1947 | wtap_file_type_subtype_description(wtap_file_type_subtype(wth))); |
1948 | } |
1949 | |
1950 | if (skip_radiotap) { |
1951 | if (ignored_bytes != 0) { |
1952 | cmdarg_err("can't skip radiotap headers and %d byte(s)", ignored_bytes); |
1953 | cmdarg_err_cont("at the start of packet at the same time"); |
1954 | ret = WS_EXIT_INVALID_OPTION1; |
1955 | goto clean_exit; |
1956 | } |
1957 | |
1958 | if (wtap_file_encap(wth) != WTAP_ENCAP_IEEE_802_11_RADIOTAP23) { |
1959 | cmdarg_err("can't skip radiotap header because input file has non-radiotap packets"); |
1960 | if (wtap_file_encap(wth) == WTAP_ENCAP_PER_PACKET-1) { |
1961 | cmdarg_err_cont("expected '%s', not all packets are necessarily that type", |
1962 | wtap_encap_description(WTAP_ENCAP_IEEE_802_11_RADIOTAP23)); |
1963 | } else { |
1964 | cmdarg_err_cont("expected '%s', packets are '%s'", |
1965 | wtap_encap_description(WTAP_ENCAP_IEEE_802_11_RADIOTAP23), |
1966 | wtap_encap_description(wtap_file_encap(wth))); |
1967 | } |
1968 | ret = WS_EXIT_INVALID_OPTION1; |
1969 | goto clean_exit; |
1970 | } |
1971 | } |
1972 | |
1973 | if (do_extract_secrets) { |
1974 | if (edit_option_specified) { |
1975 | cmdarg_err("can't extract secrets and use other options at the same time"); |
1976 | ret = WS_EXIT_INVALID_OPTION1; |
1977 | goto clean_exit; |
1978 | } |
1979 | if (compression_type != WTAP_UNCOMPRESSED) { |
1980 | cmdarg_err("compression isn't supported for extracting secrets"); |
1981 | ret = WS_EXIT_INVALID_OPTION1; |
1982 | goto clean_exit; |
1983 | } |
1984 | ret = extract_secrets(wth, argv[ws_optind+1], &read_err, &read_err_info); |
1985 | |
1986 | if (read_err != 0) { |
1987 | /* Print a message noting that the read failed somewhere along the |
1988 | * line. */ |
1989 | cfile_read_failure_message(argv[ws_optind], read_err, read_err_info); |
1990 | } |
1991 | goto clean_exit; |
1992 | } |
1993 | |
1994 | wtap_dump_params_init_no_idbs(¶ms, wth); |
1995 | |
1996 | /* |
1997 | * Discard any secrets we read in while opening the file. |
1998 | */ |
1999 | if (discard_all_secrets) { |
2000 | wtap_dump_params_discard_decryption_secrets(¶ms); |
2001 | } |
2002 | |
2003 | /* |
2004 | * Discard capture file comments. |
2005 | */ |
2006 | if (discard_cap_comments) { |
2007 | for (unsigned b = 0; b < params.shb_hdrs->len; b++) { |
2008 | wtap_block_t shb = g_array_index(params.shb_hdrs, wtap_block_t, b)(((wtap_block_t*) (void *) (params.shb_hdrs)->data) [(b)]); |
2009 | while (WTAP_OPTTYPE_SUCCESS == wtap_block_remove_nth_option_instance(shb, OPT_COMMENT1, 0)) { |
2010 | ; |
2011 | } |
2012 | } |
2013 | } |
2014 | |
2015 | /* |
2016 | * Add new capture file comments. |
2017 | */ |
2018 | if (capture_comments != NULL((void*)0)) { |
2019 | for (unsigned b = 0; b < params.shb_hdrs->len; b++) { |
2020 | wtap_block_t shb = g_array_index(params.shb_hdrs, wtap_block_t, b)(((wtap_block_t*) (void *) (params.shb_hdrs)->data) [(b)]); |
2021 | for (unsigned c = 0; c < capture_comments->len; c++) { |
2022 | char *comment = (char *)g_ptr_array_index(capture_comments, c)((capture_comments)->pdata)[c]; |
2023 | wtap_block_add_string_option(shb, OPT_COMMENT1, comment, strlen(comment)); |
2024 | } |
2025 | } |
2026 | } |
2027 | |
2028 | if (dsb_filenames) { |
2029 | for (unsigned k = 0; k < dsb_filenames->len; k++) { |
2030 | uint32_t secrets_type_id = g_array_index(dsb_types, uint32_t, k)(((uint32_t*) (void *) (dsb_types)->data) [(k)]); |
2031 | const char *secrets_filename = (const char *)g_ptr_array_index(dsb_filenames, k)((dsb_filenames)->pdata)[k]; |
2032 | char *data; |
2033 | size_t data_len; |
2034 | wtap_block_t block; |
2035 | wtapng_dsb_mandatory_t *dsb; |
2036 | GError *err = NULL((void*)0); |
2037 | |
2038 | if (!g_file_get_contents(secrets_filename, &data, &data_len, &err)) { |
2039 | cmdarg_err("\"%s\" could not be read: %s", secrets_filename, err->message); |
2040 | g_clear_error(&err); |
2041 | ret = WS_EXIT_INVALID_OPTION1; |
2042 | goto clean_exit; |
2043 | } |
2044 | if (data_len == 0) { |
2045 | cmdarg_err("\"%s\" is an empty file, ignoring", secrets_filename); |
2046 | g_free(data); |
2047 | continue; |
2048 | } |
2049 | if (data_len >= INT_MAX2147483647) { |
2050 | cmdarg_err("\"%s\" is too large, ignoring", secrets_filename); |
2051 | g_free(data); |
2052 | continue; |
2053 | } |
2054 | |
2055 | /* Warn for badly formatted files, but proceed anyway. */ |
2056 | validate_secrets_file(secrets_filename, secrets_type_id, data); |
2057 | |
2058 | block = wtap_block_create(WTAP_BLOCK_DECRYPTION_SECRETS); |
2059 | dsb = (wtapng_dsb_mandatory_t *)wtap_block_get_mandatory_data(block); |
2060 | dsb->secrets_type = secrets_type_id; |
2061 | dsb->secrets_len = (unsigned)data_len; |
2062 | dsb->secrets_data = data; |
2063 | if (params.dsbs_initial == NULL((void*)0)) { |
2064 | params.dsbs_initial = g_array_new(FALSE(0), FALSE(0), sizeof(wtap_block_t)); |
2065 | } |
2066 | g_array_append_val(params.dsbs_initial, block)g_array_append_vals (params.dsbs_initial, &(block), 1); |
2067 | } |
2068 | } |
2069 | |
2070 | /* |
2071 | * If an encapsulation type was specified, override the encapsulation |
2072 | * type of the input file. |
2073 | */ |
2074 | if (out_frame_type != -2) |
2075 | params.encap = out_frame_type; |
2076 | |
2077 | /* |
2078 | * If a snapshot length was specified, and it's less than the snapshot |
2079 | * length of the input file, override the snapshot length of the input |
2080 | * file. |
2081 | */ |
2082 | if (snaplen != 0 && snaplen < wtap_snapshot_length(wth)) |
2083 | params.snaplen = snaplen; |
2084 | |
2085 | /* |
2086 | * Now process the arguments following the input and output file |
2087 | * names, if any; they specify packets to include/exclude. |
2088 | */ |
2089 | for (i = ws_optind + 2; i < argc; i++) |
2090 | if (add_selection(argv[i], &max_packet_number) == false0) |
2091 | break; |
2092 | |
2093 | if (keep_em && max_selected == 0) { |
2094 | cmdarg_err("must specify packets to keep when using -r"); |
2095 | ret = WS_EXIT_INVALID_OPTION1; |
2096 | goto clean_exit; |
2097 | } |
2098 | |
2099 | if (!keep_em) |
2100 | max_packet_number = UINT64_MAX(18446744073709551615UL); |
2101 | |
2102 | if (dup_detect || dup_detect_by_time) { |
2103 | for (i = 0; i < dup_window; i++) { |
2104 | memset(&fd_hash[i].digest, 0, 16); |
2105 | fd_hash[i].len = 0; |
2106 | nstime_set_unset(&fd_hash[i].frame_time); |
2107 | } |
2108 | } |
2109 | |
2110 | /* Set up an array of all IDBs seen */ |
2111 | idbs_seen = g_array_new(FALSE(0), FALSE(0), sizeof(wtap_block_t)); |
2112 | |
2113 | /* Read all of the packets in turn */ |
2114 | wtap_rec_init(&read_rec, 1514); |
2115 | while (wtap_read(wth, &read_rec, &read_err, &read_err_info, &data_offset)) { |
2116 | /* |
2117 | * XXX - what about non-packet records in the file after this? |
2118 | * NRBs, DSBs, and ISBs are now written when wtap_dump_close() calls |
2119 | * pcapng_dump_finish(), and we handle IDBs below, but what about |
2120 | * custom blocks? |
2121 | */ |
2122 | if (max_packet_number <= read_count) |
2123 | break; |
2124 | |
2125 | read_count++; |
2126 | |
2127 | /* Extra actions for the first packet */ |
2128 | if (read_count == 1) { |
2129 | if (split_packet_count != 0 || !nstime_is_unset(&secs_per_block)) { |
2130 | filename = fileset_get_filename_by_pattern(block_cnt++, |
2131 | (read_rec.presence_flags & WTAP_HAS_TS0x00000001) ? &read_rec.ts : NULL((void*)0), |
2132 | fprefix, fsuffix); |
2133 | } else { |
2134 | filename = g_strdup(argv[ws_optind+1])g_strdup_inline (argv[ws_optind+1]); |
2135 | } |
2136 | ws_assert(filename)do { if ((1) && !(filename)) ws_log_fatal_full("Main" , LOG_LEVEL_ERROR, "editcap.c", 2136, __func__, "assertion failed: %s" , "filename"); } while (0); |
2137 | |
2138 | /* If we don't have an application name add one */ |
2139 | if (wtap_block_get_string_option_value(g_array_index(params.shb_hdrs, wtap_block_t, 0)(((wtap_block_t*) (void *) (params.shb_hdrs)->data) [(0)]), OPT_SHB_USERAPPL4, &shb_user_appl) != WTAP_OPTTYPE_SUCCESS) { |
2140 | wtap_block_add_string_option_format(g_array_index(params.shb_hdrs, wtap_block_t, 0)(((wtap_block_t*) (void *) (params.shb_hdrs)->data) [(0)]), OPT_SHB_USERAPPL4, "%s", get_appname_and_version()); |
2141 | } |
2142 | |
2143 | pdh = editcap_dump_open(filename, ¶ms, idbs_seen, &write_err, |
2144 | &write_err_info, compression_type); |
2145 | |
2146 | if (pdh == NULL((void*)0)) { |
2147 | cfile_dump_open_failure_message(filename, |
2148 | write_err, write_err_info, |
2149 | out_file_type_subtype); |
2150 | ret = WS_EXIT_INVALID_FILE3; |
2151 | goto clean_exit; |
2152 | } |
2153 | } /* first packet only handling */ |
2154 | |
2155 | /* |
2156 | * Process whatever IDBs we haven't seen yet. |
2157 | */ |
2158 | if (!process_new_idbs(wth, pdh, idbs_seen, &write_err, &write_err_info)) { |
2159 | cfile_write_failure_message(argv[ws_optind], filename, |
2160 | write_err, write_err_info, |
2161 | read_count, |
2162 | out_file_type_subtype); |
2163 | ret = DUMP_ERROR2; |
2164 | |
2165 | /* |
2166 | * Close the dump file, but don't report an error |
2167 | * or set the exit code, as we've already reported |
2168 | * an error. |
2169 | */ |
2170 | wtap_dump_close(pdh, NULL((void*)0), &write_err, &write_err_info); |
2171 | goto clean_exit; |
2172 | } |
2173 | |
2174 | buf = ws_buffer_start_ptr(&read_rec.data); |
2175 | |
2176 | /* |
2177 | * Not all packets have time stamps. Only process the time |
2178 | * stamp if we have one. |
2179 | */ |
2180 | if (read_rec.presence_flags & WTAP_HAS_TS0x00000001) { |
2181 | if (!nstime_is_unset(&secs_per_block)) { |
2182 | if (nstime_is_unset(&block_next)) { |
2183 | block_next = read_rec.ts; |
2184 | nstime_add(&block_next, &secs_per_block)nstime_sum(&block_next, &block_next, &secs_per_block ); |
2185 | } |
2186 | while (nstime_cmp(&read_rec.ts, &block_next) > 0) { /* time for the next file */ |
2187 | |
2188 | /* We presumably want to write the DSBs from files given |
2189 | * on the command line to every file. |
2190 | */ |
2191 | wtap_block_array_ref(params.dsbs_initial); |
2192 | if (!wtap_dump_close(pdh, NULL((void*)0), &write_err, &write_err_info)) { |
2193 | cfile_close_failure_message(filename, write_err, |
2194 | write_err_info); |
2195 | ret = WRITE_ERROR2; |
2196 | goto clean_exit; |
2197 | } |
2198 | g_free(filename); |
2199 | /* Use the interval start time for the filename. */ |
2200 | filename = fileset_get_filename_by_pattern(block_cnt++, &block_next, fprefix, fsuffix); |
2201 | ws_assert(filename)do { if ((1) && !(filename)) ws_log_fatal_full("Main" , LOG_LEVEL_ERROR, "editcap.c", 2201, __func__, "assertion failed: %s" , "filename"); } while (0); |
2202 | nstime_add(&block_next, &secs_per_block)nstime_sum(&block_next, &block_next, &secs_per_block ); /* reset for next interval */ |
2203 | |
2204 | if (verbose) |
2205 | fprintf(stderrstderr, "Continuing writing in file %s\n", filename); |
2206 | |
2207 | pdh = editcap_dump_open(filename, ¶ms, idbs_seen, |
2208 | &write_err, &write_err_info, compression_type); |
2209 | |
2210 | if (pdh == NULL((void*)0)) { |
2211 | cfile_dump_open_failure_message(filename, |
2212 | write_err, |
2213 | write_err_info, |
2214 | out_file_type_subtype); |
2215 | ret = WS_EXIT_INVALID_FILE3; |
2216 | goto clean_exit; |
2217 | } |
2218 | } |
2219 | } |
2220 | } /* time stamp handling */ |
2221 | |
2222 | if (split_packet_count != 0) { |
2223 | /* time for the next file? */ |
2224 | if (written_count > 0 && (written_count % split_packet_count) == 0) { |
2225 | |
2226 | /* We presumably want to write the DSBs from files given |
2227 | * on the command line to every file. |
2228 | */ |
2229 | wtap_block_array_ref(params.dsbs_initial); |
2230 | if (!wtap_dump_close(pdh, NULL((void*)0), &write_err, &write_err_info)) { |
2231 | cfile_close_failure_message(filename, write_err, |
2232 | write_err_info); |
2233 | ret = WRITE_ERROR2; |
2234 | goto clean_exit; |
2235 | } |
2236 | |
2237 | g_free(filename); |
2238 | filename = fileset_get_filename_by_pattern(block_cnt++, |
2239 | (read_rec.presence_flags & WTAP_HAS_TS0x00000001) ? &read_rec.ts : NULL((void*)0), |
2240 | fprefix, fsuffix); |
2241 | ws_assert(filename)do { if ((1) && !(filename)) ws_log_fatal_full("Main" , LOG_LEVEL_ERROR, "editcap.c", 2241, __func__, "assertion failed: %s" , "filename"); } while (0); |
2242 | |
2243 | if (verbose) |
2244 | fprintf(stderrstderr, "Continuing writing in file %s\n", filename); |
2245 | |
2246 | pdh = editcap_dump_open(filename, ¶ms, idbs_seen, |
2247 | &write_err, &write_err_info, compression_type); |
2248 | if (pdh == NULL((void*)0)) { |
2249 | cfile_dump_open_failure_message(filename, |
2250 | write_err, write_err_info, |
2251 | out_file_type_subtype); |
2252 | ret = WS_EXIT_INVALID_FILE3; |
2253 | goto clean_exit; |
2254 | } |
2255 | } |
2256 | } /* split packet handling */ |
2257 | |
2258 | if (check_startstop) { |
2259 | ts_okay = false0; |
2260 | /* |
2261 | * Is the packet in the selected timeframe? |
2262 | * If the packet has no time stamp, the answer is "no". |
2263 | */ |
2264 | if (read_rec.presence_flags & WTAP_HAS_TS0x00000001) { |
2265 | if (have_starttime && have_stoptime) { |
2266 | ts_okay = nstime_cmp(&read_rec.ts, &starttime) >= 0 && |
2267 | nstime_cmp(&read_rec.ts, &stoptime) < 0; |
2268 | } else if (have_starttime) { |
2269 | ts_okay = nstime_cmp(&read_rec.ts, &starttime) >= 0; |
2270 | } else if (have_stoptime) { |
2271 | ts_okay = nstime_cmp(&read_rec.ts, &stoptime) < 0; |
2272 | } |
2273 | } |
2274 | } else { |
2275 | /* |
2276 | * No selected timeframe, so all packets are "in the |
2277 | * selected timeframe". |
2278 | */ |
2279 | ts_okay = true1; |
2280 | } |
2281 | |
2282 | if (ts_okay && ((!selected(count) && !keep_em) |
2283 | || (selected(count) && keep_em))) { |
2284 | /* Write the record, possibly after modifying it. */ |
2285 | |
2286 | if (verbose && !dup_detect && !dup_detect_by_time) |
2287 | fprintf(stderrstderr, "Packet: %" PRIu64"l" "u" "\n", count); |
2288 | |
2289 | if (read_rec.presence_flags & WTAP_HAS_TS0x00000001) { |
2290 | /* Do we adjust timestamps to ensure strict chronological |
2291 | * order? */ |
2292 | if (do_strict_time_adjustment) { |
2293 | if (previous_time.secs || previous_time.nsecs) { |
2294 | if (!strict_time_adj.is_negative) { |
2295 | nstime_t current; |
2296 | nstime_t delta; |
2297 | |
2298 | current = read_rec.ts; |
2299 | |
2300 | nstime_delta(&delta, ¤t, &previous_time); |
2301 | |
2302 | if (delta.secs < 0 || delta.nsecs < 0) { |
2303 | /* |
2304 | * A negative delta indicates that the current packet |
2305 | * has an absolute timestamp less than the previous packet |
2306 | * that it is being compared to. This is NOT a normal |
2307 | * situation since trace files usually have packets in |
2308 | * chronological order (oldest to newest). |
2309 | */ |
2310 | /* fprintf(stderr, "++out of order, need to adjust this packet!\n"); */ |
2311 | read_rec.ts.secs = previous_time.secs + strict_time_adj.tv.secs; |
2312 | read_rec.ts.nsecs = previous_time.nsecs; |
2313 | if (read_rec.ts.nsecs + strict_time_adj.tv.nsecs >= ONE_BILLION1000000000) { |
2314 | /* carry */ |
2315 | read_rec.ts.secs++; |
2316 | read_rec.ts.nsecs += strict_time_adj.tv.nsecs - ONE_BILLION1000000000; |
2317 | } else { |
2318 | read_rec.ts.nsecs += strict_time_adj.tv.nsecs; |
2319 | } |
2320 | } |
2321 | } else { |
2322 | /* |
2323 | * A negative strict time adjustment is requested. |
2324 | * Unconditionally set each timestamp to previous |
2325 | * packet's timestamp plus delta. |
2326 | */ |
2327 | read_rec.ts.secs = previous_time.secs + strict_time_adj.tv.secs; |
2328 | read_rec.ts.nsecs = previous_time.nsecs; |
2329 | if (read_rec.ts.nsecs + strict_time_adj.tv.nsecs >= ONE_BILLION1000000000) { |
2330 | /* carry */ |
2331 | read_rec.ts.secs++; |
2332 | read_rec.ts.nsecs += strict_time_adj.tv.nsecs - ONE_BILLION1000000000; |
2333 | } else { |
2334 | read_rec.ts.nsecs += strict_time_adj.tv.nsecs; |
2335 | } |
2336 | } |
2337 | } |
2338 | previous_time = read_rec.ts; |
2339 | } |
2340 | |
2341 | if (time_adj.tv.secs != 0) { |
2342 | if (time_adj.is_negative) |
2343 | read_rec.ts.secs -= time_adj.tv.secs; |
2344 | else |
2345 | read_rec.ts.secs += time_adj.tv.secs; |
2346 | } |
2347 | |
2348 | if (time_adj.tv.nsecs != 0) { |
2349 | if (time_adj.is_negative) { /* subtract */ |
2350 | if (read_rec.ts.nsecs < time_adj.tv.nsecs) { /* borrow */ |
2351 | read_rec.ts.secs--; |
2352 | read_rec.ts.nsecs += ONE_BILLION1000000000; |
2353 | } |
2354 | read_rec.ts.nsecs -= time_adj.tv.nsecs; |
2355 | } else { /* add */ |
2356 | if (read_rec.ts.nsecs + time_adj.tv.nsecs >= ONE_BILLION1000000000) { |
2357 | /* carry */ |
2358 | read_rec.ts.secs++; |
2359 | read_rec.ts.nsecs += time_adj.tv.nsecs - ONE_BILLION1000000000; |
2360 | } else { |
2361 | read_rec.ts.nsecs += time_adj.tv.nsecs; |
2362 | } |
2363 | } |
2364 | } |
2365 | } /* time stamp adjustment */ |
2366 | |
2367 | if (read_rec.rec_type == REC_TYPE_PACKET0) { |
2368 | if (snaplen != 0) { |
2369 | /* Limit capture length to snaplen */ |
2370 | if (read_rec.rec_header.packet_header.caplen > snaplen) { |
2371 | read_rec.rec_header.packet_header.caplen = snaplen; |
2372 | } |
2373 | /* If -L, also set reported length to snaplen */ |
2374 | if (adjlen && read_rec.rec_header.packet_header.len > snaplen) { |
2375 | read_rec.rec_header.packet_header.len = snaplen; |
2376 | } |
2377 | } |
2378 | |
2379 | /* |
2380 | * If an encapsulation type was specified, override the |
2381 | * encapsulation type of the packet. |
2382 | */ |
2383 | if (out_frame_type != -2) { |
2384 | read_rec.rec_header.packet_header.pkt_encap = out_frame_type; |
2385 | } |
2386 | |
2387 | /* |
2388 | * CHOP |
2389 | */ |
2390 | handle_chopping(chop, &read_rec.rec_header.packet_header, |
2391 | &buf, adjlen); |
2392 | |
2393 | /* set unused info */ |
2394 | if (set_unused) { |
2395 | /* set unused bytes to zero so that duplicates check ignores unused bytes */ |
2396 | set_unused_info(&read_rec.rec_header.packet_header, buf); |
2397 | } |
2398 | |
2399 | /* remove vlan info */ |
2400 | if (rem_vlan) { |
2401 | remove_vlan_info(&read_rec.rec_header.packet_header, buf); |
2402 | } |
2403 | |
2404 | /* suppress duplicates by packet window */ |
2405 | if (dup_detect) { |
2406 | if (is_duplicate(buf, read_rec.rec_header.packet_header.caplen)) { |
2407 | if (verbose) { |
2408 | fprintf(stderrstderr, "Skipped: %" PRIu64"l" "u" ", Len: %u, MD5 Hash: ", |
2409 | count, |
2410 | read_rec.rec_header.packet_header.caplen); |
2411 | for (i = 0; i < 16; i++) |
2412 | fprintf(stderrstderr, "%02x", |
2413 | (unsigned char)fd_hash[cur_dup_entry].digest[i]); |
2414 | fprintf(stderrstderr, "\n"); |
2415 | } |
2416 | duplicate_count++; |
2417 | count++; |
2418 | continue; |
2419 | } else { |
2420 | if (verbose) { |
2421 | fprintf(stderrstderr, "Packet: %" PRIu64"l" "u" ", Len: %u, MD5 Hash: ", |
2422 | count, |
2423 | read_rec.rec_header.packet_header.caplen); |
2424 | for (i = 0; i < 16; i++) |
2425 | fprintf(stderrstderr, "%02x", |
2426 | (unsigned char)fd_hash[cur_dup_entry].digest[i]); |
2427 | fprintf(stderrstderr, "\n"); |
2428 | } |
2429 | } |
2430 | } /* suppression of duplicates */ |
2431 | |
2432 | if (read_rec.presence_flags & WTAP_HAS_TS0x00000001) { |
2433 | /* suppress duplicates by time window */ |
2434 | if (dup_detect_by_time) { |
2435 | nstime_t current; |
2436 | |
2437 | current.secs = read_rec.ts.secs; |
2438 | current.nsecs = read_rec.ts.nsecs; |
2439 | |
2440 | if (is_duplicate_rel_time(buf, |
2441 | read_rec.rec_header.packet_header.caplen, |
2442 | ¤t)) { |
2443 | if (verbose) { |
2444 | fprintf(stderrstderr, "Skipped: %" PRIu64"l" "u" ", Len: %u, MD5 Hash: ", |
2445 | count, |
2446 | read_rec.rec_header.packet_header.caplen); |
2447 | for (i = 0; i < 16; i++) |
2448 | fprintf(stderrstderr, "%02x", |
2449 | (unsigned char)fd_hash[cur_dup_entry].digest[i]); |
2450 | fprintf(stderrstderr, "\n"); |
2451 | } |
2452 | duplicate_count++; |
2453 | count++; |
2454 | continue; |
2455 | } else { |
2456 | if (verbose) { |
2457 | fprintf(stderrstderr, "Packet: %" PRIu64"l" "u" ", Len: %u, MD5 Hash: ", |
2458 | count, |
2459 | read_rec.rec_header.packet_header.caplen); |
2460 | for (i = 0; i < 16; i++) |
2461 | fprintf(stderrstderr, "%02x", |
2462 | (unsigned char)fd_hash[cur_dup_entry].digest[i]); |
2463 | fprintf(stderrstderr, "\n"); |
2464 | } |
2465 | } |
2466 | } |
2467 | } /* suppress duplicates by time window */ |
2468 | } |
2469 | |
2470 | /* Random error mutation */ |
2471 | if (err_prob > 0.0) { |
2472 | mutate_packet_data(&read_rec, buf, change_offset, count); |
2473 | } /* random error mutation */ |
2474 | |
2475 | /* Discard all packet comments when writing */ |
2476 | if (discard_pkt_comments) { |
2477 | while (WTAP_OPTTYPE_SUCCESS == wtap_block_remove_nth_option_instance(read_rec.block, OPT_COMMENT1, 0)) { |
2478 | read_rec.block_was_modified = true1; |
2479 | } |
2480 | } |
2481 | |
2482 | /* Find a packet comment we may need to write */ |
2483 | if (frames_user_comments) { |
2484 | const char *comment = |
2485 | (const char*)g_tree_lookup(frames_user_comments, &read_count); |
2486 | if (comment != NULL((void*)0)) { |
2487 | /* Erase any existing comments before adding the new one */ |
2488 | while (WTAP_OPTTYPE_SUCCESS == wtap_block_remove_nth_option_instance(read_rec.block, OPT_COMMENT1, 0)) { |
2489 | read_rec.block_was_modified = true1; |
2490 | } |
2491 | |
2492 | /* The comment is not modified by dumper, cast away. */ |
2493 | wtap_block_add_string_option(read_rec.block, OPT_COMMENT1, (char *)comment, strlen((char *)comment)); |
2494 | read_rec.block_was_modified = true1; |
2495 | } else { |
2496 | read_rec.block_was_modified = false0; |
2497 | } |
2498 | } |
2499 | |
2500 | if (discard_all_secrets) { |
2501 | /* |
2502 | * Discard any secrets we've read since the last packet |
2503 | * we wrote. |
2504 | */ |
2505 | wtap_dump_discard_decryption_secrets(pdh); |
2506 | } |
2507 | |
2508 | /* Attempt to dump out current frame to the output file */ |
2509 | if (!wtap_dump(pdh, &read_rec, buf, &write_err, &write_err_info)) { |
2510 | cfile_write_failure_message(argv[ws_optind], filename, |
2511 | write_err, write_err_info, |
2512 | read_count, |
2513 | out_file_type_subtype); |
2514 | ret = DUMP_ERROR2; |
2515 | |
2516 | /* |
2517 | * Close the dump file, but don't report an error |
2518 | * or set the exit code, as we've already reported |
2519 | * an error. |
2520 | */ |
2521 | wtap_dump_close(pdh, NULL((void*)0), &write_err, &write_err_info); |
2522 | goto clean_exit; |
2523 | } |
2524 | written_count++; |
2525 | } |
2526 | count++; |
2527 | wtap_rec_reset(&read_rec); |
2528 | } |
2529 | wtap_rec_cleanup(&read_rec); |
2530 | |
2531 | if (verbose) |
2532 | fprintf(stderrstderr, "Total selected: %" PRIu64"l" "u" "\n", written_count); |
2533 | |
2534 | if (read_err != 0) { |
2535 | /* Print a message noting that the read failed somewhere along the |
2536 | * line. */ |
2537 | cfile_read_failure_message(argv[ws_optind], read_err, read_err_info); |
2538 | } |
2539 | |
2540 | if (!pdh) { |
2541 | /* No valid packets found, open the outfile so we can write an |
2542 | * empty header */ |
2543 | g_free (filename); |
2544 | filename = g_strdup(argv[ws_optind+1])g_strdup_inline (argv[ws_optind+1]); |
2545 | |
2546 | pdh = editcap_dump_open(filename, ¶ms, idbs_seen, &write_err, |
2547 | &write_err_info, compression_type); |
2548 | if (pdh == NULL((void*)0)) { |
2549 | cfile_dump_open_failure_message(filename, |
2550 | write_err, write_err_info, |
2551 | out_file_type_subtype); |
2552 | ret = WS_EXIT_INVALID_FILE3; |
2553 | goto clean_exit; |
2554 | } |
2555 | } |
2556 | |
2557 | /* |
2558 | * Process whatever IDBs we haven't seen yet. |
2559 | */ |
2560 | if (!process_new_idbs(wth, pdh, idbs_seen, &write_err, &write_err_info)) { |
2561 | cfile_write_failure_message(argv[ws_optind], filename, |
2562 | write_err, write_err_info, |
2563 | read_count, |
2564 | out_file_type_subtype); |
2565 | ret = DUMP_ERROR2; |
2566 | |
2567 | /* |
2568 | * Close the dump file, but don't report an error |
2569 | * or set the exit code, as we've already reported |
2570 | * an error. |
2571 | */ |
2572 | wtap_dump_close(pdh, NULL((void*)0), &write_err, &write_err_info); |
2573 | goto clean_exit; |
2574 | } |
2575 | |
2576 | if (!wtap_dump_close(pdh, NULL((void*)0), &write_err, &write_err_info)) { |
2577 | cfile_close_failure_message(filename, write_err, write_err_info); |
2578 | ret = WRITE_ERROR2; |
2579 | goto clean_exit; |
2580 | } |
2581 | |
2582 | if (dup_detect) { |
2583 | fprintf(stderrstderr, "%" PRIu64"l" "u" " packet%s seen, %" PRIu64"l" "u" " packet%s skipped with duplicate window of %i packets.\n", |
2584 | count - 1, plurality(count - 1, "", "s")((count - 1) == 1 ? ("") : ("s")), duplicate_count, |
2585 | plurality(duplicate_count, "", "s")((duplicate_count) == 1 ? ("") : ("s")), dup_window); |
2586 | } else if (dup_detect_by_time) { |
2587 | fprintf(stderrstderr, "%" PRIu64"l" "u" " packet%s seen, %" PRIu64"l" "u" " packet%s skipped with duplicate time window equal to or less than %ld.%09ld seconds.\n", |
2588 | count - 1, plurality(count - 1, "", "s")((count - 1) == 1 ? ("") : ("s")), duplicate_count, |
2589 | plurality(duplicate_count, "", "s")((duplicate_count) == 1 ? ("") : ("s")), |
2590 | (long)relative_time_window.secs, |
2591 | (long int)relative_time_window.nsecs); |
2592 | } |
2593 | |
2594 | clean_exit: |
2595 | g_free(fprefix); |
2596 | g_free(fsuffix); |
2597 | |
2598 | if (filename) { |
2599 | g_free(filename); |
2600 | } |
2601 | if (frames_user_comments) { |
2602 | g_tree_destroy(frames_user_comments); |
2603 | } |
2604 | if (dsb_filenames) { |
2605 | g_array_free(dsb_types, TRUE(!(0))); |
2606 | g_ptr_array_free(dsb_filenames, TRUE(!(0))); |
2607 | } |
2608 | if (idbs_seen != NULL((void*)0)) { |
2609 | for (unsigned b = 0; b < idbs_seen->len; b++) { |
2610 | wtap_block_t if_data = g_array_index(idbs_seen, wtap_block_t, b)(((wtap_block_t*) (void *) (idbs_seen)->data) [(b)]); |
2611 | wtap_block_unref(if_data); |
2612 | } |
2613 | g_array_free(idbs_seen, TRUE(!(0))); |
2614 | } |
2615 | g_free(params.idb_inf); |
2616 | wtap_dump_params_cleanup(¶ms); |
2617 | if (wth != NULL((void*)0)) |
2618 | wtap_close(wth); |
2619 | wtap_rec_reset(&read_rec); |
2620 | wtap_cleanup(); |
2621 | free_progdirs(); |
2622 | if (capture_comments != NULL((void*)0)) { |
2623 | g_ptr_array_free(capture_comments, TRUE(!(0))); |
2624 | capture_comments = NULL((void*)0); |
2625 | } |
2626 | return ret; |
2627 | } |
2628 | |
2629 | /* Skip meta-information read from file to return offset of real |
2630 | * protocol data */ |
2631 | static unsigned |
2632 | find_dct2000_real_data(const uint8_t *buf) |
2633 | { |
2634 | unsigned n = 0; |
2635 | |
2636 | for (n = 0; buf[n] != '\0'; n++); /* Context name */ |
2637 | n++; |
2638 | n++; /* Context port number */ |
2639 | for (; buf[n] != '\0'; n++); /* Timestamp */ |
2640 | n++; |
2641 | for (; buf[n] != '\0'; n++); /* Protocol name */ |
2642 | n++; |
2643 | for (; buf[n] != '\0'; n++); /* Variant number (as string) */ |
2644 | n++; |
2645 | for (; buf[n] != '\0'; n++); /* Outhdr (as string) */ |
2646 | n++; |
2647 | n += 2; /* Direction & encap */ |
2648 | |
2649 | return n; |
2650 | } |
2651 | |
2652 | /* |
2653 | * We support up to 2 chopping regions in a single pass: one specified by the |
2654 | * positive chop length, and one by the negative chop length. |
2655 | */ |
2656 | static void |
2657 | handle_chopping(chop_t chop, wtap_packet_header *phdr, uint8_t **buf, |
2658 | bool_Bool adjlen) |
2659 | { |
2660 | /* If we're not chopping anything from one side, then the offset for that |
2661 | * side is meaningless. */ |
2662 | if (chop.len_begin == 0) |
2663 | chop.off_begin_pos = chop.off_begin_neg = 0; |
2664 | if (chop.len_end == 0) |
2665 | chop.off_end_pos = chop.off_end_neg = 0; |
2666 | |
2667 | if (chop.off_begin_neg < 0) { |
2668 | chop.off_begin_pos += phdr->caplen + chop.off_begin_neg; |
2669 | chop.off_begin_neg = 0; |
2670 | } |
2671 | if (chop.off_end_pos > 0) { |
2672 | chop.off_end_neg += chop.off_end_pos - phdr->caplen; |
2673 | chop.off_end_pos = 0; |
2674 | } |
2675 | |
2676 | /* If we've crossed chopping regions, swap them */ |
2677 | if (chop.len_begin && chop.len_end) { |
2678 | if (chop.off_begin_pos > ((int)phdr->caplen + chop.off_end_neg)) { |
2679 | int tmp_len, tmp_off; |
2680 | |
2681 | tmp_off = phdr->caplen + chop.off_end_neg + chop.len_end; |
2682 | tmp_len = -chop.len_end; |
2683 | |
2684 | chop.off_end_neg = chop.len_begin + chop.off_begin_pos - phdr->caplen; |
2685 | chop.len_end = -chop.len_begin; |
2686 | |
2687 | chop.len_begin = tmp_len; |
2688 | chop.off_begin_pos = tmp_off; |
2689 | } |
2690 | } |
2691 | |
2692 | /* Make sure we don't chop off more than we have available */ |
2693 | if (phdr->caplen < (uint32_t)(chop.off_begin_pos - chop.off_end_neg)) { |
2694 | chop.len_begin = 0; |
2695 | chop.len_end = 0; |
2696 | } |
2697 | if ((uint32_t)(chop.len_begin - chop.len_end) > |
2698 | (phdr->caplen - (uint32_t)(chop.off_begin_pos - chop.off_end_neg))) { |
2699 | chop.len_begin = phdr->caplen - (chop.off_begin_pos - chop.off_end_neg); |
2700 | chop.len_end = 0; |
2701 | } |
2702 | |
2703 | /* Handle chopping from the beginning. Note that if a beginning offset |
2704 | * was specified, we need to keep that piece */ |
2705 | if (chop.len_begin > 0) { |
2706 | if (chop.off_begin_pos > 0) { |
2707 | memmove(*buf + chop.off_begin_pos, |
2708 | *buf + chop.off_begin_pos + chop.len_begin, |
2709 | phdr->caplen - (chop.off_begin_pos + chop.len_begin)); |
2710 | } else { |
2711 | *buf += chop.len_begin; |
2712 | } |
2713 | phdr->caplen -= chop.len_begin; |
2714 | |
2715 | if (adjlen) { |
2716 | if (phdr->len > (uint32_t)chop.len_begin) |
2717 | phdr->len -= chop.len_begin; |
2718 | else |
2719 | phdr->len = 0; |
2720 | } |
2721 | } |
2722 | |
2723 | /* Handle chopping from the end. Note that if an ending offset was |
2724 | * specified, we need to keep that piece */ |
2725 | if (chop.len_end < 0) { |
2726 | if (chop.off_end_neg < 0) { |
2727 | memmove(*buf + (int)phdr->caplen + (chop.len_end + chop.off_end_neg), |
2728 | *buf + (int)phdr->caplen + chop.off_end_neg, |
2729 | -chop.off_end_neg); |
2730 | } |
2731 | phdr->caplen += chop.len_end; |
2732 | |
2733 | if (adjlen) { |
2734 | if (((signed int) phdr->len + chop.len_end) > 0) |
2735 | phdr->len += chop.len_end; |
2736 | else |
2737 | phdr->len = 0; |
2738 | } |
2739 | } |
2740 | } |
2741 | |
2742 | /* |
2743 | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
2744 | * |
2745 | * Local variables: |
2746 | * c-basic-offset: 4 |
2747 | * tab-width: 8 |
2748 | * indent-tabs-mode: nil |
2749 | * End: |
2750 | * |
2751 | * vi: set shiftwidth=4 tabstop=8 expandtab: |
2752 | * :indentSize=4:tabSize=8:noTabs=true: |
2753 | */ |