]> The Tcpdump Group git mirrors - tcpslice/blob - tcpslice.c
Merge pull request #22 from jsoref/spelling
[tcpslice] / tcpslice.c
1 /*
2 * Copyright (c) 1991, 1992, 1993, 1995, 1996, 1997, 1999, 2000
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that: (1) source code distributions
7 * retain the above copyright notice and this paragraph in its entirety, (2)
8 * distributions including binary code include the above copyright notice and
9 * this paragraph in its entirety in the documentation or other materials
10 * provided with the distribution, and (3) all advertising materials mentioning
11 * features or use of this software display the following acknowledgement:
12 * ``This product includes software developed by the University of California,
13 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
14 * the University nor the names of its contributors may be used to endorse
15 * or promote products derived from this software without specific prior
16 * written permission.
17 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 */
21
22 /*
23 * tcpslice - extract pieces of and/or glue together pcap files
24 */
25
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29
30 #include <sys/types.h>
31 #include <sys/file.h>
32 #include <sys/stat.h>
33 #include <sys/time.h>
34
35 #include <ctype.h>
36 #ifdef HAVE_FCNTL_H
37 #include <fcntl.h>
38 #endif
39 #include <memory.h>
40 #include <pcap.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <time.h>
45 #include <unistd.h>
46 #include <errno.h>
47
48 #if HAVE_STDINT_H
49 #include <stdint.h>
50 #endif
51 #ifndef INT32_MAX
52 #define INT32_MAX (2147483647)
53 #endif
54
55 #ifdef HAVE_OS_PROTO_H
56 #include "os-proto.h"
57 #endif
58
59 #ifdef HAVE_LIBNIDS
60 #include <nids.h>
61 # ifdef HAVE_LIBOOH323C
62 # include <ootypes.h>
63 # endif /* HAVE_LIBOOH323C */
64 #endif /* HAVE_LIBNIDS */
65
66 #include "tcpslice.h"
67 #include "gmt2local.h"
68 #include "machdep.h"
69 #include "sessions.h"
70
71 /* For Solaris before 11. */
72 /* compute a + b, store in c */
73 #ifndef timeradd
74 #define timeradd(a, b, c) { \
75 (c)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
76 (c)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
77 if ((c)->tv_usec > 1000000) { \
78 (c)->tv_usec -= 1000000; \
79 (c)->tv_sec += 1; \
80 } \
81 }
82 #endif /* timeradd */
83 /* compute a - b, store in c */
84 #ifndef timersub
85 #define timersub(a, b, c) { \
86 (c)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
87 if ((a)->tv_usec < (b)->tv_usec) { \
88 (c)->tv_sec -= 1; /* need to borrow */ \
89 (c)->tv_usec = ((a)->tv_usec + 1000000) - (b)->tv_usec; \
90 } else { \
91 (c)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
92 } \
93 }
94 #endif /* timersub */
95
96 /* The structure used to keep track of files being merged. */
97 struct state {
98 int64_t start_pos, /* seek position corresponding to start time */
99 stop_pos; /* seek position corresponding to stop time */
100 struct timeval
101 file_start_time, /* time of first pkt in file */
102 file_stop_time, /* time of last pkt in file */
103 last_pkt_time; /* time of most recently read pkt */
104 pcap_t *p;
105 struct pcap_pkthdr hdr;
106 const u_char *pkt;
107 char *filename;
108 int done;
109 };
110
111 /* Style in which to print timestamps; RAW is "secs.usecs"; READABLE is
112 * ala the Unix "date" tool; and PARSEABLE is tcpslice's custom format,
113 * designed to be easy to parse. The default is RAW.
114 */
115 enum stamp_styles { TIMESTAMP_RAW, TIMESTAMP_READABLE, TIMESTAMP_PARSEABLE };
116 static enum stamp_styles timestamp_style = TIMESTAMP_RAW;
117
118 /* Let's for now define that as far as tcpslice command-line argument parsing
119 * of raw timestamps goes, valid Unix time is the non-negative range of a
120 * 32-bit signed integer. This way it is possible to validate input without
121 * knowing the size and signedness of the local time_t type. Someone please
122 * invent a better solution before year 2038.
123 */
124 #define TS_RAW_S_MAX_VALUE INT32_MAX
125 #define TS_RAW_US_MAX_DIGITS 6 /* 000000~999999 */
126 #define TS_PARSEABLE_MAX_TOKENS 7 /* ymdhmsu */
127
128 struct parseable_token_t {
129 unsigned amount;
130 /* Bigger units must have bigger integer values for validation. */
131 enum {
132 MICROSECOND,
133 SECOND,
134 MINUTE, /* the default for "m" */
135 HOUR,
136 DAY,
137 MONTH, /* potentially after disambiguation */
138 YEAR,
139 } unit;
140 };
141
142
143 static unsigned char timestamp_input_format_correct(const char *str);
144 static struct timeval parse_time(const char *time_string, struct timeval base_time);
145 static void fill_tm(const char *time_string, const int is_delta, struct tm *t, time_t *usecs_addr);
146 static struct timeval lowest_start_time(const struct state *states, int numfiles);
147 static struct timeval latest_end_time(const struct state *states, int numfiles);
148 static struct state *open_files(char *filenames[], const int numfiles);
149 static u_char validate_files(struct state[], const int);
150 static void close_files(struct state[], const int);
151 static void extract_slice(struct state *states, const int numfiles,
152 const char *write_file_name,
153 const struct timeval *start_time, struct timeval *stop_time,
154 const int keep_dups, const int relative_time_merge,
155 const struct timeval *base_time);
156 static void dump_times(const struct state *states, int numfiles);
157 static void print_usage(FILE *);
158
159
160 pcap_dumper_t *global_dumper = 0;
161
162 extern char *optarg;
163 extern int optind, opterr;
164
165 extern int snaplen; /* needed by search.c, extract_slice() */
166 int snaplen = 0;
167
168 int
169 main(int argc, char **argv)
170 {
171 int op;
172 int dump_flag = 0;
173 int keep_dups = 0;
174 int report_times = 0;
175 int relative_time_merge = 0;
176 int numfiles;
177 char *start_time_string = NULL;
178 char *stop_time_string = NULL;
179 const char *write_file_name = "-"; /* default is stdout */
180 struct timeval first_time, start_time, stop_time;
181 char ebuf[PCAP_ERRBUF_SIZE];
182 struct state *states;
183
184 /*
185 * On platforms where the CPU doesn't support unaligned loads,
186 * force unaligned accesses to abort with SIGBUS, rather than
187 * being fixed up (slowly) by the OS kernel; on those platforms,
188 * misaligned accesses are bugs, and we want tcpslice to crash so
189 * that the bugs are reported.
190 */
191 if (abort_on_misalignment(ebuf, sizeof(ebuf)) < 0)
192 error("%s", ebuf);
193
194 opterr = 0;
195 while ((op = getopt(argc, argv, "dDe:f:hlRrs:tvw:")) != EOF)
196 switch (op) {
197
198 case 'd':
199 dump_flag = 1;
200 break;
201
202 case 'D':
203 keep_dups = 1;
204 break;
205
206 case 'e':
207 sessions_expiration_delay = atoi(optarg);
208 break;
209
210 case 'f':
211 sessions_file_format = optarg;
212 break;
213
214 case 'h':
215 print_usage(stdout);
216 exit(0);
217 break;
218
219 case 'l':
220 relative_time_merge = 1;
221 break;
222
223 case 'R':
224 ++report_times;
225 timestamp_style = TIMESTAMP_RAW;
226 break;
227
228 case 'r':
229 ++report_times;
230 timestamp_style = TIMESTAMP_READABLE;
231 break;
232
233 case 's':
234 timestamp_style = TIMESTAMP_PARSEABLE;
235 sessions_init(optarg);
236 break;
237
238 case 't':
239 ++report_times;
240 timestamp_style = TIMESTAMP_PARSEABLE;
241 break;
242
243 case 'v':
244 ++verbose;
245 break;
246
247 case 'w':
248 write_file_name = optarg;
249 break;
250
251 default:
252 (void)fprintf(stderr, "Error: invalid command-line option and/or argument!\n");
253 print_usage(stderr);
254 exit(-1);
255 /* NOTREACHED */
256 }
257
258 if ( report_times > 1 )
259 error( "only one of -R, -r, or -t can be specified" );
260
261 /* As far as command-line argument parsing is concerned, iff a string
262 * conforms to a timestamp format, it is a time argument no matter
263 * which format and what value. Whether a parseable time argument
264 * produces a valid date and a valid time is a different test a bit
265 * later with an additional input (base time) and a different error
266 * reporting. This way, the argument "25h70m80s" should always make
267 * tcpslice exit with an invalid time argument error instead of trying
268 * to open a file with that name.
269 */
270 if (optind < argc)
271 if (timestamp_input_format_correct(argv[optind]))
272 start_time_string = argv[optind++];
273
274 if (optind < argc)
275 if (timestamp_input_format_correct(argv[optind]))
276 stop_time_string = argv[optind++];
277
278 if (optind >= argc)
279 error("at least one input file must be given");
280
281 numfiles = argc - optind;
282
283 if ( numfiles == 1 )
284 keep_dups = 1; /* no dups can occur, so don't do the work */
285
286 states = open_files(&argv[optind], numfiles);
287 /* validate_files() might identify multiple issues before returning. */
288 if (validate_files(states, numfiles))
289 exit(1);
290 first_time = lowest_start_time(states, numfiles);
291
292 if (start_time_string)
293 start_time = parse_time(start_time_string, first_time);
294 else
295 start_time = first_time;
296
297 if (stop_time_string)
298 stop_time = parse_time(stop_time_string, start_time);
299 else
300 stop_time = latest_end_time(states, numfiles);
301
302 if (report_times) {
303 dump_times(states, numfiles);
304 }
305
306 if (dump_flag) {
307 printf( "start\t%s\nstop\t%s\n",
308 timestamp_to_string( &start_time ),
309 timestamp_to_string( &stop_time ) );
310 }
311
312 if (! report_times && ! dump_flag) {
313 if ( ! strcmp( write_file_name, "-" ) &&
314 isatty( fileno(stdout) ) )
315 error("stdout is a terminal; redirect or use -w");
316
317 extract_slice(states, numfiles, write_file_name,
318 &start_time, &stop_time, keep_dups, relative_time_merge,
319 &first_time);
320 }
321
322 close_files (states, numfiles);
323 return 0;
324 }
325
326 /* Test if the string has a form of "sssssssss" or "sssssssss.uuuuuu" (as
327 * discussed in the man page) and the integer part does not exceed the upper
328 * limit and the fractional part (if any) does not try to specify more
329 * precision than the format allows. Return 1 on good and 0 on bad.
330 */
331 static unsigned char
332 timestamp_raw_format_correct(const char *str)
333 {
334 enum { START, SECONDS, POINT, MICROSECONDS } fsm_state = START;
335 uint64_t s_value = 0; /* Initialize to squelch a warning. */
336 unsigned us_digits = 0; /* Initialize to squelch a warning. */
337
338 while (1) {
339 switch (fsm_state) {
340 case START: /* Have not seen anything yet. */
341 if (! isdigit((u_char)*str))
342 return 0;
343 s_value = *str - '0';
344 fsm_state = SECONDS;
345 break;
346 case SECONDS: /* Have seen one or more digits for the seconds. */
347 if (*str == '\0')
348 return 1; /* "uuuuuuuuu" */
349 if (*str == '.') {
350 fsm_state = POINT;
351 break;
352 }
353 if (! isdigit((u_char)*str) ||
354 (s_value = s_value * 10 + *str - '0') > TS_RAW_S_MAX_VALUE)
355 return 0;
356 break;
357 case POINT: /* Have seen the decimal point. */
358 if (! isdigit((u_char)*str))
359 return 0;
360 us_digits = 1;
361 fsm_state = MICROSECONDS;
362 break;
363 case MICROSECONDS: /* Have seen one or more digits for the microseconds. */
364 if (*str == '\0')
365 return 1; /* "uuuuuuuuu.ssssss" */
366 if (! isdigit((u_char)*str) || ++us_digits > TS_RAW_US_MAX_DIGITS)
367 return 0;
368 break;
369 default:
370 error("invalid FSM state in %s()", __func__);
371 } /* switch (fsm_state) */
372 str++;
373 } /* while (1) */
374 }
375
376 /* Try to read one complete token (amount, unit) from the given string into
377 * the provided structure by advancing the pointer and consuming characters.
378 * Accept the unit characters in both lowercase and uppercase to be consistent
379 * with the undocumented behaviour of fill_tm(). Do not check whether the
380 * amount is valid for the unit. Return the advanced pointer on success or
381 * NULL otherwise.
382 */
383 static const char *
384 parse_token(const char *str, struct parseable_token_t *token)
385 {
386 enum { START, AMOUNT, UNIT } fsm_state = START;
387 uint64_t amount = 0; /* Initialize to squelch a warning. */
388 char char_unit = 0; /* Initialize to squelch a warning. */
389
390 while (1) {
391 switch (fsm_state) {
392 case START: /* Have not seen anything yet. */
393 if (! isdigit((u_char)*str))
394 return NULL;
395 amount = *str - '0';
396 fsm_state = AMOUNT;
397 break;
398 case AMOUNT: /* Have seen one or more digits for the amount. */
399 if (isalpha((u_char)*str)) {
400 token->amount = amount;
401 char_unit = tolower((u_char)*str);
402 fsm_state = UNIT;
403 break;
404 }
405 if (! isdigit((u_char)*str) ||
406 (amount = amount * 10 + *str - '0') > INT32_MAX)
407 return NULL;
408 break;
409 case UNIT: /* Have seen a character, could be a valid unit. */
410 switch (char_unit) {
411 case 'y':
412 token->unit = YEAR;
413 break;
414 /* no month */
415 case 'd':
416 token->unit = DAY;
417 break;
418 case 'h':
419 token->unit = HOUR;
420 break;
421 case 'm':
422 token->unit = MINUTE;
423 break;
424 case 's':
425 token->unit = SECOND;
426 break;
427 case 'u':
428 token->unit = MICROSECOND;
429 break;
430 default:
431 return NULL;
432 } /* switch (char_unit) */
433 return str;
434 default:
435 error("invalid FSM state in %s()", __func__);
436 } /* switch (fsm_state) */
437 str++;
438 } /* while (1) */
439 }
440
441 /* Test if the string conforms to the "ymdhmsu" format (as discussed in the
442 * man page). Do not test individual amounts to be valid for their time units
443 * or the date to be a valid date or the time to be a valid time. Return 1 on
444 * good and 0 on bad.
445 */
446 static unsigned char
447 timestamp_parseable_format_correct(const char *str)
448 {
449 struct parseable_token_t token[TS_PARSEABLE_MAX_TOKENS];
450 unsigned numtokens = 0;
451
452 /* Try to tokenize the full string, fail as early as possible. */
453 while (*str != '\0') {
454 if (numtokens == TS_PARSEABLE_MAX_TOKENS ||
455 (str = parse_token(str, token + numtokens)) == NULL)
456 return 0;
457 numtokens++;
458 }
459
460 if (numtokens > 1) {
461 unsigned i;
462 /* Disambiguate "m". */
463 for (i = 0; i < numtokens - 1; i++)
464 if (token[i].unit == MINUTE && token[i + 1].unit == DAY)
465 token[i].unit = MONTH;
466 /* Require each time unit in the vector to appear at most once
467 * and in the order of strictly decreasing magnitude.
468 */
469 for (i = 0; i < numtokens - 1; i++)
470 if (token[i].unit <= token[i + 1].unit)
471 return 0;
472 }
473
474 return numtokens > 0;
475 }
476
477 /* Test if the string conforms to a valid input time format (with an optional
478 * leading "+"). Return 1 on good and 0 on bad.
479 */
480 static unsigned char
481 timestamp_input_format_correct(const char *str)
482 {
483 if (*str == '+')
484 str++;
485 return timestamp_parseable_format_correct(str) ||
486 timestamp_raw_format_correct(str);
487 }
488
489 /* No-op iff the date and the time in the given broken-down time are valid
490 * and the year is within the [1970, 2069] range declared in the man page.
491 */
492 static void
493 assert_valid_tm(const struct tm t)
494 {
495 int year, maxdays;
496
497 /* yy: [70, 99] in tm_year means [1970, 1999]
498 * yy: [0, 69] in tm_year means [2000, 2069]
499 * yyyy: [70, 99] in tm_year means [1970, 1999].
500 * yyyy: [100, 169] in tm_year means [2000, 2069].
501 */
502 year = 1900 + t.tm_year;
503 if (year < 1970)
504 year += 100;
505 if (year < 1970 || year > 2069)
506 error("year %d is not valid\n", year);
507
508 if (t.tm_mon < 0 || t.tm_mon > 11) /* 11 is December */
509 error("month %d is not valid\n", t.tm_mon + 1);
510
511 maxdays = days_in_month[t.tm_mon];
512 if (t.tm_mon == 1 && IS_LEAP_YEAR(year)) /* 1 is February */
513 maxdays++;
514 if (t.tm_mday < 1 || t.tm_mday > maxdays)
515 error("day %d is not valid\n", t.tm_mday);
516
517 if (t.tm_hour < 0 || t.tm_hour > 23)
518 error("hour %d is not valid\n", t.tm_hour);
519
520 if (t.tm_min < 0 || t.tm_min > 59)
521 error("minute %d is not valid\n", t.tm_min);
522
523 if (t.tm_sec < 0 || t.tm_sec > 59)
524 error("second %d is not valid\n", t.tm_sec);
525 }
526
527 /* Given a string specifying a time (or a time offset) and a "base time"
528 * from which to compute offsets and fill in defaults, returns a timeval
529 * containing the specified time.
530 */
531 static struct timeval
532 parse_time(const char *time_string, struct timeval base_time)
533 {
534 struct tm *bt = localtime((time_t *) &base_time.tv_sec);
535 struct tm t;
536 struct timeval result;
537 time_t usecs = 0;
538 int is_delta = (time_string[0] == '+');
539
540 if ( is_delta )
541 ++time_string; /* skip over '+' sign */
542
543 if (timestamp_raw_format_correct(time_string))
544 { /* interpret as a raw timestamp or timestamp offset */
545 char *time_ptr;
546
547 result.tv_sec = atoi( time_string );
548 time_ptr = strchr( time_string, '.' );
549
550 if ( time_ptr )
551 { /* microseconds are specified, too */
552 int num_digits = strlen( time_ptr + 1 );
553 result.tv_usec = atoi( time_ptr + 1 );
554
555 /* turn 123.456 into 123 seconds plus 456000 usec */
556 while ( num_digits++ < 6 )
557 result.tv_usec *= 10;
558 }
559
560 else
561 result.tv_usec = 0;
562
563 if ( is_delta )
564 timeradd(&result, &base_time, &result);
565
566 return result;
567 }
568
569 if (is_delta) {
570 t = *bt;
571 usecs = base_time.tv_usec;
572 } else {
573 /* Zero struct (easy way around lack of tm_gmtoff/tm_zone
574 * under older systems) */
575 memset((char *)&t, 0, sizeof(t));
576
577 /* Set values to "not set" flag so we can later identify
578 * and default them.
579 */
580 t.tm_sec = t.tm_min = t.tm_hour = t.tm_mday = t.tm_mon =
581 t.tm_year = -1;
582 }
583
584 /* Terminate the program on error. */
585 fill_tm(time_string, is_delta, &t, &usecs);
586
587 /* Now until we reach a field that was specified, fill in the
588 * missing fields from the base time.
589 */
590 #define CHECK_FIELD(field_name) \
591 if (t.field_name < 0) \
592 t.field_name = bt->field_name; \
593 else \
594 break
595
596 do { /* bogus do-while loop so "break" in CHECK_FIELD will work */
597 CHECK_FIELD(tm_year);
598 CHECK_FIELD(tm_mon);
599 CHECK_FIELD(tm_mday);
600 CHECK_FIELD(tm_hour);
601 CHECK_FIELD(tm_min);
602 CHECK_FIELD(tm_sec);
603 } while ( 0 );
604
605 /* Set remaining unspecified fields to 0. */
606 #define ZERO_FIELD_IF_NOT_SET(field_name,zero_val) \
607 if (t.field_name < 0) \
608 t.field_name = zero_val
609
610 if (! is_delta) {
611 ZERO_FIELD_IF_NOT_SET(tm_year,90); /* should never happen */
612 ZERO_FIELD_IF_NOT_SET(tm_mon,0);
613 ZERO_FIELD_IF_NOT_SET(tm_mday,1);
614 ZERO_FIELD_IF_NOT_SET(tm_hour,0);
615 ZERO_FIELD_IF_NOT_SET(tm_min,0);
616 ZERO_FIELD_IF_NOT_SET(tm_sec,0);
617 }
618
619 assert_valid_tm(t); /* Terminate the program on error. */
620 result.tv_sec = gwtm2secs(&t);
621 result.tv_sec -= gmt2local(result.tv_sec);
622 result.tv_usec = usecs;
623
624 return result;
625 }
626
627 /* Fill in (or add to, if is_delta is true) the time values in the
628 * tm struct "t" as specified by the time specified in the string
629 * "time_string". "usecs_addr" is updated with the specified number
630 * of microseconds, if any.
631 */
632 static void
633 fill_tm(const char *time_string, const int is_delta, struct tm *t, time_t *usecs_addr)
634 {
635 const char *t_start, *t_stop;
636
637 #define SET_VAL(lhs,rhs) \
638 if (is_delta) \
639 lhs += rhs; \
640 else \
641 lhs = rhs
642
643 /* Loop through the time string parsing one specification at
644 * a time. Each specification has the form <number><letter>
645 * where <number> indicates the amount of time and <letter>
646 * the units.
647 */
648 for (t_stop = t_start = time_string; *t_start; t_start = ++t_stop) {
649 if (! isdigit((u_char)*t_start))
650 error("bad date format %s, problem starting at %s",
651 time_string, t_start);
652
653 while (isdigit((u_char)*t_stop))
654 ++t_stop;
655 if (! (*t_stop))
656 error("bad date format %s, problem starting at %s",
657 time_string, t_start);
658
659 int val = atoi(t_start);
660
661 char format_ch = *t_stop;
662 if (isupper((u_char)format_ch))
663 format_ch = tolower((u_char)format_ch);
664
665 switch (format_ch) {
666 case 'y':
667 if ( val >= 100 && val < 1970)
668 error("Can't handle year %d\n", val);
669 if ( val > 1900 )
670 val -= 1900;
671 SET_VAL(t->tm_year, val);
672 break;
673
674 case 'm':
675 if (strchr(t_stop+1, 'D') ||
676 strchr(t_stop+1, 'd'))
677 /* it's months */
678 SET_VAL(t->tm_mon, val - 1);
679 else /* it's minutes */
680 SET_VAL(t->tm_min, val);
681 break;
682
683 case 'd':
684 SET_VAL(t->tm_mday, val);
685 break;
686
687 case 'h':
688 SET_VAL(t->tm_hour, val);
689 break;
690
691 case 's':
692 SET_VAL(t->tm_sec, val);
693 break;
694
695 case 'u':
696 SET_VAL(*usecs_addr, val);
697 break;
698
699 default:
700 error(
701 "bad date format %s, problem starting at %s",
702 time_string, t_start);
703 }
704 }
705 }
706
707 /* Of all the files, what is the lowest start time. */
708 static struct timeval
709 lowest_start_time(const struct state *states, int numfiles)
710 {
711 struct timeval min_time = states->file_start_time;
712
713 while (numfiles--) {
714 if (sf_timestamp_less_than(&states->file_start_time, &min_time)) {
715 min_time = states->file_start_time;
716 }
717 ++states;
718 }
719 return min_time;
720 }
721
722 /* Of all the files, what is the latest end time. */
723 static struct timeval
724 latest_end_time(const struct state *states, int numfiles)
725 {
726 struct timeval max_time = states->file_stop_time;
727
728 while (numfiles--) {
729 if (sf_timestamp_less_than(&max_time, &states->file_stop_time)) {
730 max_time = states->file_stop_time;
731 }
732 ++states;
733 }
734 return max_time;
735 }
736
737 /* Get the next record in a file. Deal with end of file.
738 *
739 * This routine also prevents time from going "backwards"
740 * within a single file.
741 */
742 static void
743 get_next_packet(struct state *s)
744 {
745 struct timeval tvbuf;
746
747 do {
748 s->pkt = pcap_next(s->p, &s->hdr);
749 if (! s->pkt) {
750 s->done = 1;
751 if (track_sessions)
752 sessions_exit();
753 pcap_close(s->p);
754 }
755 TIMEVAL_FROM_PKTHDR_TS(tvbuf, s->hdr.ts);
756 } while ((! s->done) &&
757 sf_timestamp_less_than(&tvbuf, &s->last_pkt_time));
758
759 s->last_pkt_time = tvbuf;
760 }
761
762 static struct state *
763 open_files(char *filenames[], const int numfiles)
764 {
765 struct state *states;
766 struct state *s;
767 char errbuf[PCAP_ERRBUF_SIZE];
768 int i;
769
770 if (numfiles == 0)
771 error("no input files specified");
772
773 /* allocate memory for all the files */
774 states = (struct state *) malloc(sizeof(struct state) * numfiles);
775 if (! states)
776 error("unable to allocate memory for %d input files", numfiles);
777 memset(states, 0, sizeof(struct state) * numfiles);
778
779 for (i = 0; i < numfiles; ++i) {
780 s = &states[i];
781 s->filename = filenames[i];
782 s->p = pcap_open_offline(s->filename, errbuf);
783 if (! s->p)
784 error( "bad pcap file %s: %s", s->filename, errbuf );
785
786 #ifdef HAVE_POSIX_FADVISE
787 FILE *pf = pcap_file(s->p);
788 if (pf == NULL)
789 error("pcap_file() failed");
790 int fd = fileno(pf);
791 if (fd == -1)
792 error("fileno() failed: %s", strerror(errno));
793 int padv_err;
794 if (0 != (padv_err = posix_fadvise(fd, 0, 0, POSIX_FADV_RANDOM)))
795 warning("warning: posix_fadvise() failed: %s", strerror(padv_err));
796 if (0 != (padv_err = posix_fadvise(fd, 0, 0, POSIX_FADV_NOREUSE)))
797 warning("warning: posix_fadvise() failed: %s", strerror(padv_err));
798 #endif
799
800 if (track_sessions)
801 sessions_nids_init(s->p);
802
803 int this_snap = pcap_snapshot( s->p );
804 if (this_snap > snaplen) {
805 snaplen = this_snap;
806 }
807
808 s->start_pos = ftell64( pcap_file( s->p ) );
809
810 if (pcap_next(s->p, &s->hdr) == NULL)
811 error( "error reading packet in %s: %s",
812 s->filename, pcap_geterr( s->p ) );
813
814 TIMEVAL_FROM_PKTHDR_TS(s->file_start_time, s->hdr.ts);
815
816 if ( ! sf_find_end( s->p, &s->file_start_time,
817 &s->file_stop_time ) )
818 error( "problems finding end packet of file %s",
819 s->filename );
820
821 s->stop_pos = ftell64( pcap_file( s->p ) );
822 }
823
824 return states;
825 }
826
827 /* Return 0 on no errors. */
828 static u_char
829 validate_files(struct state states[], const int numfiles)
830 {
831 u_char ret = 0;
832 int i, first_dlt, this_dlt;
833
834 for (i = 0; i < numfiles; i++) {
835 this_dlt = pcap_datalink(states[i].p);
836 if (i == 0)
837 first_dlt = this_dlt;
838 else if (first_dlt != this_dlt) {
839 warning("file '%s' uses DLT %d, and the first file '%s' uses DLT %d",
840 states[i].filename, this_dlt, states[0].filename, first_dlt);
841 ret = 1;
842 }
843
844 /* Do a minimal sanity check of the timestamps. */
845 if (sf_timestamp_less_than(&states[i].file_stop_time,
846 &states[i].file_start_time)) {
847 warning("'%s' has the last timestamp before the first timestamp",
848 states[i].filename);
849 ret = 1;
850 }
851 }
852 return ret;
853 }
854
855 static void
856 close_files(struct state states[], const int numfiles)
857 {
858 int i;
859
860 for (i = 0; i < numfiles; i++)
861 if (!states[i].done)
862 pcap_close(states[i].p);
863 free(states);
864 }
865
866 /*
867 * Extract from a given set of files all packets with timestamps between
868 * the two time values given (inclusive). These packets are written
869 * to the save file given by write_file_name.
870 *
871 * Upon return, start_time is adjusted to reflect a time just after
872 * that of the last packet written to the output.
873 */
874 static void
875 extract_slice(struct state *states, const int numfiles, const char *write_file_name,
876 const struct timeval *start_time, struct timeval *stop_time,
877 const int keep_dups, const int relative_time_merge,
878 const struct timeval *base_time)
879 {
880 struct state *s, *min_state;
881 struct timeval temp1, temp2, relative_start, relative_stop;
882 int i;
883
884 struct state *last_state; /* remember the last packet */
885 struct pcap_pkthdr last_hdr; /* in order to remove duplicates */
886 u_char* last_pkt;
887
888 if (numfiles == 0)
889 error("no input files specified");
890
891 last_state = 0;
892 last_hdr.ts.tv_sec = last_hdr.ts.tv_usec = 0;
893 last_hdr.caplen = last_hdr.len = 0;
894 last_pkt = (u_char*) malloc(snaplen);
895
896 if (! last_pkt)
897 error("out of memory");
898
899 memset(last_pkt, 0, snaplen);
900
901 timersub(start_time, base_time, &relative_start);
902 timersub(stop_time, base_time, &relative_stop);
903
904 /* Always write the output file, use the first input file's DLT. */
905 global_dumper = pcap_dump_open(states[0].p, write_file_name);
906 if (!global_dumper) {
907 error("error creating output file '%s': %s",
908 write_file_name, pcap_geterr(states[0].p));
909 }
910
911 for (i = 0; i < numfiles; ++i) {
912 s = &states[i];
913
914 /* compute the first packet time within *this* file */
915 if (relative_time_merge) {
916 /* relative time within this file */
917 timeradd(&s->file_start_time, &relative_start, &temp1);
918 } else {
919 /* absolute time */
920 temp1 = *start_time;
921 }
922
923 /* check if this file has *anything* for us ... */
924 if (sf_timestamp_less_than(&s->file_stop_time, &temp1)) {
925 /* there aren't any packets of interest in this file */
926 s->done = 1;
927 pcap_close(s->p);
928 continue;
929 }
930
931 /*
932 * sf_find_packet() requires that the time it's passed as
933 * its last argument be in the range [min_time, max_time],
934 * so we enforce that constraint here.
935 */
936
937 if (sf_timestamp_less_than(&temp1, &s->file_start_time)){
938 temp1 = s->file_start_time;
939 }
940
941 sf_find_packet(s->p, &s->file_start_time, s->start_pos,
942 &s->file_stop_time, s->stop_pos,
943 &temp1);
944
945 /* get first packet for this file */
946 get_next_packet(s);
947 }
948
949
950 /*
951 * Now, loop through all the packets in all the files,
952 * putting packets out in timestamp order.
953 *
954 * Quite often, the files will not have overlapping
955 * timestamps, so it would be nice to try to deal
956 * efficiently with that situation. (XXX)
957 */
958
959 while (1) {
960 struct timeval tvbuf;
961
962 min_state = 0;
963 for (i = 0; i < numfiles; ++i) {
964 s = &states[i];
965 if (! s->done) {
966 if (! min_state)
967 min_state = s;
968
969 if (relative_time_merge) {
970 /* compare *relative* times */
971 timersub(&s->hdr.ts,
972 &s->file_start_time, &temp1);
973 timersub(&min_state->hdr.ts,
974 &min_state->file_start_time, &temp2);
975 } else {
976 /* compare *absolute* times */
977 TIMEVAL_FROM_PKTHDR_TS(temp1, s->hdr.ts);
978 TIMEVAL_FROM_PKTHDR_TS(temp2, min_state->hdr.ts);
979 }
980 if (sf_timestamp_less_than( &temp1, &temp2))
981 min_state = s;
982 }
983 }
984
985 if (! min_state)
986 break; /* didn't find any !done files */
987
988 if (relative_time_merge) {
989 /* relative time w/in this file */
990 timeradd(&min_state->file_start_time, &relative_stop, &temp1);
991 } else
992 /* take absolute times */
993 temp1 = *stop_time;
994
995 TIMEVAL_FROM_PKTHDR_TS(tvbuf, min_state->hdr.ts);
996 if (sf_timestamp_less_than(&temp1, &tvbuf)) {
997 if (!sessions_count) {
998 /* We've gone beyond the end of the region
999 * of interest ... We're done with this file.
1000 */
1001 if (track_sessions)
1002 sessions_exit();
1003 min_state->done = 1;
1004 pcap_close(min_state->p);
1005 break;
1006 } else {
1007 /* We need to wait for the sessions to close */
1008 bonus_time = 1;
1009 *stop_time = min_state->file_stop_time;
1010 }
1011 }
1012
1013 if (relative_time_merge) {
1014 timersub(&min_state->hdr.ts, &min_state->file_start_time, &temp1);
1015 timeradd(&temp1, base_time, &min_state->hdr.ts);
1016 }
1017
1018 #ifdef HAVE_LIBNIDS
1019 /* Keep track of sessions, if specified by the user */
1020 if (track_sessions && min_state->hdr.caplen) {
1021 /*
1022 * Copy the packet buffer to deconstify it for the function.
1023 */
1024 u_char *pkt_copy = malloc(min_state->hdr.caplen);
1025
1026 if (!pkt_copy)
1027 error("malloc() failed in %s()", __func__);
1028 memcpy(pkt_copy, min_state->pkt, min_state->hdr.caplen);
1029 nids_pcap_handler((u_char *)min_state->p, &min_state->hdr, pkt_copy);
1030 free(pkt_copy);
1031 }
1032 #endif
1033
1034 /* Dump it, unless it's a duplicate. */
1035 if (!bonus_time)
1036 if ( keep_dups ||
1037 min_state == last_state ||
1038 memcmp(&last_hdr, &min_state->hdr, sizeof(last_hdr)) ||
1039 memcmp(last_pkt, min_state->pkt, last_hdr.caplen) ) {
1040 pcap_dump((u_char *) global_dumper, &min_state->hdr, min_state->pkt);
1041
1042 if ( ! keep_dups ) {
1043 last_state = min_state;
1044 last_hdr = min_state->hdr;
1045 memcpy(last_pkt, min_state->pkt, min_state->hdr.caplen);
1046 }
1047 }
1048
1049 get_next_packet(min_state);
1050 }
1051
1052 pcap_dump_close(global_dumper);
1053 free(last_pkt);
1054 }
1055
1056 /* Translates a timestamp to the time format specified by the user.
1057 * Returns a pointer to the translation residing in a static buffer.
1058 * There are two such buffers, which are alternated on subsequent
1059 * calls, so two calls may be made to this routine without worrying
1060 * about the results of the first call being overwritten by the
1061 * results of the second.
1062 */
1063 char *
1064 timestamp_to_string(const struct timeval *timestamp)
1065 {
1066 struct tm *t;
1067 #define NUM_BUFFERS 2
1068 #define DATEBUFSIZE 128
1069 static char buffers[NUM_BUFFERS][DATEBUFSIZE];
1070 static int buffer_to_use = 0;
1071 char *buf;
1072
1073 buf = buffers[buffer_to_use];
1074 buffer_to_use = (buffer_to_use + 1) % NUM_BUFFERS;
1075
1076 switch ( timestamp_style ) {
1077
1078 case TIMESTAMP_RAW:
1079 snprintf( buf, DATEBUFSIZE, "%u.%06u",
1080 (u_int32_t)timestamp->tv_sec,
1081 (u_int32_t)timestamp->tv_usec );
1082 break;
1083
1084 case TIMESTAMP_READABLE:
1085 t = localtime((const time_t *) &timestamp->tv_sec);
1086 /* Mimic asctime() with C99 format specifiers. */
1087 strftime(buf, DATEBUFSIZE, "%a %b %e %T %Y", t);
1088 break;
1089
1090 case TIMESTAMP_PARSEABLE:
1091 t = localtime((const time_t *) &timestamp->tv_sec);
1092 snprintf( buf, DATEBUFSIZE, "%04dy%02dm%02dd%02dh%02dm%02ds%06uu",
1093 t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
1094 t->tm_hour, t->tm_min, t->tm_sec,
1095 (u_int32_t)timestamp->tv_usec );
1096 break;
1097
1098 }
1099
1100 return buf;
1101 }
1102
1103 /* Given a pcap save filename, reports on the times of the first
1104 * and last packets in the file.
1105 */
1106 static void
1107 dump_times(const struct state *states, int numfiles)
1108 {
1109 for (; numfiles--; states++) {
1110 printf( "%s\t%s\t%s\n",
1111 states->filename,
1112 timestamp_to_string( &states->file_start_time ),
1113 timestamp_to_string( &states->file_stop_time ) );
1114 }
1115 }
1116
1117 static void
1118 print_usage(FILE *f)
1119 {
1120 #ifndef HAVE_PCAP_LIB_VERSION
1121 #ifdef HAVE_PCAP_VERSION
1122 extern char pcap_version[];
1123 #else /* HAVE_PCAP_VERSION */
1124 static char pcap_version[] = "unknown";
1125 #endif /* HAVE_PCAP_VERSION */
1126 #endif /* HAVE_PCAP_LIB_VERSION */
1127
1128 (void)fprintf(f, "tcpslice version %s\n", PACKAGE_VERSION);
1129 #ifdef HAVE_PCAP_LIB_VERSION
1130 (void)fprintf(f, "%s\n", pcap_lib_version());
1131 #else /* HAVE_PCAP_LIB_VERSION */
1132 (void)fprintf(f, "libpcap version %s\n", pcap_version);
1133 #endif /* HAVE_PCAP_LIB_VERSION */
1134
1135 #ifdef HAVE_LIBNIDS
1136 (void)fprintf(f, "libnids version %u.%u\n", NIDS_MAJOR, NIDS_MINOR);
1137
1138 #ifdef HAVE_LIBOSIPPARSER2
1139 (void)fprintf(f, "libosip2 unknown version\n");
1140 #endif /* HAVE_LIBOSIPPARSER2 */
1141
1142 #ifdef HAVE_LIBOOH323C
1143 (void)fprintf(f, "libooh323c %s\n", OOH323C_VERSION);
1144 #endif /* HAVE_LIBOOH323C */
1145
1146 #endif /* HAVE_LIBNIDS */
1147
1148 (void)fprintf(f,
1149 "Usage: tcpslice [-DdhlRrtv] [-w file]\n"
1150 " [ -s types [ -e seconds ] [ -f format ] ]\n"
1151 " [start-time [end-time]] file ... \n");
1152 }