# include "config.h"
#endif
+#include <stdio.h>
#include <stdlib.h> /* for exit() */
#include <glib.h>
#include <unistd.h>
#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#if defined(__APPLE__) && defined(__LP64__)
+#include <sys/utsname.h>
+#endif
+
#include <signal.h>
#include <errno.h>
-#ifdef NEED_GETOPT_H
-#include "getopt.h"
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#else
+#include "wsutil/wsgetopt.h"
#endif
#ifdef HAVE_NETDB_H
#ifdef HAVE_LIBCAP
# include <sys/prctl.h>
# include <sys/capability.h>
-# include <stdio.h>
#endif
#include "ringbuffer.h"
#include "clopts_common.h"
+#include "console_io.h"
#include "cmdarg_err.h"
#include "version_info.h"
-#include <pcap.h>
-#include "pcapio.h"
-
#include "capture-pcap-util.h"
+#include "pcapio.h"
+
#ifdef _WIN32
#include "capture-wpcap.h"
+#include <wsutil/unicode-utils.h>
#endif
-#ifdef _WIN32
-#include "epan/unicode-utils.h"
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif
+
+#ifdef NEED_INET_V6DEFS_H
+# include "wsutil/inet_v6defs.h"
#endif
-#include "epan/privileges.h"
+#include <wsutil/privileges.h>
#include "sync_pipe.h"
#include "capture_opts.h"
+#include "capture_ifinfo.h"
#include "capture_sync.h"
#include "conditions.h"
#include "tempfile.h"
#include "log.h"
-#include "file_util.h"
+#include "wsutil/file_util.h"
/*
* Get information about libpcap format from "wiretap/libpcap.h".
/* is defined */
#endif
+#ifdef _WIN32
+#define USE_THREADS
+#endif
+
static gboolean capture_child = FALSE; /* FALSE: standalone call, TRUE: this is an Wireshark capture child */
#ifdef _WIN32
static gchar *sig_pipe_name = NULL;
static HANDLE sig_pipe_handle = NULL;
+static gboolean signal_pipe_check_running(void);
+#endif
+
+#ifdef USE_THREADS
+static GAsyncQueue *cap_pipe_pending_q, *cap_pipe_done_q;
+static GMutex *cap_pipe_read_mtx;
#endif
+#ifdef SIGINFO
+static gboolean infodelay; /* if TRUE, don't print capture info in SIGINFO handler */
+static gboolean infoprint; /* if TRUE, print capture info after clearing infodelay */
+#endif /* SIGINFO */
+
/** Stop a low-level capture (stops the capture child). */
static void capture_loop_stop(void);
* is interrupted by a signal on UN*X, just go back and try again to
* read again.
*
- * On UN*X, we catch SIGUSR1 as a "stop capturing" signal, and, in
+ * On UN*X, we catch SIGINT as a "stop capturing" signal, and, in
* the signal handler, set a flag to stop capturing; however, without
* a guarantee of that sort, we can't guarantee that we'll stop capturing
* if the read will be retried and won't time out if no packets arrive.
* exit pcap_dispatch() with an indication that no packets have arrived,
* and will break out of the capture loop at that point.
*
- * On Windows, we can't send a SIGUSR1 to stop capturing, so none of this
+ * On Windows, we can't send a SIGINT to stop capturing, so none of this
* applies in any case.
*
* XXX - the various BSDs appear to define BSD in <sys/param.h>; we don't
int err; /* if non-zero, error seen while capturing */
gint packet_count; /* Number of packets we have already captured */
gint packet_max; /* Number of packets we're supposed to capture - 0 means infinite */
+ gint inpkts_to_sync_pipe; /* Packets not already send out to the sync_pipe */
+#ifdef SIGINFO
+ gboolean report_packet_count; /* Set by SIGINFO handler; print packet count */
+#endif
/* pcap "input file" */
pcap_t *pcap_h; /* pcap handle */
gboolean from_cap_pipe; /* TRUE if we are capturing data from a capture pipe */
struct pcap_hdr cap_pipe_hdr; /* Pcap header when capturing from a pipe */
struct pcaprec_modified_hdr cap_pipe_rechdr; /* Pcap record header when capturing from a pipe */
+#ifdef _WIN32
+ HANDLE cap_pipe_h; /* The handle of the capture pipe */
+#else
int cap_pipe_fd; /* the file descriptor of the capture pipe */
+#endif
gboolean cap_pipe_modified; /* TRUE if data in the pipe uses modified pcap headers */
gboolean cap_pipe_byte_swapped; /* TRUE if data in the pipe is byte swapped */
- unsigned int cap_pipe_bytes_to_read;/* Used by cap_pipe_dispatch */
- unsigned int cap_pipe_bytes_read; /* Used by cap_pipe_dispatch */
+#ifdef USE_THREADS
+ char * cap_pipe_buf; /* Pointer to the data buffer we read into */
+#endif /* USE_THREADS */
+ int cap_pipe_bytes_to_read;/* Used by cap_pipe_dispatch */
+ int cap_pipe_bytes_read; /* Used by cap_pipe_dispatch */
enum {
STATE_EXPECT_REC_HDR,
STATE_READ_REC_HDR,
} cap_pipe_state;
enum { PIPOK, PIPEOF, PIPERR, PIPNEXIST } cap_pipe_err;
- /* output file */
+ /* output file(s) */
FILE *pdh;
+ int save_file_fd;
int linktype;
+ int file_snaplen;
gint wtap_linktype;
long bytes_written;
-
+ guint32 autostop_files;
} loop_data;
/*
"(This is not a crash; please do not report it as such.)";
/*
- * This needs to be static, so that the SIGUSR1 handler can clear the "go"
+ * This needs to be static, so that the SIGINT handler can clear the "go"
* flag.
*/
-static loop_data ld;
+static loop_data global_ld;
/*
* Timeout, in milliseconds, for reads from the stream of captured packets.
+ *
+ * A bug in Mac OS X 10.6 and 10.6.1 causes calls to pcap_open_live(), in
+ * 64-bit applications, with sub-second timeouts not to work. The bug is
+ * fixed in 10.6.2.
*/
+#if defined(__APPLE__) && defined(__LP64__)
+static gboolean need_timeout_workaround;
+
+#define CAP_READ_TIMEOUT (need_timeout_workaround ? 1000 : 250)
+#else
#define CAP_READ_TIMEOUT 250
-static char *cap_pipe_err_str;
+#endif
+
+/*
+ * Timeout, in microseconds, for threaded reads from a pipe.
+ */
+#define THREAD_READ_TIMEOUT 100
+static const char *cap_pipe_err_str;
static void
console_log_handler(const char *log_domain, GLogLevelFlags log_level,
/* capture related options */
static capture_options global_capture_opts;
-static capture_options *capture_opts = &global_capture_opts;
+static gboolean quiet;
static void capture_loop_packet_cb(u_char *user, const struct pcap_pkthdr *phdr,
const u_char *pd);
static void capture_loop_get_errmsg(char *errmsg, int errmsglen, const char *fname,
int err, gboolean is_close);
-
-#if __GNUC__ >= 2
-static void exit_main(int err) __attribute__ ((noreturn));
-#else
-static void exit_main(int err);
-#endif
+static void exit_main(int err) G_GNUC_NORETURN;
static void report_new_capture_file(const char *filename);
static void report_packet_count(int packet_count);
-static void report_packet_drops(int drops);
+static void report_packet_drops(guint32 drops);
static void report_capture_error(const char *error_msg, const char *secondary_error_msg);
static void report_cfilter_error(const char *cfilter, const char *errmsg);
-#ifdef _WIN32
-static gboolean signal_pipe_check_running(void);
-#endif
+#define MSG_MAX_LENGTH 4096
static void
print_usage(gboolean print_ver) {
fprintf(output, " -f <capture filter> packet filter in libpcap filter syntax\n");
fprintf(output, " -s <snaplen> packet snapshot length (def: 65535)\n");
fprintf(output, " -p don't capture in promiscuous mode\n");
-#ifdef _WIN32
+#ifdef HAVE_PCAP_CREATE
+ fprintf(output, " -I capture in monitor mode, if available\n");
+#endif
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
fprintf(output, " -B <buffer size> size of kernel buffer (def: 1MB)\n");
#endif
fprintf(output, " -y <link type> link layer type (def: first appropriate)\n");
fprintf(output, " -D print list of interfaces and exit\n");
fprintf(output, " -L print list of link-layer types of iface and exit\n");
+#ifdef HAVE_BPF_IMAGE
+ fprintf(output, " -d print generated BPF code for capture filter\n");
+#endif
fprintf(output, " -S print statistics for each interface once every second\n");
- fprintf(output, " -M for -D, -L, and -S produce machine-readable output\n");
+ fprintf(output, " -M for -D, -L, and -S, produce machine-readable output\n");
fprintf(output, "\n");
#ifdef HAVE_PCAP_REMOTE
fprintf(output, "\nRPCAP options:\n");
fprintf(output, " -b <ringbuffer opt.> ... duration:NUM - switch to next file after NUM secs\n");
fprintf(output, " filesize:NUM - switch to next file after NUM KB\n");
fprintf(output, " files:NUM - ringbuffer: replace after NUM files\n");
+ fprintf(output, " -n use pcapng format instead of pcap\n");
/*fprintf(output, "\n");*/
fprintf(output, "Miscellaneous:\n");
+ fprintf(output, " -q don't report packet capture counts\n");
fprintf(output, " -v print version information and exit\n");
fprintf(output, " -h display this help and exit\n");
fprintf(output, "\n");
wireshark_svnversion, get_copyright_info() ,comp_info_str->str, runtime_info_str->str);
}
+/*
+ * Print to the standard error. This is a command-line tool, so there's
+ * no need to pop up a console.
+ */
+void
+vfprintf_stderr(const char *fmt, va_list ap)
+{
+ vfprintf(stderr, fmt, ap);
+}
+
+void
+fprintf_stderr(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf_stderr(fmt, ap);
+ va_end(ap);
+}
+
/*
* Report an error in command-line arguments.
*/
}
}
+#ifdef HAVE_LIBCAP
+static void
+#if 0 /* Set to enable capability debugging */
+/* see 'man cap_to_text()' for explanation of output */
+/* '=' means 'all= ' ie: no capabilities */
+/* '=ip' means 'all=ip' ie: all capabilities are permissible and inheritable */
+/* .... */
+print_caps(const char *pfx) {
+ cap_t caps = cap_get_proc();
+ g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG,
+ "%s: EUID: %d Capabilities: %s", pfx,
+ geteuid(), cap_to_text(caps, NULL));
+ cap_free(caps);
+#else
+print_caps(const char *pfx _U_) {
+#endif
+}
+
+static void
+relinquish_all_capabilities(void)
+{
+ /* Drop any and all capabilities this process may have. */
+ /* Allowed whether or not process has any privileges. */
+ cap_t caps = cap_init(); /* all capabilities initialized to off */
+ print_caps("Pre-clear");
+ if (cap_set_proc(caps)) {
+ cmdarg_err("cap_set_proc() fail return: %s", strerror(errno));
+ }
+ print_caps("Post-clear");
+ cap_free(caps);
+}
+#endif
+
+static pcap_t *
+open_capture_device(capture_options *capture_opts,
+ char (*open_err_str)[PCAP_ERRBUF_SIZE])
+{
+ pcap_t *pcap_h;
+#ifdef HAVE_PCAP_CREATE
+ int err;
+#endif
+#ifdef HAVE_PCAP_REMOTE
+ struct pcap_rmtauth auth;
+#endif
+
+ /* Open the network interface to capture from it.
+ Some versions of libpcap may put warnings into the error buffer
+ if they succeed; to tell if that's happened, we have to clear
+ the error buffer, and check if it's still a null string. */
+ (*open_err_str)[0] = '\0';
+#ifdef HAVE_PCAP_OPEN
+ /*
+ * If we're opening a remote device, use pcap_open(); that's currently
+ * the only open routine that supports remote devices.
+ */
+ if (strncmp (capture_opts->iface, "rpcap://", 8) == 0) {
+ auth.type = capture_opts->auth_type == CAPTURE_AUTH_PWD ?
+ RPCAP_RMTAUTH_PWD : RPCAP_RMTAUTH_NULL;
+ auth.username = capture_opts->auth_username;
+ auth.password = capture_opts->auth_password;
+
+ pcap_h = pcap_open(capture_opts->iface,
+ capture_opts->has_snaplen ? capture_opts->snaplen :
+ WTAP_MAX_PACKET_SIZE,
+ /* flags */
+ (capture_opts->promisc_mode ? PCAP_OPENFLAG_PROMISCUOUS : 0) |
+ (capture_opts->datatx_udp ? PCAP_OPENFLAG_DATATX_UDP : 0) |
+ (capture_opts->nocap_rpcap ? PCAP_OPENFLAG_NOCAPTURE_RPCAP : 0),
+ CAP_READ_TIMEOUT, &auth, *open_err_str);
+ } else
+#endif /* HAVE_PCAP_OPEN */
+ {
+ /*
+ * If we're not opening a remote device, use pcap_create() and
+ * pcap_activate() if we have them, so that we can set the buffer
+ * size, otherwise use pcap_open_live().
+ */
+#ifdef HAVE_PCAP_CREATE
+ pcap_h = pcap_create(capture_opts->iface, *open_err_str);
+ if (pcap_h != NULL) {
+ pcap_set_snaplen(pcap_h, capture_opts->has_snaplen ? capture_opts->snaplen : WTAP_MAX_PACKET_SIZE);
+ pcap_set_promisc(pcap_h, capture_opts->promisc_mode);
+ pcap_set_timeout(pcap_h, CAP_READ_TIMEOUT);
+
+ if (capture_opts->buffer_size > 1) {
+ pcap_set_buffer_size(pcap_h, capture_opts->buffer_size * 1024 * 1024);
+ }
+ if (capture_opts->monitor_mode)
+ pcap_set_rfmon(pcap_h, 1);
+ err = pcap_activate(pcap_h);
+ if (err < 0) {
+ /* Failed to activate, set to NULL */
+ if (err == PCAP_ERROR)
+ g_strlcpy(*open_err_str, pcap_geterr(pcap_h), sizeof *open_err_str);
+ else
+ g_strlcpy(*open_err_str, pcap_statustostr(err), sizeof *open_err_str);
+ pcap_close(pcap_h);
+ pcap_h = NULL;
+ }
+ }
+#else
+ pcap_h = pcap_open_live(capture_opts->iface,
+ capture_opts->has_snaplen ? capture_opts->snaplen :
+ WTAP_MAX_PACKET_SIZE,
+ capture_opts->promisc_mode, CAP_READ_TIMEOUT,
+ *open_err_str);
+#endif
+ }
+
+ /* If not using libcap: we now can now set euid/egid to ruid/rgid */
+ /* to remove any suid privileges. */
+ /* If using libcap: we can now remove NET_RAW and NET_ADMIN capabilities */
+ /* (euid/egid have already previously been set to ruid/rgid. */
+ /* (See comment in main() for details) */
+#ifndef HAVE_LIBCAP
+ relinquish_special_privs_perm();
+#else
+ relinquish_all_capabilities();
+#endif
+
+ return pcap_h;
+}
+
+static void
+get_capture_device_open_failure_messages(const char *open_err_str,
+ const char *iface
+#ifndef _WIN32
+ _U_
+#endif
+ ,
+ char *errmsg, size_t errmsg_len,
+ char *secondary_errmsg,
+ size_t secondary_errmsg_len)
+{
+ const char *libpcap_warn;
+ static const char ppamsg[] = "can't find PPA for ";
+
+ /* If we got a "can't find PPA for X" message, warn the user (who
+ is running dumcap 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 X" rather than "can't find PPA for X"). */
+ if (strncmp(open_err_str, ppamsg, sizeof ppamsg - 1) == 0)
+ libpcap_warn =
+ "\n\n"
+ "You are running (T)Wireshark with a version of the libpcap library\n"
+ "that doesn't handle HP-UX network devices well; this means that\n"
+ "(T)Wireshark 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 = "";
+ g_snprintf(errmsg, (gulong) errmsg_len,
+ "The capture session could not be initiated (%s).", open_err_str);
+#ifndef _WIN32
+ g_snprintf(secondary_errmsg, (gulong) secondary_errmsg_len,
+"Please check to make sure you have sufficient permissions, and that you have "
+"the proper interface or pipe specified.%s", libpcap_warn);
+#else
+ g_snprintf(secondary_errmsg, (gulong) secondary_errmsg_len,
+"\n"
+"Please check that \"%s\" is the proper interface.\n"
+"\n"
+"\n"
+"Help can be found at:\n"
+"\n"
+" http://wiki.wireshark.org/WinPcap\n"
+" http://wiki.wireshark.org/CaptureSetup\n",
+ iface);
+#endif /* _WIN32 */
+}
+
+/* Set the data link type on a pcap. */
+static gboolean
+set_pcap_linktype(pcap_t *pcap_h, capture_options *capture_opts,
+ char *errmsg, size_t errmsg_len,
+ char *secondary_errmsg, size_t secondary_errmsg_len)
+{
+ char *set_linktype_err_str;
+
+ if (capture_opts->linktype == -1)
+ return TRUE; /* just use the default */
+#ifdef HAVE_PCAP_SET_DATALINK
+ if (pcap_set_datalink(pcap_h, capture_opts->linktype) == 0)
+ return TRUE; /* no error */
+ set_linktype_err_str = pcap_geterr(pcap_h);
+#else
+ /* Let them set it to the type it is; reject any other request. */
+ if (get_pcap_linktype(pcap_h, capture_opts->iface) == capture_opts->linktype)
+ return TRUE; /* no error */
+ set_linktype_err_str =
+ "That DLT isn't one of the DLTs supported by this device";
+#endif
+ g_snprintf(errmsg, (gulong) errmsg_len, "Unable to set data link type (%s).",
+ set_linktype_err_str);
+ /*
+ * If the error isn't "XXX is not one of the DLTs supported by this device",
+ * tell the user to tell the Wireshark developers about it.
+ */
+ if (strstr(set_linktype_err_str, "is not one of the DLTs supported by this device") == NULL)
+ g_snprintf(secondary_errmsg, (gulong) secondary_errmsg_len, please_report);
+ else
+ secondary_errmsg[0] = '\0';
+ return FALSE;
+}
+
+static gboolean
+compile_capture_filter(const char *iface, pcap_t *pcap_h,
+ struct bpf_program *fcode, char *cfilter)
+{
+ bpf_u_int32 netnum, netmask;
+ gchar lookup_net_err_str[PCAP_ERRBUF_SIZE];
+
+ if (pcap_lookupnet(iface, &netnum, &netmask, lookup_net_err_str) < 0) {
+ /*
+ * Well, we can't get the netmask for this interface; it's used
+ * only for filters that check for broadcast IP addresses, so
+ * we just punt and use 0. It might be nice to warn the user,
+ * but that's a pain in a GUI application, as it'd involve popping
+ * up a message box, and it's not clear how often this would make
+ * a difference (only filters that check for IP broadcast addresses
+ * use the netmask).
+ */
+ /*cmdarg_err(
+ "Warning: Couldn't obtain netmask info (%s).", lookup_net_err_str);*/
+ netmask = 0;
+ }
+ if (pcap_compile(pcap_h, fcode, cfilter, 1, netmask) < 0)
+ return FALSE;
+ return TRUE;
+}
+
+#ifdef HAVE_BPF_IMAGE
+static gboolean
+show_filter_code(capture_options *capture_opts)
+{
+ pcap_t *pcap_h;
+ gchar open_err_str[PCAP_ERRBUF_SIZE];
+ char errmsg[MSG_MAX_LENGTH+1];
+ char secondary_errmsg[MSG_MAX_LENGTH+1];
+ struct bpf_program fcode;
+ struct bpf_insn *insn;
+ u_int i;
+
+ pcap_h = open_capture_device(capture_opts, &open_err_str);
+ if (pcap_h == NULL) {
+ /* Open failed; get messages */
+ get_capture_device_open_failure_messages(open_err_str,
+ capture_opts->iface,
+ errmsg, sizeof errmsg,
+ secondary_errmsg,
+ sizeof secondary_errmsg);
+ /* And report them */
+ report_capture_error(errmsg, secondary_errmsg);
+ return FALSE;
+ }
+
+ /* Set the link-layer type. */
+ if (!set_pcap_linktype(pcap_h, capture_opts, errmsg, sizeof errmsg,
+ secondary_errmsg, sizeof secondary_errmsg)) {
+ pcap_close(pcap_h);
+ report_capture_error(errmsg, secondary_errmsg);
+ return FALSE;
+ }
+
+ /* OK, try to compile the capture filter. */
+ if (!compile_capture_filter(capture_opts->iface, pcap_h, &fcode,
+ capture_opts->cfilter)) {
+ pcap_close(pcap_h);
+ report_cfilter_error(capture_opts->cfilter, errmsg);
+ return FALSE;
+ }
+ pcap_close(pcap_h);
+
+ if (capture_child) {
+ /* Let our parent know we succeeded. */
+ pipe_write_block(2, SP_SUCCESS, NULL);
+ }
+
+ /* Now print the filter code. */
+ insn = fcode.bf_insns;
+
+ for (i = 0; i < fcode.bf_len; insn++, i++)
+ printf("%s\n", bpf_image(insn, i));
+ return TRUE;
+}
+#endif
+
+/*
+ * capture_interface_list() is expected to do the right thing to get
+ * a list of interfaces.
+ *
+ * In most of the programs in the Wireshark suite, "the right thing"
+ * is to run dumpcap and ask it for the list, because dumpcap may
+ * be the only program in the suite with enough privileges to get
+ * the list.
+ *
+ * In dumpcap itself, however, we obviously can't run dumpcap to
+ * ask for the list. Therefore, our capture_interface_list() should
+ * just call get_interface_list().
+ */
+GList *
+capture_interface_list(int *err, char **err_str)
+{
+ return get_interface_list(err, err_str);
+}
+
+/*
+ * Get the data-link type for a libpcap device.
+ * This works around AIX 5.x's non-standard and incompatible-with-the-
+ * rest-of-the-universe libpcap.
+ */
+static int
+get_pcap_linktype(pcap_t *pch, const char *devname
+#ifndef _AIX
+ _U_
+#endif
+)
+{
+ int linktype;
+#ifdef _AIX
+ const char *ifacename;
+#endif
+
+ linktype = pcap_datalink(pch);
+#ifdef _AIX
+
+ /*
+ * The libpcap that comes with AIX 5.x uses RFC 1573 ifType values
+ * rather than DLT_ values for link-layer types; the ifType values
+ * for LAN devices are:
+ *
+ * Ethernet 6
+ * 802.3 7
+ * Token Ring 9
+ * FDDI 15
+ *
+ * and the ifType value for a loopback device is 24.
+ *
+ * The AIX names for LAN devices begin with:
+ *
+ * Ethernet en
+ * 802.3 et
+ * Token Ring tr
+ * FDDI fi
+ *
+ * and the AIX names for loopback devices begin with "lo".
+ *
+ * (The difference between "Ethernet" and "802.3" is presumably
+ * whether packets have an Ethernet header, with a packet type,
+ * or an 802.3 header, with a packet length, followed by an 802.2
+ * header and possibly a SNAP header.)
+ *
+ * If the device name matches "linktype" interpreted as an ifType
+ * value, rather than as a DLT_ value, we will assume this is AIX's
+ * non-standard, incompatible libpcap, rather than a standard libpcap,
+ * and will map the link-layer type to the standard DLT_ value for
+ * that link-layer type, as that's what the rest of Wireshark expects.
+ *
+ * (This means the capture files won't be readable by a tcpdump
+ * linked with AIX's non-standard libpcap, but so it goes. They
+ * *will* be readable by standard versions of tcpdump, Wireshark,
+ * and so on.)
+ *
+ * XXX - if we conclude we're using AIX libpcap, should we also
+ * set a flag to cause us to assume the time stamps are in
+ * seconds-and-nanoseconds form, and to convert them to
+ * seconds-and-microseconds form before processing them and
+ * writing them out?
+ */
+
+ /*
+ * Find the last component of the device name, which is the
+ * interface name.
+ */
+ ifacename = strchr(devname, '/');
+ if (ifacename == NULL)
+ ifacename = devname;
+
+ /* See if it matches any of the LAN device names. */
+ if (strncmp(ifacename, "en", 2) == 0) {
+ if (linktype == 6) {
+ /*
+ * That's the RFC 1573 value for Ethernet; map it to DLT_EN10MB.
+ */
+ linktype = 1;
+ }
+ } else if (strncmp(ifacename, "et", 2) == 0) {
+ if (linktype == 7) {
+ /*
+ * That's the RFC 1573 value for 802.3; map it to DLT_EN10MB.
+ * (libpcap, tcpdump, Wireshark, etc. don't care if it's Ethernet
+ * or 802.3.)
+ */
+ linktype = 1;
+ }
+ } else if (strncmp(ifacename, "tr", 2) == 0) {
+ if (linktype == 9) {
+ /*
+ * That's the RFC 1573 value for 802.5 (Token Ring); map it to
+ * DLT_IEEE802, which is what's used for Token Ring.
+ */
+ linktype = 6;
+ }
+ } else if (strncmp(ifacename, "fi", 2) == 0) {
+ if (linktype == 15) {
+ /*
+ * That's the RFC 1573 value for FDDI; map it to DLT_FDDI.
+ */
+ linktype = 10;
+ }
+ } else if (strncmp(ifacename, "lo", 2) == 0) {
+ if (linktype == 24) {
+ /*
+ * That's the RFC 1573 value for "software loopback" devices; map it
+ * to DLT_NULL, which is what's used for loopback devices on BSD.
+ */
+ linktype = 0;
+ }
+ }
+#endif
+
+ return linktype;
+}
+
+static data_link_info_t *
+create_data_link_info(int dlt)
+{
+ data_link_info_t *data_link_info;
+ const char *text;
+
+ data_link_info = (data_link_info_t *)g_malloc(sizeof (data_link_info_t));
+ data_link_info->dlt = dlt;
+ text = pcap_datalink_val_to_name(dlt);
+ if (text != NULL)
+ data_link_info->name = g_strdup(text);
+ else
+ data_link_info->name = g_strdup_printf("DLT %d", dlt);
+ text = pcap_datalink_val_to_description(dlt);
+ if (text != NULL)
+ data_link_info->description = g_strdup(text);
+ else
+ data_link_info->description = NULL;
+ return data_link_info;
+}
+
+/*
+ * Get the capabilities of a network device.
+ */
+static if_capabilities_t *
+get_if_capabilities(const char *devname, gboolean monitor_mode
+#ifndef HAVE_PCAP_CREATE
+ _U_
+#endif
+, char **err_str)
+{
+ if_capabilities_t *caps;
+ char errbuf[PCAP_ERRBUF_SIZE];
+ pcap_t *pch;
+#ifdef HAVE_PCAP_CREATE
+ int status;
+#endif
+ int deflt;
+#ifdef HAVE_PCAP_LIST_DATALINKS
+ int *linktypes;
+ int i, nlt;
+#endif
+ data_link_info_t *data_link_info;
+
+ /*
+ * Allocate the interface capabilities structure.
+ */
+ caps = g_malloc(sizeof *caps);
+
+#ifdef HAVE_PCAP_OPEN
+ pch = pcap_open(devname, MIN_PACKET_SIZE, 0, 0, NULL, errbuf);
+ caps->can_set_rfmon = FALSE;
+ if (pch == NULL) {
+ if (err_str != NULL)
+ *err_str = g_strdup(errbuf);
+ g_free(caps);
+ return NULL;
+ }
+#elif defined(HAVE_PCAP_CREATE)
+ pch = pcap_create(devname, errbuf);
+ if (pch == NULL) {
+ if (err_str != NULL)
+ *err_str = g_strdup(errbuf);
+ g_free(caps);
+ return NULL;
+ }
+ status = pcap_can_set_rfmon(pch);
+ if (status < 0) {
+ /* Error. */
+ if (status == PCAP_ERROR)
+ *err_str = g_strdup_printf("pcap_can_set_rfmon() failed: %s",
+ pcap_geterr(pch));
+ else
+ *err_str = g_strdup(pcap_statustostr(status));
+ pcap_close(pch);
+ g_free(caps);
+ return NULL;
+ }
+ if (status == 0)
+ caps->can_set_rfmon = FALSE;
+ else if (status == 1) {
+ caps->can_set_rfmon = TRUE;
+ if (monitor_mode)
+ pcap_set_rfmon(pch, 1);
+ } else {
+ if (err_str != NULL) {
+ *err_str = g_strdup_printf("pcap_can_set_rfmon() returned %d",
+ status);
+ }
+ pcap_close(pch);
+ g_free(caps);
+ return NULL;
+ }
+
+ status = pcap_activate(pch);
+ if (status < 0) {
+ /* Error. We ignore warnings (status > 0). */
+ if (err_str != NULL) {
+ if (status == PCAP_ERROR)
+ *err_str = g_strdup_printf("pcap_activate() failed: %s",
+ pcap_geterr(pch));
+ else
+ *err_str = g_strdup(pcap_statustostr(status));
+ }
+ pcap_close(pch);
+ g_free(caps);
+ return NULL;
+ }
+#else
+ pch = pcap_open_live(devname, MIN_PACKET_SIZE, 0, 0, errbuf);
+ caps->can_set_rfmon = FALSE;
+ if (pch == NULL) {
+ if (err_str != NULL)
+ *err_str = g_strdup(errbuf);
+ g_free(caps);
+ return NULL;
+ }
+#endif
+ deflt = get_pcap_linktype(pch, devname);
+#ifdef HAVE_PCAP_LIST_DATALINKS
+ nlt = pcap_list_datalinks(pch, &linktypes);
+ if (nlt == 0 || linktypes == NULL) {
+ pcap_close(pch);
+ if (err_str != NULL)
+ *err_str = NULL; /* an empty list doesn't mean an error */
+ return NULL;
+ }
+ caps->data_link_types = NULL;
+ for (i = 0; i < nlt; i++) {
+ data_link_info = create_data_link_info(linktypes[i]);
+
+ /*
+ * XXX - for 802.11, make the most detailed 802.11
+ * version the default, rather than the one the
+ * device has as the default?
+ */
+ if (linktypes[i] == deflt)
+ caps->data_link_types = g_list_prepend(caps->data_link_types,
+ data_link_info);
+ else
+ caps->data_link_types = g_list_append(caps->data_link_types,
+ data_link_info);
+ }
+#ifdef HAVE_PCAP_FREE_DATALINKS
+ pcap_free_datalinks(linktypes);
+#else
+ /*
+ * In Windows, there's no guarantee that if you have a library
+ * built with one version of the MSVC++ run-time library, and
+ * it returns a pointer to allocated data, you can free that
+ * data from a program linked with another version of the
+ * MSVC++ run-time library.
+ *
+ * This is not an issue on UN*X.
+ *
+ * See the mail threads starting at
+ *
+ * http://www.winpcap.org/pipermail/winpcap-users/2006-September/001421.html
+ *
+ * and
+ *
+ * http://www.winpcap.org/pipermail/winpcap-users/2008-May/002498.html
+ */
+#ifndef _WIN32
+#define xx_free free /* hack so checkAPIs doesn't complain */
+ xx_free(linktypes);
+#endif /* _WIN32 */
+#endif /* HAVE_PCAP_FREE_DATALINKS */
+#else /* HAVE_PCAP_LIST_DATALINKS */
+
+ data_link_info = create_data_link_info(deflt);
+ caps->data_link_types = g_list_append(caps->data_link_types,
+ data_link_info);
+#endif /* HAVE_PCAP_LIST_DATALINKS */
+
+ pcap_close(pch);
+
+ if (err_str != NULL)
+ *err_str = NULL;
+ return caps;
+}
+
+#define ADDRSTRLEN 46 /* Covers IPv4 & IPv6 */
+static void
+print_machine_readable_interfaces(GList *if_list)
+{
+ int i;
+ GList *if_entry;
+ if_info_t *if_info;
+ GSList *addr;
+ if_addr_t *if_addr;
+ char addr_str[ADDRSTRLEN];
+
+ if (capture_child) {
+ /* Let our parent know we succeeded. */
+ pipe_write_block(2, SP_SUCCESS, NULL);
+ }
+
+ i = 1; /* Interface id number */
+ for (if_entry = g_list_first(if_list); if_entry != NULL;
+ if_entry = g_list_next(if_entry)) {
+ if_info = (if_info_t *)if_entry->data;
+ printf("%d. %s", i++, if_info->name);
+
+ /*
+ * Print the contents of the if_entry struct in a parseable format.
+ * Each if_entry element is tab-separated. Addresses are comma-
+ * separated.
+ */
+ /* XXX - Make sure our description doesn't contain a tab */
+ if (if_info->description != NULL)
+ printf("\t%s\t", if_info->description);
+ else
+ printf("\t\t");
+
+ for(addr = g_slist_nth(if_info->addrs, 0); addr != NULL;
+ addr = g_slist_next(addr)) {
+ if (addr != g_slist_nth(if_info->addrs, 0))
+ printf(",");
+
+ if_addr = (if_addr_t *)addr->data;
+ switch(if_addr->ifat_type) {
+ case IF_AT_IPv4:
+ if (inet_ntop(AF_INET, &if_addr->addr.ip4_addr, addr_str,
+ ADDRSTRLEN)) {
+ printf("%s", addr_str);
+ } else {
+ printf("<unknown IPv4>");
+ }
+ break;
+ case IF_AT_IPv6:
+ if (inet_ntop(AF_INET6, &if_addr->addr.ip6_addr,
+ addr_str, ADDRSTRLEN)) {
+ printf("%s", addr_str);
+ } else {
+ printf("<unknown IPv6>");
+ }
+ break;
+ default:
+ printf("<type unknown %u>", if_addr->ifat_type);
+ }
+ }
+
+ if (if_info->loopback)
+ printf("\tloopback");
+ else
+ printf("\tnetwork");
+
+ printf("\n");
+ }
+}
+
+/*
+ * If you change the machine-readable output format of this function,
+ * you MUST update capture_ifinfo.c:capture_get_if_capabilities() accordingly!
+ */
+static void
+print_machine_readable_if_capabilities(if_capabilities_t *caps)
+{
+ GList *lt_entry;
+ data_link_info_t *data_link_info;
+ const gchar *desc_str;
+
+ if (capture_child) {
+ /* Let our parent know we succeeded. */
+ pipe_write_block(2, SP_SUCCESS, NULL);
+ }
+
+ if (caps->can_set_rfmon)
+ printf("1\n");
+ else
+ printf("0\n");
+ for (lt_entry = caps->data_link_types; lt_entry != NULL;
+ lt_entry = g_list_next(lt_entry)) {
+ data_link_info = (data_link_info_t *)lt_entry->data;
+ if (data_link_info->description != NULL)
+ desc_str = data_link_info->description;
+ else
+ desc_str = "(not supported)";
+ printf("%d\t%s\t%s\n", data_link_info->dlt, data_link_info->name,
+ desc_str);
+ }
+}
+
+typedef struct {
+ char *name;
+ pcap_t *pch;
+} if_stat_t;
+
+/* Print the number of packets captured for each interface until we're killed. */
+static int
+print_statistics_loop(gboolean machine_readable)
+{
+ GList *if_list, *if_entry, *stat_list = NULL, *stat_entry;
+ if_info_t *if_info;
+ if_stat_t *if_stat;
+ int err;
+ gchar *err_str;
+ pcap_t *pch;
+ char errbuf[PCAP_ERRBUF_SIZE];
+ struct pcap_stat ps;
+
+ if_list = get_interface_list(&err, &err_str);
+ if (if_list == NULL) {
+ switch (err) {
+ case CANT_GET_INTERFACE_LIST:
+ cmdarg_err("%s", err_str);
+ g_free(err_str);
+ break;
+
+ case NO_INTERFACES_FOUND:
+ cmdarg_err("There are no interfaces on which a capture can be done");
+ break;
+ }
+ return err;
+ }
+
+ for (if_entry = g_list_first(if_list); if_entry != NULL; if_entry = g_list_next(if_entry)) {
+ if_info = (if_info_t *)if_entry->data;
+#ifdef HAVE_PCAP_OPEN
+ pch = pcap_open(if_info->name, MIN_PACKET_SIZE, 0, 0, NULL, errbuf);
+#else
+ pch = pcap_open_live(if_info->name, MIN_PACKET_SIZE, 0, 0, errbuf);
+#endif
+
+ if (pch) {
+ if_stat = (if_stat_t *)g_malloc(sizeof(if_stat_t));
+ if_stat->name = g_strdup(if_info->name);
+ if_stat->pch = pch;
+ stat_list = g_list_append(stat_list, if_stat);
+ }
+ }
+
+ if (!stat_list) {
+ cmdarg_err("There are no interfaces on which a capture can be done");
+ return 2;
+ }
+
+ if (capture_child) {
+ /* Let our parent know we succeeded. */
+ pipe_write_block(2, SP_SUCCESS, NULL);
+ }
+
+ if (!machine_readable) {
+ printf("%-15s %10s %10s\n", "Interface", "Received",
+ "Dropped");
+ }
+
+ global_ld.go = TRUE;
+ while (global_ld.go) {
+ for (stat_entry = g_list_first(stat_list); stat_entry != NULL; stat_entry = g_list_next(stat_entry)) {
+ if_stat = (if_stat_t *)stat_entry->data;
+ pcap_stats(if_stat->pch, &ps);
+
+ if (!machine_readable) {
+ printf("%-15s %10u %10u\n", if_stat->name,
+ ps.ps_recv, ps.ps_drop);
+ } else {
+ printf("%s\t%u\t%u\n", if_stat->name,
+ ps.ps_recv, ps.ps_drop);
+ fflush(stdout);
+ }
+ }
+#ifdef _WIN32
+ if (! global_ld.from_cap_pipe)
+ Sleep(1 * 1000);
+#else
+ sleep(1);
+#endif
+ }
+
+ /* XXX - Not reached. Should we look for 'q' in stdin? */
+ for (stat_entry = g_list_first(stat_list); stat_entry != NULL; stat_entry = g_list_next(stat_entry)) {
+ if_stat = (if_stat_t *)stat_entry->data;
+ pcap_close(if_stat->pch);
+ g_free(if_stat->name);
+ g_free(if_stat);
+ }
+ g_list_free(stat_list);
+ free_interface_list(if_list);
+
+ return 0;
+}
+
#ifdef _WIN32
static BOOL WINAPI
-capture_cleanup(DWORD dwCtrlType)
+capture_cleanup_handler(DWORD dwCtrlType)
{
/* CTRL_C_EVENT is sort of like SIGINT, CTRL_BREAK_EVENT is unique to
Windows, CTRL_CLOSE_EVENT is sort of like SIGHUP, CTRL_LOGOFF_EVENT
}
#else
static void
-capture_cleanup(int signum)
+capture_cleanup_handler(int signum _U_)
{
/* On UN*X, we cleanly shut down the capture on SIGINT, SIGHUP, and
SIGTERM. We assume that if the user wanted it to keep running
after they logged out, they'd have nohupped it. */
- g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO,
- "Console: Signal");
- g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG,
- "Console: Signal, signal value: %u", signum);
+ /* Note: don't call g_log() in the signal handler: if we happened to be in
+ * g_log() in process context when the signal came in, g_log will detect
+ * the "recursion" and abort.
+ */
capture_loop_stop();
}
#endif
+
+#ifdef SIGINFO
+static void
+report_counts(void)
+{
+ /* Don't print this if we're a capture child. */
+ if (!capture_child) {
+ if (quiet) {
+ /* Report the count only if we aren't printing a packet count
+ as packets arrive. */
+ fprintf(stderr, "%u packet%s captured\n", global_ld.packet_count,
+ plurality(global_ld.packet_count, "", "s"));
+ }
+ }
+ infoprint = FALSE; /* we just reported it */
+}
+
+static void
+report_counts_siginfo(int signum _U_)
+{
+ int sav_errno = errno;
+
+ /* If we've been told to delay printing, just set a flag asking
+ that we print counts (if we're supposed to), otherwise print
+ the count of packets captured (if we're supposed to). */
+ if (infodelay)
+ infoprint = TRUE;
+ else
+ report_counts();
+ errno = sav_errno;
+}
+#endif /* SIGINFO */
+
static void exit_main(int status)
{
#ifdef _WIN32
* CAP_NET_ADMIN and CAP_NET_RAW, then relinquish our permissions.
* (See comment in main() for details)
*/
-
-static void
-#if 0 /* Set to enable capability debugging */
-/* see 'man cap_to_text()' for explanation of output */
-/* '=' means 'all= ' ie: no capabilities */
-/* '=ip' means 'all=ip' ie: all capabilities are permissible and inheritable */
-/* .... */
-print_caps(char *pfx) {
- cap_t caps = cap_get_proc();
- g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG,
- "%s: EUID: %d Capabilities: %s", pfx,
- geteuid(), cap_to_text(caps, NULL));
- cap_free(caps);
-#else
-print_caps(char *pfx _U_) {
-#endif
-}
-
static void
relinquish_privs_except_capture(void)
{
- /* If 'started_with_special_privs' (ie: suid) then enable for
+ /* If 'started_with_special_privs' (ie: suid) then enable for
* ourself the NET_ADMIN and NET_RAW capabilities and then
* drop our suid privileges.
*
}
}
-
-static void
-relinquish_all_capabilities()
-{
- /* Drop any and all capabilities this process may have. */
- /* Allowed whether or not process has any privileges. */
- cap_t caps = cap_init(); /* all capabilities initialized to off */
- print_caps("Pre-clear");
- if (cap_set_proc(caps)) {
- cmdarg_err("cap_set_proc() fail return: %s", strerror(errno));
- }
- print_caps("Post-clear");
- cap_free(caps);
-}
-
#endif /* HAVE_LIBCAP */
/* Take care of byte order in the libpcap headers read from pipes.
}
}
-/* Provide select() functionality for a single file descriptor
- * on both UNIX/POSIX and Windows.
+#ifdef USE_THREADS
+/*
+ * Thread function that reads from a pipe and pushes the data
+ * to the main application thread.
+ */
+/*
+ * XXX Right now we use async queues for basic signaling. The main thread
+ * sets cap_pipe_buf and cap_bytes_to_read, then pushes an item onto
+ * cap_pipe_pending_q which triggers a read in the cap_pipe_read thread.
+ * Iff the read is successful cap_pipe_read pushes an item onto
+ * cap_pipe_done_q, otherwise an error is signaled. No data is passed in
+ * the queues themselves (yet).
*
- * The Windows version calls WaitForSingleObject instead of
- * select().
+ * We might want to move some of the cap_pipe_dispatch logic here so that
+ * we can let cap_pipe_read run independently, queuing up multiple reads
+ * for the main thread (and possibly get rid of cap_pipe_read_mtx).
+ */
+static void *cap_pipe_read(void *ld_ptr) {
+ loop_data *ld = (loop_data *)ld_ptr;
+ int bytes_read;
+#ifdef _WIN32
+ BOOL res;
+ DWORD b, last_err;
+#else /* _WIN32 */
+ int b;
+#endif /* _WIN32 */
+
+ while (ld->cap_pipe_err == PIPOK) {
+ g_async_queue_pop(cap_pipe_pending_q); /* Wait for our cue (ahem) from the main thread */
+ g_mutex_lock(cap_pipe_read_mtx);
+ bytes_read = 0;
+ while (bytes_read < (int) ld->cap_pipe_bytes_to_read) {
+#ifdef _WIN32
+ /* If we try to use read() on a named pipe on Windows with partial
+ * data it appears to return EOF.
+ */
+ res = ReadFile(ld->cap_pipe_h, ld->cap_pipe_buf+bytes_read,
+ ld->cap_pipe_bytes_to_read - bytes_read,
+ &b, NULL);
+
+ bytes_read += b;
+ if (!res) {
+ last_err = GetLastError();
+ if (last_err == ERROR_MORE_DATA) {
+ continue;
+ } else if (last_err == ERROR_HANDLE_EOF || last_err == ERROR_BROKEN_PIPE || last_err == ERROR_PIPE_NOT_CONNECTED) {
+ ld->cap_pipe_err = PIPEOF;
+ bytes_read = 0;
+ break;
+ }
+ ld->cap_pipe_err = PIPERR;
+ bytes_read = -1;
+ break;
+ } else if (b == 0 && ld->cap_pipe_bytes_to_read > 0) {
+ ld->cap_pipe_err = PIPEOF;
+ bytes_read = 0;
+ break;
+ }
+#else /* _WIN32 */
+ b = read(ld->cap_pipe_fd, ld->cap_pipe_buf+bytes_read,
+ ld->cap_pipe_bytes_to_read - bytes_read);
+ if (b <= 0) {
+ if (b == 0) {
+ ld->cap_pipe_err = PIPEOF;
+ bytes_read = 0;
+ break;
+ } else {
+ ld->cap_pipe_err = PIPERR;
+ bytes_read = -1;
+ break;
+ }
+ } else {
+ bytes_read += b;
+ }
+#endif /*_WIN32 */
+ }
+ ld->cap_pipe_bytes_read = bytes_read;
+ if (ld->cap_pipe_bytes_read >= ld->cap_pipe_bytes_to_read) {
+ g_async_queue_push(cap_pipe_done_q, ld->cap_pipe_buf); /* Any non-NULL value will do */
+ }
+ g_mutex_unlock(cap_pipe_read_mtx);
+ }
+ return NULL;
+}
+#endif /* USE_THREADS */
+
+/* Provide select() functionality for a single file descriptor
+ * on UNIX/POSIX. Windows uses cap_pipe_read via a thread.
*
* Returns the same values as select. If an error is returned,
* the string cap_pipe_err_str should be used instead of errno.
*/
static int
cap_pipe_select(int pipe_fd) {
-#ifndef _WIN32
fd_set rfds;
struct timeval timeout, *pto;
int sel_ret;
cap_pipe_err_str = strerror(errno);
return sel_ret;
}
-#else
- /* XXX - Should we just use file handles exclusively under Windows?
- * Otherwise we have to convert between file handles and file descriptors
- * here and when we open a named pipe.
- */
- HANDLE hPipe = (HANDLE) _get_osfhandle(pipe_fd);
- wchar_t *err_str;
- DWORD wait_ret;
-
- if (hPipe == INVALID_HANDLE_VALUE) {
- cap_pipe_err_str = "Could not open standard input";
- return -1;
- }
-
- cap_pipe_err_str = "Unknown error";
-
- wait_ret = WaitForSingleObject(hPipe, CAP_READ_TIMEOUT);
- switch (wait_ret) {
- /* XXX - This probably isn't correct */
- case WAIT_ABANDONED:
- errno = EINTR;
- return -1;
- case WAIT_OBJECT_0:
- return 1;
- case WAIT_TIMEOUT:
- return 0;
- case WAIT_FAILED:
- FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
- NULL, GetLastError(), 0, (LPTSTR) &err_str, 0, NULL);
- cap_pipe_err_str = utf_16to8(err_str);
- LocalFree(err_str);
- return -1;
- default:
- g_assert_not_reached();
- return -1;
- }
-}
-#endif
/* Mimic pcap_open_live() for pipe captures
- * We check if "pipename" is "-" (stdin) or a FIFO, open it, and read the
- * header.
+
+ * We check if "pipename" is "-" (stdin), a AF_UNIX socket, 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
+static void
cap_pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld,
- char *errmsg, int errmsgl)
+ char *errmsg, int errmsgl)
{
#ifndef _WIN32
struct stat pipe_stat;
-#else
+ struct sockaddr_un sa;
+ int sel_ret;
+ int b;
+ unsigned int bytes_read;
+ int fd;
+#else /* _WIN32 */
#if 1
char *pncopy, *pos;
wchar_t *err_str;
#endif
- HANDLE hPipe = NULL;
#endif
- int sel_ret;
- int fd;
- int b;
- guint32 magic;
- unsigned int bytes_read;
+ guint32 magic = 0;
+#ifndef _WIN32
+ ld->cap_pipe_fd = -1;
+#else
+ ld->cap_pipe_h = INVALID_HANDLE_VALUE;
+#endif
g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "cap_pipe_open_live: %s", pipename);
/*
- * XXX (T)Wireshark blocks until we return
+ * XXX - this blocks until a pcap per-file header has been written to
+ * the pipe, so it could block indefinitely.
*/
if (strcmp(pipename, "-") == 0) {
+#ifndef _WIN32
fd = 0; /* read from stdin */
-#ifdef _WIN32
- /*
- * This is needed to set the stdin pipe into binary mode, otherwise
- * CR/LF are mangled...
- */
- _setmode(0, _O_BINARY);
+#else /* _WIN32 */
+ ld->cap_pipe_h = GetStdHandle(STD_INPUT_HANDLE);
#endif /* _WIN32 */
} else {
#ifndef _WIN32
- if (eth_stat(pipename, &pipe_stat) < 0) {
+ if (ws_stat(pipename, &pipe_stat) < 0) {
if (errno == ENOENT || errno == ENOTDIR)
ld->cap_pipe_err = PIPNEXIST;
else {
g_snprintf(errmsg, errmsgl,
"The capture session could not be initiated "
- "due to error on pipe: %s", strerror(errno));
+ "due to error getting information on pipe/socket: %s", strerror(errno));
ld->cap_pipe_err = PIPERR;
}
- return -1;
+ return;
}
- if (! S_ISFIFO(pipe_stat.st_mode)) {
+ if (S_ISFIFO(pipe_stat.st_mode)) {
+ fd = ws_open(pipename, O_RDONLY | O_NONBLOCK, 0000 /* no creation so don't matter */);
+ if (fd == -1) {
+ g_snprintf(errmsg, errmsgl,
+ "The capture session could not be initiated "
+ "due to error on pipe open: %s", strerror(errno));
+ ld->cap_pipe_err = PIPERR;
+ return;
+ }
+ } else if (S_ISSOCK(pipe_stat.st_mode)) {
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd == -1) {
+ g_snprintf(errmsg, errmsgl,
+ "The capture session could not be initiated "
+ "due to error on socket create: %s", strerror(errno));
+ ld->cap_pipe_err = PIPERR;
+ return;
+ }
+ sa.sun_family = AF_UNIX;
+ /*
+ * The Single UNIX Specification says:
+ *
+ * The size of sun_path has intentionally been left undefined.
+ * This is because different implementations use different sizes.
+ * For example, 4.3 BSD uses a size of 108, and 4.4 BSD uses a size
+ * of 104. Since most implementations originate from BSD versions,
+ * the size is typically in the range 92 to 108.
+ *
+ * Applications should not assume a particular length for sun_path
+ * or assume that it can hold {_POSIX_PATH_MAX} bytes (256).
+ *
+ * It also says
+ *
+ * The <sys/un.h> header shall define the sockaddr_un structure,
+ * which shall include at least the following members:
+ *
+ * sa_family_t sun_family Address family.
+ * char sun_path[] Socket pathname.
+ *
+ * so we assume that it's an array, with a specified size,
+ * and that the size reflects the maximum path length.
+ */
+ if (g_strlcpy(sa.sun_path, pipename, sizeof sa.sun_path) > sizeof sa.sun_path) {
+ /* Path name too long */
+ g_snprintf(errmsg, errmsgl,
+ "The capture session coud not be initiated "
+ "due to error on socket connect: Path name too long");
+ ld->cap_pipe_err = PIPERR;
+ return;
+ }
+ b = connect(fd, (struct sockaddr *)&sa, sizeof sa);
+ if (b == -1) {
+ g_snprintf(errmsg, errmsgl,
+ "The capture session coud not be initiated "
+ "due to error on socket connect: %s", strerror(errno));
+ ld->cap_pipe_err = PIPERR;
+ return;
+ }
+ } else {
if (S_ISCHR(pipe_stat.st_mode)) {
/*
* Assume the user specified an interface on a system where
{
g_snprintf(errmsg, errmsgl,
"The capture session could not be initiated because\n"
- "\"%s\" is neither an interface nor a pipe", pipename);
+ "\"%s\" is neither an interface nor a socket nor a pipe", pipename);
ld->cap_pipe_err = PIPERR;
}
- return -1;
- }
- fd = eth_open(pipename, O_RDONLY | O_NONBLOCK, 0000 /* no creation so don't matter */);
- if (fd == -1) {
- g_snprintf(errmsg, errmsgl,
- "The capture session could not be initiated "
- "due to error on pipe open: %s", strerror(errno));
- ld->cap_pipe_err = PIPERR;
- return -1;
+ return;
}
#else /* _WIN32 */
#define PIPE_STR "\\pipe\\"
"The capture session could not be initiated because\n"
"\"%s\" is neither an interface nor a pipe", pipename);
ld->cap_pipe_err = PIPNEXIST;
- return -1;
+ return;
}
/* Wait for the pipe to appear */
while (1) {
- hPipe = CreateFile(utf_8to16(pipename), GENERIC_READ, 0, NULL,
+ ld->cap_pipe_h = CreateFile(utf_8to16(pipename), GENERIC_READ, 0, NULL,
OPEN_EXISTING, 0, NULL);
- if (hPipe != INVALID_HANDLE_VALUE)
+ if (ld->cap_pipe_h != INVALID_HANDLE_VALUE)
break;
if (GetLastError() != ERROR_PIPE_BUSY) {
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL, GetLastError(), 0, (LPTSTR) &err_str, 0, NULL);
g_snprintf(errmsg, errmsgl,
- "The capture session on \"%s\" could not be initiated "
- "due to error on pipe open: pipe busy: %s (error %d)",
- pipename, utf_16to8(err_str), GetLastError());
+ "The capture session on \"%s\" could not be started "
+ "due to error on pipe open: %s (error %d)",
+ pipename, utf_16to8(err_str), GetLastError());
LocalFree(err_str);
ld->cap_pipe_err = PIPERR;
- return -1;
+ return;
}
if (!WaitNamedPipe(utf_8to16(pipename), 30 * 1000)) {
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL, GetLastError(), 0, (LPTSTR) &err_str, 0, NULL);
g_snprintf(errmsg, errmsgl,
- "The capture session could not be initiated "
- "due to error on named pipe open: %s (error %d)",
- utf_16to8(err_str), GetLastError());
+ "The capture session on \"%s\" timed out during "
+ "pipe open: %s (error %d)",
+ pipename, utf_16to8(err_str), GetLastError());
LocalFree(err_str);
ld->cap_pipe_err = PIPERR;
- return -1;
+ return;
}
}
-
- fd = _open_osfhandle((long) hPipe, _O_RDONLY);
- if (fd == -1) {
- g_snprintf(errmsg, errmsgl,
- "The capture session could not be initiated "
- "due to error on pipe open: %s", strerror(errno));
- ld->cap_pipe_err = PIPERR;
- return -1;
- }
#endif /* _WIN32 */
}
ld->from_cap_pipe = TRUE;
+#ifndef USE_THREADS
/* read the pcap header */
bytes_read = 0;
while (bytes_read < sizeof magic) {
b = read(fd, ((char *)&magic)+bytes_read, sizeof magic-bytes_read);
if (b <= 0) {
if (b == 0)
- g_snprintf(errmsg, errmsgl, "End of file on pipe during open");
+ g_snprintf(errmsg, errmsgl, "End of file on pipe magic during open");
else
- g_snprintf(errmsg, errmsgl, "Error on pipe during open: %s",
+ g_snprintf(errmsg, errmsgl, "Error on pipe magic during open: %s",
strerror(errno));
goto error;
}
bytes_read += b;
}
}
+#else /* USE_THREADS */
+ g_thread_create(&cap_pipe_read, ld, FALSE, NULL);
+
+ ld->cap_pipe_buf = (char *) &magic;
+ ld->cap_pipe_bytes_read = 0;
+ ld->cap_pipe_bytes_to_read = sizeof(magic);
+ /* We don't have to worry about cap_pipe_read_mtx here */
+ g_async_queue_push(cap_pipe_pending_q, ld->cap_pipe_buf);
+ g_async_queue_pop(cap_pipe_done_q);
+ if (ld->cap_pipe_bytes_read <= 0) {
+ if (ld->cap_pipe_bytes_read == 0)
+ g_snprintf(errmsg, errmsgl, "End of file on pipe magic during open");
+ else
+ g_snprintf(errmsg, errmsgl, "Error on pipe magic during open: %s",
+ strerror(errno));
+ goto error;
+ }
+
+#endif /* USE_THREADS */
switch (magic) {
case PCAP_MAGIC:
goto error;
}
+#ifndef USE_THREADS
/* Read the rest of the header */
bytes_read = 0;
while (bytes_read < sizeof(struct pcap_hdr)) {
sizeof(struct pcap_hdr) - bytes_read);
if (b <= 0) {
if (b == 0)
- g_snprintf(errmsg, errmsgl, "End of file on pipe during open");
+ g_snprintf(errmsg, errmsgl, "End of file on pipe header during open");
else
- g_snprintf(errmsg, errmsgl, "Error on pipe during open: %s",
+ g_snprintf(errmsg, errmsgl, "Error on pipe header during open: %s",
strerror(errno));
goto error;
}
bytes_read += b;
}
}
+#else /* USE_THREADS */
+ ld->cap_pipe_buf = (char *) hdr;
+ ld->cap_pipe_bytes_read = 0;
+ ld->cap_pipe_bytes_to_read = sizeof(struct pcap_hdr);
+ g_async_queue_push(cap_pipe_pending_q, ld->cap_pipe_buf);
+ g_async_queue_pop(cap_pipe_done_q);
+ if (ld->cap_pipe_bytes_read <= 0) {
+ if (ld->cap_pipe_bytes_read == 0)
+ g_snprintf(errmsg, errmsgl, "End of file on pipe header during open");
+ else
+ g_snprintf(errmsg, errmsgl, "Error on pipe header header during open: %s",
+ strerror(errno));
+ goto error;
+ }
+#endif /* USE_THREADS */
if (ld->cap_pipe_byte_swapped) {
/* Byte-swap the header fields about which we care. */
ld->cap_pipe_state = STATE_EXPECT_REC_HDR;
ld->cap_pipe_err = PIPOK;
- return fd;
+#ifndef _WIN32
+ ld->cap_pipe_fd = fd;
+#endif
+ return;
error:
g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "cap_pipe_open_live: error %s", errmsg);
ld->cap_pipe_err = PIPERR;
- eth_close(fd);
- return -1;
+#ifndef _WIN32
+ ws_close(fd);
+ ld->cap_pipe_fd = -1;
+#endif
+ return;
}
cap_pipe_dispatch(loop_data *ld, guchar *data, char *errmsg, int errmsgl)
{
struct pcap_pkthdr phdr;
- int b;
enum { PD_REC_HDR_READ, PD_DATA_READ, PD_PIPE_EOF, PD_PIPE_ERR,
- PD_ERR } result;
-
+ PD_ERR } result;
+#ifdef USE_THREADS
+ GTimeVal wait_time;
+ gpointer q_status;
+#else
+ int b;
+#endif
+#ifdef _WIN32
+ wchar_t *err_str;
+#endif
#ifdef LOG_CAPTURE_VERBOSE
g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "cap_pipe_dispatch");
switch (ld->cap_pipe_state) {
case STATE_EXPECT_REC_HDR:
+#ifdef USE_THREADS
+ if (g_mutex_trylock(cap_pipe_read_mtx)) {
+#endif
+
+ ld->cap_pipe_state = STATE_READ_REC_HDR;
ld->cap_pipe_bytes_to_read = ld->cap_pipe_modified ?
sizeof(struct pcaprec_modified_hdr) : sizeof(struct pcaprec_hdr);
ld->cap_pipe_bytes_read = 0;
- ld->cap_pipe_state = STATE_READ_REC_HDR;
+
+#ifdef USE_THREADS
+ ld->cap_pipe_buf = (char *) &ld->cap_pipe_rechdr;
+ g_async_queue_push(cap_pipe_pending_q, ld->cap_pipe_buf);
+ g_mutex_unlock(cap_pipe_read_mtx);
+ }
+#endif
/* Fall through */
case STATE_READ_REC_HDR:
+#ifndef USE_THREADS
b = read(ld->cap_pipe_fd, ((char *)&ld->cap_pipe_rechdr)+ld->cap_pipe_bytes_read,
ld->cap_pipe_bytes_to_read - ld->cap_pipe_bytes_read);
if (b <= 0) {
result = PD_PIPE_ERR;
break;
}
- if ((ld->cap_pipe_bytes_read += b) < ld->cap_pipe_bytes_to_read)
+ ld->cap_pipe_bytes_read += b;
+#else /* USE_THREADS */
+ g_get_current_time(&wait_time);
+ g_time_val_add(&wait_time, THREAD_READ_TIMEOUT);
+ q_status = g_async_queue_timed_pop(cap_pipe_done_q, &wait_time);
+ if (ld->cap_pipe_err == PIPEOF) {
+ result = PD_PIPE_EOF;
+ break;
+ } else if (ld->cap_pipe_err == PIPERR) {
+ result = PD_PIPE_ERR;
+ break;
+ }
+ if (!q_status) {
+ return 0;
+ }
+#endif /* USE_THREADS */
+ if ((ld->cap_pipe_bytes_read) < ld->cap_pipe_bytes_to_read)
return 0;
result = PD_REC_HDR_READ;
break;
case STATE_EXPECT_DATA:
- ld->cap_pipe_bytes_read = 0;
+#ifdef USE_THREADS
+ if (g_mutex_trylock(cap_pipe_read_mtx)) {
+#endif
+
ld->cap_pipe_state = STATE_READ_DATA;
+ ld->cap_pipe_bytes_to_read = ld->cap_pipe_rechdr.hdr.incl_len;
+ ld->cap_pipe_bytes_read = 0;
+
+#ifdef USE_THREADS
+ ld->cap_pipe_buf = (char *) data;
+ g_async_queue_push(cap_pipe_pending_q, ld->cap_pipe_buf);
+ g_mutex_unlock(cap_pipe_read_mtx);
+ }
+#endif
/* Fall through */
case STATE_READ_DATA:
+#ifndef USE_THREADS
b = read(ld->cap_pipe_fd, data+ld->cap_pipe_bytes_read,
- ld->cap_pipe_rechdr.hdr.incl_len - ld->cap_pipe_bytes_read);
+ ld->cap_pipe_bytes_to_read - ld->cap_pipe_bytes_read);
if (b <= 0) {
if (b == 0)
result = PD_PIPE_EOF;
result = PD_PIPE_ERR;
break;
}
- if ((ld->cap_pipe_bytes_read += b) < ld->cap_pipe_rechdr.hdr.incl_len)
+ ld->cap_pipe_bytes_read += b;
+#else /* USE_THREADS */
+ g_get_current_time(&wait_time);
+ g_time_val_add(&wait_time, THREAD_READ_TIMEOUT);
+ q_status = g_async_queue_timed_pop(cap_pipe_done_q, &wait_time);
+ if (ld->cap_pipe_err == PIPEOF) {
+ result = PD_PIPE_EOF;
+ break;
+ } else if (ld->cap_pipe_err == PIPERR) {
+ result = PD_PIPE_ERR;
+ break;
+ }
+ if (!q_status) {
return 0;
+ }
+#endif /* USE_THREADS */
+ if ((ld->cap_pipe_bytes_read) < ld->cap_pipe_bytes_to_read)
+ return 0;
result = PD_DATA_READ;
break;
return -1;
case PD_PIPE_ERR:
+#ifdef _WIN32
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ NULL, GetLastError(), 0, (LPTSTR) &err_str, 0, NULL);
+ g_snprintf(errmsg, errmsgl,
+ "Error reading from pipe: %s (error %d)",
+ utf_16to8(err_str), GetLastError());
+ LocalFree(err_str);
+#else
g_snprintf(errmsg, errmsgl, "Error reading from pipe: %s",
strerror(errno));
+#endif
/* Fall through */
case PD_ERR:
break;
{
gchar open_err_str[PCAP_ERRBUF_SIZE];
gchar *sync_msg_str;
- static const char ppamsg[] = "can't find PPA for ";
- const char *set_linktype_err_str;
- const char *libpcap_warn;
#ifdef _WIN32
- gchar *sync_secondary_msg_str;
int err;
+ gchar *sync_secondary_msg_str;
WORD wVersionRequested;
WSADATA wsaData;
#endif
-#ifdef HAVE_PCAP_REMOTE
- struct pcap_rmtauth auth;
-#endif
-
g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_open_input : %s", capture_opts->iface);
switch (err) {
case WSASYSNOTREADY:
- g_snprintf(errmsg, errmsg_len,
+ g_snprintf(errmsg, (gulong) errmsg_len,
"Couldn't initialize Windows Sockets: Network system not ready for network communication");
break;
case WSAVERNOTSUPPORTED:
- g_snprintf(errmsg, errmsg_len,
+ g_snprintf(errmsg, (gulong) errmsg_len,
"Couldn't initialize Windows Sockets: Windows Sockets version %u.%u not supported",
LOBYTE(wVersionRequested), HIBYTE(wVersionRequested));
break;
case WSAEINPROGRESS:
- g_snprintf(errmsg, errmsg_len,
+ g_snprintf(errmsg, (gulong) errmsg_len,
"Couldn't initialize Windows Sockets: Blocking operation is in progress");
break;
case WSAEPROCLIM:
- g_snprintf(errmsg, errmsg_len,
+ g_snprintf(errmsg, (gulong) errmsg_len,
"Couldn't initialize Windows Sockets: Limit on the number of tasks supported by this WinSock implementation has been reached");
break;
case WSAEFAULT:
- g_snprintf(errmsg, errmsg_len,
+ g_snprintf(errmsg, (gulong) errmsg_len,
"Couldn't initialize Windows Sockets: Bad pointer passed to WSAStartup");
break;
default:
- g_snprintf(errmsg, errmsg_len,
+ g_snprintf(errmsg, (gulong) errmsg_len,
"Couldn't initialize Windows Sockets: error %d", err);
break;
}
- g_snprintf(secondary_errmsg, secondary_errmsg_len, please_report);
+ g_snprintf(secondary_errmsg, (gulong) secondary_errmsg_len, please_report);
return FALSE;
}
#endif
- /* Open the network interface to capture from it.
- Some versions of libpcap may put warnings into the error buffer
- if they succeed; to tell if that's happened, we have to clear
- the error buffer, and check if it's still a null string. */
- open_err_str[0] = '\0';
-#ifdef HAVE_PCAP_OPEN
- auth.type = capture_opts->auth_type == CAPTURE_AUTH_PWD ?
- RPCAP_RMTAUTH_PWD : RPCAP_RMTAUTH_NULL;
- auth.username = capture_opts->auth_username;
- auth.password = capture_opts->auth_password;
-
- ld->pcap_h = pcap_open(capture_opts->iface,
- capture_opts->has_snaplen ? capture_opts->snaplen :
- WTAP_MAX_PACKET_SIZE,
- /* flags */
- (capture_opts->promisc_mode ? PCAP_OPENFLAG_PROMISCUOUS : 0) |
- (capture_opts->datatx_udp ? PCAP_OPENFLAG_DATATX_UDP : 0) |
- (capture_opts->nocap_rpcap ? PCAP_OPENFLAG_NOCAPTURE_RPCAP : 0),
- CAP_READ_TIMEOUT, &auth, open_err_str);
-#else
- ld->pcap_h = pcap_open_live(capture_opts->iface,
- capture_opts->has_snaplen ? capture_opts->snaplen :
- WTAP_MAX_PACKET_SIZE,
- capture_opts->promisc_mode, CAP_READ_TIMEOUT,
- open_err_str);
-#endif
-
-/* If not using libcap: we now can now set euid/egid to ruid/rgid */
-/* to remove any suid privileges. */
-/* If using libcap: we can now remove NET_RAW and NET_ADMIN capabilities */
-/* (euid/egid have already previously been set to ruid/rgid. */
-/* (See comment in main() for details) */
-#ifndef HAVE_LIBCAP
- relinquish_special_privs_perm();
-#else
- relinquish_all_capabilities();
-#endif
+ ld->pcap_h = open_capture_device(capture_opts, &open_err_str);
if (ld->pcap_h != NULL) {
/* we've opened "iface" as a network device */
#ifdef _WIN32
/* try to set the capture buffer size */
- if (pcap_setbuff(ld->pcap_h, capture_opts->buffer_size * 1024 * 1024) != 0) {
+ if (capture_opts->buffer_size > 1 &&
+ pcap_setbuff(ld->pcap_h, capture_opts->buffer_size * 1024 * 1024) != 0) {
sync_secondary_msg_str = g_strdup_printf(
- "The capture buffer size of %luMB seems to be too high for your machine,\n"
+ "The capture buffer size of %dMB seems to be too high for your machine,\n"
"the default of 1MB will be used.\n"
"\n"
"Nonetheless, the capture is started.\n",
#endif
#if defined(HAVE_PCAP_REMOTE) && defined(HAVE_PCAP_SETSAMPLING)
- if (capture_opts->sampling_method != CAPTURE_SAMP_NONE)
+ if ((capture_opts->sampling_method != CAPTURE_SAMP_NONE) &&
+ (strncmp (capture_opts->iface, "rpcap://", 8) == 0))
{
struct pcap_samp *samp;
#endif
/* setting the data link type only works on real interfaces */
- if (capture_opts->linktype != -1) {
- set_linktype_err_str = set_pcap_linktype(ld->pcap_h, capture_opts->iface,
- capture_opts->linktype);
- if (set_linktype_err_str != NULL) {
- g_snprintf(errmsg, errmsg_len, "Unable to set data link type (%s).",
- set_linktype_err_str);
- g_snprintf(secondary_errmsg, secondary_errmsg_len, please_report);
- return FALSE;
- }
- }
+ if (!set_pcap_linktype(ld->pcap_h, capture_opts, errmsg, errmsg_len,
+ secondary_errmsg, secondary_errmsg_len))
+ return FALSE;
ld->linktype = get_pcap_linktype(ld->pcap_h, capture_opts->iface);
} else {
/* We couldn't open "iface" as a network device. */
/* Try to open it as a pipe */
- ld->cap_pipe_fd = cap_pipe_open_live(capture_opts->iface, &ld->cap_pipe_hdr, ld, errmsg, errmsg_len);
+ cap_pipe_open_live(capture_opts->iface, &ld->cap_pipe_hdr, ld, errmsg, (int) errmsg_len);
+#ifndef _WIN32
if (ld->cap_pipe_fd == -1) {
+#else
+ if (ld->cap_pipe_h == INVALID_HANDLE_VALUE) {
+#endif
if (ld->cap_pipe_err == PIPNEXIST) {
- /* Pipe doesn't exist, so output message for interface */
-
- /* If we got a "can't find PPA for X" message, warn the user (who
- is running (T)Wireshark 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 X" rather than "can't find PPA for X"). */
- if (strncmp(open_err_str, ppamsg, sizeof ppamsg - 1) == 0)
- libpcap_warn =
- "\n\n"
- "You are running (T)Wireshark with a version of the libpcap library\n"
- "that doesn't handle HP-UX network devices well; this means that\n"
- "(T)Wireshark 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 = "";
- g_snprintf(errmsg, errmsg_len,
- "The capture session could not be initiated (%s).", open_err_str);
-#ifndef _WIN32
- g_snprintf(secondary_errmsg, secondary_errmsg_len,
-"Please check to make sure you have sufficient permissions, and that you have "
-"the proper interface or pipe specified.%s", libpcap_warn);
-#else
- g_snprintf(secondary_errmsg, secondary_errmsg_len,
-"\n"
-"Please check that \"%s\" is the proper interface.\n"
-"\n"
-"\n"
-"Help can be found at:\n"
-"\n"
-" http://wiki.wireshark.org/CaptureSetup\n"
-"\n"
-"64-bit Windows:\n"
-"WinPcap does not support 64-bit Windows; you will have to use some other\n"
-"tool to capture traffic, such as netcap.\n"
-"For netcap details see: http://support.microsoft.com/?id=310875\n"
-"\n"
-"Modem (PPP/WAN):\n"
-"Note that version 3.0 of WinPcap, and earlier versions of WinPcap, don't\n"
-"support capturing on PPP/WAN interfaces on Windows NT 4.0 / 2000 / XP /\n"
-"Server 2003.\n"
-"WinPcap 3.1 has support for it on Windows 2000 / XP / Server 2003, but has no\n"
-"support for it on Windows NT 4.0 or Windows Vista (Beta 1).",
- capture_opts->iface);
-#endif /* _WIN32 */
+ /* Pipe doesn't exist, so output message for interface */
+ get_capture_device_open_failure_messages(open_err_str,
+ capture_opts->iface,
+ errmsg,
+ errmsg_len,
+ secondary_errmsg,
+ secondary_errmsg_len);
}
/*
* Else pipe (or file) does exist and cap_pipe_open_live() has
return TRUE;
}
-
/* close the capture input file (pcap or capture pipe) */
static void capture_loop_close_input(loop_data *ld) {
g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_close_input");
/* if open, close the capture pipe "input file" */
+#ifndef _WIN32
if (ld->cap_pipe_fd >= 0) {
g_assert(ld->from_cap_pipe);
- eth_close(ld->cap_pipe_fd);
+ ws_close(ld->cap_pipe_fd);
ld->cap_pipe_fd = 0;
}
+#else
+ if (ld->cap_pipe_h != INVALID_HANDLE_VALUE) {
+ CloseHandle(ld->cap_pipe_h);
+ ld->cap_pipe_h = INVALID_HANDLE_VALUE;
+ }
+#endif
/* if open, close the pcap "input file" */
if(ld->pcap_h != NULL) {
- g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_close_input: closing %p", ld->pcap_h);
+ g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_close_input: closing %p", (void *)ld->pcap_h);
g_assert(!ld->from_cap_pipe);
pcap_close(ld->pcap_h);
ld->pcap_h = NULL;
/* init the capture filter */
static initfilter_status_t
-capture_loop_init_filter(pcap_t *pcap_h, gboolean from_cap_pipe, gchar * iface, gchar * cfilter) {
- bpf_u_int32 netnum, netmask;
- gchar lookup_net_err_str[PCAP_ERRBUF_SIZE];
+capture_loop_init_filter(pcap_t *pcap_h, gboolean from_cap_pipe,
+ gchar * iface, gchar * cfilter)
+{
struct bpf_program fcode;
-
g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_init_filter: %s", cfilter);
/* capture filters only work on real interfaces */
if (cfilter && !from_cap_pipe) {
/* A capture filter was specified; set it up. */
- if (pcap_lookupnet(iface, &netnum, &netmask, lookup_net_err_str) < 0) {
- /*
- * Well, we can't get the netmask for this interface; it's used
- * only for filters that check for broadcast IP addresses, so
- * we just punt and use 0. It might be nice to warn the user,
- * but that's a pain in a GUI application, as it'd involve popping
- * up a message box, and it's not clear how often this would make
- * a difference (only filters that check for IP broadcast addresses
- * use the netmask).
- */
- /*cmdarg_err(
- "Warning: Couldn't obtain netmask info (%s).", lookup_net_err_str);*/
- netmask = 0;
- }
- if (pcap_compile(pcap_h, &fcode, cfilter, 1, netmask) < 0) {
+ if (!compile_capture_filter(iface, pcap_h, &fcode, cfilter)) {
/* Treat this specially - our caller might try to compile this
as a display filter and, if that succeeds, warn the user that
the display and capture filter syntaxes are different. */
/* set up to write to the already-opened capture output file/files */
static gboolean
-capture_loop_init_output(capture_options *capture_opts, int save_file_fd, loop_data *ld, char *errmsg, int errmsg_len) {
- int file_snaplen;
+capture_loop_init_output(capture_options *capture_opts, loop_data *ld, char *errmsg, int errmsg_len) {
int err;
/* get snaplen */
if (ld->from_cap_pipe) {
- file_snaplen = ld->cap_pipe_hdr.snaplen;
+ ld->file_snaplen = ld->cap_pipe_hdr.snaplen;
} else
{
- file_snaplen = pcap_snapshot(ld->pcap_h);
+ ld->file_snaplen = pcap_snapshot(ld->pcap_h);
}
/* Set up to write to the capture file. */
if (capture_opts->multi_files_on) {
- ld->pdh = ringbuf_init_libpcap_fdopen(ld->linktype, file_snaplen,
- &ld->bytes_written, &err);
+ ld->pdh = ringbuf_init_libpcap_fdopen(&err);
} else {
- ld->pdh = libpcap_fdopen(save_file_fd, ld->linktype, file_snaplen,
- &ld->bytes_written, &err);
+ ld->pdh = libpcap_fdopen(ld->save_file_fd, &err);
+ }
+ if (ld->pdh) {
+ gboolean successful;
+
+ ld->bytes_written = 0;
+ if (capture_opts->use_pcapng) {
+ char appname[100];
+
+ g_snprintf(appname, sizeof(appname), "Dumpcap " VERSION "%s", wireshark_svnversion);
+ successful = libpcap_write_session_header_block(ld->pdh, appname, &ld->bytes_written, &err) &&
+ libpcap_write_interface_description_block(ld->pdh, capture_opts->iface, capture_opts->cfilter, ld->linktype, ld->file_snaplen, &ld->bytes_written, &err);
+ } else {
+ successful = libpcap_write_file_header(ld->pdh, ld->linktype, ld->file_snaplen,
+ &ld->bytes_written, &err);
+ }
+ if (!successful) {
+ fclose(ld->pdh);
+ ld->pdh = NULL;
+ }
}
if (ld->pdh == NULL) {
default:
if (err < 0) {
g_snprintf(errmsg, errmsg_len,
- "The file to which the capture would be"
- " saved (\"%s\") could not be opened: Error %d.",
- capture_opts->save_file, err);
+ "The file to which the capture would be"
+ " saved (\"%s\") could not be opened: Error %d.",
+ capture_opts->save_file, err);
} else {
g_snprintf(errmsg, errmsg_len,
- "The file to which the capture would be"
- " saved (\"%s\") could not be opened: %s.",
- capture_opts->save_file, strerror(err));
+ "The file to which the capture would be"
+ " saved (\"%s\") could not be opened: %s.",
+ capture_opts->save_file, strerror(err));
}
break;
}
if (capture_opts->multi_files_on) {
return ringbuf_libpcap_dump_close(&capture_opts->save_file, err_close);
} else {
+ if (capture_opts->use_pcapng) {
+ libpcap_write_interface_statistics_block(ld->pdh, 0, ld->pcap_h, &ld->bytes_written, err_close);
+ }
return libpcap_dump_close(ld->pdh, err_close);
}
}
char *errmsg, int errmsg_len)
{
int inpkts;
- int sel_ret;
gint packet_count_before;
guchar pcap_data[WTAP_MAX_PACKET_SIZE];
+#ifndef USE_THREADS
+ int sel_ret;
+#endif
packet_count_before = ld->packet_count;
if (ld->from_cap_pipe) {
#ifdef LOG_CAPTURE_VERBOSE
g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: from capture pipe");
#endif
+#ifndef USE_THREADS
sel_ret = cap_pipe_select(ld->cap_pipe_fd);
if (sel_ret <= 0) {
inpkts = 0;
/*
* "select()" says we can read from the pipe without blocking
*/
+#endif /* USE_THREADS */
inpkts = cap_pipe_dispatch(ld, pcap_data, errmsg, errmsg_len);
if (inpkts < 0) {
ld->go = FALSE;
}
+#ifndef USE_THREADS
}
+#endif
}
else
{
inpkts = pcap_dispatch(ld->pcap_h, 1, capture_loop_packet_cb,
(u_char *)ld);
if (inpkts < 0) {
- ld->pcap_err = TRUE;
+ if (inpkts == -1) {
+ /* Error, rather than pcap_breakloop(). */
+ ld->pcap_err = TRUE;
+ }
ld->go = FALSE; /* error or pcap_breakloop() - stop capturing */
}
} else {
capture_loop_open_output(capture_options *capture_opts, int *save_file_fd,
char *errmsg, int errmsg_len) {
- char tmpname[128+1];
+ char *tmpname;
gchar *capfile_name;
gboolean is_tempfile;
-
+#ifndef _WIN32
+ int ret;
+#endif
g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_open_output: %s",
(capture_opts->save_file) ? capture_opts->save_file : "");
}
} else {
/* Try to open/create the specified file for use as a capture buffer. */
- *save_file_fd = eth_open(capfile_name, O_RDWR|O_BINARY|O_TRUNC|O_CREAT,
+ *save_file_fd = ws_open(capfile_name, O_RDWR|O_BINARY|O_TRUNC|O_CREAT,
0600);
}
}
is_tempfile = FALSE;
} else {
/* Choose a random name for the temporary capture buffer */
- *save_file_fd = create_tempfile(tmpname, sizeof tmpname, "ether");
+ *save_file_fd = create_tempfile(&tmpname, "wireshark");
capfile_name = g_strdup(tmpname);
is_tempfile = TRUE;
}
/* capture_opts.save_file is "g_free"ed later, which is equivalent to
"g_free(capfile_name)". */
#ifndef _WIN32
- fchown(*save_file_fd, capture_opts->owner, capture_opts->group);
+ ret = fchown(*save_file_fd, capture_opts->owner, capture_opts->group);
#endif
return TRUE;
}
-static void
-capture_loop_stop_signal_handler(int signo _U_)
-{
- g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Signal: Stop capture");
- capture_loop_stop();
-}
-
#ifdef _WIN32
#define TIME_GET() GetTickCount()
#else
#define TIME_GET() time(NULL)
#endif
+/* Do the work of handling either the file size or file duration capture
+ conditions being reached, and switching files or stopping. */
+static gboolean
+do_file_switch_or_stop(capture_options *capture_opts,
+ condition *cnd_autostop_files,
+ condition *cnd_autostop_size,
+ condition *cnd_file_duration)
+{
+ if (capture_opts->multi_files_on) {
+ if (cnd_autostop_files != NULL &&
+ cnd_eval(cnd_autostop_files, ++global_ld.autostop_files)) {
+ /* no files left: stop here */
+ global_ld.go = FALSE;
+ return FALSE;
+ }
+
+ /* Switch to the next ringbuffer file */
+ if (ringbuf_switch_file(&global_ld.pdh, &capture_opts->save_file,
+ &global_ld.save_file_fd, &global_ld.err)) {
+ gboolean successful;
+
+ /* File switch succeeded: reset the conditions */
+ global_ld.bytes_written = 0;
+ if (capture_opts->use_pcapng) {
+ char appname[100];
+
+ g_snprintf(appname, sizeof(appname), "Dumpcap " VERSION "%s", wireshark_svnversion);
+ successful = libpcap_write_session_header_block(global_ld.pdh, appname, &global_ld.bytes_written, &global_ld.err) &&
+ libpcap_write_interface_description_block(global_ld.pdh, capture_opts->iface, capture_opts->cfilter, global_ld.linktype, global_ld.file_snaplen, &global_ld.bytes_written, &global_ld.err);
+ } else {
+ successful = libpcap_write_file_header(global_ld.pdh, global_ld.linktype, global_ld.file_snaplen,
+ &global_ld.bytes_written, &global_ld.err);
+ }
+ if (!successful) {
+ fclose(global_ld.pdh);
+ global_ld.pdh = NULL;
+ global_ld.go = FALSE;
+ return FALSE;
+ }
+ if(cnd_autostop_size)
+ cnd_reset(cnd_autostop_size);
+ if(cnd_file_duration)
+ cnd_reset(cnd_file_duration);
+ libpcap_dump_flush(global_ld.pdh, NULL);
+ if (!quiet)
+ report_packet_count(global_ld.inpkts_to_sync_pipe);
+ global_ld.inpkts_to_sync_pipe = 0;
+ report_new_capture_file(capture_opts->save_file);
+ } else {
+ /* File switch failed: stop here */
+ global_ld.go = FALSE;
+ return FALSE;
+ }
+ } else {
+ /* single file, stop now */
+ global_ld.go = FALSE;
+ return FALSE;
+ }
+ return TRUE;
+}
+
/* Do the low-level work of a capture.
Returns TRUE if it succeeds, FALSE otherwise. */
static gboolean
capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct pcap_stat *stats)
{
-#ifndef _WIN32
- struct sigaction act;
-#endif
time_t upd_time, cur_time;
time_t start_time;
int err_close;
int inpkts;
- gint inpkts_to_sync_pipe = 0; /* packets not already send out to the sync_pipe */
condition *cnd_file_duration = NULL;
condition *cnd_autostop_files = NULL;
condition *cnd_autostop_size = NULL;
condition *cnd_autostop_duration = NULL;
- guint32 autostop_files = 0;
gboolean write_ok;
gboolean close_ok;
gboolean cfilter_error = FALSE;
-#define MSG_MAX_LENGTH 4096
char errmsg[MSG_MAX_LENGTH+1];
char secondary_errmsg[MSG_MAX_LENGTH+1];
- int save_file_fd = -1;
*errmsg = '\0';
*secondary_errmsg = '\0';
/* init the loop data */
- ld.go = TRUE;
- ld.packet_count = 0;
+ global_ld.go = TRUE;
+ global_ld.packet_count = 0;
+#ifdef SIGINFO
+ global_ld.report_packet_count = FALSE;
+#endif
if (capture_opts->has_autostop_packets)
- ld.packet_max = capture_opts->autostop_packets;
+ global_ld.packet_max = capture_opts->autostop_packets;
else
- ld.packet_max = 0; /* no limit */
- ld.err = 0; /* no error seen yet */
- ld.wtap_linktype = WTAP_ENCAP_UNKNOWN;
- ld.pcap_err = FALSE;
- ld.from_cap_pipe = FALSE;
- ld.pdh = NULL;
- ld.cap_pipe_fd = -1;
+ global_ld.packet_max = 0; /* no limit */
+ global_ld.inpkts_to_sync_pipe = 0;
+ global_ld.err = 0; /* no error seen yet */
+ global_ld.wtap_linktype = WTAP_ENCAP_UNKNOWN;
+ global_ld.pcap_err = FALSE;
+ global_ld.from_cap_pipe = FALSE;
+ global_ld.pdh = NULL;
+#ifndef _WIN32
+ global_ld.cap_pipe_fd = -1;
+#else
+ global_ld.cap_pipe_h = INVALID_HANDLE_VALUE;
+#endif
#ifdef MUST_DO_SELECT
- ld.pcap_fd = 0;
+ global_ld.pcap_fd = 0;
#endif
+ global_ld.autostop_files = 0;
+ global_ld.save_file_fd = -1;
/* We haven't yet gotten the capture statistics. */
*stats_known = FALSE;
-#ifndef _WIN32
- /*
- * Catch SIGUSR1, so that we exit cleanly if the parent process
- * kills us with it due to the user selecting "Capture->Stop".
- */
- act.sa_handler = capture_loop_stop_signal_handler;
- /*
- * Arrange that system calls not get restarted, because when
- * our signal handler returns we don't want to restart
- * a call that was waiting for packets to arrive.
- */
- act.sa_flags = 0;
- sigemptyset(&act.sa_mask);
- sigaction(SIGUSR1, &act, NULL);
-#endif /* _WIN32 */
-
g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Capture loop starting ...");
capture_opts_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, capture_opts);
/* open the "input file" from network interface or capture pipe */
- if (!capture_loop_open_input(capture_opts, &ld, errmsg, sizeof(errmsg),
+ if (!capture_loop_open_input(capture_opts, &global_ld, errmsg, sizeof(errmsg),
secondary_errmsg, sizeof(secondary_errmsg))) {
goto error;
}
/* init the input filter from the network interface (capture pipe will do nothing) */
- switch (capture_loop_init_filter(ld.pcap_h, ld.from_cap_pipe, capture_opts->iface, capture_opts->cfilter)) {
+ switch (capture_loop_init_filter(global_ld.pcap_h, global_ld.from_cap_pipe,
+ capture_opts->iface,
+ capture_opts->cfilter)) {
case INITFILTER_NO_ERROR:
break;
case INITFILTER_BAD_FILTER:
cfilter_error = TRUE;
- g_snprintf(errmsg, sizeof(errmsg), "%s", pcap_geterr(ld.pcap_h));
+ g_snprintf(errmsg, sizeof(errmsg), "%s", pcap_geterr(global_ld.pcap_h));
goto error;
case INITFILTER_OTHER_ERROR:
g_snprintf(errmsg, sizeof(errmsg), "Can't install filter (%s).",
- pcap_geterr(ld.pcap_h));
+ pcap_geterr(global_ld.pcap_h));
g_snprintf(secondary_errmsg, sizeof(secondary_errmsg), "%s", please_report);
goto error;
}
/* If we're supposed to write to a capture file, open it for output
(temporary/specified name/ringbuffer) */
if (capture_opts->saving_to_file) {
- if (!capture_loop_open_output(capture_opts, &save_file_fd, errmsg, sizeof(errmsg))) {
+ if (!capture_loop_open_output(capture_opts, &global_ld.save_file_fd,
+ errmsg, sizeof(errmsg))) {
goto error;
}
/* set up to write to the already-opened capture output file/files */
- if (!capture_loop_init_output(capture_opts, save_file_fd, &ld, errmsg, sizeof(errmsg))) {
+ if (!capture_loop_init_output(capture_opts, &global_ld, errmsg,
+ sizeof(errmsg))) {
goto error;
}
message to our parent so that they'll open the capture file and
update its windows to indicate that we have a live capture in
progress. */
- libpcap_dump_flush(ld.pdh, NULL);
+ libpcap_dump_flush(global_ld.pdh, NULL);
report_new_capture_file(capture_opts->save_file);
}
/* WOW, everything is prepared! */
/* please fasten your seat belts, we will enter now the actual capture loop */
- while (ld.go) {
+ while (global_ld.go) {
/* dispatch incoming packets */
- inpkts = capture_loop_dispatch(capture_opts, &ld, errmsg, sizeof(errmsg));
+ inpkts = capture_loop_dispatch(capture_opts, &global_ld, errmsg,
+ sizeof(errmsg));
+
+#ifdef SIGINFO
+ /* Were we asked to print packet counts by the SIGINFO handler? */
+ if (global_ld.report_packet_count) {
+ fprintf(stderr, "%u packet%s captured\n", global_ld.packet_count,
+ plurality(global_ld.packet_count, "", "s"));
+ global_ld.report_packet_count = FALSE;
+ }
+#endif
#ifdef _WIN32
/* any news from our parent (signal pipe)? -> just stop the capture */
if (!signal_pipe_check_running()) {
- ld.go = FALSE;
+ global_ld.go = FALSE;
}
#endif
if (inpkts > 0) {
- inpkts_to_sync_pipe += inpkts;
+ global_ld.inpkts_to_sync_pipe += inpkts;
/* check capture size condition */
if (cnd_autostop_size != NULL &&
- cnd_eval(cnd_autostop_size, (guint32)ld.bytes_written)){
+ cnd_eval(cnd_autostop_size, (guint32)global_ld.bytes_written)) {
/* Capture size limit reached, do we have another file? */
- if (capture_opts->multi_files_on) {
- if (cnd_autostop_files != NULL &&
- cnd_eval(cnd_autostop_files, ++autostop_files)) {
- /* no files left: stop here */
- ld.go = FALSE;
- continue;
- }
-
- /* Switch to the next ringbuffer file */
- if (ringbuf_switch_file(&ld.pdh, &capture_opts->save_file,
- &save_file_fd, &ld.bytes_written, &ld.err)) {
- /* File switch succeeded: reset the conditions */
- cnd_reset(cnd_autostop_size);
- if (cnd_file_duration) {
- cnd_reset(cnd_file_duration);
- }
- libpcap_dump_flush(ld.pdh, NULL);
- report_packet_count(inpkts_to_sync_pipe);
- inpkts_to_sync_pipe = 0;
- report_new_capture_file(capture_opts->save_file);
- } else {
- /* File switch failed: stop here */
- ld.go = FALSE;
- continue;
- }
- } else {
- /* single file, stop now */
- ld.go = FALSE;
+ if (!do_file_switch_or_stop(capture_opts, cnd_autostop_files,
+ cnd_autostop_size, cnd_file_duration))
continue;
- }
} /* cnd_autostop_size */
if (capture_opts->output_to_pipe) {
- libpcap_dump_flush(ld.pdh, NULL);
+ libpcap_dump_flush(global_ld.pdh, NULL);
}
} /* inpkts */
}*/
/* Let the parent process know. */
- if (inpkts_to_sync_pipe) {
+ if (global_ld.inpkts_to_sync_pipe) {
/* do sync here */
- libpcap_dump_flush(ld.pdh, NULL);
+ libpcap_dump_flush(global_ld.pdh, NULL);
- /* Send our parent a message saying we've written out "inpkts_to_sync_pipe"
- packets to the capture file. */
- report_packet_count(inpkts_to_sync_pipe);
+ /* Send our parent a message saying we've written out
+ "global_ld.inpkts_to_sync_pipe" packets to the capture file. */
+ if (!quiet)
+ report_packet_count(global_ld.inpkts_to_sync_pipe);
- inpkts_to_sync_pipe = 0;
+ global_ld.inpkts_to_sync_pipe = 0;
}
/* check capture duration condition */
if (cnd_autostop_duration != NULL && cnd_eval(cnd_autostop_duration)) {
/* The maximum capture time has elapsed; stop the capture. */
- ld.go = FALSE;
+ global_ld.go = FALSE;
continue;
}
/* check capture file duration condition */
if (cnd_file_duration != NULL && cnd_eval(cnd_file_duration)) {
/* duration limit reached, do we have another file? */
- if (capture_opts->multi_files_on) {
- if (cnd_autostop_files != NULL &&
- cnd_eval(cnd_autostop_files, ++autostop_files)) {
- /* no files left: stop here */
- ld.go = FALSE;
- continue;
- }
-
- /* Switch to the next ringbuffer file */
- if (ringbuf_switch_file(&ld.pdh, &capture_opts->save_file,
- &save_file_fd, &ld.bytes_written, &ld.err)) {
- /* file switch succeeded: reset the conditions */
- cnd_reset(cnd_file_duration);
- if(cnd_autostop_size)
- cnd_reset(cnd_autostop_size);
- libpcap_dump_flush(ld.pdh, NULL);
- report_packet_count(inpkts_to_sync_pipe);
- inpkts_to_sync_pipe = 0;
- report_new_capture_file(capture_opts->save_file);
- } else {
- /* File switch failed: stop here */
- ld.go = FALSE;
- continue;
- }
- } else {
- /* single file, stop now */
- ld.go = FALSE;
+ if (!do_file_switch_or_stop(capture_opts, cnd_autostop_files,
+ cnd_autostop_size, cnd_file_duration))
continue;
- }
} /* cnd_file_duration */
}
- } /* while (ld.go) */
+ } /* while (global_ld.go) */
g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Capture loop stopping ...");
cnd_delete(cnd_autostop_duration);
/* did we had a pcap (input) error? */
- if (ld.pcap_err) {
+ if (global_ld.pcap_err) {
/* On Linux, if an interface goes down while you're capturing on it,
you'll get a "recvfrom: Network is down" error (ENETDOWN).
(At least you will if strerror() doesn't show a local translation
These should *not* be reported to the Wireshark developers. */
char *cap_err_str;
- cap_err_str = pcap_geterr(ld.pcap_h);
+ cap_err_str = pcap_geterr(global_ld.pcap_h);
if (strcmp(cap_err_str, "recvfrom: Network is down") == 0 ||
strcmp(cap_err_str, "read: Device not configured") == 0 ||
strcmp(cap_err_str, "read: I/O error") == 0) {
report_capture_error(errmsg, please_report);
}
}
- else if (ld.from_cap_pipe && ld.cap_pipe_err == PIPERR)
+ else if (global_ld.from_cap_pipe && global_ld.cap_pipe_err == PIPERR)
report_capture_error(errmsg, "");
/* did we had an error while capturing? */
- if (ld.err == 0) {
+ if (global_ld.err == 0) {
write_ok = TRUE;
} else {
- capture_loop_get_errmsg(errmsg, sizeof(errmsg), capture_opts->save_file, ld.err,
- FALSE);
+ capture_loop_get_errmsg(errmsg, sizeof(errmsg), capture_opts->save_file,
+ global_ld.err, FALSE);
report_capture_error(errmsg, please_report);
write_ok = FALSE;
}
if (capture_opts->saving_to_file) {
/* close the wiretap (output) file */
- close_ok = capture_loop_close_output(capture_opts, &ld, &err_close);
+ close_ok = capture_loop_close_output(capture_opts, &global_ld, &err_close);
} else
close_ok = TRUE;
/* there might be packets not yet notified to the parent */
/* (do this after closing the file, so all packets are already flushed) */
- if(inpkts_to_sync_pipe) {
- report_packet_count(inpkts_to_sync_pipe);
- inpkts_to_sync_pipe = 0;
+ if(global_ld.inpkts_to_sync_pipe) {
+ if (!quiet)
+ report_packet_count(global_ld.inpkts_to_sync_pipe);
+ global_ld.inpkts_to_sync_pipe = 0;
}
/* If we've displayed a message about a write error, there's no point
*/
/* get packet drop statistics from pcap */
- if(ld.pcap_h != NULL) {
- g_assert(!ld.from_cap_pipe);
+ if(global_ld.pcap_h != NULL) {
+ g_assert(!global_ld.from_cap_pipe);
/* Get the capture statistics, so we know how many packets were
dropped. */
- if (pcap_stats(ld.pcap_h, stats) >= 0) {
+ if (pcap_stats(global_ld.pcap_h, stats) >= 0) {
*stats_known = TRUE;
/* Let the parent process know. */
report_packet_drops(stats->ps_drop);
} else {
g_snprintf(errmsg, sizeof(errmsg),
"Can't get packet-drop statistics: %s",
- pcap_geterr(ld.pcap_h));
+ pcap_geterr(global_ld.pcap_h));
report_capture_error(errmsg, please_report);
}
}
/* close the input file (pcap or capture pipe) */
- capture_loop_close_input(&ld);
+ capture_loop_close_input(&global_ld);
g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Capture loop stopped!");
} else {
/* We can't use the save file, and we have no FILE * for the stream
to close in order to close it, so close the FD directly. */
- if(save_file_fd != -1) {
- eth_close(save_file_fd);
+ if(global_ld.save_file_fd != -1) {
+ ws_close(global_ld.save_file_fd);
}
/* We couldn't even start the capture, so get rid of the capture
file. */
if(capture_opts->save_file != NULL) {
- eth_unlink(capture_opts->save_file);
+ ws_unlink(capture_opts->save_file);
g_free(capture_opts->save_file);
}
}
report_capture_error(errmsg, secondary_errmsg);
/* close the input file (pcap or cap_pipe) */
- capture_loop_close_input(&ld);
+ capture_loop_close_input(&global_ld);
g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Capture loop stopped with error");
static void capture_loop_stop(void)
{
#ifdef HAVE_PCAP_BREAKLOOP
- if(ld.pcap_h != NULL)
- pcap_breakloop(ld.pcap_h);
+ if(global_ld.pcap_h != NULL)
+ pcap_breakloop(global_ld.pcap_h);
#endif
- ld.go = FALSE;
+ global_ld.go = FALSE;
}
capture_loop_packet_cb(u_char *user, const struct pcap_pkthdr *phdr,
const u_char *pd)
{
- loop_data *ld = (void *) user;
+ loop_data *ld = (loop_data *) (void *) user;
int err;
- /* if the user told us to stop after x packets, do we already have enough? */
- if ((ld->packet_max > 0) && (ld->packet_count >= ld->packet_max))
- {
- ld->go = FALSE;
- return;
- }
-
/* We may be called multiple times from pcap_dispatch(); if we've set
the "stop capturing" flag, ignore this packet, as we're not
supposed to be saving any more packets. */
return;
if (ld->pdh) {
+ gboolean successful;
/* 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 (!libpcap_write_packet(ld->pdh, phdr, pd, &ld->bytes_written, &err)) {
+ if (global_capture_opts.use_pcapng) {
+ successful = libpcap_write_enhanced_packet_block(ld->pdh, phdr, 0, pd, &ld->bytes_written, &err);
+ } else {
+ successful = libpcap_write_packet(ld->pdh, phdr, pd, &ld->bytes_written, &err);
+ }
+ if (!successful) {
ld->go = FALSE;
ld->err = err;
- } else
+ } else {
ld->packet_count++;
+ /* if the user told us to stop after x packets, do we already have enough? */
+ if ((ld->packet_max > 0) && (ld->packet_count >= ld->packet_max))
+ {
+ ld->go = FALSE;
+ }
+ }
}
}
main(int argc, char *argv[])
{
int opt;
- extern char *optarg;
gboolean arg_error = FALSE;
#ifdef _WIN32
WSADATA wsaData;
-#endif /* _WIN32 */
-#ifndef _WIN32
+#else
struct sigaction action, oldaction;
#endif
GLogLevelFlags log_flags;
gboolean list_interfaces = FALSE;
gboolean list_link_layer_types = FALSE;
+#ifdef HAVE_BPF_IMAGE
+ gboolean print_bpf_code = FALSE;
+#endif
gboolean machine_readable = FALSE;
gboolean print_statistics = FALSE;
int status, run_once_args = 0;
gint i;
+#if defined(__APPLE__) && defined(__LP64__)
+ struct utsname osinfo;
+#endif
+
+#ifdef _WIN32
+ /*
+ * Initialize our DLL search path. MUST be called before LoadLibrary
+ * or g_module_open.
+ */
+ ws_init_dll_search_path();
+#endif
#ifdef HAVE_PCAP_REMOTE
-#define OPTSTRING_INIT "a:A:b:c:Df:hi:Lm:MprSs:uvw:y:Z:"
+#define OPTSTRING_A "A:"
+#define OPTSTRING_r "r"
+#define OPTSTRING_u "u"
#else
-#define OPTSTRING_INIT "a:b:c:Df:hi:LMpSs:vw:y:Z:"
+#define OPTSTRING_A ""
+#define OPTSTRING_r ""
+#define OPTSTRING_u ""
#endif
-#ifdef _WIN32
-#define OPTSTRING_WIN32 "B:"
+#ifdef HAVE_PCAP_SETSAMPLING
+#define OPTSTRING_m "m:"
#else
-#define OPTSTRING_WIN32 ""
-#endif /* _WIN32 */
+#define OPTSTRING_m ""
+#endif
+
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
+#define OPTSTRING_B "B:"
+#else
+#define OPTSTRING_B ""
+#endif /* _WIN32 or HAVE_PCAP_CREATE */
+
+#ifdef HAVE_PCAP_CREATE
+#define OPTSTRING_I "I"
+#else
+#define OPTSTRING_I ""
+#endif
- char optstring[sizeof(OPTSTRING_INIT) + sizeof(OPTSTRING_WIN32) - 1] =
- OPTSTRING_INIT OPTSTRING_WIN32;
+#ifdef HAVE_BPF_IMAGE
+#define OPTSTRING_d "d"
+#else
+#define OPTSTRING_d ""
+#endif
+
+#define OPTSTRING "a:" OPTSTRING_A "b:" OPTSTRING_B "c:" OPTSTRING_d "Df:hi:" OPTSTRING_I "L" OPTSTRING_m "Mnpq" OPTSTRING_r "Ss:" OPTSTRING_u "vw:y:Z:"
#ifdef DEBUG_CHILD_DUMPCAP
- if ((debug_log = fopen("dumpcap_debug_log.tmp","w")) == NULL) {
+ if ((debug_log = ws_fopen("dumpcap_debug_log.tmp","w")) == NULL) {
fprintf (stderr, "Unable to open debug log file !\n");
exit (1);
}
#endif
- /* Determine if dumpcap is being requested to run in a special */
- /* capture_child mode by going thru the command line args to see if */
- /* a -Z is present. (-Z is a hidden option). */
- /* The primary result of running in capture_child mode is that */
- /* all messages sent out on stderr are in a special type/len/string */
- /* format to allow message processing by type. */
- /* These messages include various 'status' messages which are sent */
- /* when an actual capture is in progress. Capture_child mode */
- /* would normally be requested by a parent process which invokes */
- /* dumpcap and obtains dumpcap stderr output via a pipe to which */
- /* dumpcap stderr has been redirected. */
- /* Capture_child mode needs to be determined immediately upon */
- /* startup so that any messages generated by dumpcap in this mode */
- /* (eg: during initialization) will be formatted properly. */
+#if defined(__APPLE__) && defined(__LP64__)
+ /*
+ * Is this Mac OS X 10.6.x, other than 10.6.2? If so, we need a bug
+ * workaround - timeouts less than 1 second don't work with libpcap
+ * in 64-bit code. (The bug was introduced in 10.6, fixed in 10.6.2,
+ * and re-introduced in 10.6.3. We don't know whether it'll be fixed
+ * again in a later 10.6.x release; we'll assume that it'll be fixed
+ * in any future major releases.)
+ */
+ if (uname(&osinfo) == 0) {
+ /*
+ * Mac OS X 10.x uses Darwin {x+4}.0.0. Mac OS X 10.x.y uses Darwin
+ * {x+4}.y.0 (except that 10.6.1 appears to have a uname version
+ * number of 10.0.0, not 10.1.0 - go figure).
+ */
+ if (strncmp(osinfo.release, "10.", 3) == 0) {
+ /*
+ * OK, it's Snow Leopard - which version?
+ */
+ if (strcmp(osinfo.release, "10.2.0") != 0) {
+ /* Not 10.6.2. */
+ need_timeout_workaround = TRUE;
+ }
+ }
+ }
+#endif
+
+ /*
+ * Determine if dumpcap is being requested to run in a special
+ * capture_child mode by going thru the command line args to see if
+ * a -Z is present. (-Z is a hidden option).
+ *
+ * The primary result of running in capture_child mode is that
+ * all messages sent out on stderr are in a special type/len/string
+ * format to allow message processing by type. These messages include
+ * error messages if dumpcap fails to start the operation it was
+ * requested to do, as well as various "status" messages which are sent
+ * when an actual capture is in progress, and a "success" message sent
+ * if dumpcap was requested to perform an operation other than a
+ * capture.
+ *
+ * Capture_child mode would normally be requested by a parent process
+ * which invokes dumpcap and obtains dumpcap stderr output via a pipe
+ * to which dumpcap stderr has been redirected. It might also have
+ * another pipe to obtain dumpcap stdout output; for operations other
+ * than a capture, that information is formatted specially for easier
+ * parsing by the parent process.
+ *
+ * Capture_child mode needs to be determined immediately upon
+ * startup so that any messages generated by dumpcap in this mode
+ * (eg: during initialization) will be formatted properly.
+ */
for (i=1; i<argc; i++) {
if (strcmp("-Z", argv[i]) == 0) {
capture_child = TRUE;
+ machine_readable = TRUE; /* request machine-readable output */
#ifdef _WIN32
/* set output pipe to binary mode, to avoid ugly text conversions */
_setmode(2, O_BINARY);
WSAStartup( MAKEWORD( 1, 1 ), &wsaData );
/* Set handler for Ctrl+C key */
- SetConsoleCtrlHandler(capture_cleanup, TRUE);
+ SetConsoleCtrlHandler(capture_cleanup_handler, TRUE);
+
+ /* Prepare to read from a pipe */
+ if (!g_thread_supported ())
+ g_thread_init (NULL);
+ cap_pipe_pending_q = g_async_queue_new();
+ cap_pipe_done_q = g_async_queue_new();
+ cap_pipe_read_mtx = g_mutex_new();
+
#else
/* Catch SIGINT and SIGTERM and, if we get either of them, clean up
and exit. */
- action.sa_handler = capture_cleanup;
+ action.sa_handler = capture_cleanup_handler;
+ /*
+ * Arrange that system calls not get restarted, because when
+ * our signal handler returns we don't want to restart
+ * a call that was waiting for packets to arrive.
+ */
action.sa_flags = 0;
sigemptyset(&action.sa_mask);
sigaction(SIGTERM, &action, NULL);
sigaction(SIGINT, &action, NULL);
+ sigaction(SIGPIPE, &action, NULL);
sigaction(SIGHUP, NULL, &oldaction);
if (oldaction.sa_handler == SIG_DFL)
sigaction(SIGHUP, &action, NULL);
+
+#ifdef SIGINFO
+ /* Catch SIGINFO and, if we get it and we're capturing in
+ quiet mode, report the number of packets we've captured. */
+ action.sa_handler = report_counts_siginfo;
+ action.sa_flags = SA_RESTART;
+ sigemptyset(&action.sa_mask);
+ sigaction(SIGINFO, &action, NULL);
+#endif /* SIGINFO */
#endif /* _WIN32 */
/* ----------------------------------------------------------------- */
/* capabilities; Drop all other capabilities; */
/* - If not -w (ie: doing -S or -D, etc) run to completion; */
/* else: after pcap_open_live() in capture_loop_open_input() */
- /* drop all capabilities (NET_RAW and NET_ADMIN) */
+ /* drop all capabilities (NET_RAW and NET_ADMIN); */
/* (Note: this means that the process, although logged in */
/* as root, does not have various permissions such as the */
- /* ability to bypass file access permissions. */
+ /* ability to bypass file access permissions). */
/* XXX: Should we just leave capabilities alone in this case */
/* so that user gets expected effect that root can do */
/* anything ?? */
/* Action: */
/* - If not -w (ie: doing -S or -D, etc) run to completion; */
/* else: after pcap_open_live() in capture_loop_open_input() */
- /* drop same (euid=ruid). (ie: keep suid until after */
- /* pcap_open_live */
+ /* drop suid root (set euid=ruid).(ie: keep suid until after */
+ /* pcap_open_live). */
/* */
/* 5. Running as suid root (euid=0, ruid=n); Using libcap. */
/* Action: */
/* - Near start of program: Enable NET_RAW and NET_ADMIN */
/* capabilities; Drop all other capabilities; */
- /* Drop suid privileges (euid=ruid). */
+ /* Drop suid privileges (euid=ruid); */
/* - If not -w (ie: doing -S or -D, etc) run to completion; */
/* else: after pcap_open_live() in capture_loop_open_input() */
- /* drop all capabilities (NET_RAW and NET_ADMIN) */
+ /* drop all capabilities (NET_RAW and NET_ADMIN). */
/* */
/* XXX: For some Linux versions/distros with capabilities */
/* a 'normal' process with any capabilities cannot be */
/* This behaviour will apparently be changed in the kernel */
/* to allow the kill (signal) in this case. */
/* See the following for details: */
- /* http://www.mail-archive.com/ [wrapped] */
+ /* http://www.mail-archive.com/ [wrapped] */
/* linux-security-module@vger.kernel.org/msg02913.html */
/* */
/* It is therefore conceivable that if dumpcap somehow hangs */
/* in pcap_open_live or before that wireshark will not */
- /* be able to stop dumpcap using a signal (USR1, TERM, etc). */
+ /* be able to stop dumpcap using a signal (INT, TERM, etc). */
/* In this case, exiting wireshark will kill the child */
/* dumpcap process. */
/* */
/* drop all capabilities (NET_RAW and NET_ADMIN) */
/* */
/* ToDo: -S (stats) should drop privileges/capabilities when no */
- /* onger required (similar to capture). */
+ /* longer required (similar to capture). */
/* */
/* ----------------------------------------------------------------- */
relinquish_privs_except_capture();
#endif
- /* Set the initial values in the capture_opts. This might be overwritten
+ /* Set the initial values in the capture options. This might be overwritten
by the command line parameters. */
- capture_opts_init(capture_opts, NULL);
+ capture_opts_init(&global_capture_opts, NULL);
/* Default to capturing the entire packet. */
- capture_opts->snaplen = WTAP_MAX_PACKET_SIZE;
+ global_capture_opts.snaplen = WTAP_MAX_PACKET_SIZE;
/* We always save to a file - if no file was specified, we save to a
temporary file. */
- capture_opts->saving_to_file = TRUE;
- capture_opts->has_ring_num_files = TRUE;
+ global_capture_opts.saving_to_file = TRUE;
+ global_capture_opts.has_ring_num_files = TRUE;
/* Now get our args */
- while ((opt = getopt(argc, argv, optstring)) != -1) {
+ while ((opt = getopt(argc, argv, OPTSTRING)) != -1) {
switch (opt) {
case 'h': /* Print help and exit */
print_usage(TRUE);
case 'c': /* Capture x packets */
case 'f': /* capture filter */
case 'i': /* Use interface x */
+ case 'n': /* Use pcapng format */
case 'p': /* Don't capture in promiscuous mode */
case 's': /* Set the snapshot (capture) length */
case 'w': /* Write to capture file x */
#ifdef HAVE_PCAP_SETSAMPLING
case 'm': /* Sampling */
#endif
-#ifdef _WIN32
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
case 'B': /* Buffer size */
-#endif /* _WIN32 */
- status = capture_opts_add_opt(capture_opts, opt, optarg, &start_capture);
+#endif /* _WIN32 or HAVE_PCAP_CREATE */
+#ifdef HAVE_PCAP_CREATE
+ case 'I': /* Monitor mode */
+#endif
+ status = capture_opts_add_opt(&global_capture_opts, opt, optarg, &start_capture);
if(status != 0) {
exit_main(status);
}
#endif
break;
+ case 'q': /* Quiet */
+ quiet = TRUE;
+ break;
+
/*** all non capture option specific ***/
case 'D': /* Print a list of capture devices and exit */
list_interfaces = TRUE;
list_link_layer_types = TRUE;
run_once_args++;
break;
+#ifdef HAVE_BPF_IMAGE
+ case 'd': /* Print BPF code for capture filter and exit */
+ print_bpf_code = TRUE;
+ run_once_args++;
+ break;
+#endif
case 'S': /* Print interface statistics once a second */
print_statistics = TRUE;
run_once_args++;
break;
- case 'M': /* For -D and -L, print machine-readable output */
+ case 'M': /* For -D, -L, and -S, print machine-readable output */
machine_readable = TRUE;
break;
default:
if (run_once_args > 1) {
cmdarg_err("Only one of -D, -L, or -S may be supplied.");
exit_main(1);
- } else if (list_link_layer_types) {
- /* We're supposed to list the link-layer types for an interface;
- did the user also specify a capture file to be read? */
- /* No - did they specify a ring buffer option? */
- if (capture_opts->multi_files_on) {
+ } else if (run_once_args == 1) {
+ /* We're supposed to print some information, rather than
+ to capture traffic; did they specify a ring buffer option? */
+ if (global_capture_opts.multi_files_on) {
cmdarg_err("Ring buffer requested, but a capture isn't being done.");
exit_main(1);
}
} else {
- /* No - was the ring buffer option specified and, if so, does it make
- sense? */
- if (capture_opts->multi_files_on) {
+ /* We're supposed to capture traffic; was the ring buffer option
+ specified and, if so, does it make sense? */
+ if (global_capture_opts.multi_files_on) {
/* Ring buffer works only under certain conditions:
a) ring buffer does not work with temporary files;
b) it makes no sense to enable the ring buffer if the maximum
file size is set to "infinite". */
- if (capture_opts->save_file == NULL) {
+ if (global_capture_opts.save_file == NULL) {
cmdarg_err("Ring buffer requested, but capture isn't being saved to a permanent file.");
- capture_opts->multi_files_on = FALSE;
+ global_capture_opts.multi_files_on = FALSE;
}
- if (!capture_opts->has_autostop_filesize && !capture_opts->has_file_duration) {
+ if (!global_capture_opts.has_autostop_filesize && !global_capture_opts.has_file_duration) {
cmdarg_err("Ring buffer requested, but no maximum capture file size or duration were specified.");
/* XXX - this must be redesigned as the conditions changed */
-/* capture_opts->multi_files_on = FALSE;*/
+/* global_capture_opts.multi_files_on = FALSE;*/
}
}
}
- if (capture_opts_trim_iface(capture_opts, NULL) == FALSE) {
+ /*
+ * "-D" requires no interface to be selected; it's supposed to list
+ * all interfaces.
+ */
+ if (list_interfaces) {
+ /* Get the list of interfaces */
+ GList *if_list;
+ int err;
+ gchar *err_str;
+
+ if_list = capture_interface_list(&err, &err_str);
+ if (if_list == NULL) {
+ switch (err) {
+ case CANT_GET_INTERFACE_LIST:
+ cmdarg_err("%s", err_str);
+ g_free(err_str);
+ exit_main(2);
+ break;
+
+ case NO_INTERFACES_FOUND:
+ /*
+ * If we're being run by another program, just give them
+ * an empty list of interfaces, don't report this as
+ * an error; that lets them decide whether to report
+ * this as an error or not.
+ */
+ if (!machine_readable) {
+ cmdarg_err("There are no interfaces on which a capture can be done");
+ exit_main(2);
+ }
+ break;
+ }
+ }
+
+ if (machine_readable) /* tab-separated values to stdout */
+ print_machine_readable_interfaces(if_list);
+ else
+ capture_opts_print_interfaces(if_list);
+ free_interface_list(if_list);
+ exit_main(0);
+ }
+
+ /*
+ * "-S" requires no interface to be selected; it gives statistics
+ * for all interfaces.
+ */
+ if (print_statistics) {
+ status = print_statistics_loop(machine_readable);
+ exit_main(status);
+ }
+
+ /*
+ * "-L", "-d", and capturing act on a particular interface, so we have to
+ * have an interface; if none was specified, pick a default.
+ */
+ if (capture_opts_trim_iface(&global_capture_opts, NULL) == FALSE) {
/* cmdarg_err() already called .... */
exit_main(1);
}
/* Let the user know what interface was chosen. */
/* get_interface_descriptive_name() is not available! */
- g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Interface: %s\n", capture_opts->iface);
+ g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Interface: %s\n", global_capture_opts.iface);
+
+ if (list_link_layer_types) {
+ /* Get the list of link-layer types for the capture device. */
+ if_capabilities_t *caps;
+ gchar *err_str;
+
+ caps = get_if_capabilities(global_capture_opts.iface,
+ global_capture_opts.monitor_mode, &err_str);
+ if (caps == NULL) {
+ cmdarg_err("The capabilities of the capture device \"%s\" could not be obtained (%s).\n"
+ "Please check to make sure you have sufficient permissions, and that\n"
+ "you have the proper interface or pipe specified.", global_capture_opts.iface, err_str);
+ g_free(err_str);
+ exit_main(2);
+ }
+ if (caps->data_link_types == NULL) {
+ cmdarg_err("The capture device \"%s\" has no data link types.", global_capture_opts.iface);
+ exit_main(2);
+ }
+ if (machine_readable) /* tab-separated values to stdout */
+ print_machine_readable_if_capabilities(caps);
+ else
+ capture_opts_print_if_capabilities(caps,
+ global_capture_opts.monitor_mode);
+ free_if_capabilities(caps);
+ exit_main(0);
+ }
- if (list_interfaces) {
- status = capture_opts_list_interfaces(machine_readable);
- exit_main(status);
- } else if (list_link_layer_types) {
- status = capture_opts_list_link_layer_types(capture_opts, machine_readable);
- exit_main(status);
- } else if (print_statistics) {
- status = capture_opts_print_statistics(machine_readable);
- exit_main(status);
+ /* We're supposed to do a capture, or print the BPF code for a filter.
+ Process the snapshot length, as that affects the generated BPF code. */
+ capture_opts_trim_snaplen(&global_capture_opts, MIN_PACKET_SIZE);
+
+#ifdef HAVE_BPF_IMAGE
+ if (print_bpf_code) {
+ show_filter_code(&global_capture_opts);
+ exit_main(0);
}
+#endif
- capture_opts_trim_snaplen(capture_opts, MIN_PACKET_SIZE);
- capture_opts_trim_ring_num_files(capture_opts);
+ /* We're supposed to do a capture. Process the ring buffer arguments. */
+ capture_opts_trim_ring_num_files(&global_capture_opts);
/* Now start the capture. */
- if(capture_loop_start(capture_opts, &stats_known, &stats) == TRUE) {
+ if(capture_loop_start(&global_capture_opts, &stats_known, &stats) == TRUE) {
/* capture ok */
exit_main(0);
} else {
/* indication report routines */
-void
+static void
report_packet_count(int packet_count)
{
char tmp[SP_DECISIZE+1+1];
g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "File: %s", filename);
pipe_write_block(2, SP_FILE, filename);
} else {
+#ifdef SIGINFO
+ /*
+ * Prevent a SIGINFO handler from writing to the standard error
+ * while we're doing so; instead, have it just set a flag telling
+ * us to print that information when we're done.
+ */
+ infodelay = TRUE;
+#endif /* SIGINFO */
fprintf(stderr, "File: %s\n", filename);
/* stderr could be line buffered */
fflush(stderr);
+
+#ifdef SIGINFO
+ /*
+ * Allow SIGINFO handlers to write.
+ */
+ infodelay = FALSE;
+
+ /*
+ * If a SIGINFO handler asked us to write out capture counts, do so.
+ */
+ if (infoprint)
+ report_counts();
+#endif /* SIGINFO */
}
}
"Secondary Error: %s", secondary_error_msg);
sync_pipe_errmsg_to_parent(2, error_msg, secondary_error_msg);
} else {
- fprintf(stderr, "%s\n%s\n", error_msg, secondary_error_msg);
+ fprintf(stderr, "%s\n", error_msg);
+ if (secondary_error_msg[0] != '\0')
+ fprintf(stderr, "%s\n", secondary_error_msg);
}
}
void
-report_packet_drops(int drops)
+report_packet_drops(guint32 drops)
{
char tmp[SP_DECISIZE+1+1];
- g_snprintf(tmp, sizeof(tmp), "%d", drops);
+ g_snprintf(tmp, sizeof(tmp), "%u", drops);
if(capture_child) {
g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Packets dropped: %s", tmp);
}
}
#endif
+
+/*
+ * Editor modelines - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */