wireshark/dumpcap.c

5817 lines
219 KiB
C

/* dumpcap.c
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config.h>
#define WS_LOG_DOMAIN LOG_DOMAIN_CAPCHILD
#include <stdio.h>
#include <stdlib.h> /* for exit() */
#include <glib.h>
#include <string.h>
#include <sys/types.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#include <wsutil/ws_getopt.h>
#if defined(__APPLE__) && defined(__LP64__)
#include <sys/utsname.h>
#endif
#include <signal.h>
#include <errno.h>
#include <wsutil/cmdarg_err.h>
#include <wsutil/strtoi.h>
#include <cli_main.h>
#include <wsutil/version_info.h>
#include <wsutil/socket.h>
#include <wsutil/wslog.h>
#include <wsutil/file_util.h>
#ifdef HAVE_LIBCAP
# include <sys/prctl.h>
# include <sys/capability.h>
#endif
#include "ringbuffer.h"
#include "capture/capture_ifinfo.h"
#include "capture/capture-pcap-util.h"
#include "capture/capture-pcap-util-int.h"
#ifdef _WIN32
#include "capture/capture-wpcap.h"
#endif /* _WIN32 */
#include "writecap/pcapio.h"
#ifndef _WIN32
#include <sys/un.h>
#endif
#include <wsutil/clopts_common.h>
#include <wsutil/privileges.h>
#include "sync_pipe.h"
#include "capture_opts.h"
#include <capture/capture_session.h>
#include <capture/capture_sync.h>
#include "wsutil/tempfile.h"
#include "wsutil/file_util.h"
#include "wsutil/cpu_info.h"
#include "wsutil/os_version_info.h"
#include "wsutil/str_util.h"
#include "wsutil/inet_addr.h"
#include "wsutil/time_util.h"
#include "wsutil/please_report_bug.h"
#include "wsutil/glib-compat.h"
#include <wsutil/ws_assert.h>
#include "capture/ws80211_utils.h"
#include "extcap.h"
/*
* Get information about libpcap format from "wiretap/libpcap.h".
* Get information about pcapng format from "wiretap/pcapng_module.h".
* XXX - can we just use pcap_open_offline() to read the pipe?
*/
#include "wiretap/libpcap.h"
#include "wiretap/pcapng_module.h"
#include "wiretap/pcapng.h"
/**#define DEBUG_DUMPCAP**/
/**#define DEBUG_CHILD_DUMPCAP**/
#ifdef _WIN32
#include "wsutil/win32-utils.h"
#ifdef DEBUG_DUMPCAP
#include <conio.h> /* _getch() */
#endif
#endif
#ifdef DEBUG_CHILD_DUMPCAP
FILE *debug_log; /* for logging debug messages to */
/* a file if DEBUG_CHILD_DUMPCAP */
/* is defined */
#endif
static GAsyncQueue *pcap_queue;
static gint64 pcap_queue_bytes;
static gint64 pcap_queue_packets;
static gint64 pcap_queue_byte_limit = 0;
static gint64 pcap_queue_packet_limit = 0;
static gboolean capture_child = FALSE; /* FALSE: standalone call, TRUE: this is an Wireshark capture child */
static const char *report_capture_filename = NULL; /* capture child file name */
#ifdef _WIN32
static gchar *sig_pipe_name = NULL;
static HANDLE sig_pipe_handle = NULL;
static gboolean signal_pipe_check_running(void);
#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);
/** Close a pipe, or socket if \a from_socket is TRUE */
static void cap_pipe_close(int pipe_fd, gboolean from_socket);
#if defined (__linux__)
/* whatever the deal with pcap_breakloop, linux doesn't support timeouts
* in pcap_dispatch(); on the other hand, select() works just fine there.
* Hence we use a select for that come what may.
*
* XXX - with TPACKET_V1 and TPACKET_V2, it currently uses select()
* internally, and, with TPACKET_V3, once that's supported, it'll
* support timeouts, at least as I understand the way the code works.
*/
#define MUST_DO_SELECT
#endif
/** init the capture filter */
typedef enum {
INITFILTER_NO_ERROR,
INITFILTER_BAD_FILTER,
INITFILTER_OTHER_ERROR
} initfilter_status_t;
typedef enum {
STATE_EXPECT_REC_HDR,
STATE_READ_REC_HDR,
STATE_EXPECT_DATA,
STATE_READ_DATA
} cap_pipe_state_t;
typedef enum {
PIPOK,
PIPEOF,
PIPERR,
PIPNEXIST
} cap_pipe_err_t;
typedef struct _pcap_pipe_info {
gboolean byte_swapped; /**< TRUE if data in the pipe is byte swapped. */
struct pcap_hdr hdr; /**< Pcap header when capturing from a pipe */
struct pcaprec_modified_hdr rechdr; /**< Pcap record header when capturing from a pipe */
} pcap_pipe_info_t;
typedef struct _pcapng_pipe_info {
pcapng_block_header_t bh; /**< Pcapng general block header when capturing from a pipe */
GArray *src_iface_to_global; /**< Int array mapping local IDB numbers to global_ld.interface_data */
} pcapng_pipe_info_t;
struct _loop_data; /* forward declaration so we can use it in the cap_pipe_dispatch function pointer */
/*
* A source of packets from which we're capturing.
*/
typedef struct _capture_src {
guint32 received;
guint32 dropped;
guint32 flushed;
pcap_t *pcap_h;
#ifdef MUST_DO_SELECT
int pcap_fd; /**< pcap file descriptor */
#endif
gboolean pcap_err;
guint interface_id;
GThread *tid;
int snaplen;
int linktype;
gboolean ts_nsec; /**< TRUE if we're using nanosecond precision. */
/**< capture pipe (unix only "input file") */
gboolean from_cap_pipe; /**< TRUE if we are capturing data from a capture pipe */
gboolean from_cap_socket; /**< TRUE if we're capturing from socket */
gboolean from_pcapng; /**< TRUE if we're capturing from pcapng format */
union {
pcap_pipe_info_t pcap; /**< Pcap info when capturing from a pipe */
pcapng_pipe_info_t pcapng; /**< Pcapng info when capturing from a pipe */
} cap_pipe_info;
#ifdef _WIN32
HANDLE cap_pipe_h; /**< The handle of the capture pipe */
#endif
int cap_pipe_fd; /**< the file descriptor of the capture pipe */
gboolean cap_pipe_modified; /**< TRUE if data in the pipe uses modified pcap headers */
char * cap_pipe_databuf; /**< Pointer to the data buffer we've allocated */
size_t cap_pipe_databuf_size; /**< Current size of the data buffer */
guint cap_pipe_max_pkt_size; /**< Maximum packet size allowed */
#if defined(_WIN32)
char * cap_pipe_buf; /**< Pointer to the buffer we read into */
DWORD cap_pipe_bytes_to_read; /**< Used by cap_pipe_dispatch */
DWORD cap_pipe_bytes_read; /**< Used by cap_pipe_dispatch */
#else
size_t cap_pipe_bytes_to_read; /**< Used by cap_pipe_dispatch */
size_t cap_pipe_bytes_read; /**< Used by cap_pipe_dispatch */
#endif
int (*cap_pipe_dispatch)(struct _loop_data *, struct _capture_src *, char *, size_t);
cap_pipe_state_t cap_pipe_state;
cap_pipe_err_t cap_pipe_err;
#if defined(_WIN32)
GMutex *cap_pipe_read_mtx;
GAsyncQueue *cap_pipe_pending_q, *cap_pipe_done_q;
#endif
} capture_src;
typedef struct _saved_idb {
gboolean deleted;
guint interface_id; /* capture_src->interface_id for the associated SHB */
guint8 *idb; /* If non-NULL, IDB read from capture_src. This is an interface specified on the command line otherwise. */
guint idb_len;
} saved_idb_t;
/*
* Global capture loop state.
*/
typedef struct _loop_data {
/* common */
gboolean go; /**< TRUE as long as we're supposed to keep capturing */
int err; /**< if non-zero, error seen while capturing */
gint packets_captured; /**< Number of packets we have already captured */
guint 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
GArray *pcaps; /**< Array of capture_src's on which we're capturing */
gboolean pcapng_passthrough; /**< We have one source and it's pcapng. Pass its SHB and IDBs through. */
guint8 *saved_shb; /**< SHB to write when we have one pcapng input */
GArray *saved_idbs; /**< Array of saved_idb_t, written when we have a new section or output file. */
GRWLock saved_shb_idb_lock; /**< Saved IDB RW mutex */
/* output file(s) */
FILE *pdh;
int save_file_fd;
char *io_buffer; /**< Our IO buffer if we increase the size from the standard size */
guint64 bytes_written; /**< Bytes written for the current file. */
/* autostop conditions */
int packets_written; /**< Packets written for the current file. */
int file_count;
/* ring buffer conditions */
GTimer *file_duration_timer;
time_t next_interval_time;
int interval_s;
} loop_data;
typedef struct _pcap_queue_element {
capture_src *pcap_src;
union {
struct pcap_pkthdr phdr;
pcapng_block_header_t bh;
} u;
u_char *pd;
} pcap_queue_element;
/*
* This needs to be static, so that the SIGINT handler can clear the "go"
* flag and for saved_shb_idb_lock.
*/
static loop_data global_ld;
/*
* Timeout, in milliseconds, for reads from the stream of captured packets
* from a capture device.
*
* 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, re-broken in 10.6.3, and again fixed in 10.6.5.
*/
#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
#endif
/*
* Timeout, in microseconds, for reads from the stream of captured packets
* from a pipe. Pipes don't have the same problem that BPF devices do
* in Mac OS X 10.6, 10.6.1, 10.6.3, and 10.6.4, so we always use a timeout
* of 250ms, i.e. the same value as CAP_READ_TIMEOUT when not on one
* of the offending versions of Snow Leopard.
*
* On Windows this value is converted to milliseconds and passed to
* WaitForSingleObject. If it's less than 1000 WaitForSingleObject
* will return immediately.
*/
#if defined(_WIN32)
#define PIPE_READ_TIMEOUT 100000
#else
#define PIPE_READ_TIMEOUT 250000
#endif
#define WRITER_THREAD_TIMEOUT 100000 /* usecs */
static void
dumpcap_log_writer(const char *domain, enum ws_log_level level,
struct timespec timestamp,
const char *file, long line, const char *func,
const char *user_format, va_list user_ap,
void *user_data);
/* capture related options */
static capture_options global_capture_opts;
static GPtrArray *capture_comments = NULL;
static gboolean quiet = FALSE;
static gboolean use_threads = FALSE;
static guint64 start_time;
static void capture_loop_write_packet_cb(u_char *pcap_src_p, const struct pcap_pkthdr *phdr,
const u_char *pd);
static void capture_loop_queue_packet_cb(u_char *pcap_src_p, const struct pcap_pkthdr *phdr,
const u_char *pd);
static void capture_loop_write_pcapng_cb(capture_src *pcap_src, const pcapng_block_header_t *bh, u_char *pd);
static void capture_loop_queue_pcapng_cb(capture_src *pcap_src, const pcapng_block_header_t *bh, u_char *pd);
static void capture_loop_get_errmsg(char *errmsg, size_t errmsglen,
char *secondary_errmsg,
size_t secondary_errmsglen,
const char *fname, int err,
gboolean is_close);
WS_NORETURN static void exit_main(int err);
static void report_new_capture_file(const char *filename);
static void report_packet_count(unsigned int packet_count);
static void report_packet_drops(guint32 received, guint32 pcap_drops, guint32 drops, guint32 flushed, guint32 ps_ifdrop, gchar *name);
static void report_capture_error(const char *error_msg, const char *secondary_error_msg);
static void report_cfilter_error(capture_options *capture_opts, guint i, const char *errmsg);
#define MSG_MAX_LENGTH 4096
static void
print_usage(FILE *output)
{
fprintf(output, "\nUsage: dumpcap [options] ...\n");
fprintf(output, "\n");
fprintf(output, "Capture interface:\n");
fprintf(output, " -i <interface>, --interface <interface>\n");
fprintf(output, " name or idx of interface (def: first non-loopback),\n"
" or for remote capturing, use one of these formats:\n"
" rpcap://<host>/<interface>\n"
" TCP@<host>:<port>\n");
fprintf(output, " --ifname <name> name to use in the capture file for a pipe from which\n");
fprintf(output, " we're capturing\n");
fprintf(output, " --ifdescr <description>\n");
fprintf(output, " description to use in the capture file for a pipe\n");
fprintf(output, " from which we're capturing\n");
fprintf(output, " -f <capture filter> packet filter in libpcap filter syntax\n");
fprintf(output, " -s <snaplen>, --snapshot-length <snaplen>\n");
#ifdef HAVE_PCAP_CREATE
fprintf(output, " packet snapshot length (def: appropriate maximum)\n");
#else
fprintf(output, " packet snapshot length (def: %u)\n", WTAP_MAX_PACKET_SIZE_STANDARD);
#endif
fprintf(output, " -p, --no-promiscuous-mode\n");
fprintf(output, " don't capture in promiscuous mode\n");
#ifdef HAVE_PCAP_CREATE
fprintf(output, " -I, --monitor-mode capture in monitor mode, if available\n");
#endif
#ifdef CAN_SET_CAPTURE_BUFFER_SIZE
fprintf(output, " -B <buffer size>, --buffer-size <buffer size>\n");
fprintf(output, " size of kernel buffer in MiB (def: %dMiB)\n", DEFAULT_CAPTURE_BUFFER_SIZE);
#endif
fprintf(output, " -y <link type>, --linktype <link type>\n");
fprintf(output, " link layer type (def: first appropriate)\n");
fprintf(output, " --time-stamp-type <type> timestamp method for interface\n");
fprintf(output, " -D, --list-interfaces print list of interfaces and exit\n");
fprintf(output, " -L, --list-data-link-types\n");
fprintf(output, " print list of link-layer types of iface and exit\n");
fprintf(output, " --list-time-stamp-types print list of timestamp types for iface and exit\n");
fprintf(output, " -d print generated BPF code for capture filter\n");
fprintf(output, " -k <freq>,[<type>],[<center_freq1>],[<center_freq2>]\n");
fprintf(output, " set channel on wifi interface\n");
fprintf(output, " -S print statistics for each interface once per second\n");
fprintf(output, " -M for -D, -L, and -S, produce machine-readable output\n");
fprintf(output, "\n");
#ifdef HAVE_PCAP_REMOTE
fprintf(output, "RPCAP options:\n");
fprintf(output, " -r don't ignore own RPCAP traffic in capture\n");
fprintf(output, " -u use UDP for RPCAP data transfer\n");
fprintf(output, " -A <user>:<password> use RPCAP password authentication\n");
#ifdef HAVE_PCAP_SETSAMPLING
fprintf(output, " -m <sampling type> use packet sampling\n");
fprintf(output, " count:NUM - capture one packet of every NUM\n");
fprintf(output, " timer:NUM - capture no more than 1 packet in NUM ms\n");
#endif
#endif
fprintf(output, "Stop conditions:\n");
fprintf(output, " -c <packet count> stop after n packets (def: infinite)\n");
fprintf(output, " -a <autostop cond.> ..., --autostop <autostop cond.> ...\n");
fprintf(output, " duration:NUM - stop after NUM seconds\n");
fprintf(output, " filesize:NUM - stop this file after NUM kB\n");
fprintf(output, " files:NUM - stop after NUM files\n");
fprintf(output, " packets:NUM - stop after NUM packets\n");
/*fprintf(output, "\n");*/
fprintf(output, "Output (files):\n");
fprintf(output, " -w <filename> name of file to save (def: tempfile)\n");
fprintf(output, " -g enable group read access on the output file(s)\n");
fprintf(output, " -b <ringbuffer opt.> ..., --ring-buffer <ringbuffer opt.>\n");
fprintf(output, " 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, " packets:NUM - ringbuffer: replace after NUM packets\n");
fprintf(output, " interval:NUM - switch to next file when the time is\n");
fprintf(output, " an exact multiple of NUM secs\n");
fprintf(output, " printname:FILE - print filename to FILE when written\n");
fprintf(output, " (can use 'stdout' or 'stderr')\n");
fprintf(output, " -n use pcapng format instead of pcap (default)\n");
fprintf(output, " -P use libpcap format instead of pcapng\n");
fprintf(output, " --capture-comment <comment>\n");
fprintf(output, " add a capture comment to the output file\n");
fprintf(output, " (only for pcapng)\n");
fprintf(output, " --temp-dir <directory> write temporary files to this directory\n");
fprintf(output, " (default: %s)\n", g_get_tmp_dir());
fprintf(output, "\n");
ws_log_print_usage(output);
fprintf(output, "\n");
fprintf(output, "Miscellaneous:\n");
fprintf(output, " -N <packet_limit> maximum number of packets buffered within dumpcap\n");
fprintf(output, " -C <byte_limit> maximum number of bytes used for buffering packets\n");
fprintf(output, " within dumpcap\n");
fprintf(output, " -t use a separate thread per interface\n");
fprintf(output, " -q don't report packet capture counts\n");
fprintf(output, " -v, --version print version information and exit\n");
fprintf(output, " -h, --help display this help and exit\n");
fprintf(output, "\n");
#ifdef __linux__
fprintf(output, "Dumpcap can benefit from an enabled BPF JIT compiler if available.\n");
fprintf(output, "You might want to enable it by executing:\n");
fprintf(output, " \"echo 1 > /proc/sys/net/core/bpf_jit_enable\"\n");
fprintf(output, "Note that this can make your system less secure!\n");
fprintf(output, "\n");
#endif
fprintf(output, "Example: dumpcap -i eth0 -a duration:60 -w output.pcapng\n");
fprintf(output, "\"Capture packets from interface eth0 until 60s passed into output.pcapng\"\n");
fprintf(output, "\n");
fprintf(output, "Use Ctrl-C to stop capturing at any time.\n");
}
/*
* Report an error in command-line arguments.
* If we're a capture child, send a message back to the parent, otherwise
* just print it.
*/
static void
dumpcap_cmdarg_err(const char *fmt, va_list ap)
{
if (capture_child) {
gchar *msg;
/* Generate a 'special format' message back to parent */
msg = ws_strdup_vprintf(fmt, ap);
sync_pipe_errmsg_to_parent(2, msg, "");
g_free(msg);
} else {
fprintf(stderr, "dumpcap: ");
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
}
}
/*
* Report additional information for an error in command-line arguments.
* If we're a capture child, send a message back to the parent, otherwise
* just print it.
*/
static void
dumpcap_cmdarg_err_cont(const char *fmt, va_list ap)
{
if (capture_child) {
gchar *msg;
msg = ws_strdup_vprintf(fmt, ap);
sync_pipe_errmsg_to_parent(2, msg, "");
g_free(msg);
} else {
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
}
}
#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();
ws_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", g_strerror(errno));
}
print_caps("Post-clear");
cap_free(caps);
}
#endif
/*
* Platform-dependent suggestions for fixing permissions.
*/
#if defined(__linux__)
#define PLATFORM_PERMISSIONS_SUGGESTION \
"\n\n" \
"On Debian and Debian derivatives such as Ubuntu, if you have " \
"installed Wireshark from a package, try running" \
"\n\n" \
" sudo dpkg-reconfigure wireshark-common" \
"\n\n" \
"selecting \"<Yes>\" in response to the question" \
"\n\n" \
" Should non-superusers be able to capture packets?" \
"\n\n" \
"adding yourself to the \"wireshark\" group by running" \
"\n\n" \
" sudo usermod -a -G wireshark {your username}" \
"\n\n" \
"and then logging out and logging back in again."
#elif defined(__APPLE__)
#define PLATFORM_PERMISSIONS_SUGGESTION \
"\n\n" \
"If you installed Wireshark using the package from wireshark.org, " \
"close this dialog and click on the \"installing ChmodBPF\" link in " \
"\"You can fix this by installing ChmodBPF.\" on the main screen, " \
"and then complete the installation procedure."
#else
#define PLATFORM_PERMISSIONS_SUGGESTION
#endif
static const char *
get_pcap_failure_secondary_error_message(cap_device_open_status open_status,
const char *open_status_str)
{
#ifdef _WIN32
/*
* On Windows, first make sure they *have* Npcap installed.
*/
if (!has_wpcap) {
return
"In order to capture packets, Npcap or WinPcap must be installed. See\n"
"\n"
" https://npcap.com/\n"
"\n"
"for a downloadable version of Npcap and for instructions on how to\n"
"install it.";
}
#endif
/*
* OK, now just return a largely platform-independent error that might
* have platform-specific suggestions at the end (for example, suggestions
* for how to get permission to capture).
*/
if (open_status == CAP_DEVICE_OPEN_ERR_GENERIC) {
/*
* We don't know what kind of error it is. See if there's a hint
* in the error string; if not, throw all generic suggestions at
* the user.
*/
static const char promisc_failed[] =
"failed to set hardware filter to promiscuous mode";
/*
* Does the error string begin with the error produced by WinPcap
* and Npcap if attempting to set promiscuous mode fails?
* (Note that this string could have a specific error message
* from an NDIS error after the initial part, so we do a prefix
* check rather than an exact match check.)
*/
if (strncmp(open_status_str, promisc_failed, sizeof promisc_failed - 1) == 0) {
/*
* Yes. Suggest that the user turn off promiscuous mode on that
* device.
*/
return
"Please turn off promiscuous mode for this device";
} else {
return
"Please check to make sure you have sufficient permissions, and that you have "
"the proper interface or pipe specified."
PLATFORM_PERMISSIONS_SUGGESTION;
}
} else if (open_status == CAP_DEVICE_OPEN_ERR_PERMISSIONS) {
/*
* This is a permissions error, so no need to specify any other
* warnings.
*/
return
"Please check to make sure you have sufficient permissions."
PLATFORM_PERMISSIONS_SUGGESTION;
} else {
/*
* This is not a permissons error, so no need to suggest
* checking permissions.
*/
return
"Please check that you have the proper interface or pipe specified.";
}
}
static void
get_capture_device_open_failure_messages(cap_device_open_status open_status,
const char *open_status_str,
const char *iface,
char *errmsg, size_t errmsg_len,
char *secondary_errmsg,
size_t secondary_errmsg_len)
{
snprintf(errmsg, (gulong) errmsg_len,
"The capture session could not be initiated on capture device \"%s\" (%s).",
iface, open_status_str);
snprintf(secondary_errmsg, (gulong) secondary_errmsg_len, "%s",
get_pcap_failure_secondary_error_message(open_status, open_status_str));
}
static gboolean
compile_capture_filter(const char *iface, pcap_t *pcap_h,
struct bpf_program *fcode, const 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;
}
/*
* Sigh. Older versions of libpcap don't properly declare the
* third argument to pcap_compile() as a const pointer. Cast
* away the warning.
*/
DIAG_OFF(cast-qual)
if (pcap_compile(pcap_h, fcode, (char *)cfilter, 1, netmask) < 0)
return FALSE;
DIAG_ON(cast-qual)
return TRUE;
}
static gboolean
show_filter_code(capture_options *capture_opts)
{
interface_options *interface_opts;
pcap_t *pcap_h;
cap_device_open_status open_status;
gchar open_status_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;
guint j;
for (j = 0; j < capture_opts->ifaces->len; j++) {
interface_opts = &g_array_index(capture_opts->ifaces, interface_options, j);
pcap_h = open_capture_device(capture_opts, interface_opts,
CAP_READ_TIMEOUT, &open_status, &open_status_str);
if (pcap_h == NULL) {
/* Open failed; get messages */
get_capture_device_open_failure_messages(open_status, open_status_str,
interface_opts->name,
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_datalink(pcap_h, interface_opts->linktype, interface_opts->name,
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(interface_opts->name, pcap_h, &fcode,
interface_opts->cfilter)) {
snprintf(errmsg, sizeof(errmsg), "%s", pcap_geterr(pcap_h));
pcap_close(pcap_h);
report_cfilter_error(capture_opts, j, errmsg);
return FALSE;
}
pcap_close(pcap_h);
/* 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));
}
/* 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
if (capture_child) {
/* Let our parent know we succeeded. */
pipe_write_block(2, SP_SUCCESS, NULL);
}
return TRUE;
}
/*
* 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, void(*update_cb)(void) _U_)
{
return get_interface_list(err, err_str);
}
/*
* Output a machine readable list of the interfaces
* This list is retrieved by the sync_interface_list_open() function
* The actual output of this function can be viewed with the command "dumpcap -D -Z none"
*/
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[WS_INET6_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\t", 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->vendor_description != NULL)
printf("%s\t", if_info->vendor_description);
else
printf("\t");
/* XXX - Make sure our friendly name doesn't contain a tab */
if (if_info->friendly_name != NULL)
printf("%s\t", if_info->friendly_name);
else
printf("\t");
printf("%i\t", if_info->type);
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:
printf("%s", ws_inet_ntop4(&if_addr->addr.ip4_addr, addr_str, sizeof(addr_str)));
break;
case IF_AT_IPv6:
printf("%s", ws_inet_ntop6(&if_addr->addr.ip6_addr, addr_str, sizeof(addr_str)));
break;
default:
printf("<type unknown %i>", if_addr->ifat_type);
}
}
if (if_info->loopback)
printf("\tloopback");
else
printf("\tnetwork");
printf("\t%s", if_info->extcap);
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, int queries)
{
GList *lt_entry, *ts_entry;
const gchar *desc_str;
if (capture_child) {
/* Let our parent know we succeeded. */
pipe_write_block(2, SP_SUCCESS, NULL);
}
if (queries & CAPS_QUERY_LINK_TYPES) {
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_t *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);
}
}
printf("\n");
if (queries & CAPS_QUERY_TIMESTAMP_TYPES) {
for (ts_entry = caps->timestamp_types; ts_entry != NULL;
ts_entry = g_list_next(ts_entry)) {
timestamp_info_t *timestamp = (timestamp_info_t *)ts_entry->data;
if (timestamp->description != NULL)
desc_str = timestamp->description;
else
desc_str = "(none)";
printf("%s\t%s\n", timestamp->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) {
if (err == 0)
cmdarg_err("There are no interfaces on which a capture can be done");
else {
cmdarg_err("%s", err_str);
g_free(err_str);
}
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 __linux__
/* On Linux nf* interfaces don't collect stats properly and don't allows multiple
* connections. We avoid collecting stats on them.
*/
if (!strncmp(if_info->name, "nf", 2)) {
ws_debug("Skipping interface %s for stats", if_info->name);
continue;
}
#endif
#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 = g_new(if_stat_t, 1);
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 we have a dummy signal pipe check it */
if (!signal_pipe_check_running()) {
global_ld.go = FALSE;
}
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_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
is also sort of like SIGHUP, and CTRL_SHUTDOWN_EVENT is sort of
like SIGTERM at least when the machine's shutting down.
For now, if we're running as a command rather than a capture child,
we handle all but CTRL_LOGOFF_EVENT as indications that we should
clean up and quit, just as we handle SIGINT, SIGHUP, and SIGTERM
in that way on UN*X.
If we're not running as a capture child, we might be running as
a service; ignore CTRL_LOGOFF_EVENT, so we keep running after the
user logs out. (XXX - can we explicitly check whether we're
running as a service?) */
ws_info("Console: Control signal");
ws_debug("Console: Control signal, CtrlType: %lu", dwCtrlType);
/* Keep capture running if we're a service and a user logs off */
if (capture_child || (dwCtrlType != CTRL_LOGOFF_EVENT)) {
capture_loop_stop();
return TRUE;
} else {
return FALSE;
}
}
#else
static void
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. */
capture_loop_stop();
}
#endif
static void
report_capture_count(gboolean reportit)
{
/* Don't print this if we're a capture child. */
if (!capture_child && reportit) {
fprintf(stderr, "\rPackets captured: %d\n", global_ld.packets_captured);
/* stderr could be line buffered */
fflush(stderr);
}
}
#ifdef SIGINFO
static void
report_counts_for_siginfo(void)
{
report_capture_count(quiet);
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_for_siginfo();
errno = sav_errno;
}
#endif /* SIGINFO */
static void
exit_main(int status)
{
ws_cleanup_sockets();
#ifdef _WIN32
/* can be helpful for debugging */
#ifdef DEBUG_DUMPCAP
printf("Press any key\n");
_getch();
#endif
#endif /* _WIN32 */
if (ringbuf_is_initialized()) {
/* save_file is managed by ringbuffer, be sure to release the memory and
* avoid capture_opts_cleanup from double-freeing 'save_file'. */
ringbuf_free();
global_capture_opts.save_file = NULL;
}
capture_opts_cleanup(&global_capture_opts);
exit(status);
}
#ifdef HAVE_LIBCAP
/*
* If we were linked with libcap (not related to libpcap), make sure we have
* CAP_NET_ADMIN and CAP_NET_RAW, then relinquish our permissions.
* (See comment in main() for details)
*/
static void
relinquish_privs_except_capture(void)
{
/* If 'started_with_special_privs' (ie: suid) then enable for
* ourself the NET_ADMIN and NET_RAW capabilities and then
* drop our suid privileges.
*
* CAP_NET_ADMIN: Promiscuous mode and a truckload of other
* stuff we don't need (and shouldn't have).
* CAP_NET_RAW: Packet capture (raw sockets).
*/
if (started_with_special_privs()) {
cap_value_t cap_list[2] = { CAP_NET_ADMIN, CAP_NET_RAW };
int cl_len = sizeof(cap_list) / sizeof(cap_value_t);
cap_t caps = cap_init(); /* all capabilities initialized to off */
print_caps("Pre drop, pre set");
if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) {
cmdarg_err("prctl() fail return: %s", g_strerror(errno));
}
cap_set_flag(caps, CAP_PERMITTED, cl_len, cap_list, CAP_SET);
cap_set_flag(caps, CAP_INHERITABLE, cl_len, cap_list, CAP_SET);
if (cap_set_proc(caps)) {
cmdarg_err("cap_set_proc() fail return: %s", g_strerror(errno));
}
print_caps("Pre drop, post set");
relinquish_special_privs_perm();
print_caps("Post drop, pre set");
cap_set_flag(caps, CAP_EFFECTIVE, cl_len, cap_list, CAP_SET);
if (cap_set_proc(caps)) {
cmdarg_err("cap_set_proc() fail return: %s", g_strerror(errno));
}
print_caps("Post drop, post set");
cap_free(caps);
}
}
#endif /* HAVE_LIBCAP */
/* Take care of byte order in the libpcap headers read from pipes.
* (function taken from wiretap/libpcap.c) */
static void
cap_pipe_adjust_pcap_header(gboolean byte_swapped, struct pcap_hdr *hdr, struct pcaprec_hdr *rechdr)
{
if (byte_swapped) {
/* Byte-swap the record header fields. */
rechdr->ts_sec = GUINT32_SWAP_LE_BE(rechdr->ts_sec);
rechdr->ts_usec = GUINT32_SWAP_LE_BE(rechdr->ts_usec);
rechdr->incl_len = GUINT32_SWAP_LE_BE(rechdr->incl_len);
rechdr->orig_len = GUINT32_SWAP_LE_BE(rechdr->orig_len);
}
/* In file format version 2.3, the "incl_len" and "orig_len" fields were
swapped, in order to match the BPF header layout.
Unfortunately, some files were, according to a comment in the "libpcap"
source, written with version 2.3 in their headers but without the
interchanged fields, so if "incl_len" is greater than "orig_len" - which
would make no sense - we assume that we need to swap them. */
if (hdr->version_major == 2 &&
(hdr->version_minor < 3 ||
(hdr->version_minor == 3 && rechdr->incl_len > rechdr->orig_len))) {
guint32 temp;
temp = rechdr->orig_len;
rechdr->orig_len = rechdr->incl_len;
rechdr->incl_len = temp;
}
}
/* Wrapper: distinguish between recv/read if we're reading on Windows,
* or just read().
*/
static ssize_t
cap_pipe_read(int pipe_fd, char *buf, size_t sz, gboolean from_socket _U_)
{
#ifdef _WIN32
if (from_socket) {
return recv(pipe_fd, buf, (int)sz, 0);
} else {
return -1;
}
#else
return ws_read(pipe_fd, buf, sz);
#endif
}
#if defined(_WIN32)
/*
* 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).
*
* We might want to move some of the cap_pipe_dispatch logic here so that
* we can let cap_thread_read run independently, queuing up multiple reads
* for the main thread (and possibly get rid of cap_pipe_read_mtx).
*/
static void *cap_thread_read(void *arg)
{
capture_src *pcap_src;
#ifdef _WIN32
BOOL res;
DWORD last_err, bytes_read;
#else /* _WIN32 */
size_t bytes_read;
#endif /* _WIN32 */
pcap_src = (capture_src *)arg;
while (pcap_src->cap_pipe_err == PIPOK) {
g_async_queue_pop(pcap_src->cap_pipe_pending_q); /* Wait for our cue (ahem) from the main thread */
g_mutex_lock(pcap_src->cap_pipe_read_mtx);
bytes_read = 0;
while (bytes_read < pcap_src->cap_pipe_bytes_to_read) {
if ((pcap_src->from_cap_socket)
#ifndef _WIN32
|| 1
#endif
)
{
ssize_t b;
b = cap_pipe_read(pcap_src->cap_pipe_fd, pcap_src->cap_pipe_buf+bytes_read,
pcap_src->cap_pipe_bytes_to_read - bytes_read, pcap_src->from_cap_socket);
if (b <= 0) {
if (b == 0) {
pcap_src->cap_pipe_err = PIPEOF;
bytes_read = 0;
break;
} else {
pcap_src->cap_pipe_err = PIPERR;
bytes_read = -1;
break;
}
} else {
bytes_read += (DWORD)b;
}
}
#ifdef _WIN32
else
{
/* If we try to use read() on a named pipe on Windows with partial
* data it appears to return EOF.
*/
DWORD b;
res = ReadFile(pcap_src->cap_pipe_h, pcap_src->cap_pipe_buf+bytes_read,
pcap_src->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) {
pcap_src->cap_pipe_err = PIPEOF;
bytes_read = 0;
break;
}
pcap_src->cap_pipe_err = PIPERR;
bytes_read = -1;
break;
} else if (b == 0 && pcap_src->cap_pipe_bytes_to_read > 0) {
pcap_src->cap_pipe_err = PIPEOF;
bytes_read = 0;
break;
}
}
#endif /*_WIN32 */
}
pcap_src->cap_pipe_bytes_read = bytes_read;
if (pcap_src->cap_pipe_bytes_read >= pcap_src->cap_pipe_bytes_to_read) {
g_async_queue_push(pcap_src->cap_pipe_done_q, pcap_src->cap_pipe_buf); /* Any non-NULL value will do */
}
g_mutex_unlock(pcap_src->cap_pipe_read_mtx);
}
/* Post to queue if we didn't read enough data as the main thread waits for the message */
g_mutex_lock(pcap_src->cap_pipe_read_mtx);
if (pcap_src->cap_pipe_bytes_read < pcap_src->cap_pipe_bytes_to_read) {
/* There's still more of the record to read. */
g_async_queue_push(pcap_src->cap_pipe_done_q, pcap_src->cap_pipe_buf); /* Any non-NULL value will do */
}
g_mutex_unlock(pcap_src->cap_pipe_read_mtx);
return NULL;
}
/*
* Do a blocking read from a pipe within the main thread, by pushing
* the read onto the pipe queue and then popping it off that queue;
* the pipe will block until the pushed read completes.
*
* We do it with another thread because we can't use select() on
* pipes on Windows, as we can on UN*Xes, we can only use it on
* sockets.
*/
void
pipe_read_sync(capture_src *pcap_src, void *buf, DWORD nbytes)
{
pcap_src->cap_pipe_buf = (char *) buf;
pcap_src->cap_pipe_bytes_read = 0;
pcap_src->cap_pipe_bytes_to_read = nbytes;
/* We don't have to worry about cap_pipe_read_mtx here */
g_async_queue_push(pcap_src->cap_pipe_pending_q, pcap_src->cap_pipe_buf);
g_async_queue_pop(pcap_src->cap_pipe_done_q);
}
#endif
/* 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.
*/
static int
cap_pipe_select(int pipe_fd)
{
fd_set rfds;
struct timeval timeout;
FD_ZERO(&rfds);
FD_SET(pipe_fd, &rfds);
timeout.tv_sec = PIPE_READ_TIMEOUT / 1000000;
timeout.tv_usec = PIPE_READ_TIMEOUT % 1000000;
return select(pipe_fd+1, &rfds, NULL, NULL, &timeout);
}
#define DEF_TCP_PORT 19000
static int
cap_open_socket(char *pipename, capture_src *pcap_src, char *errmsg, size_t errmsgl)
{
struct sockaddr_storage sa;
socklen_t sa_len;
int fd;
/* Skip the initial "TCP@" in the pipename. */
if (ws_socket_ptoa(&sa, pipename + 4, DEF_TCP_PORT) < 0) {
snprintf(errmsg, (gulong)errmsgl,
"The capture session could not be initiated because"
"\"%s\" is not a valid socket specification", pipename);
pcap_src->cap_pipe_err = PIPERR;
return -1;
}
if ((fd = (int)socket(sa.ss_family, SOCK_STREAM, 0)) < 0) {
snprintf(errmsg, (gulong)errmsgl,
"The capture session could not be initiated because"
" the socket couldn't be created due to the socket error: \n"
#ifdef _WIN32
" %s", win32strerror(WSAGetLastError()));
#else
" %d: %s", errno, g_strerror(errno));
#endif
pcap_src->cap_pipe_err = PIPERR;
return -1;
}
if (sa.ss_family == AF_INET6)
sa_len = sizeof(struct sockaddr_in6);
else
sa_len = sizeof(struct sockaddr_in);
if (connect(fd, (struct sockaddr *)&sa, sa_len) < 0) {
snprintf(errmsg, (gulong)errmsgl,
"The capture session could not be initiated because"
" the socket couldn't be connected due to the socket error: \n"
#ifdef _WIN32
" %s", win32strerror(WSAGetLastError()));
#else
" %d: %s", errno, g_strerror(errno));
#endif
pcap_src->cap_pipe_err = PIPERR;
cap_pipe_close(fd, TRUE);
return -1;
}
pcap_src->from_cap_socket = TRUE;
return fd;
}
/* Wrapper: distinguish between closesocket on Windows; use ws_close
* otherwise.
*/
static void
cap_pipe_close(int pipe_fd, gboolean from_socket)
{
#ifdef _WIN32
if (from_socket) {
closesocket(pipe_fd);
}
#else
(void) from_socket; /* Mark unused, similar to Q_UNUSED */
ws_close(pipe_fd);
#endif
}
/** Read bytes from a capture source, which is assumed to be a pipe or
* socket.
*
* Returns -1, or the number of bytes read similar to read(2).
* Sets pcap_src->cap_pipe_err on error or EOF.
*/
static ssize_t
cap_pipe_read_data_bytes(capture_src *pcap_src, char *errmsg, size_t errmsgl)
{
int sel_ret;
int fd = pcap_src->cap_pipe_fd;
#ifdef _WIN32
DWORD sz, bytes_read = 0;
#else /* _WIN32 */
ssize_t sz, bytes_read = 0;
#endif /* _WIN32 */
ssize_t b;
#ifdef LOG_CAPTURE_VERBOSE
ws_debug("cap_pipe_read_data_bytes read %lu of %lu",
pcap_src->cap_pipe_bytes_read, pcap_src->cap_pipe_bytes_to_read);
#endif
sz = pcap_src->cap_pipe_bytes_to_read - pcap_src->cap_pipe_bytes_read;
while (bytes_read < sz) {
if (fd == -1) {
snprintf(errmsg, (gulong)errmsgl, "Invalid file descriptor.");
pcap_src->cap_pipe_err = PIPNEXIST;
return -1;
}
sel_ret = cap_pipe_select(fd);
if (sel_ret < 0) {
snprintf(errmsg, (gulong)errmsgl,
"Unexpected error from select: %s.", g_strerror(errno));
pcap_src->cap_pipe_err = PIPERR;
return -1;
} else if (sel_ret > 0) {
b = cap_pipe_read(fd, pcap_src->cap_pipe_databuf+pcap_src->cap_pipe_bytes_read+bytes_read,
sz-bytes_read, pcap_src->from_cap_socket);
if (b <= 0) {
if (b == 0) {
snprintf(errmsg, (gulong)errmsgl,
"End of file reading from pipe or socket.");
pcap_src->cap_pipe_err = PIPEOF;
} else {
#ifdef _WIN32
/*
* On Windows, we only do this for sockets.
*/
DWORD lastError = WSAGetLastError();
errno = lastError;
snprintf(errmsg, (gulong)errmsgl,
"Error reading from pipe or socket: %s.",
win32strerror(lastError));
#else
snprintf(errmsg, (gulong)errmsgl,
"Error reading from pipe or socket: %s.",
g_strerror(errno));
#endif
pcap_src->cap_pipe_err = PIPERR;
}
return -1;
}
#ifdef _WIN32
bytes_read += (DWORD)b;
#else
bytes_read += b;
#endif
}
}
pcap_src->cap_pipe_bytes_read += bytes_read;
#ifdef LOG_CAPTURE_VERBOSE
ws_debug("cap_pipe_read_data_bytes read %lu of %lu",
pcap_src->cap_pipe_bytes_read, pcap_src->cap_pipe_bytes_to_read);
#endif
return bytes_read;
}
/* Some forward declarations for breaking up cap_pipe_open_live for pcap and pcapng formats */
static void pcap_pipe_open_live(int fd, capture_src *pcap_src,
struct pcap_hdr *hdr,
char *errmsg, size_t errmsgl,
char *secondary_errmsg, size_t secondary_errmsgl);
static void pcapng_pipe_open_live(int fd, capture_src *pcap_src,
char *errmsg, size_t errmsgl);
static int pcapng_pipe_dispatch(loop_data *ld, capture_src *pcap_src,
char *errmsg, size_t errmsgl);
/* For problems that are probably Not Our Fault. */
static char not_our_bug[] =
"Please report this to the developers of the program writing to the pipe.";
/* Mimic pcap_open_live() for pipe captures
* 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 void
cap_pipe_open_live(char *pipename,
capture_src *pcap_src,
void *hdr,
char *errmsg, size_t errmsgl,
char *secondary_errmsg, size_t secondary_errmsgl)
{
#ifndef _WIN32
ws_statb64 pipe_stat;
struct sockaddr_un sa;
#else /* _WIN32 */
char *pncopy, *pos;
guintptr extcap_pipe_handle;
#endif
gboolean extcap_pipe = FALSE;
ssize_t b;
int fd = -1, sel_ret;
size_t bytes_read;
guint32 magic = 0;
pcap_src->cap_pipe_fd = -1;
#ifdef _WIN32
pcap_src->cap_pipe_h = INVALID_HANDLE_VALUE;
#endif
ws_debug("cap_pipe_open_live: %s", pipename);
/*
* 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 */
#else /* _WIN32 */
pcap_src->cap_pipe_h = GetStdHandle(STD_INPUT_HANDLE);
#endif /* _WIN32 */
} else if (!strncmp(pipename, "TCP@", 4)) {
if ((fd = cap_open_socket(pipename, pcap_src, errmsg, errmsgl)) < 0) {
return;
}
} else {
#ifndef _WIN32
if ( g_strrstr(pipename, EXTCAP_PIPE_PREFIX) != NULL )
extcap_pipe = TRUE;
if (ws_stat64(pipename, &pipe_stat) < 0) {
if (errno == ENOENT || errno == ENOTDIR)
pcap_src->cap_pipe_err = PIPNEXIST;
else {
snprintf(errmsg, (gulong)errmsgl,
"The capture session could not be initiated "
"due to error getting information on pipe or socket: %s.", g_strerror(errno));
pcap_src->cap_pipe_err = PIPERR;
}
return;
}
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) {
snprintf(errmsg, (gulong)errmsgl,
"The capture session could not be initiated "
"due to error on pipe open: %s.", g_strerror(errno));
pcap_src->cap_pipe_err = PIPERR;
return;
}
} else if (S_ISSOCK(pipe_stat.st_mode)) {
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd == -1) {
snprintf(errmsg, (gulong)errmsgl,
"The capture session could not be initiated "
"due to error on socket create: %s.", g_strerror(errno));
pcap_src->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 */
snprintf(errmsg, (gulong)errmsgl,
"The capture session coud not be initiated "
"due to error on socket connect: Path name too long.");
pcap_src->cap_pipe_err = PIPERR;
ws_close(fd);
return;
}
b = connect(fd, (struct sockaddr *)&sa, sizeof sa);
if (b == -1) {
snprintf(errmsg, (gulong)errmsgl,
"The capture session coud not be initiated "
"due to error on socket connect: %s.", g_strerror(errno));
pcap_src->cap_pipe_err = PIPERR;
ws_close(fd);
return;
}
} else {
if (S_ISCHR(pipe_stat.st_mode)) {
/*
* Assume the user specified an interface on a system where
* interfaces are in /dev. Pretend we haven't seen it.
*/
pcap_src->cap_pipe_err = PIPNEXIST;
} else {
snprintf(errmsg, (gulong)errmsgl,
"The capture session could not be initiated because\n"
"\"%s\" is neither an interface nor a socket nor a pipe.", pipename);
pcap_src->cap_pipe_err = PIPERR;
}
return;
}
#else /* _WIN32 */
if (sscanf(pipename, EXTCAP_PIPE_PREFIX "%" SCNuPTR, &extcap_pipe_handle) == 1)
{
/* The client is already connected to extcap pipe.
* We have inherited the handle from parent process.
*/
extcap_pipe = TRUE;
pcap_src->cap_pipe_h = (HANDLE)extcap_pipe_handle;
}
else
{
#define PIPE_STR "\\pipe\\"
/* Under Windows, named pipes _must_ have the form
* "\\<server>\pipe\<pipename>". <server> may be "." for localhost.
*/
pncopy = g_strdup(pipename);
if ((pos = strstr(pncopy, "\\\\")) == pncopy) {
pos = strchr(pncopy + 3, '\\');
if (pos && g_ascii_strncasecmp(pos, PIPE_STR, strlen(PIPE_STR)) != 0)
pos = NULL;
}
g_free(pncopy);
if (!pos) {
snprintf(errmsg, (gulong)errmsgl,
"The capture session could not be initiated because\n"
"\"%s\" is neither an interface nor a pipe.", pipename);
pcap_src->cap_pipe_err = PIPNEXIST;
return;
}
/* Wait for the pipe to appear */
while (1) {
pcap_src->cap_pipe_h = CreateFile(utf_8to16(pipename), GENERIC_READ, 0, NULL,
OPEN_EXISTING, 0, NULL);
if (pcap_src->cap_pipe_h != INVALID_HANDLE_VALUE)
break;
if (GetLastError() != ERROR_PIPE_BUSY) {
snprintf(errmsg, (gulong)errmsgl,
"The capture session on \"%s\" could not be started "
"due to error on pipe open: %s.",
pipename, win32strerror(GetLastError()));
pcap_src->cap_pipe_err = PIPERR;
return;
}
if (!WaitNamedPipe(utf_8to16(pipename), 30 * 1000)) {
snprintf(errmsg, (gulong)errmsgl,
"The capture session on \"%s\" timed out during "
"pipe open: %s.",
pipename, win32strerror(GetLastError()));
pcap_src->cap_pipe_err = PIPERR;
return;
}
}
}
#endif /* _WIN32 */
}
pcap_src->from_cap_pipe = TRUE;
/*
* We start with a 2KB buffer for packet data, which should be
* large enough for most regular network packets. We increase it,
* up to the maximum size we allow, as necessary.
*/
pcap_src->cap_pipe_databuf = (char*)g_malloc(2048);
pcap_src->cap_pipe_databuf_size = 2048;
/*
* Read the first 4 bytes of data from the pipe.
*
* If a pcap file is being written to it, that will be
* the pcap magic number.
*
* If a pcapng file is being written to it, that will be
* the block type of the initial SHB.
*/
#ifdef _WIN32
/*
* On UN*X, we can use select() on pipes or sockets.
*
* On Windows, we can only use it on sockets; to do non-blocking
* reads from pipes, we currently do reads in a separate thread
* and use GLib asynchronous queues from the main thread to start
* read operations and to wait for them to complete.
*/
if (pcap_src->from_cap_socket)
#endif
{
bytes_read = 0;
while (bytes_read < sizeof magic) {
sel_ret = cap_pipe_select(fd);
if (sel_ret < 0) {
snprintf(errmsg, (gulong)errmsgl,
"Unexpected error from select: %s.",
g_strerror(errno));
goto error;
} else if (sel_ret > 0) {
b = cap_pipe_read(fd, ((char *)&magic)+bytes_read,
sizeof magic-bytes_read,
pcap_src->from_cap_socket);
/* jump messaging, if extcap had an error, stderr will provide the correct message */
if (extcap_pipe && b <= 0)
goto error;
if (b <= 0) {
if (b == 0)
snprintf(errmsg, (gulong)errmsgl,
"End of file on pipe magic during open.");
else
snprintf(errmsg, (gulong)errmsgl,
"Error on pipe magic during open: %s.",
g_strerror(errno));
goto error;
}
bytes_read += b;
}
}
}
#ifdef _WIN32
else {
/* Create a thread to read from this pipe */
g_thread_new("cap_pipe_open_live", &cap_thread_read, pcap_src);
pipe_read_sync(pcap_src, &magic, sizeof(magic));
/* jump messaging, if extcap had an error, stderr will provide the correct message */
if (pcap_src->cap_pipe_bytes_read <= 0 && extcap_pipe)
goto error;
if (pcap_src->cap_pipe_bytes_read <= 0) {
if (pcap_src->cap_pipe_bytes_read == 0)
snprintf(errmsg, (gulong)errmsgl,
"End of file on pipe magic during open.");
else
snprintf(errmsg, (gulong)errmsgl,
"Error on pipe magic during open: %s.",
g_strerror(errno));
goto error;
}
}
#endif
switch (magic) {
case PCAP_MAGIC:
case PCAP_NSEC_MAGIC:
/* This is a pcap file.
The host that wrote it has our byte order, and was running
a program using either standard or ss990417 libpcap. */
pcap_src->cap_pipe_info.pcap.byte_swapped = FALSE;
pcap_src->cap_pipe_modified = FALSE;
pcap_src->ts_nsec = magic == PCAP_NSEC_MAGIC;
break;
case PCAP_MODIFIED_MAGIC:
/* This is a pcap file.
The host that wrote it has our byte order, but was running
a program using either ss990915 or ss991029 libpcap. */
pcap_src->cap_pipe_info.pcap.byte_swapped = FALSE;
pcap_src->cap_pipe_modified = TRUE;
break;
case PCAP_SWAPPED_MAGIC:
case PCAP_SWAPPED_NSEC_MAGIC:
/* This is a pcap file.
The host that wrote it has a byte order opposite to ours,
and was running a program using either standard or
ss990417 libpcap. */
pcap_src->cap_pipe_info.pcap.byte_swapped = TRUE;
pcap_src->cap_pipe_modified = FALSE;
pcap_src->ts_nsec = magic == PCAP_SWAPPED_NSEC_MAGIC;
break;
case PCAP_SWAPPED_MODIFIED_MAGIC:
/* This is a pcap file.
The host that wrote it out has a byte order opposite to
ours, and was running a program using either ss990915
or ss991029 libpcap. */
pcap_src->cap_pipe_info.pcap.byte_swapped = TRUE;
pcap_src->cap_pipe_modified = TRUE;
break;
case BLOCK_TYPE_SHB:
/* This is a pcapng file. */
pcap_src->from_pcapng = TRUE;
pcap_src->cap_pipe_dispatch = pcapng_pipe_dispatch;
pcap_src->cap_pipe_info.pcapng.src_iface_to_global = g_array_new(FALSE, FALSE, sizeof(guint32));
global_capture_opts.use_pcapng = TRUE; /* we can only output in pcapng format */
break;
default:
/* Not a pcapng file, and either not a pcap type we know about
or not a pcap file, either. */
snprintf(errmsg, (gulong)errmsgl,
"File type is neither a supported pcap nor pcapng format. (magic = 0x%08x)", magic);
snprintf(secondary_errmsg, (gulong)secondary_errmsgl, "%s",
not_our_bug);
goto error;
}
if (pcap_src->from_pcapng)
pcapng_pipe_open_live(fd, pcap_src, errmsg, errmsgl);
else
pcap_pipe_open_live(fd, pcap_src, (struct pcap_hdr *) hdr, errmsg, errmsgl,
secondary_errmsg, secondary_errmsgl);
return;
error:
ws_debug("cap_pipe_open_live: error %s", errmsg);
pcap_src->cap_pipe_err = PIPERR;
cap_pipe_close(fd, pcap_src->from_cap_socket);
pcap_src->cap_pipe_fd = -1;
#ifdef _WIN32
pcap_src->cap_pipe_h = INVALID_HANDLE_VALUE;
#endif
}
/*
* Read the part of the pcap file header that follows the magic
* number (we've already read the magic number).
*/
static void
pcap_pipe_open_live(int fd,
capture_src *pcap_src,
struct pcap_hdr *hdr,
char *errmsg, size_t errmsgl,
char *secondary_errmsg, size_t secondary_errmsgl)
{
size_t bytes_read;
ssize_t b;
int sel_ret;
/*
* We're reading from a pcap file. We've already read the magic
* number; read the rest of the header.
*
* (Note that struct pcap_hdr is a structure for the part of a
* pcap file header *following the magic number*; it does not
* include the magic number itself.)
*/
#ifdef _WIN32
if (pcap_src->from_cap_socket)
#endif
{
/* Keep reading until we get the rest of the header. */
bytes_read = 0;
while (bytes_read < sizeof(struct pcap_hdr)) {
sel_ret = cap_pipe_select(fd);
if (sel_ret < 0) {
snprintf(errmsg, (gulong)errmsgl,
"Unexpected error from select: %s.",
g_strerror(errno));
goto error;
} else if (sel_ret > 0) {
b = cap_pipe_read(fd, ((char *)hdr)+bytes_read,
sizeof(struct pcap_hdr) - bytes_read,
pcap_src->from_cap_socket);
if (b <= 0) {
if (b == 0)
snprintf(errmsg, (gulong)errmsgl,
"End of file on pipe header during open.");
else
snprintf(errmsg, (gulong)errmsgl,
"Error on pipe header during open: %s.",
g_strerror(errno));
snprintf(secondary_errmsg, (gulong)secondary_errmsgl,
"%s", not_our_bug);
goto error;
}
bytes_read += b;
}
}
}
#ifdef _WIN32
else {
pipe_read_sync(pcap_src, hdr, sizeof(struct pcap_hdr));
if (pcap_src->cap_pipe_bytes_read <= 0) {
if (pcap_src->cap_pipe_bytes_read == 0)
snprintf(errmsg, (gulong)errmsgl,
"End of file on pipe header during open.");
else
snprintf(errmsg, (gulong)errmsgl,
"Error on pipe header header during open: %s.",
g_strerror(errno));
snprintf(secondary_errmsg, (gulong)secondary_errmsgl, "%s",
not_our_bug);
goto error;
}
}
#endif
if (pcap_src->cap_pipe_info.pcap.byte_swapped) {
/* Byte-swap the header fields about which we care. */
hdr->version_major = GUINT16_SWAP_LE_BE(hdr->version_major);
hdr->version_minor = GUINT16_SWAP_LE_BE(hdr->version_minor);
hdr->snaplen = GUINT32_SWAP_LE_BE(hdr->snaplen);
hdr->network = GUINT32_SWAP_LE_BE(hdr->network);
}
pcap_src->linktype = hdr->network;
/* Pick the appropriate maximum packet size for the link type */
switch (pcap_src->linktype) {
case 231: /* DLT_DBUS */
pcap_src->cap_pipe_max_pkt_size = WTAP_MAX_PACKET_SIZE_DBUS;
break;
case 279: /* DLT_EBHSCR */
pcap_src->cap_pipe_max_pkt_size = WTAP_MAX_PACKET_SIZE_EBHSCR;
break;
case 249: /* DLT_USBPCAP */
pcap_src->cap_pipe_max_pkt_size = WTAP_MAX_PACKET_SIZE_USBPCAP;
break;
default:
pcap_src->cap_pipe_max_pkt_size = WTAP_MAX_PACKET_SIZE_STANDARD;
break;
}
if (hdr->version_major < 2) {
snprintf(errmsg, (gulong)errmsgl,
"The old pcap format version %d.%d is not supported.",
hdr->version_major, hdr->version_minor);
snprintf(secondary_errmsg, (gulong)secondary_errmsgl, "%s",
not_our_bug);
goto error;
}
pcap_src->cap_pipe_fd = fd;
return;
error:
ws_debug("pcap_pipe_open_live: error %s", errmsg);
pcap_src->cap_pipe_err = PIPERR;
cap_pipe_close(fd, pcap_src->from_cap_socket);
pcap_src->cap_pipe_fd = -1;
#ifdef _WIN32
pcap_src->cap_pipe_h = INVALID_HANDLE_VALUE;
#endif
}
/*
* Synchronously read the fixed portion of the pcapng section header block
* (we've already read the pcapng block header).
*/
static int
pcapng_read_shb(capture_src *pcap_src,
char *errmsg,
size_t errmsgl)
{
pcapng_section_header_block_t shb;
#ifdef _WIN32
if (pcap_src->from_cap_socket)
#endif
{
pcap_src->cap_pipe_bytes_to_read = sizeof(pcapng_block_header_t) + sizeof(pcapng_section_header_block_t);
if (cap_pipe_read_data_bytes(pcap_src, errmsg, errmsgl) < 0) {
return -1;
}
}
#ifdef _WIN32
else {
pipe_read_sync(pcap_src, pcap_src->cap_pipe_databuf + sizeof(pcapng_block_header_t),
sizeof(pcapng_section_header_block_t));
if (pcap_src->cap_pipe_bytes_read <= 0) {
if (pcap_src->cap_pipe_bytes_read == 0)
snprintf(errmsg, (gulong)errmsgl,
"End of file reading from pipe or socket.");
else
snprintf(errmsg, (gulong)errmsgl,
"Error reading from pipe or socket: %s.",
g_strerror(errno));
return -1;
}
/* Continuing with STATE_EXPECT_DATA requires reading into cap_pipe_databuf at offset cap_pipe_bytes_read */
pcap_src->cap_pipe_bytes_read = sizeof(pcapng_block_header_t) + sizeof(pcapng_section_header_block_t);
}
#endif
memcpy(&shb, pcap_src->cap_pipe_databuf + sizeof(pcapng_block_header_t), sizeof(pcapng_section_header_block_t));
switch (shb.magic)
{
case PCAPNG_MAGIC:
ws_debug("pcapng SHB MAGIC");
break;
case PCAPNG_SWAPPED_MAGIC:
ws_debug("pcapng SHB SWAPPED MAGIC");
/*
* pcapng sources can contain all sorts of block types.
* Rather than add a bunch of complexity to this code (which is
* often privileged), punt and tell the user to swap bytes
* elsewhere.
*
* XXX - punting means that the Wireshark test suite must be
* modified to:
*
* 1) have both little-endian and big-endian versions of
* all pcapng files piped to dumpcap;
*
* 2) pipe the appropriate file to dumpcap, depending on
* the byte order of the host on which the tests are
* being run;
*
* as per comments in bug 15772 and 15754.
*
* Are we *really* certain that the complexity added would be
* significant enough to make adding it a security risk? And
* why would this code even be running with any elevated
* privileges if you're capturing from a pipe? We should not
* only have given up all additional privileges if we're reading
* from a pipe, we should give them up in such a fashion that
* we can't reclaim them.
*/
#if G_BYTE_ORDER == G_BIG_ENDIAN
#define OUR_ENDIAN "big"
#define IFACE_ENDIAN "little"
#else
#define OUR_ENDIAN "little"
#define IFACE_ENDIAN "big"
#endif
snprintf(errmsg, (gulong)errmsgl,
"Interface %u is " IFACE_ENDIAN " endian but we're " OUR_ENDIAN " endian.",
pcap_src->interface_id);
return -1;
default:
/* Not a pcapng type we know about, or not pcapng at all. */
snprintf(errmsg, (gulong)errmsgl,
"Unrecognized pcapng format or not pcapng data.");
return -1;
}
pcap_src->cap_pipe_max_pkt_size = WTAP_MAX_PACKET_SIZE_STANDARD;
/* Setup state to capture any options following the section header block */
pcap_src->cap_pipe_state = STATE_EXPECT_DATA;
return 0;
}
/*
* Save IDB blocks for playback whenever we change output files.
* Rewrite EPB and ISB interface IDs.
*/
static gboolean
pcapng_adjust_block(capture_src *pcap_src, const pcapng_block_header_t *bh, u_char *pd)
{
switch(bh->block_type) {
case BLOCK_TYPE_SHB:
{
if (global_ld.pcapng_passthrough) {
/*
* We have a single pcapng input. We pass the SHB through when
* writing a single output file and for the first ring buffer
* file. We need to save it for the second and subsequent ring
* buffer files.
*/
g_free(global_ld.saved_shb);
global_ld.saved_shb = (guint8 *) g_memdup2(pd, bh->block_total_length);
/*
* We're dealing with one section at a time, so we can (and must)
* get rid of our old IDBs.
*/
for (unsigned i = 0; i < global_ld.saved_idbs->len; i++) {
saved_idb_t *idb_source = &g_array_index(global_ld.saved_idbs, saved_idb_t, i);
g_free(idb_source->idb);
}
g_array_set_size(global_ld.saved_idbs, 0);
} else {
/*
* We have a new SHB from this capture source. We need to keep
* global_ld.saved_idbs intact, so we mark IDBs we previously
* collected from this source as deleted.
*/
for (unsigned i = 0; i < pcap_src->cap_pipe_info.pcapng.src_iface_to_global->len; i++) {
guint32 iface_id = g_array_index(pcap_src->cap_pipe_info.pcapng.src_iface_to_global, guint32, i);
saved_idb_t *idb_source = &g_array_index(global_ld.saved_idbs, saved_idb_t, iface_id);
ws_assert(idb_source->interface_id == pcap_src->interface_id);
g_free(idb_source->idb);
memset(idb_source, 0, sizeof(saved_idb_t));
idb_source->deleted = TRUE;
ws_debug("%s: deleted pcapng IDB %u", G_STRFUNC, iface_id);
}
}
g_array_set_size(pcap_src->cap_pipe_info.pcapng.src_iface_to_global, 0);
}
break;
case BLOCK_TYPE_IDB:
{
/*
* Always gather IDBs. We can remove them or mark them as deleted
* when we get a new SHB.
*/
saved_idb_t idb_source = { 0 };
idb_source.interface_id = pcap_src->interface_id;
idb_source.idb_len = bh->block_total_length;
idb_source.idb = (guint8 *) g_memdup2(pd, idb_source.idb_len);
g_array_append_val(global_ld.saved_idbs, idb_source);
guint32 iface_id = global_ld.saved_idbs->len - 1;
g_array_append_val(pcap_src->cap_pipe_info.pcapng.src_iface_to_global, iface_id);
ws_debug("%s: mapped pcapng IDB %u -> %u from source %u",
G_STRFUNC, pcap_src->cap_pipe_info.pcapng.src_iface_to_global->len - 1, iface_id, pcap_src->interface_id);
}
break;
case BLOCK_TYPE_EPB:
case BLOCK_TYPE_ISB:
{
if (global_ld.pcapng_passthrough) {
/* Our input and output interface IDs are the same. */
break;
}
/* The interface ID is the first 32-bit field after the BH for both EPBs and ISBs. */
guint32 iface_id;
memcpy(&iface_id, pd + sizeof(pcapng_block_header_t), 4);
if (iface_id < pcap_src->cap_pipe_info.pcapng.src_iface_to_global->len) {
memcpy(pd + sizeof(pcapng_block_header_t),
&g_array_index(pcap_src->cap_pipe_info.pcapng.src_iface_to_global, guint32, iface_id), 4);
} else {
ws_debug("%s: pcapng EPB or ISB interface id %u > max %u", G_STRFUNC, iface_id, pcap_src->cap_pipe_info.pcapng.src_iface_to_global->len);
return FALSE;
}
}
break;
default:
break;
}
return TRUE;
}
/*
* Read the part of the initial pcapng SHB following the block type
* (we've already read the block type).
*/
static void
pcapng_pipe_open_live(int fd,
capture_src *pcap_src,
char *errmsg,
size_t errmsgl)
{
guint32 type = BLOCK_TYPE_SHB;
pcapng_block_header_t *bh = &pcap_src->cap_pipe_info.pcapng.bh;
ws_debug("pcapng_pipe_open_live: fd %d", fd);
/*
* A pcapng block begins with the block type followed by the block
* total length; we've already read the block type, now read the
* block length.
*/
#ifdef _WIN32
/*
* On UN*X, we can use select() on pipes or sockets.
*
* On Windows, we can only use it on sockets; to do non-blocking
* reads from pipes, we currently do reads in a separate thread
* and use GLib asynchronous queues from the main thread to start
* read operations and to wait for them to complete.
*/
if (pcap_src->from_cap_socket)
#endif
{
memcpy(pcap_src->cap_pipe_databuf, &type, sizeof(guint32));
pcap_src->cap_pipe_bytes_read = sizeof(guint32);
pcap_src->cap_pipe_bytes_to_read = sizeof(pcapng_block_header_t);
pcap_src->cap_pipe_fd = fd;
if (cap_pipe_read_data_bytes(pcap_src, errmsg, errmsgl) < 0) {
goto error;
}
memcpy(bh, pcap_src->cap_pipe_databuf, sizeof(pcapng_block_header_t));
}
#ifdef _WIN32
else {
g_thread_new("cap_pipe_open_live", &cap_thread_read, pcap_src);
bh->block_type = type;
pipe_read_sync(pcap_src, &bh->block_total_length,
sizeof(bh->block_total_length));
if (pcap_src->cap_pipe_bytes_read <= 0) {
if (pcap_src->cap_pipe_bytes_read == 0)
snprintf(errmsg, (gulong)errmsgl,
"End of file reading from pipe or socket.");
else
snprintf(errmsg, (gulong)errmsgl,
"Error reading from pipe or socket: %s.",
g_strerror(errno));
goto error;
}
pcap_src->cap_pipe_bytes_read = sizeof(pcapng_block_header_t);
memcpy(pcap_src->cap_pipe_databuf, bh, sizeof(pcapng_block_header_t));
pcap_src->cap_pipe_fd = fd;
}
#endif
if ((bh->block_total_length & 0x03) != 0) {
snprintf(errmsg, (gulong)errmsgl,
"block_total_length read from pipe is %u, which is not a multiple of 4.",
bh->block_total_length);
goto error;
}
if (pcapng_read_shb(pcap_src, errmsg, errmsgl)) {
goto error;
}
return;
error:
ws_debug("pcapng_pipe_open_live: error %s", errmsg);
pcap_src->cap_pipe_err = PIPERR;
cap_pipe_close(fd, pcap_src->from_cap_socket);
pcap_src->cap_pipe_fd = -1;
#ifdef _WIN32
pcap_src->cap_pipe_h = INVALID_HANDLE_VALUE;
#endif
}
/* We read one record from the pipe, take care of byte order in the record
* header, write the record to the capture file, and update capture statistics. */
static int
pcap_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, size_t errmsgl)
{
struct pcap_pkthdr phdr;
enum { PD_REC_HDR_READ, PD_DATA_READ, PD_PIPE_EOF, PD_PIPE_ERR,
PD_ERR } result;
#ifdef _WIN32
gpointer q_status;
#endif
ssize_t b;
guint new_bufsize;
pcap_pipe_info_t *pcap_info = &pcap_src->cap_pipe_info.pcap;
#ifdef LOG_CAPTURE_VERBOSE
ws_debug("pcap_pipe_dispatch");
#endif
switch (pcap_src->cap_pipe_state) {
case STATE_EXPECT_REC_HDR:
#ifdef _WIN32
if (g_mutex_trylock(pcap_src->cap_pipe_read_mtx)) {
#endif
pcap_src->cap_pipe_state = STATE_READ_REC_HDR;
pcap_src->cap_pipe_bytes_to_read = pcap_src->cap_pipe_modified ?
sizeof(struct pcaprec_modified_hdr) : sizeof(struct pcaprec_hdr);
pcap_src->cap_pipe_bytes_read = 0;
#ifdef _WIN32
pcap_src->cap_pipe_buf = (char *) &pcap_info->rechdr;
g_async_queue_push(pcap_src->cap_pipe_pending_q, pcap_src->cap_pipe_buf);
g_mutex_unlock(pcap_src->cap_pipe_read_mtx);
}
#endif
/* Fall through */
case STATE_READ_REC_HDR:
#ifdef _WIN32
if (pcap_src->from_cap_socket)
#endif
{
b = cap_pipe_read(pcap_src->cap_pipe_fd, ((char *)&pcap_info->rechdr)+pcap_src->cap_pipe_bytes_read,
pcap_src->cap_pipe_bytes_to_read - pcap_src->cap_pipe_bytes_read, pcap_src->from_cap_socket);
if (b <= 0) {
if (b == 0)
result = PD_PIPE_EOF;
else
result = PD_PIPE_ERR;
break;
}
#ifdef _WIN32
pcap_src->cap_pipe_bytes_read += (DWORD)b;
#else
pcap_src->cap_pipe_bytes_read += b;
#endif
}
#ifdef _WIN32
else {
q_status = g_async_queue_timeout_pop(pcap_src->cap_pipe_done_q, PIPE_READ_TIMEOUT);
if (pcap_src->cap_pipe_err == PIPEOF) {
result = PD_PIPE_EOF;
break;
} else if (pcap_src->cap_pipe_err == PIPERR) {
result = PD_PIPE_ERR;
break;
}
if (!q_status) {
return 0;
}
}
#endif
if (pcap_src->cap_pipe_bytes_read < pcap_src->cap_pipe_bytes_to_read) {
/* There's still more of the pcap packet header to read. */
return 0;
}
result = PD_REC_HDR_READ;
break;
case STATE_EXPECT_DATA:
#ifdef _WIN32
if (g_mutex_trylock(pcap_src->cap_pipe_read_mtx)) {
#endif
pcap_src->cap_pipe_state = STATE_READ_DATA;
pcap_src->cap_pipe_bytes_to_read = pcap_info->rechdr.hdr.incl_len;
pcap_src->cap_pipe_bytes_read = 0;
#ifdef _WIN32
pcap_src->cap_pipe_buf = pcap_src->cap_pipe_databuf;
g_async_queue_push(pcap_src->cap_pipe_pending_q, pcap_src->cap_pipe_buf);
g_mutex_unlock(pcap_src->cap_pipe_read_mtx);
}
#endif
/* Fall through */
case STATE_READ_DATA:
#ifdef _WIN32
if (pcap_src->from_cap_socket)
#endif
{
b = cap_pipe_read(pcap_src->cap_pipe_fd,
pcap_src->cap_pipe_databuf+pcap_src->cap_pipe_bytes_read,
pcap_src->cap_pipe_bytes_to_read - pcap_src->cap_pipe_bytes_read,
pcap_src->from_cap_socket);
if (b <= 0) {
if (b == 0)
result = PD_PIPE_EOF;
else
result = PD_PIPE_ERR;
break;
}
#ifdef _WIN32
pcap_src->cap_pipe_bytes_read += (DWORD)b;
#else
pcap_src->cap_pipe_bytes_read += b;
#endif
}
#ifdef _WIN32
else {
q_status = g_async_queue_timeout_pop(pcap_src->cap_pipe_done_q, PIPE_READ_TIMEOUT);
if (pcap_src->cap_pipe_err == PIPEOF) {
result = PD_PIPE_EOF;
break;
} else if (pcap_src->cap_pipe_err == PIPERR) {
result = PD_PIPE_ERR;
break;
}
if (!q_status) {
return 0;
}
}
#endif /* _WIN32 */
if (pcap_src->cap_pipe_bytes_read < pcap_src->cap_pipe_bytes_to_read) {
/* There's still more of the pcap packet data to read. */
return 0;
}
result = PD_DATA_READ;
break;
default:
snprintf(errmsg, (gulong)errmsgl,
"pcap_pipe_dispatch: invalid state");
result = PD_ERR;
} /* switch (pcap_src->cap_pipe_state) */
/*
* We've now read as much data as we were expecting, so process it.
*/
switch (result) {
case PD_REC_HDR_READ:
/*
* We've read the packet header, so we know the captured length,
* and thus the number of packet data bytes. Take care of byte order.
*/
cap_pipe_adjust_pcap_header(pcap_src->cap_pipe_info.pcap.byte_swapped, &pcap_info->hdr,
&pcap_info->rechdr.hdr);
if (pcap_info->rechdr.hdr.incl_len > pcap_src->cap_pipe_max_pkt_size) {
/*
* The record contains more data than the advertised/allowed in the
* pcap header, do not try to read more data (do not change to
* STATE_EXPECT_DATA) as that would not fit in the buffer and
* instead stop with an error.
*/
snprintf(errmsg, (gulong)errmsgl, "Frame %u too long (%d bytes)",
ld->packets_captured+1, pcap_info->rechdr.hdr.incl_len);
break;
}
if (pcap_info->rechdr.hdr.incl_len > pcap_src->cap_pipe_databuf_size) {
/*
* Grow the buffer to the packet size, rounded up to a power of
* 2.
*/
new_bufsize = pcap_info->rechdr.hdr.incl_len;
/*
* https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
*/
new_bufsize--;
new_bufsize |= new_bufsize >> 1;
new_bufsize |= new_bufsize >> 2;
new_bufsize |= new_bufsize >> 4;
new_bufsize |= new_bufsize >> 8;
new_bufsize |= new_bufsize >> 16;
new_bufsize++;
pcap_src->cap_pipe_databuf = (char*)g_realloc(pcap_src->cap_pipe_databuf, new_bufsize);
pcap_src->cap_pipe_databuf_size = new_bufsize;
}
if (pcap_info->rechdr.hdr.incl_len) {
/*
* The record has some data following the header, try
* to read it next time.
*/
pcap_src->cap_pipe_state = STATE_EXPECT_DATA;
return 0;
}
/*
* No data following the record header? Then no more data needs to be
* read and we will fallthrough and emit an empty packet.
*/
/* FALLTHROUGH */
case PD_DATA_READ:
/*
* We've read the full contents of the packet record.
* Fill in a "struct pcap_pkthdr", and process the packet.
*/
phdr.ts.tv_sec = pcap_info->rechdr.hdr.ts_sec;
phdr.ts.tv_usec = pcap_info->rechdr.hdr.ts_usec;
phdr.caplen = pcap_info->rechdr.hdr.incl_len;
phdr.len = pcap_info->rechdr.hdr.orig_len;
if (use_threads) {
capture_loop_queue_packet_cb((u_char *)pcap_src, &phdr, pcap_src->cap_pipe_databuf);
} else {
capture_loop_write_packet_cb((u_char *)pcap_src, &phdr, pcap_src->cap_pipe_databuf);
}
/*
* Now we want to read the next packet's header.
*/
pcap_src->cap_pipe_state = STATE_EXPECT_REC_HDR;
return 1;
case PD_PIPE_EOF:
pcap_src->cap_pipe_err = PIPEOF;
return -1;
case PD_PIPE_ERR:
snprintf(errmsg, (gulong)errmsgl, "Error reading from pipe: %s",
#ifdef _WIN32
win32strerror(GetLastError()));
#else
g_strerror(errno));
#endif
/* Fall through */
case PD_ERR:
break;
}
pcap_src->cap_pipe_err = PIPERR;
/* Return here rather than inside the switch to prevent GCC warning */
return -1;
}
static int
pcapng_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, size_t errmsgl)
{
enum { PD_REC_HDR_READ, PD_DATA_READ, PD_PIPE_EOF, PD_PIPE_ERR,
PD_ERR } result;
#ifdef _WIN32
gpointer q_status;
#endif
guint new_bufsize;
pcapng_block_header_t *bh = &pcap_src->cap_pipe_info.pcapng.bh;
#ifdef LOG_CAPTURE_VERBOSE
ws_debug("pcapng_pipe_dispatch");
#endif
switch (pcap_src->cap_pipe_state) {
case STATE_EXPECT_REC_HDR:
#ifdef LOG_CAPTURE_VERBOSE
ws_debug("pcapng_pipe_dispatch STATE_EXPECT_REC_HDR");
#endif
#ifdef _WIN32
if (g_mutex_trylock(pcap_src->cap_pipe_read_mtx)) {
#endif
pcap_src->cap_pipe_state = STATE_READ_REC_HDR;
pcap_src->cap_pipe_bytes_to_read = sizeof(pcapng_block_header_t);
pcap_src->cap_pipe_bytes_read = 0;
#ifdef _WIN32
if (!pcap_src->from_cap_socket) {
pcap_src->cap_pipe_buf = pcap_src->cap_pipe_databuf;
g_async_queue_push(pcap_src->cap_pipe_pending_q, pcap_src->cap_pipe_buf);
}
g_mutex_unlock(pcap_src->cap_pipe_read_mtx);
}
#endif
/* Fall through */
case STATE_READ_REC_HDR:
#ifdef LOG_CAPTURE_VERBOSE
ws_debug("pcapng_pipe_dispatch STATE_READ_REC_HDR");
#endif
#ifdef _WIN32
if (pcap_src->from_cap_socket) {
#endif
if (cap_pipe_read_data_bytes(pcap_src, errmsg, errmsgl) < 0) {
return -1;
}
#ifdef _WIN32
} else {
q_status = g_async_queue_timeout_pop(pcap_src->cap_pipe_done_q, PIPE_READ_TIMEOUT);
if (pcap_src->cap_pipe_err == PIPEOF) {
result = PD_PIPE_EOF;
break;
} else if (pcap_src->cap_pipe_err == PIPERR) {
result = PD_PIPE_ERR;
break;
}
if (!q_status) {
return 0;
}
}
#endif
if (pcap_src->cap_pipe_bytes_read < pcap_src->cap_pipe_bytes_to_read) {
/* There's still more of the pcapng block header to read. */
return 0;
}
memcpy(bh, pcap_src->cap_pipe_databuf, sizeof(pcapng_block_header_t));
result = PD_REC_HDR_READ;
break;
case STATE_EXPECT_DATA:
#ifdef LOG_CAPTURE_VERBOSE
ws_debug("pcapng_pipe_dispatch STATE_EXPECT_DATA");
#endif
#ifdef _WIN32
if (g_mutex_trylock(pcap_src->cap_pipe_read_mtx)) {
#endif
pcap_src->cap_pipe_state = STATE_READ_DATA;
pcap_src->cap_pipe_bytes_to_read = bh->block_total_length;
#ifdef _WIN32
if (!pcap_src->from_cap_socket) {
pcap_src->cap_pipe_bytes_to_read -= pcap_src->cap_pipe_bytes_read;
pcap_src->cap_pipe_buf = pcap_src->cap_pipe_databuf + pcap_src->cap_pipe_bytes_read;
pcap_src->cap_pipe_bytes_read = 0;
g_async_queue_push(pcap_src->cap_pipe_pending_q, pcap_src->cap_pipe_buf);
}
g_mutex_unlock(pcap_src->cap_pipe_read_mtx);
}
#endif
/* Fall through */
case STATE_READ_DATA:
#ifdef LOG_CAPTURE_VERBOSE
ws_debug("pcapng_pipe_dispatch STATE_READ_DATA");
#endif
#ifdef _WIN32
if (pcap_src->from_cap_socket) {
#endif
if (cap_pipe_read_data_bytes(pcap_src, errmsg, errmsgl) < 0) {
return -1;
}
#ifdef _WIN32
} else {
q_status = g_async_queue_timeout_pop(pcap_src->cap_pipe_done_q, PIPE_READ_TIMEOUT);
if (pcap_src->cap_pipe_err == PIPEOF) {
result = PD_PIPE_EOF;
break;
} else if (pcap_src->cap_pipe_err == PIPERR) {
result = PD_PIPE_ERR;
break;
}
if (!q_status) {
return 0;
}
}
#endif /* _WIN32 */
if (pcap_src->cap_pipe_bytes_read < pcap_src->cap_pipe_bytes_to_read) {
/* There's still more of the pcap block contents to read. */
return 0;
}
result = PD_DATA_READ;
break;
default:
snprintf(errmsg, (gulong)errmsgl,
"pcapng_pipe_dispatch: invalid state");
result = PD_ERR;
} /* switch (pcap_src->cap_pipe_state) */
/*
* We've now read as much data as we were expecting, so process it.
*/
switch (result) {
case PD_REC_HDR_READ:
/*
* We've read the pcapng block header, so we know the block type
* and length.
*/
if (bh->block_type == BLOCK_TYPE_SHB) {
/*
* We need to read the fixed portion of the SHB before to
* get the endianness before we can interpret the block length.
* (The block type of the SHB is byte-order-independent, so that
* an SHB can be recognized before we know the endianness of
* the section.)
*
* Continue the read process.
*/
pcapng_read_shb(pcap_src, errmsg, errmsgl);
return 1;
}
if ((bh->block_total_length & 0x03) != 0) {
snprintf(errmsg, (gulong)errmsgl,
"Total length of pcapng block read from pipe is %u, which is not a multiple of 4.",
bh->block_total_length);
break;
}
if (bh->block_total_length > pcap_src->cap_pipe_max_pkt_size) {
/*
* The record contains more data than the advertised/allowed in the
* pcapng header, do not try to read more data (do not change to
* STATE_EXPECT_DATA) as that would not fit in the buffer and
* instead stop with an error.
*/
snprintf(errmsg, (gulong)errmsgl, "Frame %u too long (%d bytes)",
ld->packets_captured+1, bh->block_total_length);
break;
}
if (bh->block_total_length > pcap_src->cap_pipe_databuf_size) {
/*
* Grow the buffer to the packet size, rounded up to a power of
* 2.
*/
new_bufsize = bh->block_total_length;
/*
* https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
*/
new_bufsize--;
new_bufsize |= new_bufsize >> 1;
new_bufsize |= new_bufsize >> 2;
new_bufsize |= new_bufsize >> 4;
new_bufsize |= new_bufsize >> 8;
new_bufsize |= new_bufsize >> 16;
new_bufsize++;
pcap_src->cap_pipe_databuf = (guchar*)g_realloc(pcap_src->cap_pipe_databuf, new_bufsize);
pcap_src->cap_pipe_databuf_size = new_bufsize;
}
/* The record always has at least the block total length following the header */
if (bh->block_total_length < sizeof(pcapng_block_header_t)+sizeof(guint32)) {
snprintf(errmsg, (gulong)errmsgl,
"malformed pcapng block_total_length < minimum");
pcap_src->cap_pipe_err = PIPEOF;
return -1;
}
/*
* Now we want to read the block contents.
*/
pcap_src->cap_pipe_state = STATE_EXPECT_DATA;
return 0;
case PD_DATA_READ:
/*
* We've read the full contents of the block.
* Process the block.
*/
if (use_threads) {
capture_loop_queue_pcapng_cb(pcap_src, bh, pcap_src->cap_pipe_databuf);
} else {
capture_loop_write_pcapng_cb(pcap_src, bh, pcap_src->cap_pipe_databuf);
}
/*
* Now we want to read the next block's header.
*/
pcap_src->cap_pipe_state = STATE_EXPECT_REC_HDR;
return 1;
case PD_PIPE_EOF:
pcap_src->cap_pipe_err = PIPEOF;
return -1;
case PD_PIPE_ERR:
snprintf(errmsg, (gulong)errmsgl, "Error reading from pipe: %s",
#ifdef _WIN32
win32strerror(GetLastError()));
#else
g_strerror(errno));
#endif
/* Fall through */
case PD_ERR:
break;
}
pcap_src->cap_pipe_err = PIPERR;
/* Return here rather than inside the switch to prevent GCC warning */
return -1;
}
/** Open the capture input sources; each one is either a pcap device,
* a capture pipe, or a capture socket.
* Returns TRUE if it succeeds, FALSE otherwise. */
static gboolean
capture_loop_open_input(capture_options *capture_opts, loop_data *ld,
char *errmsg, size_t errmsg_len,
char *secondary_errmsg, size_t secondary_errmsg_len)
{
cap_device_open_status open_status;