/* capture.c
* Routines for packet capture windows
*
- * $Id: capture.c,v 1.174 2002/05/04 09:11:28 guy Exp $
+ * $Id: capture.c,v 1.217 2003/11/17 19:40:09 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
* Copyright 1998 Gerald Combs
- *
+ *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifdef HAVE_LIBPCAP
-#ifdef HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#include <process.h> /* For spawning child process */
#endif
-/*
- * XXX - the various BSDs appear to define BSD in <sys/param.h>; we don't
- * want to include it if it's not present on this platform, however.
- */
-#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__)
-#ifndef BSD
-#define BSD
-#endif /* BSD */
-#endif /* defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) */
-
/*
* We don't want to do a "select()" on the pcap_t's file descriptor on
* BSD (because "select()" doesn't work correctly on BPF devices on at
* least some releases of some flavors of BSD), and we don't want to do
* it on Windows (because "select()" is something for sockets, not for
- * arbitrary handles).
+ * arbitrary handles). (Note that "Windows" here includes Cygwin;
+ * even in its pretend-it's-UNIX environment, we're using WinPcap, not
+ * a UNIX libpcap.)
*
* We *do* want to do it on other platforms, as, on other platforms (with
* the possible exception of Ultrix and Digital UNIX), the read timeout
* doesn't expire if no packets have arrived, so a "pcap_dispatch()" call
* will block until packets arrive, causing the UI to hang.
+ *
+ * XXX - the various BSDs appear to define BSD in <sys/param.h>; we don't
+ * want to include it if it's not present on this platform, however.
*/
-#if !defined(BSD) && !defined(_WIN32)
+#if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) && \
+ !defined(__bsdi__) && !defined(__APPLE__) && !defined(_WIN32) && \
+ !defined(__CYGWIN__)
# define MUST_DO_SELECT
#endif
-#include "gtk/main.h"
-#include "gtk/gtkglobals.h"
#include <epan/packet.h>
+#include <epan/dfilter/dfilter.h>
#include "file.h"
#include "capture.h"
#include "util.h"
#include "wiretap/libpcap.h"
#include "wiretap/wtap.h"
+#include "wiretap/wtap-capture.h"
#include "packet-atalk.h"
+#include "packet-atm.h"
#include "packet-clip.h"
#include "packet-eth.h"
#include "packet-fddi.h"
#include "packet-ieee80211.h"
#include "packet-chdlc.h"
#include "packet-prism.h"
+#include "packet-ipfc.h"
+#include "packet-arcnet.h"
-#ifdef WIN32
+#ifdef _WIN32
#include "capture-wpcap.h"
#endif
#ifndef _WIN32
static char *signame(int);
#endif
-static void capture_delete_cb(GtkWidget *, GdkEvent *, gpointer);
-static void capture_stop_cb(GtkWidget *, gpointer);
-static void capture_pcap_cb(u_char *, const struct pcap_pkthdr *,
- const u_char *);
+static void capture_pcap_cb(guchar *, const struct pcap_pkthdr *,
+ const guchar *);
static void get_capture_file_io_error(char *, int, const char *, int, gboolean);
+static void popup_errmsg(const char *);
static void send_errmsg_to_parent(const char *);
-static float pct(gint, gint);
static void stop_capture(int signo);
typedef struct _loop_data {
gint sync_packets;
gboolean pcap_err; /* TRUE if error from pcap */
gboolean from_pipe; /* TRUE if we are capturing data from a pipe */
- gboolean modified; /* TRUE if data in the pipe uses modified pcap headers */
- gboolean byte_swapped; /* TRUE if data in the pipe is byte swapped */
packet_counts counts;
wtap_dumper *pdh;
+#ifndef _WIN32
+ gboolean modified; /* TRUE if data in the pipe uses modified pcap headers */
+ gboolean byte_swapped; /* TRUE if data in the pipe is byte swapped */
+ unsigned int bytes_to_read, bytes_read; /* Used by pipe_dispatch */
+ enum {
+ STATE_EXPECT_REC_HDR, STATE_READ_REC_HDR,
+ STATE_EXPECT_DATA, STATE_READ_DATA
+ } pipe_state;
+
+ enum { PIPOK, PIPEOF, PIPERR, PIPNEXIST } pipe_err;
+#endif
} loop_data;
#ifndef _WIN32
static void adjust_header(loop_data *, struct pcap_hdr *, struct pcaprec_hdr *);
-static int pipe_open_live(char *, struct pcap_hdr *, loop_data *);
-static int pipe_dispatch(int, loop_data *, struct pcap_hdr *);
+static int pipe_open_live(char *, struct pcap_hdr *, loop_data *, char *, int);
+static int pipe_dispatch(int, loop_data *, struct pcap_hdr *, \
+ struct pcaprec_modified_hdr *, guchar *, char *, int);
#endif
/* Win32 needs the O_BINARY flag for open() */
#endif
/* Open a specified file, or create a temporary file, and start a capture
- to the file in question. */
-void
-do_capture(char *capfile_name)
+ to the file in question. Returns TRUE if the capture starts
+ successfully, FALSE otherwise. */
+gboolean
+do_capture(const char *save_file)
{
char tmpname[128+1];
gboolean is_tempfile;
- u_char c;
+ guchar c;
int i;
guint byte_count;
char *msg;
int capture_succeeded;
gboolean stats_known;
struct pcap_stat stats;
-
- if (capfile_name != NULL) {
+ gchar *capfile_name;
+
+ if (save_file != NULL) {
+ /* If the Sync option is set, we return to the caller while the capture
+ * is in progress. Therefore we need to take a copy of save_file in
+ * case the caller destroys it after we return.
+ */
+ capfile_name = g_strdup(save_file);
if (capture_opts.ringbuffer_on) {
/* ringbuffer is enabled */
cfile.save_file_fd = ringbuf_init(capfile_name,
ringbuf_error_cleanup();
}
simple_dialog(ESD_TYPE_CRIT, NULL,
- file_open_error_message(errno, TRUE), capfile_name);
+ file_open_error_message(errno, TRUE, WTAP_FILE_PCAP), capfile_name);
}
- return;
+ g_free(capfile_name);
+ return FALSE;
}
- close_cap_file(&cfile);
+ cf_close(&cfile);
g_assert(cfile.save_file == NULL);
cfile.save_file = capfile_name;
+ /* cfile.save_file is "g_free"ed below, which is equivalent to
+ "g_free(capfile_name)". */
if (capture_opts.sync_mode) { /* do the capture in a child process */
char ssnap[24];
argv = add_arg(argv, &argc, ssnap);
}
+ if (capture_opts.linktype != -1) {
+ argv = add_arg(argv, &argc, "-y");
+ sprintf(ssnap,"%d",capture_opts.linktype);
+ argv = add_arg(argv, &argc, ssnap);
+ }
+
if (capture_opts.has_autostop_filesize) {
argv = add_arg(argv, &argc, "-a");
sprintf(sautostop_filesize,"filesize:%d",capture_opts.autostop_filesize);
cfile.save_file = NULL;
simple_dialog(ESD_TYPE_CRIT, NULL, "Couldn't create sync pipe: %s",
strerror(error));
- return;
+ return FALSE;
}
/* Convert font name to a quote-encapsulated string and pass to child */
argv = add_arg(argv, &argc, sync_pipe_fd);
/* Convert filter string to a quote delimited string and pass to child */
+ filterstring = NULL;
if (cfile.cfilter != NULL && strlen(cfile.cfilter) != 0) {
argv = add_arg(argv, &argc, "-f");
filterstring = quote_encapsulate(cfile.cfilter);
/* Spawn process */
fork_child = spawnvp(_P_NOWAIT, ethereal_path, argv);
g_free(fontstring);
- g_free(filterstring);
+ if (filterstring) {
+ g_free(filterstring);
+ }
/* Keep a copy for later evaluation by _cwait() */
child_process = fork_child;
#else
- signal(SIGCHLD, SIG_IGN);
if (pipe(sync_pipe) < 0) {
/* Couldn't create the pipe between parent and child. */
error = errno;
cfile.save_file = NULL;
simple_dialog(ESD_TYPE_CRIT, NULL, "Couldn't create sync pipe: %s",
strerror(error));
- return;
+ return FALSE;
}
argv = add_arg(argv, &argc, "-m");
cfile.save_file = NULL;
simple_dialog(ESD_TYPE_CRIT, NULL, "Couldn't create child process: %s",
strerror(error));
- return;
+ return FALSE;
}
/* Read a byte count from "sync_pipe[READ]", terminated with a
g_free(cfile.save_file);
cfile.save_file = NULL;
wait_for_child(TRUE);
- return;
+ return FALSE;
}
if (c == SP_CAPSTART || c == SP_ERROR_MSG)
break;
cfile.save_file = NULL;
simple_dialog(ESD_TYPE_WARN, NULL,
"Capture child process sent us a bad message");
- return;
+ return FALSE;
}
byte_count = byte_count*10 + c - '0';
}
- if (c == SP_CAPSTART) {
- /* Success. Open the capture file, and set up to read it. */
- err = start_tail_cap_file(cfile.save_file, is_tempfile, &cfile);
- if (err == 0) {
- /* We were able to open and set up to read the capture file;
- arrange that our callback be called whenever it's possible
- to read from the sync pipe, so that it's called when
- the child process wants to tell us something. */
-#ifdef _WIN32
- /* Tricky to use pipes in win9x, as no concept of wait. NT can
- do this but that doesn't cover all win32 platforms. GTK can do
- this but doesn't seem to work over processes. Attempt to do
- something similar here, start a timer and check for data on every
- timeout. */
- cap_timer_id = gtk_timeout_add(1000, cap_timer_cb, NULL);
-#else
- cap_input_id = gtk_input_add_full(sync_pipe[READ],
- GDK_INPUT_READ|GDK_INPUT_EXCEPTION,
- cap_file_input_cb,
- NULL,
- (gpointer) &cfile,
- NULL);
-#endif
- } else {
- /* We weren't able to open the capture file; complain, and
- close the sync pipe. */
- simple_dialog(ESD_TYPE_CRIT, NULL,
- file_open_error_message(err, FALSE), cfile.save_file);
-
- /* Close the sync pipe. */
- close(sync_pipe[READ]);
-
- /* Don't unlink the save file - leave it around, for debugging
- purposes. */
- g_free(cfile.save_file);
- cfile.save_file = NULL;
- }
- } else {
+ if (c != SP_CAPSTART) {
/* Failure - the child process sent us a message indicating
what the problem was. */
if (byte_count == 0) {
"Capture child process failed: EOF reading its error message.");
wait_for_child(FALSE);
} else
- simple_dialog(ESD_TYPE_WARN, NULL, msg);
+ simple_dialog(ESD_TYPE_CRIT, NULL, msg);
g_free(msg);
}
g_free(cfile.save_file);
cfile.save_file = NULL;
}
+ return FALSE;
+ }
+
+ /* The child process started a capture.
+ Attempt to open the capture file and set up to read it. */
+ err = cf_start_tail(cfile.save_file, is_tempfile, &cfile);
+ if (err != 0) {
+ /* We weren't able to open the capture file; user has been
+ alerted. Close the sync pipe. */
+
+ close(sync_pipe[READ]);
+
+ /* Don't unlink the save file - leave it around, for debugging
+ purposes. */
+ g_free(cfile.save_file);
+ cfile.save_file = NULL;
+ return FALSE;
}
+ /* We were able to open and set up to read the capture file;
+ arrange that our callback be called whenever it's possible
+ to read from the sync pipe, so that it's called when
+ the child process wants to tell us something. */
+#ifdef _WIN32
+ /* Tricky to use pipes in win9x, as no concept of wait. NT can
+ do this but that doesn't cover all win32 platforms. GTK can do
+ this but doesn't seem to work over processes. Attempt to do
+ something similar here, start a timer and check for data on every
+ timeout. */
+ cap_timer_id = gtk_timeout_add(1000, cap_timer_cb, NULL);
+#else
+ cap_input_id = gtk_input_add_full(sync_pipe[READ],
+ GDK_INPUT_READ|GDK_INPUT_EXCEPTION,
+ cap_file_input_cb,
+ NULL,
+ (gpointer) &cfile,
+ NULL);
+#endif
} else {
/* Not sync mode. */
capture_succeeded = capture(&stats_known, &stats);
/* DON'T unlink the save file. Presumably someone wants it. */
gtk_exit(0);
}
- if (capture_succeeded) {
- /* Capture succeeded; read in the capture file. */
- if ((err = open_cap_file(cfile.save_file, is_tempfile, &cfile)) == 0) {
- /* Set the read filter to NULL. */
- cfile.rfcode = NULL;
-
- /* Get the packet-drop statistics.
-
- XXX - there are currently no packet-drop statistics stored
- in libpcap captures, and that's what we're reading.
-
- At some point, we will add support in Wiretap to return
- packet-drop statistics for capture file formats that store it,
- and will make "read_cap_file()" get those statistics from
- Wiretap. We clear the statistics (marking them as "not known")
- in "open_cap_file()", and "read_cap_file()" will only fetch
- them and mark them as known if Wiretap supplies them, so if
- we get the statistics now, after calling "open_cap_file()" but
- before calling "read_cap_file()", the values we store will
- be used by "read_cap_file()".
-
- If a future libpcap capture file format stores the statistics,
- we'll put them into the capture file that we write, and will
- thus not have to set them here - "read_cap_file()" will get
- them from the file and use them. */
- if (stats_known) {
- cfile.drops_known = TRUE;
-
- /* XXX - on some systems, libpcap doesn't bother filling in
- "ps_ifdrop" - it doesn't even set it to zero - so we don't
- bother looking at it.
-
- Ideally, libpcap would have an interface that gave us
- several statistics - perhaps including various interface
- error statistics - and would tell us which of them it
- supplies, allowing us to display only the ones it does. */
- cfile.drops = stats.ps_drop;
- }
- switch (read_cap_file(&cfile, &err)) {
-
- case READ_SUCCESS:
- case READ_ERROR:
- /* Just because we got an error, that doesn't mean we were unable
- to read any of the file; we handle what we could get from the
- file. */
- break;
-
- case READ_ABORTED:
- /* Exit by leaving the main loop, so that any quit functions
- we registered get called. */
- gtk_main_quit();
- return;
- }
+ if (!capture_succeeded) {
+ /* We didn't succeed in doing the capture, so we don't have a save
+ file. */
+ if (capture_opts.ringbuffer_on) {
+ ringbuf_free();
+ } else {
+ g_free(cfile.save_file);
}
+ cfile.save_file = NULL;
+ return FALSE;
+ }
+ /* Capture succeeded; attempt to read in the capture file. */
+ if ((err = cf_open(cfile.save_file, is_tempfile, &cfile)) != 0) {
+ /* We're not doing a capture any more, so we don't have a save
+ file. */
+ if (capture_opts.ringbuffer_on) {
+ ringbuf_free();
+ } else {
+ g_free(cfile.save_file);
+ }
+ cfile.save_file = NULL;
+ return FALSE;
+ }
+
+ /* Set the read filter to NULL. */
+ cfile.rfcode = NULL;
+
+ /* Get the packet-drop statistics.
+
+ XXX - there are currently no packet-drop statistics stored
+ in libpcap captures, and that's what we're reading.
+
+ At some point, we will add support in Wiretap to return
+ packet-drop statistics for capture file formats that store it,
+ and will make "cf_read()" get those statistics from Wiretap.
+ We clear the statistics (marking them as "not known") in
+ "cf_open()", and "cf_read()" will only fetch them and mark
+ them as known if Wiretap supplies them, so if we get the
+ statistics now, after calling "cf_open()" but before calling
+ "cf_read()", the values we store will be used by "cf_read()".
+
+ If a future libpcap capture file format stores the statistics,
+ we'll put them into the capture file that we write, and will
+ thus not have to set them here - "cf_read()" will get them from
+ the file and use them. */
+ if (stats_known) {
+ cfile.drops_known = TRUE;
+
+ /* XXX - on some systems, libpcap doesn't bother filling in
+ "ps_ifdrop" - it doesn't even set it to zero - so we don't
+ bother looking at it.
+
+ Ideally, libpcap would have an interface that gave us
+ several statistics - perhaps including various interface
+ error statistics - and would tell us which of them it
+ supplies, allowing us to display only the ones it does. */
+ cfile.drops = stats.ps_drop;
+ }
+ switch (cf_read(&cfile, &err)) {
+
+ case READ_SUCCESS:
+ case READ_ERROR:
+ /* Just because we got an error, that doesn't mean we were unable
+ to read any of the file; we handle what we could get from the
+ file. */
+ break;
+
+ case READ_ABORTED:
+ /* Exit by leaving the main loop, so that any quit functions
+ we registered get called. */
+ if (gtk_main_level() > 0)
+ gtk_main_quit();
+ return FALSE;
}
+
/* We're not doing a capture any more, so we don't have a save
file. */
if (capture_opts.ringbuffer_on) {
}
cfile.save_file = NULL;
}
+ return TRUE;
}
#ifdef _WIN32
/* There's stuff to read from the sync pipe, meaning the child has sent
us a message, or the sync pipe has closed, meaning the child has
closed it (perhaps because it exited). */
-static void
+static void
cap_file_input_cb(gpointer data, gint source _U_,
GdkInputCondition condition _U_)
{
capturing any more packets. Pick up its exit status, and
complain if it did anything other than exit with status 0. */
wait_for_child(FALSE);
-
+
/* Read what remains of the capture file, and finish the capture.
XXX - do something if this fails? */
- switch (finish_tail_cap_file(cf, &err)) {
+ switch (cf_finish_tail(cf, &err)) {
case READ_SUCCESS:
case READ_ERROR:
msglen -= chars_to_copy;
}
*r = '\0';
- simple_dialog(ESD_TYPE_WARN, NULL, msg);
+ simple_dialog(ESD_TYPE_CRIT, NULL, msg);
g_free(msg);
break;
default :
q++;
nread--;
break;
- }
+ }
}
/* Read from the capture file the number of records the child told us
it added.
XXX - do something if this fails? */
- switch (continue_tail_cap_file(cf, to_read, &err)) {
+ switch (cf_continue_tail(cf, to_read, &err)) {
case READ_SUCCESS:
case READ_ERROR:
sigmsg = "Segmentation violation";
break;
- /* http://metalab.unc.edu/pub/Linux/docs/HOWTO/GCC-HOWTO
+ /* http://metalab.unc.edu/pub/Linux/docs/HOWTO/GCC-HOWTO
Linux is POSIX compliant. These are not POSIX-defined signals ---
ISO/IEC 9945-1:1990 (IEEE Std 1003.1-1990), paragraph B.3.3.1.1 sez:
/*
* Timeout, in milliseconds, for reads from the stream of captured packets.
- *
- * XXX - Michael Tuexen says MacOS X's BPF appears to be broken, in that
- * if you use a timeout of 250 in "pcap_open_live()", you don't see
- * packets until a large number of packets arrive; the timeout doesn't
- * cause a smaller number of packets to be delivered. Perhaps a timeout
- * that's less than 1 second acts like no timeout at all, so that you
- * don't see packets until the BPF buffer fills up?
- *
- * The workaround is to use a timeout of 1000 seconds on MacOS X.
*/
-#ifdef __APPLE__
-#define CAP_READ_TIMEOUT 1000
-#else
#define CAP_READ_TIMEOUT 250
-#endif
#ifndef _WIN32
-/* Take carre of byte order in the libpcap headers read from pipes.
+/* Take care of byte order in the libpcap headers read from pipes.
* (function taken from wiretap/libpcap.c) */
static void
adjust_header(loop_data *ld, struct pcap_hdr *hdr, struct pcaprec_hdr *rechdr)
}
}
-/* Mimic pcap_open_live() for pipe captures
+/* Mimic pcap_open_live() for pipe captures
* We check if "pipename" is "-" (stdin) or a FIFO, open it, and read the
* header.
* N.B. : we can't read the libpcap formats used in RedHat 6.1 or SuSE 6.3
* because we can't seek on pipes (see wiretap/libpcap.c for details) */
static int
-pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld)
+pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld,
+ char *errmsg, int errmsgl)
{
struct stat pipe_stat;
int fd;
guint32 magic;
- int bytes_read, b;
+ int b, sel_ret;
+ unsigned int bytes_read;
+ fd_set rfds;
+ struct timeval timeout;
- if (strcmp(pipename, "-") == 0) fd = 0; /* read from stdin */
- else if (stat(pipename, &pipe_stat) == 0 && S_ISFIFO(pipe_stat.st_mode)) {
- if ((fd = open(pipename, O_RDONLY)) == -1) return -1;
- } else return -1;
+ /*
+ * XXX Ethereal blocks until we return
+ */
+ if (strcmp(pipename, "-") == 0)
+ fd = 0; /* read from stdin */
+ else {
+ if (stat(pipename, &pipe_stat) < 0) {
+ if (errno == ENOENT || errno == ENOTDIR)
+ ld->pipe_err = PIPNEXIST;
+ else {
+ snprintf(errmsg, errmsgl,
+ "The capture session could not be initiated "
+ "due to error on pipe: %s", strerror(errno));
+ ld->pipe_err = PIPERR;
+ }
+ return -1;
+ }
+ if (! S_ISFIFO(pipe_stat.st_mode)) {
+ if (S_ISCHR(pipe_stat.st_mode)) {
+ /*
+ * Assume the user specified an interface on a system where
+ * interfaces are in /dev. Pretend we haven't seen it.
+ */
+ ld->pipe_err = PIPNEXIST;
+ } else {
+ snprintf(errmsg, errmsgl,
+ "The capture session could not be initiated because\n"
+ "\"%s\" is neither an interface nor a pipe", pipename);
+ ld->pipe_err = PIPERR;
+ }
+ return -1;
+ }
+ fd = open(pipename, O_RDONLY | O_NONBLOCK);
+ if (fd == -1) {
+ snprintf(errmsg, errmsgl,
+ "The capture session could not be initiated "
+ "due to error on pipe open: %s", strerror(errno));
+ ld->pipe_err = PIPERR;
+ return -1;
+ }
+ }
ld->from_pipe = TRUE;
+
/* read the pcap header */
- if (read(fd, &magic, sizeof magic) != sizeof magic) {
- close(fd);
- return -1;
+ FD_ZERO(&rfds);
+ bytes_read = 0;
+ while (bytes_read < sizeof magic) {
+ FD_SET(fd, &rfds);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = CAP_READ_TIMEOUT*1000;
+ sel_ret = select(fd+1, &rfds, NULL, NULL, &timeout);
+ if (sel_ret < 0) {
+ snprintf(errmsg, errmsgl,
+ "Unexpected error from select: %s", strerror(errno));
+ goto error;
+ } else if (sel_ret > 0) {
+ b = read(fd, ((char *)&magic)+bytes_read, sizeof magic-bytes_read);
+ if (b <= 0) {
+ if (b == 0)
+ snprintf(errmsg, errmsgl, "End of file on pipe during open");
+ else
+ snprintf(errmsg, errmsgl, "Error on pipe during open: %s",
+ strerror(errno));
+ goto error;
+ }
+ bytes_read += b;
+ }
}
switch (magic) {
break;
default:
/* Not a "libpcap" type we know about. */
- close(fd);
- return -1;
+ snprintf(errmsg, errmsgl, "Unrecognized libpcap format");
+ goto error;
}
/* Read the rest of the header */
- bytes_read = read(fd, hdr, sizeof(struct pcap_hdr));
- if (bytes_read <= 0) {
- close(fd);
- return -1;
- }
- while ((unsigned) bytes_read < sizeof(struct pcap_hdr))
- {
- b = read(fd, ((char *)&hdr)+bytes_read, sizeof(struct pcap_hdr) - bytes_read);
- if (b <= 0) {
- close(fd);
- return -1;
+ bytes_read = 0;
+ while (bytes_read < sizeof(struct pcap_hdr)) {
+ FD_SET(fd, &rfds);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = CAP_READ_TIMEOUT*1000;
+ sel_ret = select(fd+1, &rfds, NULL, NULL, &timeout);
+ if (sel_ret < 0) {
+ snprintf(errmsg, errmsgl,
+ "Unexpected error from select: %s", strerror(errno));
+ goto error;
+ } else if (sel_ret > 0) {
+ b = read(fd, ((char *)hdr)+bytes_read,
+ sizeof(struct pcap_hdr) - bytes_read);
+ if (b <= 0) {
+ if (b == 0)
+ snprintf(errmsg, errmsgl, "End of file on pipe during open");
+ else
+ snprintf(errmsg, errmsgl, "Error on pipe during open: %s",
+ strerror(errno));
+ goto error;
+ }
+ bytes_read += b;
}
- bytes_read += b;
}
+
if (ld->byte_swapped) {
/* Byte-swap the header fields about which we care. */
hdr->version_major = BSWAP16(hdr->version_major);
hdr->snaplen = BSWAP32(hdr->snaplen);
hdr->network = BSWAP32(hdr->network);
}
+
if (hdr->version_major < 2) {
- close(fd);
- return -1;
+ snprintf(errmsg, errmsgl, "Unable to read old libpcap format");
+ goto error;
}
+ ld->pipe_state = STATE_EXPECT_REC_HDR;
+ ld->pipe_err = PIPOK;
return fd;
+
+error:
+ ld->pipe_err = PIPERR;
+ close(fd);
+ return -1;
+
}
/* We read one record from the pipe, take care of byte order in the record
* header, write the record in the capture file, and update capture statistics. */
+
static int
-pipe_dispatch(int fd, loop_data *ld, struct pcap_hdr *hdr)
+pipe_dispatch(int fd, loop_data *ld, struct pcap_hdr *hdr,
+ struct pcaprec_modified_hdr *rechdr, guchar *data,
+ char *errmsg, int errmsgl)
{
- struct wtap_pkthdr whdr;
- struct pcaprec_modified_hdr rechdr;
- int bytes_to_read, bytes_read, b;
- u_char pd[WTAP_MAX_PACKET_SIZE];
- int err;
-
- /* read the record header */
- bytes_to_read = ld->modified ? sizeof rechdr : sizeof rechdr.hdr;
- bytes_read = read(fd, &rechdr, bytes_to_read);
- if (bytes_read <= 0) {
- close(fd);
- ld->go = FALSE;
- return 0;
- }
- while (bytes_read < bytes_to_read)
- {
- b = read(fd, ((char *)&rechdr)+bytes_read, bytes_to_read - bytes_read);
+ struct pcap_pkthdr phdr;
+ int b;
+ enum { PD_REC_HDR_READ, PD_DATA_READ, PD_PIPE_EOF, PD_PIPE_ERR,
+ PD_ERR } result;
+
+ switch (ld->pipe_state) {
+
+ case STATE_EXPECT_REC_HDR:
+ ld->bytes_to_read = ld->modified ?
+ sizeof(struct pcaprec_modified_hdr) : sizeof(struct pcaprec_hdr);
+ ld->bytes_read = 0;
+ ld->pipe_state = STATE_READ_REC_HDR;
+ /* Fall through */
+
+ case STATE_READ_REC_HDR:
+ b = read(fd, ((char *)rechdr)+ld->bytes_read,
+ ld->bytes_to_read - ld->bytes_read);
if (b <= 0) {
- close(fd);
- ld->go = FALSE;
- return 0;
+ if (b == 0)
+ result = PD_PIPE_EOF;
+ else
+ result = PD_PIPE_ERR;
+ break;
}
- bytes_read += b;
- }
- /* take care of byte order */
- adjust_header(ld, hdr, &rechdr.hdr);
- if (rechdr.hdr.incl_len > WTAP_MAX_PACKET_SIZE) {
- close(fd);
- ld->go = FALSE;
- return 0;
- }
- /* read the packet data */
- bytes_read = read(fd, pd, rechdr.hdr.incl_len);
- if (bytes_read <= 0) {
- close(fd);
- ld->go = FALSE;
- return 0;
- }
- while ((unsigned) bytes_read < rechdr.hdr.incl_len)
- {
- b = read(fd, pd+bytes_read, rechdr.hdr.incl_len - bytes_read);
+ if ((ld->bytes_read += b) < ld->bytes_to_read)
+ return 0;
+ result = PD_REC_HDR_READ;
+ break;
+
+ case STATE_EXPECT_DATA:
+ ld->bytes_read = 0;
+ ld->pipe_state = STATE_READ_DATA;
+ /* Fall through */
+
+ case STATE_READ_DATA:
+ b = read(fd, data+ld->bytes_read, rechdr->hdr.incl_len - ld->bytes_read);
if (b <= 0) {
- close(fd);
- ld->go = FALSE;
- return 0;
- }
- bytes_read += b;
- }
- /* dump the packet data to the capture file */
- whdr.ts.tv_sec = rechdr.hdr.ts_sec;
- whdr.ts.tv_usec = rechdr.hdr.ts_usec;
- whdr.caplen = rechdr.hdr.incl_len;
- whdr.len = rechdr.hdr.orig_len;
- whdr.pkt_encap = ld->linktype;
- wtap_dump(ld->pdh, &whdr, NULL, pd, &err);
-
- /* update capture statistics */
- switch (ld->linktype) {
- case WTAP_ENCAP_ETHERNET:
- capture_eth(pd, 0, whdr.caplen, &ld->counts);
- break;
- case WTAP_ENCAP_FDDI:
- case WTAP_ENCAP_FDDI_BITSWAPPED:
- capture_fddi(pd, whdr.caplen, &ld->counts);
- break;
- case WTAP_ENCAP_PRISM_HEADER:
- capture_prism(pd, 0, whdr.caplen, &ld->counts);
- break;
- case WTAP_ENCAP_TOKEN_RING:
- capture_tr(pd, 0, whdr.caplen, &ld->counts);
- break;
- case WTAP_ENCAP_NULL:
- capture_null(pd, whdr.caplen, &ld->counts);
- break;
- case WTAP_ENCAP_PPP:
- capture_ppp_hdlc(pd, 0, whdr.caplen, &ld->counts);
- break;
- case WTAP_ENCAP_RAW_IP:
- capture_raw(pd, whdr.caplen, &ld->counts);
- break;
- case WTAP_ENCAP_LINUX_ATM_CLIP:
- capture_clip(pd, whdr.caplen, &ld->counts);
- break;
- case WTAP_ENCAP_IEEE_802_11:
- case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
- capture_ieee80211(pd, 0, whdr.caplen, &ld->counts);
- break;
- case WTAP_ENCAP_CHDLC:
- capture_chdlc(pd, 0, whdr.caplen, &ld->counts);
+ if (b == 0)
+ result = PD_PIPE_EOF;
+ else
+ result = PD_PIPE_ERR;
break;
- case WTAP_ENCAP_LOCALTALK:
- capture_llap(&ld->counts);
+ }
+ if ((ld->bytes_read += b) < rechdr->hdr.incl_len)
+ return 0;
+ result = PD_DATA_READ;
+ break;
+
+ default:
+ snprintf(errmsg, errmsgl, "pipe_dispatch: invalid state");
+ result = PD_ERR;
+
+ } /* switch (ld->pipe_state) */
+
+ /*
+ * We've now read as much data as we were expecting, so process it.
+ */
+ switch (result) {
+
+ case PD_REC_HDR_READ:
+ /* We've read the header. Take care of byte order. */
+ adjust_header(ld, hdr, &rechdr->hdr);
+ if (rechdr->hdr.incl_len > WTAP_MAX_PACKET_SIZE) {
+ snprintf(errmsg, errmsgl, "Frame %u too long (%d bytes)",
+ ld->counts.total+1, rechdr->hdr.incl_len);
break;
- /* XXX - FreeBSD may append 4-byte ATM pseudo-header to DLT_ATM_RFC1483,
- with LLC header following; we should implement it at some
- point. */
+ }
+ ld->pipe_state = STATE_EXPECT_DATA;
+ return 0;
+
+ case PD_DATA_READ:
+ /* Fill in a "struct pcap_pkthdr", and process the packet. */
+ phdr.ts.tv_sec = rechdr->hdr.ts_sec;
+ phdr.ts.tv_usec = rechdr->hdr.ts_usec;
+ phdr.caplen = rechdr->hdr.incl_len;
+ phdr.len = rechdr->hdr.orig_len;
+
+ capture_pcap_cb((guchar *)ld, &phdr, data);
+
+ ld->pipe_state = STATE_EXPECT_REC_HDR;
+ return 1;
+
+ case PD_PIPE_EOF:
+ ld->pipe_err = PIPEOF;
+ return -1;
+
+ case PD_PIPE_ERR:
+ snprintf(errmsg, errmsgl, "Error reading from pipe: %s",
+ strerror(errno));
+ /* Fall through */
+ case PD_ERR:
+ break;
}
- return 1;
+ ld->pipe_err = PIPERR;
+ /* Return here rather than inside the switch to prevent GCC warning */
+ return -1;
}
#endif
int
capture(gboolean *stats_known, struct pcap_stat *stats)
{
- GtkWidget *cap_w, *main_vb, *stop_bt, *counts_tb;
pcap_t *pch;
int pcap_encap;
int file_snaplen;
gchar open_err_str[PCAP_ERRBUF_SIZE];
gchar lookup_net_err_str[PCAP_ERRBUF_SIZE];
- gchar label_str[64];
bpf_u_int32 netnum, netmask;
struct bpf_program fcode;
+ const char *set_linktype_err_str;
time_t upd_time, cur_time;
+ time_t start_time;
int err, inpkts;
condition *cnd_stop_capturesize = NULL;
condition *cnd_stop_timeout = NULL;
- unsigned int i;
+ condition *cnd_ring_timeout = NULL;
static const char capstart_msg = SP_CAPSTART;
char errmsg[4096+1];
- gboolean dump_ok;
-#ifndef _WIN32
+ gboolean write_ok;
+ gboolean close_ok;
+ fd_set set1;
+ struct timeval timeout;
+ capture_info capture_ui;
+
+#ifdef _WIN32
+ WORD wVersionRequested;
+ WSADATA wsaData;
+#else
static const char ppamsg[] = "can't find PPA for ";
char *libpcap_warn;
+ int sel_ret;
+ int pipe_fd = -1;
+ struct pcap_hdr hdr;
+ struct pcaprec_modified_hdr rechdr;
+ guchar pcap_data[WTAP_MAX_PACKET_SIZE];
#endif
- fd_set set1;
- struct timeval timeout;
#ifdef MUST_DO_SELECT
int pcap_fd = 0;
#endif
-#ifdef _WIN32
- WORD wVersionRequested;
- WSADATA wsaData;
-#endif
-#ifndef _WIN32
- int pipe_fd = -1;
- struct pcap_hdr hdr;
-#endif
- struct {
- const gchar *title;
- gint *value_ptr;
- GtkWidget *label, *value, *percent;
- } counts[] = {
- { "Total", &ld.counts.total, NULL, NULL, NULL },
- { "SCTP", &ld.counts.sctp, NULL, NULL, NULL },
- { "TCP", &ld.counts.tcp, NULL, NULL, NULL },
- { "UDP", &ld.counts.udp, NULL, NULL, NULL },
- { "ICMP", &ld.counts.icmp, NULL, NULL, NULL },
- { "OSPF", &ld.counts.ospf, NULL, NULL, NULL },
- { "GRE", &ld.counts.gre, NULL, NULL, NULL },
- { "NetBIOS", &ld.counts.netbios, NULL, NULL, NULL },
- { "IPX", &ld.counts.ipx, NULL, NULL, NULL },
- { "VINES", &ld.counts.vines, NULL, NULL, NULL },
- { "Other", &ld.counts.other, NULL, NULL, NULL }
- };
-
-#define N_COUNTS (sizeof counts / sizeof counts[0])
-
- /* Initialize Windows Socket if we are in a WIN32 OS
+
+/* Size of buffer to hold decimal representation of
+ signed/unsigned 64-bit int */
+#define DECISIZE 20
+
+ /* Initialize Windows Socket if we are in a WIN32 OS
This needs to be done before querying the interface for network/netmask */
-#ifdef _WIN32
- wVersionRequested = MAKEWORD( 1, 1 );
- err = WSAStartup( wVersionRequested, &wsaData );
- if (err!=0) {
- snprintf(errmsg, sizeof errmsg,
- "Couldn't initialize Windows Sockets.");
- pch=NULL;
- goto error;
- }
-#endif
+#ifdef _WIN32
+ /* XXX - do we really require 1.1 or earlier?
+ Are there any versions that support only 2.0 or higher? */
+ wVersionRequested = MAKEWORD(1, 1);
+ err = WSAStartup(wVersionRequested, &wsaData);
+ if (err != 0) {
+ switch (err) {
+
+ case WSASYSNOTREADY:
+ snprintf(errmsg, sizeof errmsg,
+ "Couldn't initialize Windows Sockets: Network system not ready for network communication");
+ break;
+
+ case WSAVERNOTSUPPORTED:
+ snprintf(errmsg, sizeof errmsg,
+ "Couldn't initialize Windows Sockets: Windows Sockets version %u.%u not supported",
+ LOBYTE(wVersionRequested), HIBYTE(wVersionRequested));
+ break;
+
+ case WSAEINPROGRESS:
+ snprintf(errmsg, sizeof errmsg,
+ "Couldn't initialize Windows Sockets: Blocking operation is in progress");
+ break;
+
+ case WSAEPROCLIM:
+ snprintf(errmsg, sizeof errmsg,
+ "Couldn't initialize Windows Sockets: Limit on the number of tasks supported by this WinSock implementation has been reached");
+ break;
+
+ case WSAEFAULT:
+ snprintf(errmsg, sizeof errmsg,
+ "Couldn't initialize Windows Sockets: Bad pointer passed to WSAStartup");
+ break;
+
+ default:
+ snprintf(errmsg, sizeof errmsg,
+ "Couldn't initialize Windows Sockets: error %d", err);
+ break;
+ }
+ pch = NULL;
+ goto error;
+ }
+#endif
ld.go = TRUE;
ld.counts.total = 0;
ld.counts.netbios = 0;
ld.counts.vines = 0;
ld.counts.other = 0;
+ ld.counts.arp = 0;
ld.pdh = NULL;
/* We haven't yet gotten the capture statistics. */
capture_opts.promisc_mode, CAP_READ_TIMEOUT,
open_err_str);
- if (pch == NULL) {
+ if (pch != NULL) {
+ /* setting the data link type only works on real interfaces */
+ if (capture_opts.linktype != -1) {
+ set_linktype_err_str = set_pcap_linktype(pch, cfile.iface,
+ capture_opts.linktype);
+ if (set_linktype_err_str != NULL) {
+ snprintf(errmsg, sizeof errmsg, "Unable to set data link type (%s).",
+ set_linktype_err_str);
+ goto error;
+ }
+ }
+ } else {
+ /* We couldn't open "cfile.iface" as a network device. */
#ifdef _WIN32
- /* Well, we couldn't start the capture.
+ /* On Windows, we don't support capturing on pipes, so we give up.
If this is a child process that does the capturing in sync
mode or fork mode, it shouldn't do any UI stuff until we pop up the
capture-progress window, and, since we couldn't start the
"The capture session could not be initiated (%s).\n"
"Please check that you have the proper interface specified.\n"
"\n"
- "Note that the driver Ethereal uses for packet capture on Windows\n"
- "doesn't support capturing on PPP/WAN interfaces in Windows NT/2000.\n",
+ "Note that the driver Ethereal uses for packet capture on Windows doesn't\n"
+ "support capturing on PPP/WAN interfaces in Windows NT/2000/XP/.NET Server.\n",
open_err_str);
goto error;
#else
/* try to open cfile.iface as a pipe */
- pipe_fd = pipe_open_live(cfile.iface, &hdr, &ld);
+ pipe_fd = pipe_open_live(cfile.iface, &hdr, &ld, errmsg, sizeof errmsg);
if (pipe_fd == -1) {
- /* Well, we couldn't start the capture assuming the interface was
- a device, and we couldn't open the pipe, either.
-
- We choose to report the error for the device, rather than the
- file, under the assumption that you're typically capturing on
- a device.
- If this is a child process that does the capturing in sync
- mode or fork mode, it shouldn't do any UI stuff until we pop up the
- capture-progress window, and, since we couldn't start the
- capture, we haven't popped it up. */
+ /* If this is a child process that does the capturing in sync
+ * mode or fork mode, it shouldn't do any UI stuff until we pop up the
+ * capture-progress window, and, since we couldn't start the
+ * capture, we haven't popped it up.
+ */
if (!capture_child) {
while (gtk_events_pending()) gtk_main_iteration();
}
- /* If we got a "can't find PPA for XXX" message, warn the user (who
- is running Ethereal on HP-UX) that they don't have a version
- of libpcap that properly handles HP-UX (libpcap 0.6.x and later
- versions, which properly handle HP-UX, say "can't find /dev/dlpi
- PPA for XXX" rather than "can't find PPA for XXX"). */
- if (strncmp(open_err_str, ppamsg, sizeof ppamsg - 1) == 0)
- libpcap_warn =
- "\n\n"
- "You are running Ethereal with a version of the libpcap library\n"
- "that doesn't handle HP-UX network devices well; this means that\n"
- "Ethereal may not be able to capture packets.\n"
- "\n"
- "To fix this, you should install libpcap 0.6.2, or a later version\n"
- "of libpcap, rather than libpcap 0.4 or 0.5.x. It is available in\n"
- "packaged binary form from the Software Porting And Archive Centre\n"
- "for HP-UX; the Centre is at http://hpux.connect.org.uk/ - the page\n"
- "at the URL lists a number of mirror sites.";
- else
- libpcap_warn = "";
- snprintf(errmsg, sizeof errmsg,
+ if (ld.pipe_err == PIPNEXIST) {
+ /* Pipe doesn't exist, so output message for interface */
+
+ /* If we got a "can't find PPA for XXX" message, warn the user (who
+ is running Ethereal on HP-UX) that they don't have a version
+ of libpcap that properly handles HP-UX (libpcap 0.6.x and later
+ versions, which properly handle HP-UX, say "can't find /dev/dlpi
+ PPA for XXX" rather than "can't find PPA for XXX"). */
+ if (strncmp(open_err_str, ppamsg, sizeof ppamsg - 1) == 0)
+ libpcap_warn =
+ "\n\n"
+ "You are running Ethereal with a version of the libpcap library\n"
+ "that doesn't handle HP-UX network devices well; this means that\n"
+ "Ethereal may not be able to capture packets.\n"
+ "\n"
+ "To fix this, you should install libpcap 0.6.2, or a later version\n"
+ "of libpcap, rather than libpcap 0.4 or 0.5.x. It is available in\n"
+ "packaged binary form from the Software Porting And Archive Centre\n"
+ "for HP-UX; the Centre is at http://hpux.connect.org.uk/ - the page\n"
+ "at the URL lists a number of mirror sites.";
+ else
+ libpcap_warn = "";
+ snprintf(errmsg, sizeof errmsg,
"The capture session could not be initiated (%s).\n"
"Please check to make sure you have sufficient permissions, and that\n"
"you have the proper interface or pipe specified.%s", open_err_str,
libpcap_warn);
+ }
+ /*
+ * Else pipe (or file) does exist and pipe_open_live() has
+ * filled in errmsg
+ */
goto error;
- }
+ } else
+ /* pipe_open_live() succeeded; don't want
+ error message from pcap_open_live() */
+ open_err_str[0] = '\0';
#endif
}
netmask = 0;
}
if (pcap_compile(pch, &fcode, cfile.cfilter, 1, netmask) < 0) {
- snprintf(errmsg, sizeof errmsg, "Unable to parse filter string (%s).",
- pcap_geterr(pch));
+ dfilter_t *rfcode = NULL;
+ if (dfilter_compile(cfile.cfilter, &rfcode)) {
+ snprintf(errmsg, sizeof errmsg,
+ "Unable to parse capture filter string (%s).\n"
+ " Interestingly enough, this looks like a valid display filter\n"
+ " Are you sure you didn't mix them up?",
+ pcap_geterr(pch));
+ dfilter_free(rfcode);
+ } else {
+ snprintf(errmsg, sizeof errmsg,
+ "Unable to parse capture filter string (%s).",
+ pcap_geterr(pch));
+ }
goto error;
}
if (pcap_setfilter(pch, &fcode) < 0) {
goto error;
}
if (capture_opts.ringbuffer_on) {
- ld.pdh = ringbuf_init_wtap_dump_fdopen(WTAP_FILE_PCAP, ld.linktype,
+ ld.pdh = ringbuf_init_wtap_dump_fdopen(WTAP_FILE_PCAP, ld.linktype,
file_snaplen, &err);
} else {
ld.pdh = wtap_dump_fdopen(cfile.save_file_fd, WTAP_FILE_PCAP,
write(1, &capstart_msg, 1);
}
- cap_w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
- gtk_window_set_title(GTK_WINDOW(cap_w), "Ethereal: Capture");
- gtk_window_set_modal(GTK_WINDOW(cap_w), TRUE);
-
- /* Container for capture display widgets */
- main_vb = gtk_vbox_new(FALSE, 1);
- gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
- gtk_container_add(GTK_CONTAINER(cap_w), main_vb);
- gtk_widget_show(main_vb);
-
- /* Individual statistic elements */
- counts_tb = gtk_table_new(N_COUNTS, 3, TRUE);
- gtk_box_pack_start(GTK_BOX(main_vb), counts_tb, TRUE, TRUE, 3);
- gtk_widget_show(counts_tb);
-
- for (i = 0; i < N_COUNTS; i++) {
- counts[i].label = gtk_label_new(counts[i].title);
- gtk_misc_set_alignment(GTK_MISC(counts[i].label), 0.0f, 0.0f);
-
- counts[i].value = gtk_label_new("0");
- gtk_misc_set_alignment(GTK_MISC(counts[i].value), 0.0f, 0.0f);
-
- counts[i].percent = gtk_label_new("0.0%");
- gtk_misc_set_alignment(GTK_MISC(counts[i].percent), 0.0f, 0.0f);
-
- gtk_table_attach_defaults(GTK_TABLE(counts_tb),
- counts[i].label, 0, 1, i, i + 1);
-
- gtk_table_attach(GTK_TABLE(counts_tb),
- counts[i].value,
- 1, 2, i, i + 1, 0, 0, 5, 0);
-
- gtk_table_attach_defaults(GTK_TABLE(counts_tb),
- counts[i].percent, 2, 3, i, i + 1);
-
- gtk_widget_show(counts[i].label);
- gtk_widget_show(counts[i].value);
- gtk_widget_show(counts[i].percent);
- }
-
- /* allow user to either click a stop button, or the close button on
- the window to stop a capture in progress. */
- stop_bt = gtk_button_new_with_label ("Stop");
- gtk_signal_connect(GTK_OBJECT(stop_bt), "clicked",
- GTK_SIGNAL_FUNC(capture_stop_cb), (gpointer) &ld);
- gtk_signal_connect(GTK_OBJECT(cap_w), "delete_event",
- GTK_SIGNAL_FUNC(capture_delete_cb), (gpointer) &ld);
- gtk_box_pack_end(GTK_BOX(main_vb), stop_bt, FALSE, FALSE, 3);
- GTK_WIDGET_SET_FLAGS(stop_bt, GTK_CAN_DEFAULT);
- gtk_widget_grab_default(stop_bt);
- GTK_WIDGET_SET_FLAGS(stop_bt, GTK_CAN_DEFAULT);
- gtk_widget_grab_default(stop_bt);
- gtk_widget_show(stop_bt);
-
- gtk_widget_show(cap_w);
+ /* start capture info dialog */
+ capture_ui.callback_data = &ld;
+ capture_ui.counts = &ld.counts;
+ capture_info_create(&capture_ui);
+ start_time = time(NULL);
upd_time = time(NULL);
#ifdef MUST_DO_SELECT
if (!ld.from_pipe) pcap_fd = pcap_fileno(pch);
* Catch SIGUSR1, so that we exit cleanly if the parent process
* kills us with it due to the user selecting "Capture->Stop".
*/
- signal(SIGUSR1, stop_capture);
+ if (capture_child)
+ signal(SIGUSR1, stop_capture);
#endif
- /* initialize capture stop conditions */
+ /* initialize capture stop conditions */
init_capture_stop_conditions();
/* create stop conditions */
if (capture_opts.has_autostop_filesize)
cnd_stop_capturesize =
- cnd_new(CND_CLASS_CAPTURESIZE,(long)capture_opts.autostop_filesize * 1000);
+ cnd_new(CND_CLASS_CAPTURESIZE,(long)capture_opts.autostop_filesize * 1000);
if (capture_opts.has_autostop_duration)
cnd_stop_timeout =
cnd_new(CND_CLASS_TIMEOUT,(gint32)capture_opts.autostop_duration);
+ if (capture_opts.ringbuffer_on && capture_opts.has_ring_duration)
+ cnd_ring_timeout =
+ cnd_new(CND_CLASS_TIMEOUT, capture_opts.ringbuffer_duration);
+
+
+ /* WOW, everything is prepared! */
+ /* please fasten your seat belts, we will enter now the actual capture loop */
while (ld.go) {
while (gtk_events_pending()) gtk_main_iteration();
FD_SET(pipe_fd, &set1);
timeout.tv_sec = 0;
timeout.tv_usec = CAP_READ_TIMEOUT*1000;
- if (select(pipe_fd+1, &set1, NULL, NULL, &timeout) != 0) {
+ sel_ret = select(pipe_fd+1, &set1, NULL, NULL, &timeout);
+ if (sel_ret <= 0) {
+ inpkts = 0;
+ if (sel_ret < 0 && errno != EINTR) {
+ snprintf(errmsg, sizeof(errmsg),
+ "Unexpected error from select: %s", strerror(errno));
+ popup_errmsg(errmsg);
+ ld.go = FALSE;
+ }
+ } else {
/*
- * "select()" says we can read from the pipe without blocking; go for
- * it. We are not sure we can read a whole record, but at least the
- * begninning of one. pipe_dispatch() will block reading the whole
- * record.
+ * "select()" says we can read from the pipe without blocking
*/
- inpkts = pipe_dispatch(pipe_fd, &ld, &hdr);
- } else
- inpkts = 0;
+ inpkts = pipe_dispatch(pipe_fd, &ld, &hdr, &rechdr, pcap_data,
+ errmsg, sizeof errmsg);
+ if (inpkts < 0) {
+ ld.go = FALSE;
+ }
+ }
}
else
#endif
FD_SET(pcap_fd, &set1);
timeout.tv_sec = 0;
timeout.tv_usec = CAP_READ_TIMEOUT*1000;
- if (select(pcap_fd+1, &set1, NULL, NULL, &timeout) != 0) {
+ sel_ret = select(pcap_fd+1, &set1, NULL, NULL, &timeout);
+ if (sel_ret > 0) {
/*
* "select()" says we can read from it without blocking; go for
* it.
*/
- inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (u_char *) &ld);
+ inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (guchar *) &ld);
if (inpkts < 0) {
ld.pcap_err = TRUE;
ld.go = FALSE;
}
- } else
- inpkts = 0;
+ } else {
+ inpkts = 0;
+ if (sel_ret < 0 && errno != EINTR) {
+ snprintf(errmsg, sizeof(errmsg),
+ "Unexpected error from select: %s", strerror(errno));
+ popup_errmsg(errmsg);
+ ld.go = FALSE;
+ }
+ }
#else
- inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (u_char *) &ld);
+ inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (guchar *) &ld);
if (inpkts < 0) {
ld.pcap_err = TRUE;
ld.go = FALSE;
}
#endif
}
- if (inpkts > 0)
+
+ if (inpkts > 0) {
ld.sync_packets += inpkts;
- /* check capture stop conditons */
- if (cnd_stop_timeout != NULL && cnd_eval(cnd_stop_timeout)) {
- /* The specified capture time has elapsed; stop the capture. */
- ld.go = FALSE;
- } else if (cnd_stop_capturesize != NULL && cnd_eval(cnd_stop_capturesize,
- (guint32)wtap_get_bytes_dumped(ld.pdh))){
- /* Capture file reached its maximum size. */
- if (capture_opts.ringbuffer_on) {
- /* Switch to the next ringbuffer file */
- if (ringbuf_switch_file(&cfile, &ld.pdh, &ld.err)) {
- /* File switch succeeded: reset the condition */
- cnd_reset(cnd_stop_capturesize);
+ /* check capture stop conditons */
+ if (cnd_stop_capturesize != NULL && cnd_eval(cnd_stop_capturesize,
+ (guint32)wtap_get_bytes_dumped(ld.pdh))){
+ /* Capture file reached its maximum size. */
+ if (capture_opts.ringbuffer_on) {
+ /* Switch to the next ringbuffer file */
+ if (ringbuf_switch_file(&cfile, &ld.pdh, &ld.err)) {
+ /* File switch succeeded: reset the condition */
+ cnd_reset(cnd_stop_capturesize);
+ if (cnd_ring_timeout) {
+ cnd_reset(cnd_ring_timeout);
+ }
+ } else {
+ /* File switch failed: stop here */
+ ld.go = FALSE;
+ continue;
+ }
} else {
- /* File switch failed: stop here */
+ /* no ringbuffer - just stop */
ld.go = FALSE;
- continue;
}
- } else {
- /* no ringbuffer - just stop */
- ld.go = FALSE;
}
}
+
/* Only update once a second so as not to overload slow displays */
cur_time = time(NULL);
if (cur_time > upd_time) {
upd_time = cur_time;
- for (i = 0; i < N_COUNTS; i++) {
- snprintf(label_str, sizeof(label_str), "%d",
- *counts[i].value_ptr);
+ /*if (pcap_stats(pch, stats) >= 0) {
+ *stats_known = TRUE;
+ }*/
- gtk_label_set(GTK_LABEL(counts[i].value), label_str);
+ /* Let the parent process know. */
+ /* calculate and display running time */
+ cur_time -= start_time;
+ capture_ui.running_time = cur_time;
+ capture_ui.new_packets = ld.sync_packets;
+ capture_info_update(&capture_ui);
+
+ if (ld.sync_packets) {
+ /* do sync here */
+ fflush(wtap_dump_file(ld.pdh));
+
+ if (capture_child) {
+ /* This is the child process for a sync mode capture, so send
+ our parent a message saying we've written out "ld.sync_packets"
+ packets to the capture file. */
+ char tmp[DECISIZE+1+1];
+ sprintf(tmp, "%d%c", ld.sync_packets, SP_PACKET_COUNT);
+ write(1, tmp, strlen(tmp));
+ }
- snprintf(label_str, sizeof(label_str), "(%.1f%%)",
- pct(*counts[i].value_ptr, ld.counts.total));
+ ld.sync_packets = 0;
- gtk_label_set(GTK_LABEL(counts[i].percent), label_str);
}
- /* do sync here, too */
- fflush(wtap_dump_file(ld.pdh));
- if (capture_child && ld.sync_packets) {
- /* This is the child process for a sync mode capture, so send
- our parent a message saying we've written out "ld.sync_packets"
- packets to the capture file. */
- char tmp[20];
- sprintf(tmp, "%d%c", ld.sync_packets, SP_PACKET_COUNT);
- write(1, tmp, strlen(tmp));
- ld.sync_packets = 0;
+ if (cnd_stop_timeout != NULL && cnd_eval(cnd_stop_timeout)) {
+ /* The specified capture time has elapsed; stop the capture. */
+ ld.go = FALSE;
+ } else if (cnd_ring_timeout != NULL && cnd_eval(cnd_ring_timeout)) {
+ /* time elasped for this ring file, swith to the next */
+ if (ringbuf_switch_file(&cfile, &ld.pdh, &ld.err)) {
+ /* File switch succeeded: reset the condition */
+ cnd_reset(cnd_ring_timeout);
+ } else {
+ /* File switch failed: stop here */
+ ld.go = FALSE;
+ }
}
}
- }
-
+
+ } /* while (ld.go) */
+
/* delete stop conditions */
if (cnd_stop_capturesize != NULL)
cnd_delete(cnd_stop_capturesize);
if (cnd_stop_timeout != NULL)
cnd_delete(cnd_stop_timeout);
+ if (cnd_ring_timeout != NULL)
+ cnd_delete(cnd_ring_timeout);
if (ld.pcap_err) {
snprintf(errmsg, sizeof(errmsg), "Error while capturing packets: %s",
pcap_geterr(pch));
- if (capture_child) {
- /* Tell the parent, so that they can pop up the message;
- we're going to exit, so if we try to pop it up, either
- it won't pop up or it'll disappear as soon as we exit. */
- send_errmsg_to_parent(errmsg);
- } else {
- /* Just pop up the message ourselves. */
- simple_dialog(ESD_TYPE_WARN, NULL, "%s", errmsg);
- }
+ popup_errmsg(errmsg);
+#ifdef _WIN32
}
+#else
+ } else if (ld.from_pipe && ld.pipe_err == PIPERR)
+ popup_errmsg(errmsg);
+#endif
- if (ld.err != 0) {
+ if (ld.err == 0)
+ write_ok = TRUE;
+ else {
get_capture_file_io_error(errmsg, sizeof(errmsg), cfile.save_file, ld.err,
FALSE);
- if (capture_child) {
- /* Tell the parent, so that they can pop up the message;
- we're going to exit, so if we try to pop it up, either
- it won't pop up or it'll disappear as soon as we exit. */
- send_errmsg_to_parent(errmsg);
- } else {
- /* Just pop up the message ourselves. */
- simple_dialog(ESD_TYPE_WARN, NULL, "%s", errmsg);
- }
+ popup_errmsg(errmsg);
+ write_ok = FALSE;
+ }
- /* A write failed, so we've already told the user there's a problem;
- if the close fails, there's no point in telling them about that
- as well. */
- if (capture_opts.ringbuffer_on) {
- ringbuf_wtap_dump_close(&cfile, &err);
- } else {
- wtap_dump_close(ld.pdh, &err);
- }
- } else {
- if (capture_opts.ringbuffer_on) {
- dump_ok = ringbuf_wtap_dump_close(&cfile, &err);
- } else {
- dump_ok = wtap_dump_close(ld.pdh, &err);
- }
- if (!dump_ok) {
- get_capture_file_io_error(errmsg, sizeof(errmsg), cfile.save_file, err,
- TRUE);
- if (capture_child) {
- /* Tell the parent, so that they can pop up the message;
- we're going to exit, so if we try to pop it up, either
- it won't pop up or it'll disappear as soon as we exit. */
- send_errmsg_to_parent(errmsg);
- } else {
- /* Just pop up the message ourselves. */
- simple_dialog(ESD_TYPE_WARN, NULL, "%s", errmsg);
- }
- }
+ if (capture_opts.ringbuffer_on) {
+ close_ok = ringbuf_wtap_dump_close(&cfile, &err);
+ } else {
+ close_ok = wtap_dump_close(ld.pdh, &err);
}
+ /* If we've displayed a message about a write error, there's no point
+ in displaying another message about an error on close. */
+ if (!close_ok && write_ok) {
+ get_capture_file_io_error(errmsg, sizeof(errmsg), cfile.save_file, err,
+ TRUE);
+ popup_errmsg(errmsg);
+ }
+ /* Set write_ok to mean the write and the close were successful. */
+ write_ok = (write_ok && close_ok);
+
#ifndef _WIN32
- if (ld.from_pipe)
+ /*
+ * XXX We exhibit different behaviour between normal mode and sync mode
+ * when the pipe is stdin and not already at EOF. If we're a child, the
+ * parent's stdin isn't closed, so if the user starts another capture,
+ * pipe_open_live() will very likely not see the expected magic bytes and
+ * will say "Unrecognized libpcap format". On the other hand, in normal
+ * mode, pipe_open_live() will say "End of file on pipe during open".
+ */
+ if (ld.from_pipe && pipe_fd >= 0)
close(pipe_fd);
else
#endif
*stats_known = TRUE;
if (capture_child) {
/* Let the parent process know. */
- char tmp[20];
+ char tmp[DECISIZE+1+1];
sprintf(tmp, "%d%c", stats->ps_drop, SP_DROPS);
write(1, tmp, strlen(tmp));
}
snprintf(errmsg, sizeof(errmsg),
"Can't get packet-drop statistics: %s",
pcap_geterr(pch));
- if (capture_child) {
- /* Tell the parent, so that they can pop up the message;
- we're going to exit, so if we try to pop it up, either
- it won't pop up or it'll disappear as soon as we exit. */
- send_errmsg_to_parent(errmsg);
- } else {
- /* Just pop up the message ourselves. */
- simple_dialog(ESD_TYPE_WARN, NULL, "%s", errmsg);
- }
+ popup_errmsg(errmsg);
}
pcap_close(pch);
}
-#ifdef WIN32
+#ifdef _WIN32
/* Shut down windows sockets */
WSACleanup();
#endif
- gtk_grab_remove(GTK_WIDGET(cap_w));
- gtk_widget_destroy(GTK_WIDGET(cap_w));
+ capture_info_destroy(&capture_ui);
- return TRUE;
+ return write_ok;
error:
if (capture_opts.ringbuffer_on) {
g_free(cfile.save_file);
}
cfile.save_file = NULL;
- if (capture_child) {
- /* This is the child process for a sync mode capture.
- Send the error message to our parent, so they can display a
- dialog box containing it. */
- send_errmsg_to_parent(errmsg);
- } else {
- /* Display the dialog box ourselves; there's no parent. */
- simple_dialog(ESD_TYPE_CRIT, NULL, "%s", errmsg);
+ popup_errmsg(errmsg);
+
+#ifndef _WIN32
+ if (ld.from_pipe) {
+ if (pipe_fd >= 0)
+ close(pipe_fd);
+ } else
+#endif
+ {
+ if (pch != NULL)
+ pcap_close(pch);
}
- if (pch != NULL && !ld.from_pipe)
- pcap_close(pch);
return FALSE;
}
}
}
+static void
+popup_errmsg(const char *errmsg)
+{
+ if (capture_child) {
+ /* This is the child process for a sync mode capture.
+ Send the error message to our parent, so they can display a
+ dialog box containing it. */
+ send_errmsg_to_parent(errmsg);
+ } else {
+ /* Display the dialog box ourselves; there's no parent. */
+ simple_dialog(ESD_TYPE_CRIT, NULL, "%s", errmsg);
+ }
+}
+
static void
send_errmsg_to_parent(const char *errmsg)
{
int msglen = strlen(errmsg);
- char lenbuf[10+1+1];
+ char lenbuf[DECISIZE+1+1];
sprintf(lenbuf, "%u%c", msglen, SP_ERROR_MSG);
write(1, lenbuf, strlen(lenbuf));
write(1, errmsg, msglen);
}
-static float
-pct(gint num, gint denom) {
- if (denom) {
- return (float) num * 100.0 / (float) denom;
- } else {
- return 0.0;
- }
-}
-
static void
stop_capture(int signo _U_)
{
ld.go = FALSE;
}
-static void
-capture_delete_cb(GtkWidget *w _U_, GdkEvent *event _U_, gpointer data) {
- capture_stop_cb(NULL, data);
-}
+void capture_ui_stop_callback(
+gpointer callback_data)
+{
+ loop_data *ld = (loop_data *) callback_data;
-static void
-capture_stop_cb(GtkWidget *w _U_, gpointer data) {
- loop_data *ld = (loop_data *) data;
-
ld->go = FALSE;
}
+
void
capture_stop(void)
{
}
static void
-capture_pcap_cb(u_char *user, const struct pcap_pkthdr *phdr,
- const u_char *pd) {
+capture_pcap_cb(guchar *user, const struct pcap_pkthdr *phdr,
+ const guchar *pd)
+{
struct wtap_pkthdr whdr;
+ union wtap_pseudo_header pseudo_header;
loop_data *ld = (loop_data *) user;
int err;
- if ((++ld->counts.total >= ld->max) && (ld->max > 0))
+ if ((++ld->counts.total >= ld->max) && (ld->max > 0))
{
ld->go = FALSE;
}
+
+ /* Convert from libpcap to Wiretap format.
+ If that fails, set "ld->go" to FALSE, to stop the capture, and set
+ "ld->err" to the error. */
+ pd = wtap_process_pcap_packet(ld->linktype, phdr, pd, &pseudo_header,
+ &whdr, &err);
+ if (pd == NULL) {
+ ld->go = FALSE;
+ ld->err = err;
+ return;
+ }
+
if (ld->pdh) {
- /* "phdr->ts" may not necessarily be a "struct timeval" - it may
- be a "struct bpf_timeval", with member sizes wired to 32
- bits - and we may go that way ourselves in the future, so
- copy the members individually. */
- whdr.ts.tv_sec = phdr->ts.tv_sec;
- whdr.ts.tv_usec = phdr->ts.tv_usec;
- whdr.caplen = phdr->caplen;
- whdr.len = phdr->len;
- whdr.pkt_encap = ld->linktype;
-
- /* If this fails, set "ld->go" to FALSE, to stop the capture, and set
- "ld->err" to the error. */
- if (!wtap_dump(ld->pdh, &whdr, NULL, pd, &err)) {
- ld->go = FALSE;
- ld->err = err;
- }
+ /* We're supposed to write the packet to a file; do so.
+ If this fails, set "ld->go" to FALSE, to stop the capture, and set
+ "ld->err" to the error. */
+ if (!wtap_dump(ld->pdh, &whdr, &pseudo_header, pd, &err)) {
+ ld->go = FALSE;
+ ld->err = err;
+ }
}
switch (ld->linktype) {
case WTAP_ENCAP_ETHERNET:
- capture_eth(pd, 0, phdr->len, &ld->counts);
+ capture_eth(pd, 0, whdr.caplen, &ld->counts);
break;
case WTAP_ENCAP_FDDI:
case WTAP_ENCAP_FDDI_BITSWAPPED:
- capture_fddi(pd, phdr->len, &ld->counts);
+ capture_fddi(pd, whdr.caplen, &ld->counts);
break;
case WTAP_ENCAP_PRISM_HEADER:
- capture_prism(pd, 0, phdr->len, &ld->counts);
+ capture_prism(pd, 0, whdr.caplen, &ld->counts);
break;
case WTAP_ENCAP_TOKEN_RING:
- capture_tr(pd, 0, phdr->len, &ld->counts);
+ capture_tr(pd, 0, whdr.caplen, &ld->counts);
break;
case WTAP_ENCAP_NULL:
- capture_null(pd, phdr->len, &ld->counts);
+ capture_null(pd, whdr.caplen, &ld->counts);
break;
case WTAP_ENCAP_PPP:
- capture_ppp_hdlc(pd, 0, phdr->len, &ld->counts);
+ capture_ppp_hdlc(pd, 0, whdr.caplen, &ld->counts);
break;
case WTAP_ENCAP_RAW_IP:
- capture_raw(pd, phdr->len, &ld->counts);
+ capture_raw(pd, whdr.caplen, &ld->counts);
break;
case WTAP_ENCAP_SLL:
- capture_sll(pd, phdr->len, &ld->counts);
+ capture_sll(pd, whdr.caplen, &ld->counts);
break;
case WTAP_ENCAP_LINUX_ATM_CLIP:
- capture_clip(pd, phdr->len, &ld->counts);
+ capture_clip(pd, whdr.caplen, &ld->counts);
+ break;
+ case WTAP_ENCAP_IEEE_802_11:
+ case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
+ capture_ieee80211(pd, 0, whdr.caplen, &ld->counts);
+ break;
+ case WTAP_ENCAP_CHDLC:
+ capture_chdlc(pd, 0, whdr.caplen, &ld->counts);
break;
case WTAP_ENCAP_LOCALTALK:
capture_llap(&ld->counts);
break;
- /* XXX - FreeBSD may append 4-byte ATM pseudo-header to DLT_ATM_RFC1483,
- with LLC header following; we should implement it at some
- point. */
+ case WTAP_ENCAP_ATM_PDUS:
+ capture_atm(&pseudo_header, pd, whdr.caplen, &ld->counts);
+ break;
+ case WTAP_ENCAP_IP_OVER_FC:
+ capture_ipfc(pd, whdr.caplen, &ld->counts);
+ break;
+ case WTAP_ENCAP_ARCNET:
+ capture_arcnet(pd, whdr.caplen, &ld->counts, FALSE, TRUE);
+ break;
+ case WTAP_ENCAP_ARCNET_LINUX:
+ capture_arcnet(pd, whdr.caplen, &ld->counts, TRUE, FALSE);
+ break;
+ /* XXX - some ATM drivers on FreeBSD might prepend a 4-byte ATM
+ pseudo-header to DLT_ATM_RFC1483, with LLC header following;
+ we might have to implement that at some point. */
}
}