2 * Routines for packet capture windows
6 * Ethereal - Network traffic analyzer
7 * By Gerald Combs <gerald@ethereal.com>
8 * Copyright 1998 Gerald Combs
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 /* With MSVC and a libethereal.dll this file needs to import some variables
26 in a special way. Therefore _NEED_VAR_IMPORT_ is defined. */
27 #define _NEED_VAR_IMPORT_
53 #include <epan/packet.h>
54 #include <epan/dfilter/dfilter.h>
57 #include "capture_sync.h"
58 #include "capture_ui_utils.h"
60 #include "pcap-util.h"
61 #include "alert_box.h"
62 #include "simple_dialog.h"
63 #include <epan/prefs.h>
64 #include "conditions.h"
65 #include "ringbuffer.h"
68 #include "capture-wpcap.h"
73 /* Win32 needs the O_BINARY flag for open() */
78 static gboolean normal_do_capture(capture_options *capture_opts, gboolean is_tempfile);
79 static void stop_capture_signal_handler(int signo);
83 capture_opts_init(capture_options *capture_opts, void *cfile)
85 capture_opts->cf = cfile;
86 capture_opts->cfilter = g_strdup("");
87 capture_opts->iface = NULL;
89 capture_opts->buffer_size = 1; /* 1 MB */
91 capture_opts->has_snaplen = FALSE;
92 capture_opts->snaplen = MIN_PACKET_SIZE;
93 capture_opts->promisc_mode = TRUE;
94 capture_opts->linktype = -1; /* the default linktype */
95 capture_opts->capture_child = FALSE;
96 capture_opts->save_file = NULL;
97 capture_opts->save_file_fd = -1;
98 capture_opts->sync_mode = TRUE;
99 capture_opts->show_info = TRUE;
100 capture_opts->quit_after_cap = FALSE;
102 capture_opts->multi_files_on = FALSE;
103 capture_opts->has_file_duration = FALSE;
104 capture_opts->file_duration = 60; /* 1 min */
105 capture_opts->has_ring_num_files = TRUE;
106 capture_opts->ring_num_files = 2;
108 capture_opts->has_autostop_files = FALSE;
109 capture_opts->autostop_files = 1;
110 capture_opts->has_autostop_packets = FALSE;
111 capture_opts->autostop_packets = 1;
112 capture_opts->has_autostop_filesize = FALSE;
113 capture_opts->autostop_filesize = 1024 * 1024; /* 1 MB */
114 capture_opts->has_autostop_duration = FALSE;
115 capture_opts->autostop_duration = 60; /* 1 min */
120 get_natural_int(const char *string, const char *name)
125 number = strtol(string, &p, 10);
126 if (p == string || *p != '\0') {
127 fprintf(stderr, "ethereal: The specified %s \"%s\" isn't a decimal number\n",
132 fprintf(stderr, "ethereal: The specified %s \"%s\" is a negative number\n",
136 if (number > INT_MAX) {
137 fprintf(stderr, "ethereal: The specified %s \"%s\" is too large (greater than %d)\n",
138 name, string, INT_MAX);
146 get_positive_int(const char *string, const char *name)
150 number = get_natural_int(string, name);
153 fprintf(stderr, "ethereal: The specified %s is zero\n",
163 * Given a string of the form "<autostop criterion>:<value>", as might appear
164 * as an argument to a "-a" option, parse it and set the criterion in
165 * question. Return an indication of whether it succeeded or failed
169 set_autostop_criterion(capture_options *capture_opts, const char *autostoparg)
173 colonp = strchr(autostoparg, ':');
181 * Skip over any white space (there probably won't be any, but
182 * as we allow it in the preferences file, we might as well
185 while (isspace((guchar)*p))
189 * Put the colon back, so if our caller uses, in an
190 * error message, the string they passed us, the message
196 if (strcmp(autostoparg,"duration") == 0) {
197 capture_opts->has_autostop_duration = TRUE;
198 capture_opts->autostop_duration = get_positive_int(p,"autostop duration");
199 } else if (strcmp(autostoparg,"filesize") == 0) {
200 capture_opts->has_autostop_filesize = TRUE;
201 capture_opts->autostop_filesize = get_positive_int(p,"autostop filesize");
205 *colonp = ':'; /* put the colon back */
210 * Given a string of the form "<ring buffer file>:<duration>", as might appear
211 * as an argument to a "-b" option, parse it and set the arguments in
212 * question. Return an indication of whether it succeeded or failed
216 get_ring_arguments(capture_options *capture_opts, const char *arg)
218 gchar *p = NULL, *colonp;
220 colonp = strchr(arg, ':');
222 if (colonp != NULL) {
227 capture_opts->ring_num_files =
228 get_natural_int(arg, "number of ring buffer files");
234 * Skip over any white space (there probably won't be any, but
235 * as we allow it in the preferences file, we might as well
238 while (isspace((guchar)*p))
242 * Put the colon back, so if our caller uses, in an
243 * error message, the string they passed us, the message
250 capture_opts->has_file_duration = TRUE;
251 capture_opts->file_duration = get_positive_int(p,
252 "ring buffer duration");
254 *colonp = ':'; /* put the colon back */
260 capture_opt_add(capture_options *capture_opts, int opt, const char *optarg, gboolean *start_capture)
266 case 'a': /* autostop criteria */
267 if (set_autostop_criterion(capture_opts, optarg) == FALSE) {
268 fprintf(stderr, "ethereal: Invalid or unknown -a flag \"%s\"\n", optarg);
272 case 'b': /* Ringbuffer option */
273 capture_opts->multi_files_on = TRUE;
274 capture_opts->has_ring_num_files = TRUE;
275 if (get_ring_arguments(capture_opts, optarg) == FALSE) {
276 fprintf(stderr, "ethereal: Invalid or unknown -b arg \"%s\"\n", optarg);
280 case 'c': /* Capture xxx packets */
281 capture_opts->has_autostop_packets = TRUE;
282 capture_opts->autostop_packets = get_positive_int(optarg, "packet count");
284 case 'f': /* capture filter */
285 if (capture_opts->cfilter)
286 g_free(capture_opts->cfilter);
287 capture_opts->cfilter = g_strdup(optarg);
289 case 'H': /* Hide capture info dialog box */
290 capture_opts->show_info = FALSE;
292 case 'i': /* Use interface xxx */
293 capture_opts->iface = g_strdup(optarg);
295 case 'k': /* Start capture immediately */
296 *start_capture = TRUE;
298 /*case 'l':*/ /* Automatic scrolling in live capture mode */
299 case 'p': /* Don't capture in promiscuous mode */
300 capture_opts->promisc_mode = FALSE;
302 case 'Q': /* Quit after capture (just capture to file) */
303 capture_opts->quit_after_cap = TRUE;
304 *start_capture = TRUE; /*** -Q implies -k !! ***/
306 case 's': /* Set the snapshot (capture) length */
307 capture_opts->has_snaplen = TRUE;
308 capture_opts->snaplen = get_positive_int(optarg, "snapshot length");
310 case 'S': /* "Sync" mode: used for following file ala tail -f */
311 capture_opts->sync_mode = TRUE;
313 case 'w': /* Write to capture file xxx */
314 capture_opts->save_file = g_strdup(optarg);
316 case 'W': /* Write to capture file FD xxx */
317 capture_opts->save_file_fd = atoi(optarg);
319 case 'y': /* Set the pcap data link type */
320 #ifdef HAVE_PCAP_DATALINK_NAME_TO_VAL
321 capture_opts->linktype = pcap_datalink_name_to_val(optarg);
322 if (capture_opts->linktype == -1) {
323 fprintf(stderr, "ethereal: The specified data link type \"%s\" isn't valid\n",
327 #else /* HAVE_PCAP_DATALINK_NAME_TO_VAL */
328 /* XXX - just treat it as a number */
329 capture_opts->linktype = get_natural_int(optarg, "data link type");
330 #endif /* HAVE_PCAP_DATALINK_NAME_TO_VAL */
333 /* Hidden option supporting Sync mode */
334 case 'Z': /* Write to pipe FD XXX */
335 /* associate stdout with pipe */
337 if (dup2(i, 1) < 0) {
338 fprintf(stderr, "Unable to dup pipe handle\n");
344 /* the caller is responsible to send us only the right opt's */
345 g_assert_not_reached();
349 /* open the output file (temporary/specified name/ringbuffer) and close the old one */
350 /* Returns TRUE if the file opened successfully, FALSE otherwise. */
352 capture_open_output(capture_options *capture_opts, gboolean *is_tempfile) {
357 if (capture_opts->save_file != NULL) {
358 /* If the Sync option is set, we return to the caller while the capture
359 * is in progress. Therefore we need to take a copy of save_file in
360 * case the caller destroys it after we return.
362 capfile_name = g_strdup(capture_opts->save_file);
363 if (capture_opts->multi_files_on) {
364 /* ringbuffer is enabled */
365 capture_opts->save_file_fd = ringbuf_init(capfile_name,
366 (capture_opts->has_ring_num_files) ? capture_opts->ring_num_files : 0);
368 /* Try to open/create the specified file for use as a capture buffer. */
369 capture_opts->save_file_fd = open(capfile_name, O_RDWR|O_BINARY|O_TRUNC|O_CREAT,
372 *is_tempfile = FALSE;
374 /* Choose a random name for the temporary capture buffer */
375 capture_opts->save_file_fd = create_tempfile(tmpname, sizeof tmpname, "ether");
376 capfile_name = g_strdup(tmpname);
380 /* did we fail to open the output file? */
381 if (capture_opts->save_file_fd == -1) {
383 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
384 "The temporary file to which the capture would be saved (\"%s\")"
385 "could not be opened: %s.", capfile_name, strerror(errno));
387 if (capture_opts->multi_files_on) {
388 ringbuf_error_cleanup();
390 open_failure_alert_box(capfile_name, errno, TRUE);
392 g_free(capfile_name);
396 /* close the old file */
397 cf_close(capture_opts->cf);
398 g_assert(capture_opts->save_file == NULL);
399 capture_opts->save_file = capfile_name;
400 /* capture_opts.save_file is "g_free"ed later, which is equivalent to
401 "g_free(capfile_name)". */
407 /* Open a specified file, or create a temporary file, and start a capture
408 to the file in question. */
409 /* Returns TRUE if the capture starts successfully, FALSE otherwise. */
411 do_capture(capture_options *capture_opts)
413 gboolean is_tempfile;
417 /* open the output file (temporary/specified name/ringbuffer) and close the old one */
418 if(!capture_open_output(capture_opts, &is_tempfile)) {
422 title = g_strdup_printf("%s: Capturing - Ethereal",
423 get_interface_descriptive_name(capture_opts->iface));
424 if (capture_opts->sync_mode) {
425 /* sync mode: do the capture in a child process */
426 ret = sync_pipe_do_capture(capture_opts, is_tempfile);
427 /* capture is still running */
428 set_main_window_name(title);
430 /* normal mode: do the capture synchronously */
431 set_main_window_name(title);
432 ret = normal_do_capture(capture_opts, is_tempfile);
433 /* capture is finished here */
441 /* start a normal capture session */
443 normal_do_capture(capture_options *capture_opts, gboolean is_tempfile)
445 int capture_succeeded;
446 gboolean stats_known;
447 struct pcap_stat stats;
451 capture_succeeded = capture_start(capture_opts, &stats_known, &stats);
452 if (capture_opts->quit_after_cap) {
453 /* DON'T unlink the save file. Presumably someone wants it. */
456 if (!capture_succeeded) {
457 /* We didn't succeed in doing the capture, so we don't have a save
459 if (capture_opts->multi_files_on) {
462 g_free(capture_opts->save_file);
464 capture_opts->save_file = NULL;
467 /* Capture succeeded; attempt to read in the capture file. */
468 if (cf_open(capture_opts->cf, capture_opts->save_file, is_tempfile, &err) != CF_OK) {
469 /* We're not doing a capture any more, so we don't have a save
471 if (capture_opts->multi_files_on) {
474 g_free(capture_opts->save_file);
476 capture_opts->save_file = NULL;
480 /* Set the read filter to NULL. */
481 cf_set_rfcode(capture_opts->cf, NULL);
483 /* Get the packet-drop statistics.
485 XXX - there are currently no packet-drop statistics stored
486 in libpcap captures, and that's what we're reading.
488 At some point, we will add support in Wiretap to return
489 packet-drop statistics for capture file formats that store it,
490 and will make "cf_read()" get those statistics from Wiretap.
491 We clear the statistics (marking them as "not known") in
492 "cf_open()", and "cf_read()" will only fetch them and mark
493 them as known if Wiretap supplies them, so if we get the
494 statistics now, after calling "cf_open()" but before calling
495 "cf_read()", the values we store will be used by "cf_read()".
497 If a future libpcap capture file format stores the statistics,
498 we'll put them into the capture file that we write, and will
499 thus not have to set them here - "cf_read()" will get them from
500 the file and use them. */
502 cf_set_drops_known(capture_opts->cf, TRUE);
504 /* XXX - on some systems, libpcap doesn't bother filling in
505 "ps_ifdrop" - it doesn't even set it to zero - so we don't
506 bother looking at it.
508 Ideally, libpcap would have an interface that gave us
509 several statistics - perhaps including various interface
510 error statistics - and would tell us which of them it
511 supplies, allowing us to display only the ones it does. */
512 cf_set_drops(capture_opts->cf, stats.ps_drop);
514 switch (cf_read(capture_opts->cf)) {
518 /* Just because we got an error, that doesn't mean we were unable
519 to read any of the file; we handle what we could get from the
523 case CF_READ_ABORTED:
524 /* Exit by leaving the main loop, so that any quit functions
525 we registered get called. */
526 main_window_nested_quit();
530 /* We're not doing a capture any more, so we don't have a save
532 if (capture_opts->multi_files_on) {
535 g_free(capture_opts->save_file);
537 capture_opts->save_file = NULL;
539 /* if we didn't captured even a single packet, close the file again */
540 if(cf_packet_count(capture_opts->cf) == 0) {
541 simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK,
542 "%sNo packets captured!%s\n\n"
543 "As no data was captured, closing the %scapture file!",
544 simple_dialog_primary_start(), simple_dialog_primary_end(),
545 (cf_is_tempfile(capture_opts->cf)) ? "temporary " : "");
546 cf_close(capture_opts->cf);
553 stop_capture_signal_handler(int signo _U_)
560 capture_start(capture_options *capture_opts, gboolean *stats_known, struct pcap_stat *stats)
564 * Catch SIGUSR1, so that we exit cleanly if the parent process
565 * kills us with it due to the user selecting "Capture->Stop".
567 if (capture_opts->capture_child)
568 signal(SIGUSR1, stop_capture_signal_handler);
571 return capture_loop_start(capture_opts, stats_known, stats);
575 capture_stop(capture_options *capture_opts)
578 if (capture_opts->sync_mode) {
579 sync_pipe_stop(capture_opts);
586 capture_kill_child(capture_options *capture_opts)
588 if (capture_opts->sync_mode) {
589 sync_pipe_kill(capture_opts);
594 #endif /* HAVE_LIBPCAP */