X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=dumpcap.c;h=8bac9138e949ef342f894966a5a6b49bfa25c805;hb=7dd916459e2ea350dbb1e1fb5dc3fb7d5bb383a8;hp=d8e359ebef3e58b454f4d5458f800bc596f40f60;hpb=1e70d66184480b20600492b9f62b331690eed012;p=metze%2Fwireshark%2Fwip.git diff --git a/dumpcap.c b/dumpcap.c index d8e359ebef..8bac9138e9 100644 --- a/dumpcap.c +++ b/dumpcap.c @@ -18,12 +18,10 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif +#include "config.h" #include #include /* for exit() */ @@ -36,6 +34,14 @@ # include #endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#ifdef HAVE_NETINET_IN_H +#include +#endif + #ifdef HAVE_SYS_STAT_H # include #endif @@ -59,9 +65,7 @@ #include #include -#ifdef HAVE_GETOPT_H -#include -#else +#ifndef HAVE_GETOPT #include "wsutil/wsgetopt.h" #endif @@ -81,6 +85,9 @@ #include "version_info.h" #include "capture-pcap-util.h" +#ifdef _WIN32 +#include "capture-wpcap.h" +#endif /* _WIN32 */ #include "pcapio.h" @@ -90,7 +97,6 @@ #endif #ifndef _WIN32 -#include #include #endif @@ -113,6 +119,8 @@ #include "log.h" #include "wsutil/file_util.h" +#include "ws80211_utils.h" + /* * Get information about libpcap format from "wiretap/libpcap.h". * XXX - can we just use pcap_open_offline() to read the pipe? @@ -134,10 +142,6 @@ FILE *debug_log; /* for logging debug messages to */ /* is defined */ #endif -#ifdef _WIN32 -#define USE_THREADS -#endif - static GAsyncQueue *pcap_queue; static gint64 pcap_queue_bytes; static gint64 pcap_queue_packets; @@ -158,6 +162,8 @@ static gboolean infoprint; /* if TRUE, print capture info after clearing in /** 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 _U_); #if !defined (__linux__) #ifndef HAVE_PCAP_BREAKLOOP @@ -218,12 +224,6 @@ typedef enum { INITFILTER_OTHER_ERROR } initfilter_status_t; -typedef struct _pcap_queue_element { - guint interface_id; - struct pcap_pkthdr phdr; - u_char *pd; -} pcap_queue_element; - typedef struct _pcap_options { guint32 received; guint32 dropped; @@ -236,20 +236,21 @@ typedef struct _pcap_options { 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 */ 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 + 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 */ gboolean cap_pipe_byte_swapped; /* TRUE if data in the pipe is byte swapped */ -#ifdef USE_THREADS +#if defined(_WIN32) char * cap_pipe_buf; /* Pointer to the data buffer we read into */ -#endif /* USE_THREADS */ +#endif int cap_pipe_bytes_to_read;/* Used by cap_pipe_dispatch */ int cap_pipe_bytes_read; /* Used by cap_pipe_dispatch */ enum { @@ -259,7 +260,7 @@ typedef struct _pcap_options { STATE_READ_DATA } cap_pipe_state; enum { PIPOK, PIPEOF, PIPERR, PIPNEXIST } cap_pipe_err; -#ifdef USE_THREADS +#if defined(_WIN32) GMutex *cap_pipe_read_mtx; GAsyncQueue *cap_pipe_pending_q, *cap_pipe_done_q; #endif @@ -271,7 +272,7 @@ typedef struct _loop_data { 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 */ + 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 @@ -283,6 +284,12 @@ typedef struct _loop_data { guint32 autostop_files; } loop_data; +typedef struct _pcap_queue_element { + pcap_options *pcap_opts; + struct pcap_pkthdr phdr; + u_char *pd; +} pcap_queue_element; + /* * Standard secondary message for unexpected errors. */ @@ -324,12 +331,11 @@ static gboolean need_timeout_workaround; * WaitForSingleObject. If it's less than 1000 WaitForSingleObject * will return immediately. */ -#ifndef USE_THREADS -#define PIPE_READ_TIMEOUT 250000 -#else +#if defined(_WIN32) #define PIPE_READ_TIMEOUT 100000 +#else +#define PIPE_READ_TIMEOUT 250000 #endif -static const char *cap_pipe_err_str; #define WRITER_THREAD_TIMEOUT 100000 /* usecs */ @@ -341,10 +347,11 @@ console_log_handler(const char *log_domain, GLogLevelFlags log_level, static capture_options global_capture_opts; static gboolean quiet = FALSE; static gboolean use_threads = FALSE; +static guint64 start_time; -static void capture_loop_write_packet_cb(u_char *user, const struct pcap_pkthdr *phdr, +static void capture_loop_write_packet_cb(u_char *pcap_opts_p, const struct pcap_pkthdr *phdr, const u_char *pd); -static void capture_loop_queue_packet_cb(u_char *user, const struct pcap_pkthdr *phdr, +static void capture_loop_queue_packet_cb(u_char *pcap_opts_p, 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); @@ -352,13 +359,65 @@ static void capture_loop_get_errmsg(char *errmsg, int errmsglen, const char *fna static void WS_MSVC_NORETURN 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_count(unsigned int packet_count); static void report_packet_drops(guint32 received, guint32 drops, gchar *name); 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); +static void report_cfilter_error(capture_options *capture_opts, guint i, const char *errmsg); #define MSG_MAX_LENGTH 4096 +/* Copied from pcapio.c libpcap_write_interface_statistics_block()*/ +static guint64 +create_timestamp(void) { + guint64 timestamp; +#ifdef _WIN32 + FILETIME now; +#else + struct timeval now; +#endif + +#ifdef _WIN32 + /* + * Current time, represented as 100-nanosecond intervals since + * January 1, 1601, 00:00:00 UTC. + * + * I think DWORD might be signed, so cast both parts of "now" + * to guint32 so that the sign bit doesn't get treated specially. + * + * Windows 8 provides GetSystemTimePreciseAsFileTime which we + * might want to use instead. + */ + GetSystemTimeAsFileTime(&now); + timestamp = (((guint64)(guint32)now.dwHighDateTime) << 32) + + (guint32)now.dwLowDateTime; + + /* + * Convert to same thing but as 1-microsecond, i.e. 1000-nanosecond, + * intervals. + */ + timestamp /= 10; + + /* + * Subtract difference, in microseconds, between January 1, 1601 + * 00:00:00 UTC and January 1, 1970, 00:00:00 UTC. + */ + timestamp -= G_GINT64_CONSTANT(11644473600000000U); +#else + /* + * Current time, represented as seconds and microseconds since + * January 1, 1970, 00:00:00 UTC. + */ + gettimeofday(&now, NULL); + + /* + * Convert to delta in microseconds. + */ + timestamp = (guint64)(now.tv_sec) * 1000000 + + (guint64)(now.tv_usec); +#endif + return timestamp; +} + static void print_usage(gboolean print_ver) { @@ -368,7 +427,7 @@ print_usage(gboolean print_ver) output = stdout; fprintf(output, "Dumpcap " VERSION "%s\n" - "Capture network packets and dump them into a libpcap file.\n" + "Capture network packets and dump them into a pcapng file.\n" "See http://www.wireshark.org for more information.\n", wireshark_svnversion); } else { @@ -377,7 +436,10 @@ print_usage(gboolean print_ver) fprintf(output, "\nUsage: dumpcap [options] ...\n"); fprintf(output, "\n"); fprintf(output, "Capture interface:\n"); - fprintf(output, " -i name or idx of interface (def: first non-loopback)\n"); + fprintf(output, " -i name or idx of interface (def: first non-loopback),\n" + " or for remote capturing, use one of these formats:\n" + " rpcap:///\n" + " TCP@:\n"); fprintf(output, " -f packet filter in libpcap filter syntax\n"); fprintf(output, " -s packet snapshot length (def: 65535)\n"); fprintf(output, " -p don't capture in promiscuous mode\n"); @@ -393,11 +455,12 @@ print_usage(gboolean print_ver) #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, " -k 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, "\nRPCAP options:\n"); + 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 : use RPCAP password authentication\n"); @@ -419,16 +482,17 @@ print_usage(gboolean print_ver) fprintf(output, " -b ... 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, " -n use pcapng format instead of pcap (default)\n"); + fprintf(output, " -P use libpcap format instead of pcapng\n"); + fprintf(output, "\n"); fprintf(output, "Miscellaneous:\n"); fprintf(output, " -t use a separate thread per interface\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"); - fprintf(output, "Example: dumpcap -i eth0 -a duration:60 -w output.pcap\n"); - fprintf(output, "\"Capture network packets from interface eth0 until 60s passed into output.pcap\"\n"); + 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"); } @@ -540,7 +604,7 @@ relinquish_all_capabilities(void) 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)); + cmdarg_err("cap_set_proc() fail return: %s", g_strerror(errno)); } print_caps("Post-clear"); cap_free(caps); @@ -563,6 +627,7 @@ open_capture_device(interface_options *interface_opts, 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. */ + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Entering open_capture_device()."); (*open_err_str)[0] = '\0'; #if defined(HAVE_PCAP_OPEN) && defined(HAVE_PCAP_REMOTE) /* @@ -575,12 +640,28 @@ open_capture_device(interface_options *interface_opts, auth.username = interface_opts->auth_username; auth.password = interface_opts->auth_password; + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "Calling pcap_open() using name %s, snaplen %d, promisc_mode %d, datatx_udp %d, nocap_rpcap %d.", + interface_opts->name, interface_opts->snaplen, interface_opts->promisc_mode, + interface_opts->datatx_udp, interface_opts->nocap_rpcap); pcap_h = pcap_open(interface_opts->name, interface_opts->snaplen, /* flags */ (interface_opts->promisc_mode ? PCAP_OPENFLAG_PROMISCUOUS : 0) | (interface_opts->datatx_udp ? PCAP_OPENFLAG_DATATX_UDP : 0) | (interface_opts->nocap_rpcap ? PCAP_OPENFLAG_NOCAPTURE_RPCAP : 0), CAP_READ_TIMEOUT, &auth, *open_err_str); + if (pcap_h == NULL) { + /* Error - did pcap actually supply an error message? */ + if ((*open_err_str)[0] == '\0') { + /* Work around known WinPcap bug wherein no error message is + filled in on a failure to open an rpcap: URL. */ + g_strlcpy(*open_err_str, + "Unknown error (pcap bug; actual error cause not reported)", + sizeof *open_err_str); + } + } + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "pcap_open() returned %p.", (void *)pcap_h); } else #endif { @@ -590,18 +671,32 @@ open_capture_device(interface_options *interface_opts, * size, otherwise use pcap_open_live(). */ #ifdef HAVE_PCAP_CREATE + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "Calling pcap_create() using %s.", interface_opts->name); pcap_h = pcap_create(interface_opts->name, *open_err_str); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "pcap_create() returned %p.", (void *)pcap_h); if (pcap_h != NULL) { + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "Calling pcap_set_snaplen() with snaplen %d.", interface_opts->snaplen); pcap_set_snaplen(pcap_h, interface_opts->snaplen); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "Calling pcap_set_promisc() with promisc_mode %d.", interface_opts->promisc_mode); pcap_set_promisc(pcap_h, interface_opts->promisc_mode); pcap_set_timeout(pcap_h, CAP_READ_TIMEOUT); - if (interface_opts->buffer_size > 1) { + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "buffersize %d.", interface_opts->buffer_size); + if (interface_opts->buffer_size != 0) { pcap_set_buffer_size(pcap_h, interface_opts->buffer_size * 1024 * 1024); } + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "monitor_mode %d.", interface_opts->monitor_mode); if (interface_opts->monitor_mode) pcap_set_rfmon(pcap_h, 1); err = pcap_activate(pcap_h); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "pcap_activate() returned %d.", err); if (err < 0) { /* Failed to activate, set to NULL */ if (err == PCAP_ERROR) @@ -613,11 +708,17 @@ open_capture_device(interface_options *interface_opts, } } #else + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "pcap_open_live() calling using name %s, snaplen %d, promisc_mode %d.", + interface_opts->name, interface_opts->snaplen, interface_opts->promisc_mode); pcap_h = pcap_open_live(interface_opts->name, interface_opts->snaplen, interface_opts->promisc_mode, CAP_READ_TIMEOUT, *open_err_str); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "pcap_open_live() returned %p.", (void *)pcap_h); #endif } + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "open_capture_device %s : %s", pcap_h ? "SUCCESS" : "FAILURE", interface_opts->name); return pcap_h; } @@ -632,11 +733,46 @@ get_capture_device_open_failure_messages(const char *open_err_str, char *secondary_errmsg, size_t secondary_errmsg_len) { +#ifndef _WIN32 const char *libpcap_warn; static const char ppamsg[] = "can't find PPA for "; +#endif + g_snprintf(errmsg, (gulong) errmsg_len, + "The capture session could not be initiated (%s).", open_err_str); +#ifdef _WIN32 + if (!has_wpcap) { + g_snprintf(secondary_errmsg, (gulong) secondary_errmsg_len, + "\n" + "In order to capture packets, WinPcap must be installed; see\n" + "\n" + " http://www.winpcap.org/\n" + "\n" + "or the mirror at\n" + "\n" + " http://www.mirrors.wiretapped.net/security/packet-capture/winpcap/\n" + "\n" + "or the mirror at\n" + "\n" + " http://winpcap.cs.pu.edu.tw/\n" + "\n" + "for a downloadable version of WinPcap and for instructions on how to install\n" + "WinPcap."); + } 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); + } +#else /* 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 + is running dumpcap 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"). */ @@ -654,23 +790,10 @@ get_capture_device_open_failure_messages(const char *open_err_str, "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 */ } @@ -715,7 +838,7 @@ set_pcap_linktype(pcap_t *pcap_h, int linktype, static gboolean compile_capture_filter(const char *iface, pcap_t *pcap_h, - struct bpf_program *fcode, char *cfilter) + struct bpf_program *fcode, const char *cfilter) { bpf_u_int32 netnum, netmask; gchar lookup_net_err_str[PCAP_ERRBUF_SIZE]; @@ -734,7 +857,13 @@ compile_capture_filter(const char *iface, pcap_t *pcap_h, "Warning: Couldn't obtain netmask info (%s).", lookup_net_err_str);*/ netmask = 0; } - if (pcap_compile(pcap_h, fcode, cfilter, 1, 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. + */ + if (pcap_compile(pcap_h, fcode, (char *)cfilter, 1, netmask) < 0) return FALSE; return TRUE; } @@ -781,7 +910,7 @@ show_filter_code(capture_options *capture_opts) if (!compile_capture_filter(interface_opts.name, pcap_h, &fcode, interface_opts.cfilter)) { pcap_close(pcap_h); - report_cfilter_error(interface_opts.cfilter, errmsg); + report_cfilter_error(capture_opts, j, errmsg); return FALSE; } pcap_close(pcap_h); @@ -996,12 +1125,27 @@ get_if_capabilities(const char *devname, gboolean monitor_mode */ caps = g_malloc(sizeof *caps); + /* + * WinPcap 4.1.2, and possibly earlier versions, have a bug + * wherein, when an open with an rpcap: URL fails, the error + * message for the error is not copied to errbuf and whatever + * on-the-stack junk is in errbuf is treated as the error + * message. + * + * To work around that (and any other bugs of that sort, we + * initialize errbuf to an empty string. If we get an error + * and the string is empty, we report it as an unknown error. + * (If we *don't* get an error, and the string is *non*-empty, + * that could be a warning returned, such as "can't turn + * promiscuous mode on"; we currently don't do so.) + */ + errbuf[0] = '\0'; #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); + *err_str = g_strdup(errbuf[0] == '\0' ? "Unknown error (pcap bug; actual error cause not reported)" : errbuf); g_free(caps); return NULL; } @@ -1060,7 +1204,7 @@ get_if_capabilities(const char *devname, gboolean monitor_mode caps->can_set_rfmon = FALSE; if (pch == NULL) { if (err_str != NULL) - *err_str = g_strdup(errbuf); + *err_str = g_strdup(errbuf[0] == '\0' ? "Unknown error (pcap bug; actual error cause not reported)" : errbuf); g_free(caps); return NULL; } @@ -1072,6 +1216,7 @@ get_if_capabilities(const char *devname, gboolean monitor_mode pcap_close(pch); if (err_str != NULL) *err_str = NULL; /* an empty list doesn't mean an error */ + g_free(caps); return NULL; } caps->data_link_types = NULL; @@ -1253,6 +1398,7 @@ print_statistics_loop(gboolean machine_readable) if (if_list == NULL) { switch (err) { case CANT_GET_INTERFACE_LIST: + case DONT_HAVE_PCAP: cmdarg_err("%s", err_str); g_free(err_str); break; @@ -1462,14 +1608,14 @@ relinquish_privs_except_capture(void) print_caps("Pre drop, pre set"); if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) { - cmdarg_err("prctl() fail return: %s", strerror(errno)); + 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", strerror(errno)); + cmdarg_err("cap_set_proc() fail return: %s", g_strerror(errno)); } print_caps("Pre drop, post set"); @@ -1478,7 +1624,7 @@ relinquish_privs_except_capture(void) 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", strerror(errno)); + cmdarg_err("cap_set_proc() fail return: %s", g_strerror(errno)); } print_caps("Post drop, post set"); @@ -1519,7 +1665,24 @@ cap_pipe_adjust_header(gboolean byte_swapped, struct pcap_hdr *hdr, struct pcapr } } -#ifdef USE_THREADS +/* Wrapper: distinguish between recv/read if we're reading on Windows, + * or just read(). + */ +static int +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. @@ -1533,10 +1696,10 @@ cap_pipe_adjust_header(gboolean byte_swapped, struct pcap_hdr *hdr, struct pcapr * the queues themselves (yet). * * 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 + * 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_pipe_read(void *arg) +static void *cap_thread_read(void *arg) { pcap_options *pcap_opts; int bytes_read; @@ -1553,48 +1716,57 @@ static void *cap_pipe_read(void *arg) g_mutex_lock(pcap_opts->cap_pipe_read_mtx); bytes_read = 0; while (bytes_read < (int) pcap_opts->cap_pipe_bytes_to_read) { + if ((pcap_opts->from_cap_socket) +#ifndef _WIN32 + || 1 +#endif + ) + { + b = cap_pipe_read(pcap_opts->cap_pipe_fd, pcap_opts->cap_pipe_buf+bytes_read, + pcap_opts->cap_pipe_bytes_to_read - bytes_read, pcap_opts->from_cap_socket); + if (b <= 0) { + if (b == 0) { + pcap_opts->cap_pipe_err = PIPEOF; + bytes_read = 0; + break; + } else { + pcap_opts->cap_pipe_err = PIPERR; + bytes_read = -1; + break; + } + } else { + bytes_read += b; + } + } #ifdef _WIN32 - /* If we try to use read() on a named pipe on Windows with partial - * data it appears to return EOF. - */ - res = ReadFile(pcap_opts->cap_pipe_h, pcap_opts->cap_pipe_buf+bytes_read, - pcap_opts->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_opts->cap_pipe_err = PIPEOF; - bytes_read = 0; - break; - } - pcap_opts->cap_pipe_err = PIPERR; - bytes_read = -1; - break; - } else if (b == 0 && pcap_opts->cap_pipe_bytes_to_read > 0) { - pcap_opts->cap_pipe_err = PIPEOF; - bytes_read = 0; - break; - } -#else /* _WIN32 */ - b = read(pcap_opts->cap_pipe_fd, pcap_opts->cap_pipe_buf+bytes_read, - pcap_opts->cap_pipe_bytes_to_read - bytes_read); - if (b <= 0) { - if (b == 0) { - pcap_opts->cap_pipe_err = PIPEOF; - bytes_read = 0; - break; - } else { - pcap_opts->cap_pipe_err = PIPERR; - bytes_read = -1; - break; - } - } else { - bytes_read += b; - } + else + { + /* If we try to use read() on a named pipe on Windows with partial + * data it appears to return EOF. + */ + res = ReadFile(pcap_opts->cap_pipe_h, pcap_opts->cap_pipe_buf+bytes_read, + pcap_opts->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_opts->cap_pipe_err = PIPEOF; + bytes_read = 0; + break; + } + pcap_opts->cap_pipe_err = PIPERR; + bytes_read = -1; + break; + } else if (b == 0 && pcap_opts->cap_pipe_bytes_to_read > 0) { + pcap_opts->cap_pipe_err = PIPEOF; + bytes_read = 0; + break; + } + } #endif /*_WIN32 */ } pcap_opts->cap_pipe_bytes_read = bytes_read; @@ -1605,22 +1777,18 @@ static void *cap_pipe_read(void *arg) } return NULL; } -#endif /* USE_THREADS */ +#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. If an error is returned, - * the string cap_pipe_err_str should be used instead of errno. + * Returns the same values as select. */ static int cap_pipe_select(int pipe_fd) { fd_set rfds; struct timeval timeout; - int sel_ret; - - cap_pipe_err_str = "Unknown error"; FD_ZERO(&rfds); FD_SET(pipe_fd, &rfds); @@ -1628,12 +1796,104 @@ cap_pipe_select(int pipe_fd) timeout.tv_sec = PIPE_READ_TIMEOUT / 1000000; timeout.tv_usec = PIPE_READ_TIMEOUT % 1000000; - sel_ret = select(pipe_fd+1, &rfds, NULL, NULL, &timeout); - if (sel_ret < 0) - cap_pipe_err_str = strerror(errno); - return sel_ret; + return select(pipe_fd+1, &rfds, NULL, NULL, &timeout); } +#define DEF_TCP_PORT 19000 + +static int +cap_open_socket(char *pipename, pcap_options *pcap_opts, char *errmsg, int errmsgl) +{ + char *sockname = pipename + 4; + struct sockaddr_in sa; + char buf[16]; + char *p; + unsigned long port; + size_t len; + int fd; + + memset(&sa, 0, sizeof(sa)); + + p = strchr(sockname, ':'); + if (p == NULL) { + len = strlen(sockname); + port = DEF_TCP_PORT; + } + else { + len = p - sockname; + port = strtoul(p + 1, &p, 10); + if (*p || port > 65535) { + goto fail_invalid; + } + } + + if (len > 15) { + goto fail_invalid; + } + + strncpy(buf, sockname, len); + buf[len] = '\0'; + if (!inet_pton(AF_INET, buf, &sa.sin_addr)) { + goto fail_invalid; + } + + sa.sin_family = AF_INET; + sa.sin_port = htons((u_short)port); + + if (((fd = (int)socket(AF_INET, SOCK_STREAM, 0)) < 0) || + (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)) { +#ifdef _WIN32 + LPTSTR errorText = NULL; + int lastError; + + lastError = WSAGetLastError(); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, lastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&errorText, 0, NULL); +#endif + g_snprintf(errmsg, errmsgl, + "The capture session could not be initiated due to the socket error: \n" +#ifdef _WIN32 + " %d: %S", lastError, errorText ? (char *)errorText : "Unknown"); + if (errorText) + LocalFree(errorText); +#else + " %d: %s", errno, strerror(errno)); +#endif + pcap_opts->cap_pipe_err = PIPERR; + + if (fd >= 0) + cap_pipe_close(fd, TRUE); + return -1; + } + + pcap_opts->from_cap_socket = TRUE; + return fd; + +fail_invalid: + g_snprintf(errmsg, errmsgl, + "The capture session could not be initiated because\n" + "\"%s\" is not a valid socket specification", pipename); + pcap_opts->cap_pipe_err = PIPERR; + return -1; +} + +/* Wrapper: distinguish between closesocket on Windows; use ws_close + * otherwise. + */ +static void +cap_pipe_close(int pipe_fd, gboolean from_socket _U_) +{ +#ifdef _WIN32 + if (from_socket) { + closesocket(pipe_fd); + } +#else + ws_close(pipe_fd); +#endif +} /* Mimic pcap_open_live() for pipe captures @@ -1651,23 +1911,16 @@ cap_pipe_open_live(char *pipename, #ifndef _WIN32 ws_statb64 pipe_stat; struct sockaddr_un sa; - int b; - int fd; #else /* _WIN32 */ -#if 1 char *pncopy, *pos; wchar_t *err_str; #endif -#endif -#ifndef USE_THREADS - int sel_ret; + int b, fd, sel_ret; unsigned int bytes_read; -#endif guint32 magic = 0; -#ifndef _WIN32 pcap_opts->cap_pipe_fd = -1; -#else +#ifdef _WIN32 pcap_opts->cap_pipe_h = INVALID_HANDLE_VALUE; #endif g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "cap_pipe_open_live: %s", pipename); @@ -1682,6 +1935,10 @@ cap_pipe_open_live(char *pipename, #else /* _WIN32 */ pcap_opts->cap_pipe_h = GetStdHandle(STD_INPUT_HANDLE); #endif /* _WIN32 */ + } else if (!strncmp(pipename, "TCP@", 4)) { + if ((fd = cap_open_socket(pipename, pcap_opts, errmsg, errmsgl)) < 0) { + return; + } } else { #ifndef _WIN32 if (ws_stat64(pipename, &pipe_stat) < 0) { @@ -1690,7 +1947,7 @@ cap_pipe_open_live(char *pipename, else { g_snprintf(errmsg, errmsgl, "The capture session could not be initiated " - "due to error getting information on pipe/socket: %s", strerror(errno)); + "due to error getting information on pipe/socket: %s", g_strerror(errno)); pcap_opts->cap_pipe_err = PIPERR; } return; @@ -1700,7 +1957,7 @@ cap_pipe_open_live(char *pipename, if (fd == -1) { g_snprintf(errmsg, errmsgl, "The capture session could not be initiated " - "due to error on pipe open: %s", strerror(errno)); + "due to error on pipe open: %s", g_strerror(errno)); pcap_opts->cap_pipe_err = PIPERR; return; } @@ -1709,7 +1966,7 @@ cap_pipe_open_live(char *pipename, if (fd == -1) { g_snprintf(errmsg, errmsgl, "The capture session could not be initiated " - "due to error on socket create: %s", strerror(errno)); + "due to error on socket create: %s", g_strerror(errno)); pcap_opts->cap_pipe_err = PIPERR; return; } @@ -1743,14 +2000,16 @@ cap_pipe_open_live(char *pipename, "The capture session coud not be initiated " "due to error on socket connect: Path name too long"); pcap_opts->cap_pipe_err = PIPERR; + ws_close(fd); 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)); + "due to error on socket connect: %s", g_strerror(errno)); pcap_opts->cap_pipe_err = PIPERR; + ws_close(fd); return; } } else { @@ -1800,7 +2059,7 @@ cap_pipe_open_live(char *pipename, break; if (GetLastError() != ERROR_PIPE_BUSY) { - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), 0, (LPTSTR) &err_str, 0, NULL); g_snprintf(errmsg, errmsgl, "The capture session on \"%s\" could not be started " @@ -1812,7 +2071,7 @@ cap_pipe_open_live(char *pipename, } if (!WaitNamedPipe(utf_8to16(pipename), 30 * 1000)) { - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), 0, (LPTSTR) &err_str, 0, NULL); g_snprintf(errmsg, errmsgl, "The capture session on \"%s\" timed out during " @@ -1828,54 +2087,63 @@ cap_pipe_open_live(char *pipename, pcap_opts->from_cap_pipe = TRUE; -#ifndef USE_THREADS - /* read the pcap header */ - bytes_read = 0; - while (bytes_read < sizeof magic) { - sel_ret = cap_pipe_select(fd); - if (sel_ret < 0) { - g_snprintf(errmsg, errmsgl, - "Unexpected error from select: %s", strerror(errno)); - goto error; - } else if (sel_ret > 0) { - b = read(fd, ((char *)&magic)+bytes_read, sizeof magic-bytes_read); - if (b <= 0) { - if (b == 0) - 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; - } - bytes_read += b; - } + if ((pcap_opts->from_cap_socket) +#ifndef _WIN32 + || 1 +#endif + ) + { + /* read the pcap header */ + bytes_read = 0; + while (bytes_read < sizeof magic) { + sel_ret = cap_pipe_select(fd); + if (sel_ret < 0) { + g_snprintf(errmsg, 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_opts->from_cap_socket); + if (b <= 0) { + if (b == 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", + g_strerror(errno)); + goto error; + } + bytes_read += b; + } + } } -#else /* USE_THREADS */ - g_thread_create(&cap_pipe_read, pcap_opts, FALSE, NULL); - - pcap_opts->cap_pipe_buf = (char *) &magic; - pcap_opts->cap_pipe_bytes_read = 0; - pcap_opts->cap_pipe_bytes_to_read = sizeof(magic); - /* We don't have to worry about cap_pipe_read_mtx here */ - g_async_queue_push(pcap_opts->cap_pipe_pending_q, pcap_opts->cap_pipe_buf); - g_async_queue_pop(pcap_opts->cap_pipe_done_q); - if (pcap_opts->cap_pipe_bytes_read <= 0) { - if (pcap_opts->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; +#ifdef _WIN32 + else { + g_thread_create(&cap_thread_read, pcap_opts, FALSE, NULL); + + pcap_opts->cap_pipe_buf = (char *) &magic; + pcap_opts->cap_pipe_bytes_read = 0; + pcap_opts->cap_pipe_bytes_to_read = sizeof(magic); + /* We don't have to worry about cap_pipe_read_mtx here */ + g_async_queue_push(pcap_opts->cap_pipe_pending_q, pcap_opts->cap_pipe_buf); + g_async_queue_pop(pcap_opts->cap_pipe_done_q); + if (pcap_opts->cap_pipe_bytes_read <= 0) { + if (pcap_opts->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", + g_strerror(errno)); + goto error; + } } - -#endif /* USE_THREADS */ +#endif switch (magic) { case PCAP_MAGIC: + case PCAP_NSEC_MAGIC: /* Host that wrote it has our byte order, and was running a program using either standard or ss990417 libpcap. */ pcap_opts->cap_pipe_byte_swapped = FALSE; pcap_opts->cap_pipe_modified = FALSE; + pcap_opts->ts_nsec = magic == PCAP_NSEC_MAGIC; break; case PCAP_MODIFIED_MAGIC: /* Host that wrote it has our byte order, but was running @@ -1884,11 +2152,13 @@ cap_pipe_open_live(char *pipename, pcap_opts->cap_pipe_modified = TRUE; break; case PCAP_SWAPPED_MAGIC: + case PCAP_SWAPPED_NSEC_MAGIC: /* Host that wrote it has a byte order opposite to ours, and was running a program using either standard or ss990417 libpcap. */ pcap_opts->cap_pipe_byte_swapped = TRUE; pcap_opts->cap_pipe_modified = FALSE; + pcap_opts->ts_nsec = magic == PCAP_SWAPPED_NSEC_MAGIC; break; case PCAP_SWAPPED_MODIFIED_MAGIC: /* Host that wrote it out has a byte order opposite to @@ -1903,44 +2173,52 @@ cap_pipe_open_live(char *pipename, goto error; } -#ifndef USE_THREADS - /* Read 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) { - g_snprintf(errmsg, errmsgl, - "Unexpected error from select: %s", strerror(errno)); - goto error; - } else if (sel_ret > 0) { - b = read(fd, ((char *)hdr)+bytes_read, - sizeof(struct pcap_hdr) - bytes_read); - if (b <= 0) { - if (b == 0) - g_snprintf(errmsg, errmsgl, "End of file on pipe header during open"); - else - g_snprintf(errmsg, errmsgl, "Error on pipe header during open: %s", - strerror(errno)); - goto error; - } - bytes_read += b; - } + if ((pcap_opts->from_cap_socket) +#ifndef _WIN32 + || 1 +#endif + ) + { + /* Read 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) { + g_snprintf(errmsg, 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_opts->from_cap_socket); + if (b <= 0) { + if (b == 0) + g_snprintf(errmsg, errmsgl, "End of file on pipe header during open"); + else + g_snprintf(errmsg, errmsgl, "Error on pipe header during open: %s", + g_strerror(errno)); + goto error; + } + bytes_read += b; + } + } } -#else /* USE_THREADS */ - pcap_opts->cap_pipe_buf = (char *) hdr; - pcap_opts->cap_pipe_bytes_read = 0; - pcap_opts->cap_pipe_bytes_to_read = sizeof(struct pcap_hdr); - g_async_queue_push(pcap_opts->cap_pipe_pending_q, pcap_opts->cap_pipe_buf); - g_async_queue_pop(pcap_opts->cap_pipe_done_q); - if (pcap_opts->cap_pipe_bytes_read <= 0) { - if (pcap_opts->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; +#ifdef _WIN32 + else { + pcap_opts->cap_pipe_buf = (char *) hdr; + pcap_opts->cap_pipe_bytes_read = 0; + pcap_opts->cap_pipe_bytes_to_read = sizeof(struct pcap_hdr); + g_async_queue_push(pcap_opts->cap_pipe_pending_q, pcap_opts->cap_pipe_buf); + g_async_queue_pop(pcap_opts->cap_pipe_done_q); + if (pcap_opts->cap_pipe_bytes_read <= 0) { + if (pcap_opts->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", + g_strerror(errno)); + goto error; + } } -#endif /* USE_THREADS */ +#endif if (pcap_opts->cap_pipe_byte_swapped) { /* Byte-swap the header fields about which we care. */ @@ -1958,18 +2236,14 @@ cap_pipe_open_live(char *pipename, pcap_opts->cap_pipe_state = STATE_EXPECT_REC_HDR; pcap_opts->cap_pipe_err = PIPOK; -#ifndef _WIN32 pcap_opts->cap_pipe_fd = fd; -#endif return; error: g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "cap_pipe_open_live: error %s", errmsg); pcap_opts->cap_pipe_err = PIPERR; -#ifndef _WIN32 - ws_close(fd); + cap_pipe_close(fd, pcap_opts->from_cap_socket); pcap_opts->cap_pipe_fd = -1; -#endif return; } @@ -1983,15 +2257,14 @@ cap_pipe_dispatch(loop_data *ld, pcap_options *pcap_opts, guchar *data, char *er struct pcap_pkthdr phdr; enum { PD_REC_HDR_READ, PD_DATA_READ, PD_PIPE_EOF, PD_PIPE_ERR, PD_ERR } result; -#ifdef USE_THREADS +#ifdef _WIN32 +#if !GLIB_CHECK_VERSION(2,31,18) GTimeVal wait_time; - gpointer q_status; -#else - int b; #endif -#ifdef _WIN32 + gpointer q_status; wchar_t *err_str; #endif + int b; #ifdef LOG_CAPTURE_VERBOSE g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "cap_pipe_dispatch"); @@ -2000,7 +2273,7 @@ cap_pipe_dispatch(loop_data *ld, pcap_options *pcap_opts, guchar *data, char *er switch (pcap_opts->cap_pipe_state) { case STATE_EXPECT_REC_HDR: -#ifdef USE_THREADS +#ifdef _WIN32 if (g_mutex_trylock(pcap_opts->cap_pipe_read_mtx)) { #endif @@ -2009,7 +2282,7 @@ cap_pipe_dispatch(loop_data *ld, pcap_options *pcap_opts, guchar *data, char *er sizeof(struct pcaprec_modified_hdr) : sizeof(struct pcaprec_hdr); pcap_opts->cap_pipe_bytes_read = 0; -#ifdef USE_THREADS +#ifdef _WIN32 pcap_opts->cap_pipe_buf = (char *) &pcap_opts->cap_pipe_rechdr; g_async_queue_push(pcap_opts->cap_pipe_pending_q, pcap_opts->cap_pipe_buf); g_mutex_unlock(pcap_opts->cap_pipe_read_mtx); @@ -2018,9 +2291,14 @@ cap_pipe_dispatch(loop_data *ld, pcap_options *pcap_opts, guchar *data, char *er /* Fall through */ case STATE_READ_REC_HDR: -#ifndef USE_THREADS - b = read(pcap_opts->cap_pipe_fd, ((char *)&pcap_opts->cap_pipe_rechdr)+pcap_opts->cap_pipe_bytes_read, - pcap_opts->cap_pipe_bytes_to_read - pcap_opts->cap_pipe_bytes_read); + if ((pcap_opts->from_cap_socket) +#ifndef _WIN32 + || 1 +#endif + ) + { + b = cap_pipe_read(pcap_opts->cap_pipe_fd, ((char *)&pcap_opts->cap_pipe_rechdr)+pcap_opts->cap_pipe_bytes_read, + pcap_opts->cap_pipe_bytes_to_read - pcap_opts->cap_pipe_bytes_read, pcap_opts->from_cap_socket); if (b <= 0) { if (b == 0) result = PD_PIPE_EOF; @@ -2029,10 +2307,17 @@ cap_pipe_dispatch(loop_data *ld, pcap_options *pcap_opts, guchar *data, char *er break; } pcap_opts->cap_pipe_bytes_read += b; -#else /* USE_THREADS */ + } +#ifdef _WIN32 + else + { +#if GLIB_CHECK_VERSION(2,31,18) + q_status = g_async_queue_timeout_pop(pcap_opts->cap_pipe_done_q, PIPE_READ_TIMEOUT); +#else g_get_current_time(&wait_time); g_time_val_add(&wait_time, PIPE_READ_TIMEOUT); q_status = g_async_queue_timed_pop(pcap_opts->cap_pipe_done_q, &wait_time); +#endif if (pcap_opts->cap_pipe_err == PIPEOF) { result = PD_PIPE_EOF; break; @@ -2043,14 +2328,15 @@ cap_pipe_dispatch(loop_data *ld, pcap_options *pcap_opts, guchar *data, char *er if (!q_status) { return 0; } -#endif /* USE_THREADS */ + } +#endif if ((pcap_opts->cap_pipe_bytes_read) < pcap_opts->cap_pipe_bytes_to_read) return 0; result = PD_REC_HDR_READ; break; case STATE_EXPECT_DATA: -#ifdef USE_THREADS +#ifdef _WIN32 if (g_mutex_trylock(pcap_opts->cap_pipe_read_mtx)) { #endif @@ -2058,7 +2344,7 @@ cap_pipe_dispatch(loop_data *ld, pcap_options *pcap_opts, guchar *data, char *er pcap_opts->cap_pipe_bytes_to_read = pcap_opts->cap_pipe_rechdr.hdr.incl_len; pcap_opts->cap_pipe_bytes_read = 0; -#ifdef USE_THREADS +#ifdef _WIN32 pcap_opts->cap_pipe_buf = (char *) data; g_async_queue_push(pcap_opts->cap_pipe_pending_q, pcap_opts->cap_pipe_buf); g_mutex_unlock(pcap_opts->cap_pipe_read_mtx); @@ -2067,9 +2353,14 @@ cap_pipe_dispatch(loop_data *ld, pcap_options *pcap_opts, guchar *data, char *er /* Fall through */ case STATE_READ_DATA: -#ifndef USE_THREADS - b = read(pcap_opts->cap_pipe_fd, data+pcap_opts->cap_pipe_bytes_read, - pcap_opts->cap_pipe_bytes_to_read - pcap_opts->cap_pipe_bytes_read); + if ((pcap_opts->from_cap_socket) +#ifndef _WIN32 + || 1 +#endif + ) + { + b = cap_pipe_read(pcap_opts->cap_pipe_fd, data+pcap_opts->cap_pipe_bytes_read, + pcap_opts->cap_pipe_bytes_to_read - pcap_opts->cap_pipe_bytes_read, pcap_opts->from_cap_socket); if (b <= 0) { if (b == 0) result = PD_PIPE_EOF; @@ -2078,10 +2369,18 @@ cap_pipe_dispatch(loop_data *ld, pcap_options *pcap_opts, guchar *data, char *er break; } pcap_opts->cap_pipe_bytes_read += b; -#else /* USE_THREADS */ + } +#ifdef _WIN32 + else + { + +#if GLIB_CHECK_VERSION(2,31,18) + q_status = g_async_queue_timeout_pop(pcap_opts->cap_pipe_done_q, PIPE_READ_TIMEOUT); +#else g_get_current_time(&wait_time); g_time_val_add(&wait_time, PIPE_READ_TIMEOUT); q_status = g_async_queue_timed_pop(pcap_opts->cap_pipe_done_q, &wait_time); +#endif if (pcap_opts->cap_pipe_err == PIPEOF) { result = PD_PIPE_EOF; break; @@ -2092,7 +2391,8 @@ cap_pipe_dispatch(loop_data *ld, pcap_options *pcap_opts, guchar *data, char *er if (!q_status) { return 0; } -#endif /* USE_THREADS */ + } +#endif if ((pcap_opts->cap_pipe_bytes_read) < pcap_opts->cap_pipe_bytes_to_read) return 0; result = PD_DATA_READ; @@ -2102,7 +2402,7 @@ cap_pipe_dispatch(loop_data *ld, pcap_options *pcap_opts, guchar *data, char *er g_snprintf(errmsg, errmsgl, "cap_pipe_dispatch: invalid state"); result = PD_ERR; - } /* switch (ld->cap_pipe_state) */ + } /* switch (pcap_opts->cap_pipe_state) */ /* * We've now read as much data as we were expecting, so process it. @@ -2118,8 +2418,12 @@ cap_pipe_dispatch(loop_data *ld, pcap_options *pcap_opts, guchar *data, char *er ld->packet_count+1, pcap_opts->cap_pipe_rechdr.hdr.incl_len); break; } - pcap_opts->cap_pipe_state = STATE_EXPECT_DATA; - return 0; + + if (pcap_opts->cap_pipe_rechdr.hdr.incl_len) { + pcap_opts->cap_pipe_state = STATE_EXPECT_DATA; + return 0; + } + /* no data to read? fall through */ case PD_DATA_READ: /* Fill in a "struct pcap_pkthdr", and process the packet. */ @@ -2142,7 +2446,7 @@ cap_pipe_dispatch(loop_data *ld, pcap_options *pcap_opts, guchar *data, char *er case PD_PIPE_ERR: #ifdef _WIN32 - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), 0, (LPTSTR) &err_str, 0, NULL); g_snprintf(errmsg, errmsgl, "Error reading from pipe: %s (error %d)", @@ -2150,7 +2454,7 @@ cap_pipe_dispatch(loop_data *ld, pcap_options *pcap_opts, guchar *data, char *er LocalFree(err_str); #else g_snprintf(errmsg, errmsgl, "Error reading from pipe: %s", - strerror(errno)); + g_strerror(errno)); #endif /* Fall through */ case PD_ERR: @@ -2232,7 +2536,7 @@ capture_loop_open_input(capture_options *capture_opts, loop_data *ld, if ((use_threads == FALSE) && (capture_opts->ifaces->len > 1)) { g_snprintf(errmsg, (gulong) errmsg_len, - "Using threads is required for capturing on mulitple interfaces! Use the -t option."); + "Using threads is required for capturing on multiple interfaces!"); return FALSE; } @@ -2255,25 +2559,31 @@ capture_loop_open_input(capture_options *capture_opts, loop_data *ld, pcap_opts->tid = NULL; pcap_opts->snaplen = 0; pcap_opts->linktype = -1; + pcap_opts->ts_nsec = FALSE; pcap_opts->from_cap_pipe = FALSE; + pcap_opts->from_cap_socket = FALSE; memset(&pcap_opts->cap_pipe_hdr, 0, sizeof(struct pcap_hdr)); memset(&pcap_opts->cap_pipe_rechdr, 0, sizeof(struct pcaprec_modified_hdr)); #ifdef _WIN32 pcap_opts->cap_pipe_h = INVALID_HANDLE_VALUE; -#else - pcap_opts->cap_pipe_fd = -1; #endif + pcap_opts->cap_pipe_fd = -1; pcap_opts->cap_pipe_modified = FALSE; pcap_opts->cap_pipe_byte_swapped = FALSE; -#ifdef USE_THREADS +#ifdef _WIN32 pcap_opts->cap_pipe_buf = NULL; -#endif /* USE_THREADS */ +#endif pcap_opts->cap_pipe_bytes_to_read = 0; pcap_opts->cap_pipe_bytes_read = 0; pcap_opts->cap_pipe_state = 0; pcap_opts->cap_pipe_err = PIPOK; -#ifdef USE_THREADS +#ifdef _WIN32 +#if GLIB_CHECK_VERSION(2,31,0) + pcap_opts->cap_pipe_read_mtx = g_malloc(sizeof(GMutex)); + g_mutex_init(pcap_opts->cap_pipe_read_mtx); +#else pcap_opts->cap_pipe_read_mtx = g_mutex_new(); +#endif pcap_opts->cap_pipe_pending_q = g_async_queue_new(); pcap_opts->cap_pipe_done_q = g_async_queue_new(); #endif @@ -2415,13 +2725,12 @@ static void capture_loop_close_input(loop_data *ld) for (i = 0; i < ld->pcaps->len; i++) { pcap_opts = g_array_index(ld->pcaps, pcap_options *, i); /* if open, close the capture pipe "input file" */ -#ifndef _WIN32 if (pcap_opts->cap_pipe_fd >= 0) { g_assert(pcap_opts->from_cap_pipe); - ws_close(pcap_opts->cap_pipe_fd); + cap_pipe_close(pcap_opts->cap_pipe_fd, pcap_opts->from_cap_socket); pcap_opts->cap_pipe_fd = -1; } -#else +#ifdef _WIN32 if (pcap_opts->cap_pipe_h != INVALID_HANDLE_VALUE) { CloseHandle(pcap_opts->cap_pipe_h); pcap_opts->cap_pipe_h = INVALID_HANDLE_VALUE; @@ -2429,7 +2738,7 @@ static void capture_loop_close_input(loop_data *ld) #endif /* if open, close the pcap "input file" */ if (pcap_opts->pcap_h != NULL) { - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_close_input: closing %p", pcap_opts->pcap_h); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_close_input: closing %p", (void *)pcap_opts->pcap_h); pcap_close(pcap_opts->pcap_h); pcap_opts->pcap_h = NULL; } @@ -2447,7 +2756,7 @@ static void capture_loop_close_input(loop_data *ld) /* init the capture filter */ static initfilter_status_t capture_loop_init_filter(pcap_t *pcap_h, gboolean from_cap_pipe, - gchar * name, gchar * cfilter) + const gchar * name, const gchar * cfilter) { struct bpf_program fcode; @@ -2492,7 +2801,7 @@ capture_loop_init_output(capture_options *capture_opts, loop_data *ld, char *err if ((capture_opts->use_pcapng == FALSE) && (capture_opts->ifaces->len > 1)) { g_snprintf(errmsg, errmsg_len, - "Using PCAPNG is required for capturing on mulitple interfaces! Use the -n option."); + "Using PCAPNG is required for capturing on multiple interfaces! Use the -n option."); return FALSE; } @@ -2505,9 +2814,21 @@ capture_loop_init_output(capture_options *capture_opts, loop_data *ld, char *err if (ld->pdh) { if (capture_opts->use_pcapng) { char appname[100]; + GString *os_info_str; + + os_info_str = g_string_new(""); + get_os_version_info(os_info_str); g_snprintf(appname, sizeof(appname), "Dumpcap " VERSION "%s", wireshark_svnversion); - successful = libpcap_write_session_header_block(ld->pdh, appname, &ld->bytes_written, &err); + successful = libpcap_write_session_header_block(ld->pdh, + NULL, /* Comment*/ + NULL, /* HW*/ + os_info_str->str, /* OS*/ + appname, + -1, /* section_length */ + &ld->bytes_written, + &err); + for (i = 0; successful && (i < capture_opts->ifaces->len); i++) { interface_opts = g_array_index(capture_opts->ifaces, interface_options, i); pcap_opts = g_array_index(ld->pcaps, pcap_options *, i); @@ -2516,16 +2837,23 @@ capture_loop_init_output(capture_options *capture_opts, loop_data *ld, char *err } else { pcap_opts->snaplen = pcap_snapshot(pcap_opts->pcap_h); } - successful = libpcap_write_interface_description_block(ld->pdh, - interface_opts.name, - interface_opts.cfilter, + successful = libpcap_write_interface_description_block(global_ld.pdh, + NULL, /* OPT_COMMENT 1 */ + interface_opts.name, /* IDB_NAME 2 */ + interface_opts.descr, /* IDB_DESCRIPTION 3 */ + interface_opts.cfilter, /* IDB_FILTER 11 */ + os_info_str->str, /* IDB_OS 12 */ pcap_opts->linktype, pcap_opts->snaplen, - &ld->bytes_written, - &err); + &(global_ld.bytes_written), + 0, /* IDB_IF_SPEED 8 */ + pcap_opts->ts_nsec ? 9 : 6, /* IDB_TSRESOL 9 */ + &global_ld.err); } + + g_string_free(os_info_str, TRUE); + } else { - interface_opts = g_array_index(capture_opts->ifaces, interface_options, 0); pcap_opts = g_array_index(ld->pcaps, pcap_options *, 0); if (pcap_opts->from_cap_pipe) { pcap_opts->snaplen = pcap_opts->cap_pipe_hdr.snaplen; @@ -2533,7 +2861,7 @@ capture_loop_init_output(capture_options *capture_opts, loop_data *ld, char *err pcap_opts->snaplen = pcap_snapshot(pcap_opts->pcap_h); } successful = libpcap_write_file_header(ld->pdh, pcap_opts->linktype, pcap_opts->snaplen, - &ld->bytes_written, &err); + pcap_opts->ts_nsec, &ld->bytes_written, &err); } if (!successful) { fclose(ld->pdh); @@ -2556,7 +2884,7 @@ capture_loop_init_output(capture_options *capture_opts, loop_data *ld, char *err 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)); + capture_opts->save_file, g_strerror(err)); } break; } @@ -2573,6 +2901,7 @@ capture_loop_close_output(capture_options *capture_opts, loop_data *ld, int *err unsigned int i; pcap_options *pcap_opts; + guint64 end_time = create_timestamp(); g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_close_output"); @@ -2583,7 +2912,25 @@ capture_loop_close_output(capture_options *capture_opts, loop_data *ld, int *err for (i = 0; i < global_ld.pcaps->len; i++) { pcap_opts = g_array_index(global_ld.pcaps, pcap_options *, i); if (!pcap_opts->from_cap_pipe) { - libpcap_write_interface_statistics_block(ld->pdh, i, pcap_opts->pcap_h, &ld->bytes_written, err_close); + guint64 isb_ifrecv, isb_ifdrop; + struct pcap_stat stats; + + if (pcap_stats(pcap_opts->pcap_h, &stats) >= 0) { + isb_ifrecv = pcap_opts->received; + isb_ifdrop = stats.ps_drop + pcap_opts->dropped; + } else { + isb_ifrecv = G_MAXUINT64; + isb_ifdrop = G_MAXUINT64; + } + libpcap_write_interface_statistics_block(ld->pdh, + i, + &ld->bytes_written, + "Counters provided by dumpcap", + start_time, + end_time, + isb_ifrecv, + isb_ifdrop, + err_close); } } } @@ -2609,7 +2956,7 @@ capture_loop_dispatch(loop_data *ld, int inpkts; gint packet_count_before; guchar pcap_data[WTAP_MAX_PACKET_SIZE]; -#ifndef USE_THREADS +#ifndef _WIN32 int sel_ret; #endif @@ -2619,12 +2966,12 @@ capture_loop_dispatch(loop_data *ld, #ifdef LOG_CAPTURE_VERBOSE g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: from capture pipe"); #endif -#ifndef USE_THREADS +#ifndef _WIN32 sel_ret = cap_pipe_select(pcap_opts->cap_pipe_fd); if (sel_ret <= 0) { if (sel_ret < 0 && errno != EINTR) { g_snprintf(errmsg, errmsg_len, - "Unexpected error from select: %s", strerror(errno)); + "Unexpected error from select: %s", g_strerror(errno)); report_capture_error(errmsg, please_report); ld->go = FALSE; } @@ -2632,12 +2979,12 @@ capture_loop_dispatch(loop_data *ld, /* * "select()" says we can read from the pipe without blocking */ -#endif /* USE_THREADS */ +#endif inpkts = cap_pipe_dispatch(ld, pcap_opts, pcap_data, errmsg, errmsg_len); if (inpkts < 0) { ld->go = FALSE; } -#ifndef USE_THREADS +#ifndef _WIN32 } #endif } @@ -2688,7 +3035,7 @@ capture_loop_dispatch(loop_data *ld, } else { if (sel_ret < 0 && errno != EINTR) { g_snprintf(errmsg, errmsg_len, - "Unexpected error from select: %s", strerror(errno)); + "Unexpected error from select: %s", g_strerror(errno)); report_capture_error(errmsg, please_report); ld->go = FALSE; } @@ -2863,15 +3210,17 @@ capture_loop_open_output(capture_options *capture_opts, int *save_file_fd, if (global_capture_opts.ifaces->len > 1) { prefix = g_strdup_printf("wireshark_%d_interfaces", global_capture_opts.ifaces->len); } else { + gchar *basename; #ifdef _WIN32 GString *iface; - iface = isolate_uuid(g_array_index(global_capture_opts.ifaces, interface_options, 0).name); - prefix = g_strconcat("wireshark_", g_basename(iface->str), NULL); + basename = g_path_get_basename(iface->str); g_string_free(iface, TRUE); #else - prefix = g_strconcat("wireshark_", g_basename(g_array_index(global_capture_opts.ifaces, interface_options, 0).name), NULL); + basename = g_path_get_basename(g_array_index(global_capture_opts.ifaces, interface_options, 0).name); #endif + prefix = g_strconcat("wireshark_", basename, NULL); + g_free(basename); } *save_file_fd = create_tempfile(&tmpname, prefix); g_free(prefix); @@ -2884,7 +3233,7 @@ capture_loop_open_output(capture_options *capture_opts, int *save_file_fd, if (is_tempfile) { g_snprintf(errmsg, errmsg_len, "The temporary file to which the capture would be saved (\"%s\") " - "could not be opened: %s.", capfile_name, strerror(errno)); + "could not be opened: %s.", capfile_name, g_strerror(errno)); } else { if (capture_opts->multi_files_on) { ringbuf_error_cleanup(); @@ -2893,7 +3242,7 @@ capture_loop_open_output(capture_options *capture_opts, int *save_file_fd, g_snprintf(errmsg, errmsg_len, "The file to which the capture would be saved (\"%s\") " "could not be opened: %s.", capfile_name, - strerror(errno)); + g_strerror(errno)); } g_free(capfile_name); return FALSE; @@ -2939,25 +3288,44 @@ do_file_switch_or_stop(capture_options *capture_opts, global_ld.bytes_written = 0; if (capture_opts->use_pcapng) { char appname[100]; + GString *os_info_str; + + os_info_str = g_string_new(""); + get_os_version_info(os_info_str); 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); + successful = libpcap_write_session_header_block(global_ld.pdh, + NULL, /* Comment */ + NULL, /* HW */ + os_info_str->str, /* OS */ + appname, + -1, /* section_length */ + &(global_ld.bytes_written), + &global_ld.err); + for (i = 0; successful && (i < capture_opts->ifaces->len); i++) { interface_opts = g_array_index(capture_opts->ifaces, interface_options, i); pcap_opts = g_array_index(global_ld.pcaps, pcap_options *, i); successful = libpcap_write_interface_description_block(global_ld.pdh, - interface_opts.name, - interface_opts.cfilter, + NULL, /* OPT_COMMENT 1 */ + interface_opts.name, /* IDB_NAME 2 */ + interface_opts.descr, /* IDB_DESCRIPTION 3 */ + interface_opts.cfilter, /* IDB_FILTER 11 */ + os_info_str->str, /* IDB_OS 12 */ pcap_opts->linktype, pcap_opts->snaplen, &(global_ld.bytes_written), + 0, /* IDB_IF_SPEED 8 */ + pcap_opts->ts_nsec ? 9 : 6, /* IDB_TSRESOL 9 */ &global_ld.err); } + + g_string_free(os_info_str, TRUE); + } else { - interface_opts = g_array_index(capture_opts->ifaces, interface_options, 0); pcap_opts = g_array_index(global_ld.pcaps, pcap_options *, 0); successful = libpcap_write_file_header(global_ld.pdh, pcap_opts->linktype, pcap_opts->snaplen, - &global_ld.bytes_written, &global_ld.err); + pcap_opts->ts_nsec, &global_ld.bytes_written, &global_ld.err); } if (!successful) { fclose(global_ld.pdh); @@ -3014,7 +3382,7 @@ static gboolean capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct pcap_stat *stats) { #ifdef WIN32 - time_t upd_time, cur_time; + DWORD upd_time, cur_time; /* GetTickCount() returns a "DWORD" (which is 'unsigned long') */ #else struct timeval upd_time, cur_time; #endif @@ -3031,9 +3399,8 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct char secondary_errmsg[MSG_MAX_LENGTH+1]; pcap_options *pcap_opts; interface_options interface_opts; - guint i; + guint i, error_index = 0; - interface_opts = capture_opts->default_options; *errmsg = '\0'; *secondary_errmsg = '\0'; @@ -3043,7 +3410,6 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct #ifdef SIGINFO global_ld.report_packet_count = FALSE; #endif - global_ld.pcaps = g_array_new(FALSE, FALSE, sizeof(pcap_options *)); if (capture_opts->has_autostop_packets) global_ld.packet_max = capture_opts->autostop_packets; else @@ -3069,15 +3435,21 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct pcap_opts = g_array_index(global_ld.pcaps, pcap_options *, i); interface_opts = g_array_index(capture_opts->ifaces, interface_options, i); /* init the input filter from the network interface (capture pipe will do nothing) */ + /* + * When remote capturing WinPCap crashes when the capture filter + * is NULL. This might be a bug in WPCap. Therefore we provide an empty + * string. + */ switch (capture_loop_init_filter(pcap_opts->pcap_h, pcap_opts->from_cap_pipe, interface_opts.name, - interface_opts.cfilter)) { + interface_opts.cfilter?interface_opts.cfilter:"")) { case INITFILTER_NO_ERROR: break; case INITFILTER_BAD_FILTER: cfilter_error = TRUE; + error_index = i; g_snprintf(errmsg, sizeof(errmsg), "%s", pcap_geterr(pcap_opts->pcap_h)); goto error; @@ -3146,7 +3518,7 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct #else gettimeofday(&upd_time, NULL); #endif - + start_time = create_timestamp(); g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Capture loop running!"); /* WOW, everything is prepared! */ @@ -3157,20 +3529,30 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct pcap_queue_packets = 0; for (i = 0; i < global_ld.pcaps->len; i++) { pcap_opts = g_array_index(global_ld.pcaps, pcap_options *, i); +#if GLIB_CHECK_VERSION(2,31,0) + /* XXX - Add an interface name here? */ + pcap_opts->tid = g_thread_new("Capture read", pcap_read_handler, pcap_opts); +#else pcap_opts->tid = g_thread_create(pcap_read_handler, pcap_opts, TRUE, NULL); +#endif } } - pcap_opts = g_array_index(global_ld.pcaps, pcap_options *, 0); while (global_ld.go) { /* dispatch incoming packets */ if (use_threads) { - GTimeVal write_thread_time; pcap_queue_element *queue_element; +#if GLIB_CHECK_VERSION(2,31,18) + + g_async_queue_lock(pcap_queue); + queue_element = g_async_queue_timeout_pop_unlocked(pcap_queue, WRITER_THREAD_TIMEOUT); +#else + GTimeVal write_thread_time; g_get_current_time(&write_thread_time); g_time_val_add(&write_thread_time, WRITER_THREAD_TIMEOUT); g_async_queue_lock(pcap_queue); queue_element = g_async_queue_timed_pop_unlocked(pcap_queue, &write_thread_time); +#endif if (queue_element) { pcap_queue_bytes -= queue_element->phdr.caplen; pcap_queue_packets -= 1; @@ -3179,9 +3561,9 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct if (queue_element) { g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Dequeued a packet of length %d captured on interface %d.", - queue_element->phdr.caplen, queue_element->interface_id); + queue_element->phdr.caplen, queue_element->pcap_opts->interface_id); - capture_loop_write_packet_cb((u_char *)&queue_element->interface_id, + capture_loop_write_packet_cb((u_char *) queue_element->pcap_opts, &queue_element->phdr, queue_element->pd); g_free(queue_element->pd); @@ -3191,6 +3573,7 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct inpkts = 0; } } else { + pcap_opts = g_array_index(global_ld.pcaps, pcap_options *, 0); inpkts = capture_loop_dispatch(&global_ld, errmsg, sizeof(errmsg), pcap_opts); } @@ -3233,8 +3616,8 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct #define DUMPCAP_UPD_TIME 500 #ifdef WIN32 - cur_time = GetTickCount(); - if ( (cur_time - upd_time) > DUMPCAP_UPD_TIME) { + cur_time = GetTickCount(); /* Note: wraps to 0 if sys runs for 49.7 days */ + if ((cur_time - upd_time) > DUMPCAP_UPD_TIME) { /* wrap just causes an extra update */ #else gettimeofday(&cur_time, NULL); if ((cur_time.tv_sec * 1000000 + cur_time.tv_usec) > @@ -3303,8 +3686,8 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct } g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Dequeued a packet of length %d captured on interface %d.", - queue_element->phdr.caplen, queue_element->interface_id); - capture_loop_write_packet_cb((u_char *)&queue_element->interface_id, + queue_element->phdr.caplen, queue_element->pcap_opts->interface_id); + capture_loop_write_packet_cb((u_char *)queue_element->pcap_opts, &queue_element->phdr, queue_element->pd); g_free(queue_element->pd); @@ -3334,7 +3717,7 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct /* On Linux, if an interface goes down while you're capturing on it, you'll get a "recvfrom: Network is down" or "The interface went down" error (ENETDOWN). - (At least you will if strerror() doesn't show a local translation + (At least you will if g_strerror() doesn't show a local translation of the error.) On FreeBSD and OS X, if a network adapter disappears while @@ -3463,7 +3846,7 @@ error: } capture_opts->save_file = NULL; if (cfilter_error) - report_cfilter_error(interface_opts.cfilter, errmsg); + report_cfilter_error(capture_opts, error_index, errmsg); else report_capture_error(errmsg, secondary_errmsg); @@ -3524,13 +3907,13 @@ capture_loop_get_errmsg(char *errmsg, int errmsglen, const char *fname, g_snprintf(errmsg, errmsglen, "The file to which the capture was being saved\n" "(\"%s\") could not be closed: %s.", - fname, strerror(err)); + fname, g_strerror(err)); } else { g_snprintf(errmsg, errmsglen, "An error occurred while writing to the file" " to which the capture was being saved\n" "(\"%s\"): %s.", - fname, strerror(err)); + fname, g_strerror(err)); } break; } @@ -3539,19 +3922,21 @@ capture_loop_get_errmsg(char *errmsg, int errmsglen, const char *fname, /* one packet was captured, process it */ static void -capture_loop_write_packet_cb(u_char *user, const struct pcap_pkthdr *phdr, +capture_loop_write_packet_cb(u_char *pcap_opts_p, const struct pcap_pkthdr *phdr, const u_char *pd) { - pcap_options *pcap_opts; + pcap_options *pcap_opts = (pcap_options *) (void *) pcap_opts_p; int err; + guint ts_mul = pcap_opts->ts_nsec ? 1000000000 : 1000000; /* 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. */ - if (!global_ld.go) + if (!global_ld.go) { + pcap_opts->dropped++; return; + } - pcap_opts = (pcap_options *) (void *) user; if (global_ld.pdh) { gboolean successful; @@ -3559,13 +3944,14 @@ capture_loop_write_packet_cb(u_char *user, const struct pcap_pkthdr *phdr, If this fails, set "ld->go" to FALSE, to stop the capture, and set "ld->err" to the error. */ if (global_capture_opts.use_pcapng) { - successful = libpcap_write_enhanced_packet_block(global_ld.pdh, phdr, pcap_opts->interface_id, pd, &global_ld.bytes_written, &err); + successful = libpcap_write_enhanced_packet_block(global_ld.pdh, phdr, pcap_opts->interface_id, ts_mul, pd, &global_ld.bytes_written, &err); } else { successful = libpcap_write_packet(global_ld.pdh, phdr, pd, &global_ld.bytes_written, &err); } if (!successful) { global_ld.go = FALSE; global_ld.err = err; + pcap_opts->dropped++; } else { g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Wrote a packet of length %d captured on interface %u.", @@ -3582,26 +3968,27 @@ capture_loop_write_packet_cb(u_char *user, const struct pcap_pkthdr *phdr, /* one packet was captured, queue it */ static void -capture_loop_queue_packet_cb(u_char *user, const struct pcap_pkthdr *phdr, +capture_loop_queue_packet_cb(u_char *pcap_opts_p, const struct pcap_pkthdr *phdr, const u_char *pd) { - pcap_options *pcap_opts; + pcap_options *pcap_opts = (pcap_options *) (void *) pcap_opts_p; pcap_queue_element *queue_element; gboolean limit_reached; /* 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. */ - if (!global_ld.go) + if (!global_ld.go) { + pcap_opts->dropped++; return; + } - pcap_opts = (pcap_options *) (void *) user; queue_element = (pcap_queue_element *)g_malloc(sizeof(pcap_queue_element)); if (queue_element == NULL) { pcap_opts->dropped++; return; } - queue_element->interface_id = pcap_opts->interface_id; + queue_element->pcap_opts = pcap_opts; queue_element->phdr = *phdr; queue_element->pd = (u_char *)g_malloc(phdr->caplen); if (queue_element->pd == NULL) { @@ -3641,6 +4028,49 @@ capture_loop_queue_packet_cb(u_char *user, const struct pcap_pkthdr *phdr, pcap_queue_bytes, pcap_queue_packets); } +static int +set_80211_channel(const char *iface, const char *opt) +{ + int freq = 0, type, ret; + gchar **options = NULL; + options = g_strsplit_set(opt, ",", 2); + + if (options[0]) + freq = atoi(options[0]); + + if (options[1]) { + type = ws80211_str_to_chan_type(options[1]); + if (type == -1) { + ret = EINVAL; + goto out; + } + } + else + type = -1; + + ret = ws80211_init(); + if (ret) { + cmdarg_err("%d: Failed to init ws80211: %s\n", abs(ret), g_strerror(abs(ret))); + ret = 2; + goto out; + } + ret = ws80211_set_freq(iface, freq, type); + + if (ret) { + cmdarg_err("%d: Failed to set channel: %s\n", abs(ret), g_strerror(abs(ret))); + ret = 2; + goto out; + } + + if (capture_child) + pipe_write_block(2, SP_SUCCESS, NULL); + ret = 0; + +out: + g_strfreev(options); + return ret; +} + /* And now our feature presentation... [ fade to music ] */ int main(int argc, char *argv[]) @@ -3663,6 +4093,8 @@ main(int argc, char *argv[]) #ifdef HAVE_BPF_IMAGE gboolean print_bpf_code = FALSE; #endif + gboolean set_chan = FALSE; + gchar *set_chan_arg = NULL; gboolean machine_readable = FALSE; gboolean print_statistics = FALSE; int status, run_once_args = 0; @@ -3671,6 +4103,7 @@ main(int argc, char *argv[]) #if defined(__APPLE__) && defined(__LP64__) struct utsname osinfo; #endif + GString *str; #ifdef _WIN32 arg_list_utf_16to8(argc, argv); @@ -3718,7 +4151,7 @@ main(int argc, char *argv[]) #define OPTSTRING_d "" #endif -#define OPTSTRING "a:" OPTSTRING_A "b:" OPTSTRING_B "c:" OPTSTRING_d "Df:ghi:" OPTSTRING_I "L" OPTSTRING_m "Mnpq" OPTSTRING_r "Ss:t" OPTSTRING_u "vw:y:Z:" +#define OPTSTRING "a:" OPTSTRING_A "b:" OPTSTRING_B "c:" OPTSTRING_d "Df:ghi:" OPTSTRING_I "k:L" OPTSTRING_m "MnpPq" OPTSTRING_r "Ss:t" OPTSTRING_u "vw:y:Z:" #ifdef DEBUG_CHILD_DUMPCAP if ((debug_log = ws_fopen("dumpcap_debug_log.tmp","w")) == NULL) { @@ -3815,9 +4248,14 @@ main(int argc, char *argv[]) log_flags, console_log_handler, NULL /* user_data */); + /* Initialize the pcaps list */ + global_ld.pcaps = g_array_new(FALSE, FALSE, sizeof(pcap_options *)); + +#if !GLIB_CHECK_VERSION(2,31,0) /* Initialize the thread system */ - if (!g_thread_supported()) - g_thread_init(NULL); + g_thread_init(NULL); +#endif + #ifdef _WIN32 /* Load wpcap if possible. Do this before collecting the run-time version information */ load_wpcap(); @@ -3833,7 +4271,18 @@ main(int argc, char *argv[]) SetConsoleCtrlHandler(capture_cleanup_handler, TRUE); #else /* Catch SIGINT and SIGTERM and, if we get either of them, clean up - and exit. */ + and exit. Do the same with SIGPIPE, in case, for example, + we're writing to our standard output and it's a pipe. + Do the same with SIGHUP if it's not being ignored (if we're + being run under nohup, it might be ignored, in which case we + should leave it ignored). + + XXX - apparently, Coverity complained that part of action + wasn't initialized. Perhaps it's running on Linux, where + struct sigaction has an ignored "sa_restorer" element and + where "sa_handler" and "sa_sigaction" might not be two + members of a union. */ + memset(&action, 0, sizeof(action)); action.sa_handler = capture_cleanup_handler; /* * Arrange that system calls not get restarted, because when @@ -3945,9 +4394,6 @@ main(int argc, char *argv[]) by the command line parameters. */ capture_opts_init(&global_capture_opts, NULL); - /* Default to capturing the entire packet. */ - 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. */ global_capture_opts.saving_to_file = TRUE; @@ -3985,6 +4431,7 @@ main(int argc, char *argv[]) case 'i': /* Use interface x */ case 'n': /* Use pcapng format */ case 'p': /* Don't capture in promiscuous mode */ + case 'P': /* Use pcap format */ case 's': /* Set the snapshot (capture) length */ case 'w': /* Write to capture file x */ case 'g': /* enable group read accesson file(s) */ @@ -4036,7 +4483,6 @@ main(int argc, char *argv[]) case 'q': /* Quiet */ quiet = TRUE; break; - case 't': use_threads = TRUE; break; @@ -4059,32 +4505,39 @@ main(int argc, char *argv[]) print_statistics = TRUE; run_once_args++; break; + case 'k': /* Set wireless channel */ + set_chan = TRUE; + set_chan_arg = optarg; + run_once_args++; + break; case 'M': /* For -D, -L, and -S, print machine-readable output */ machine_readable = TRUE; break; default: - case '?': /* Bad flag - print usage message */ cmdarg_err("Invalid Option: %s", argv[optind-1]); + /* FALLTHROUGH */ + case '?': /* Bad flag - print usage message */ arg_error = TRUE; break; } } - argc -= optind; - argv += optind; - if (argc >= 1) { - /* user specified file name as regular command-line argument */ - /* XXX - use it as the capture file name (or something else)? */ - argc--; - argv++; - } - - if (argc != 0) { - /* - * Extra command line arguments were specified; complain. - * XXX - interpret as capture filter, as tcpdump and tshark do? - */ - cmdarg_err("Invalid argument: %s", argv[0]); - arg_error = TRUE; + if (!arg_error) { + argc -= optind; + argv += optind; + if (argc >= 1) { + /* user specified file name as regular command-line argument */ + /* XXX - use it as the capture file name (or something else)? */ + argc--; + argv++; + } + if (argc != 0) { + /* + * Extra command line arguments were specified; complain. + * XXX - interpret as capture filter, as tcpdump and tshark do? + */ + cmdarg_err("Invalid argument: %s", argv[0]); + arg_error = TRUE; + } } if (arg_error) { @@ -4143,6 +4596,7 @@ main(int argc, char *argv[]) if (if_list == NULL) { switch (err) { case CANT_GET_INTERFACE_LIST: + case DONT_HAVE_PCAP: cmdarg_err("%s", err_str); g_free(err_str); exit_main(2); @@ -4180,6 +4634,19 @@ main(int argc, char *argv[]) exit_main(status); } + if (set_chan) { + interface_options interface_opts; + + if (global_capture_opts.ifaces->len != 1) { + cmdarg_err("Need one interface"); + exit_main(2); + } + + interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, 0); + status = set_80211_channel(interface_opts.name, set_chan_arg); + 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. @@ -4191,11 +4658,41 @@ main(int argc, char *argv[]) /* Let the user know what interfaces were chosen. */ /* get_interface_descriptive_name() is not available! */ - for (j = 0; j < global_capture_opts.ifaces->len; j++) { - interface_options interface_opts; + if (capture_child) { + for (j = 0; j < global_capture_opts.ifaces->len; j++) { + interface_options interface_opts; + + interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, j); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Interface: %s\n", + interface_opts.name); + } + } else { + str = g_string_new(""); +#ifdef _WIN32 + if (global_capture_opts.ifaces->len < 2) { +#else + if (global_capture_opts.ifaces->len < 4) { +#endif + for (j = 0; j < global_capture_opts.ifaces->len; j++) { + interface_options interface_opts; - interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, j); - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Interface: %s", interface_opts.name); + interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, j); + if (j > 0) { + if (global_capture_opts.ifaces->len > 2) { + g_string_append_printf(str, ","); + } + g_string_append_printf(str, " "); + if (j == global_capture_opts.ifaces->len - 1) { + g_string_append_printf(str, "and "); + } + } + g_string_append_printf(str, "%s", interface_opts.name); + } + } else { + g_string_append_printf(str, "%u interfaces", global_capture_opts.ifaces->len); + } + fprintf(stderr, "Capturing on %s\n", str->str); + g_string_free(str, TRUE); } if (list_link_layer_types) { @@ -4350,13 +4847,13 @@ console_log_handler(const char *log_domain, GLogLevelFlags log_level, static void -report_packet_count(int packet_count) +report_packet_count(unsigned int packet_count) { char tmp[SP_DECISIZE+1+1]; - static int count = 0; + static unsigned int count = 0; if(capture_child) { - g_snprintf(tmp, sizeof(tmp), "%d", packet_count); + g_snprintf(tmp, sizeof(tmp), "%u", packet_count); g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Packets: %s", tmp); pipe_write_block(2, SP_PACKET_COUNT, tmp); } else { @@ -4402,18 +4899,29 @@ report_new_capture_file(const char *filename) } static void -report_cfilter_error(const char *cfilter, const char *errmsg) +report_cfilter_error(capture_options *capture_opts, guint i, const char *errmsg) { - if (capture_child) { - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Capture filter error: %s", errmsg); - pipe_write_block(2, SP_BAD_FILTER, errmsg); - } else { - fprintf(stderr, - "Invalid capture filter: \"%s\"!\n" - "\n" - "That string isn't a valid capture filter (%s).\n" - "See the User's Guide for a description of the capture filter syntax.\n", - cfilter, errmsg); + interface_options interface_opts; + char tmp[MSG_MAX_LENGTH+1+6]; + + if (i < capture_opts->ifaces->len) { + if (capture_child) { + g_snprintf(tmp, sizeof(tmp), "%u:%s", i, errmsg); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Capture filter error: %s", errmsg); + pipe_write_block(2, SP_BAD_FILTER, tmp); + } else { + /* + * clopts_step_invalid_capfilter in test/suite-clopts.sh MUST match + * the error message below. + */ + interface_opts = g_array_index(capture_opts->ifaces, interface_options, i); + cmdarg_err( + "Invalid capture filter: \"%s\" for interface %s!\n" + "\n" + "That string isn't a valid capture filter (%s).\n" + "See the User's Guide for a description of the capture filter syntax.", + interface_opts.cfilter, interface_opts.name, errmsg); + } } } @@ -4427,27 +4935,30 @@ report_capture_error(const char *error_msg, const char *secondary_error_msg) "Secondary Error: %s", secondary_error_msg); sync_pipe_errmsg_to_parent(2, error_msg, secondary_error_msg); } else { - fprintf(stderr, "%s\n", error_msg); + cmdarg_err("%s", error_msg); if (secondary_error_msg[0] != '\0') - fprintf(stderr, "%s\n", secondary_error_msg); + cmdarg_err_cont("%s", secondary_error_msg); } } static void report_packet_drops(guint32 received, guint32 drops, gchar *name) { - char tmp1[SP_DECISIZE+1+1]; - char tmp2[SP_DECISIZE+1+1]; + char tmp[SP_DECISIZE+1+1]; - g_snprintf(tmp1, sizeof(tmp1), "%u", received); - g_snprintf(tmp2, sizeof(tmp2), "%u", drops); + g_snprintf(tmp, sizeof(tmp), "%u", drops); if(capture_child) { - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Packets received/dropped on interface %s: %s/%s", name, tmp1, tmp2); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "Packets received/dropped on interface %s: %u/%u", + name, received, drops); /* XXX: Need to provide interface id, changes to consumers required. */ - pipe_write_block(2, SP_DROPS, tmp2); + pipe_write_block(2, SP_DROPS, tmp); } else { - fprintf(stderr, "Packets received/dropped on interface %s: %s/%s\n", name, tmp1, tmp2); + fprintf(stderr, + "Packets received/dropped on interface %s: %u/%u (%.1f%%)\n", + name, received, drops, + received ? 100.0 * received / (received + drops) : 0.0); /* stderr could be line buffered */ fflush(stderr); } @@ -4511,6 +5022,6 @@ signal_pipe_check_running(void) * indent-tabs-mode: nil * End: * - * vi: set shiftwidth=4 tabstop=8 expandtab + * vi: set shiftwidth=4 tabstop=8 expandtab: * :indentSize=4:tabSize=8:noTabs=true: */