# include "config.h"
#endif
+#include <stdio.h>
#include <stdlib.h> /* for exit() */
#include <glib.h>
#include <signal.h>
#include <errno.h>
-#ifdef NEED_GETOPT_H
-#include "getopt.h"
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#else
+#include "wsgetopt.h"
#endif
#ifdef HAVE_NETDB_H
#ifdef HAVE_LIBCAP
# include <sys/prctl.h>
# include <sys/capability.h>
-# include <stdio.h>
#endif
#include "ringbuffer.h"
#ifdef _WIN32
#include "capture-wpcap.h"
-#endif
-
-#ifdef _WIN32
#include <wsutil/unicode-utils.h>
#endif
/* is defined */
#endif
+#ifdef _WIN32
+#define USE_THREADS
+#endif
+
static gboolean capture_child = FALSE; /* FALSE: standalone call, TRUE: this is an Wireshark capture child */
#ifdef _WIN32
static gchar *sig_pipe_name = NULL;
static HANDLE sig_pipe_handle = NULL;
+static gboolean signal_pipe_check_running(void);
+#endif
+
+#ifdef USE_THREADS
+static GAsyncQueue *cap_pipe_pending_q, *cap_pipe_done_q;
+static GMutex *cap_pipe_read_mtx;
#endif
/** Stop a low-level capture (stops the capture child). */
* is interrupted by a signal on UN*X, just go back and try again to
* read again.
*
- * On UN*X, we catch SIGUSR1 as a "stop capturing" signal, and, in
+ * On UN*X, we catch SIGINT as a "stop capturing" signal, and, in
* the signal handler, set a flag to stop capturing; however, without
* a guarantee of that sort, we can't guarantee that we'll stop capturing
* if the read will be retried and won't time out if no packets arrive.
* exit pcap_dispatch() with an indication that no packets have arrived,
* and will break out of the capture loop at that point.
*
- * On Windows, we can't send a SIGUSR1 to stop capturing, so none of this
+ * On Windows, we can't send a SIGINT to stop capturing, so none of this
* applies in any case.
*
* XXX - the various BSDs appear to define BSD in <sys/param.h>; we don't
gboolean from_cap_pipe; /* TRUE if we are capturing data from a capture pipe */
struct pcap_hdr cap_pipe_hdr; /* Pcap header when capturing from a pipe */
struct pcaprec_modified_hdr cap_pipe_rechdr; /* Pcap record header when capturing from a pipe */
+#ifdef _WIN32
+ HANDLE cap_pipe_h; /* The handle of the capture pipe */
+#else
int cap_pipe_fd; /* the file descriptor of the capture pipe */
+#endif
gboolean cap_pipe_modified; /* TRUE if data in the pipe uses modified pcap headers */
gboolean cap_pipe_byte_swapped; /* TRUE if data in the pipe is byte swapped */
- unsigned int cap_pipe_bytes_to_read;/* Used by cap_pipe_dispatch */
- unsigned int cap_pipe_bytes_read; /* Used by cap_pipe_dispatch */
+#ifdef USE_THREADS
+ char * cap_pipe_buf; /* Pointer to the data buffer we read into */
+#endif /* USE_THREADS */
+ int cap_pipe_bytes_to_read;/* Used by cap_pipe_dispatch */
+ int cap_pipe_bytes_read; /* Used by cap_pipe_dispatch */
enum {
STATE_EXPECT_REC_HDR,
STATE_READ_REC_HDR,
/* output file */
FILE *pdh;
int linktype;
+ int file_snaplen;
gint wtap_linktype;
long bytes_written;
"(This is not a crash; please do not report it as such.)";
/*
- * This needs to be static, so that the SIGUSR1 handler can clear the "go"
+ * This needs to be static, so that the SIGINT handler can clear the "go"
* flag.
*/
static loop_data global_ld;
/*
* Timeout, in milliseconds, for reads from the stream of captured packets.
*/
+#if defined(__APPLE__) && defined(__LP64__)
+#define CAP_READ_TIMEOUT 1000
+#else
#define CAP_READ_TIMEOUT 250
+#endif
+/*
+ * Timeout, in microseconds, for threaded reads from a pipe.
+ */
+#define THREAD_READ_TIMEOUT 100
static char *cap_pipe_err_str;
static void
static void capture_loop_get_errmsg(char *errmsg, int errmsglen, const char *fname,
int err, gboolean is_close);
-
-#if __GNUC__ >= 2
-static void exit_main(int err) __attribute__ ((noreturn));
-#else
-static void exit_main(int err);
-#endif
+static void exit_main(int err) G_GNUC_NORETURN;
static void report_new_capture_file(const char *filename);
static void report_packet_count(int packet_count);
static void report_capture_error(const char *error_msg, const char *secondary_error_msg);
static void report_cfilter_error(const char *cfilter, const char *errmsg);
-#ifdef _WIN32
-static gboolean signal_pipe_check_running(void);
-#endif
-
static void
print_usage(gboolean print_ver) {
fprintf(output, " -b <ringbuffer opt.> ... duration:NUM - switch to next file after NUM secs\n");
fprintf(output, " filesize:NUM - switch to next file after NUM KB\n");
fprintf(output, " files:NUM - ringbuffer: replace after NUM files\n");
+ fprintf(output, " -n use pcapng format instead of pcap\n");
/*fprintf(output, "\n");*/
fprintf(output, "Miscellaneous:\n");
fprintf(output, " -v print version information and exit\n");
pcap_t *pch;
char errbuf[PCAP_ERRBUF_SIZE];
struct pcap_stat ps;
-#ifndef _WIN32
- struct sigaction act;
-#endif
if_list = get_interface_list(&err, &err_str);
if (if_list == NULL) {
"Dropped");
}
-#ifndef _WIN32
- /* handle SIGPIPE signal to default action */
- act.sa_handler = SIG_DFL;
- sigemptyset(&act.sa_mask);
- act.sa_flags = SA_RESTART;
- sigaction(SIGPIPE,&act,NULL);
-#endif
-
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)) {
}
}
#ifdef _WIN32
- Sleep(1 * 1000);
+ if (! global_ld.from_cap_pipe)
+ Sleep(1 * 1000);
#else
sleep(1);
#endif
#ifdef _WIN32
static BOOL WINAPI
-capture_cleanup(DWORD dwCtrlType)
+capture_cleanup_handler(DWORD dwCtrlType)
{
/* CTRL_C_EVENT is sort of like SIGINT, CTRL_BREAK_EVENT is unique to
Windows, CTRL_CLOSE_EVENT is sort of like SIGHUP, CTRL_LOGOFF_EVENT
}
#else
static void
-capture_cleanup(int signum)
+capture_cleanup_handler(int signum _U_)
{
/* On UN*X, we cleanly shut down the capture on SIGINT, SIGHUP, and
SIGTERM. We assume that if the user wanted it to keep running
after they logged out, they'd have nohupped it. */
- g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO,
- "Console: Signal");
- g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG,
- "Console: Signal, signal value: %u", signum);
+ /* Note: don't call g_log() in the signal handler: if we happened to be in
+ * g_log() in process context when the signal came in, g_log will detect
+ * the "recursion" and abort.
+ */
capture_loop_stop();
}
}
}
-/* Provide select() functionality for a single file descriptor
- * on both UNIX/POSIX and Windows.
+#ifdef USE_THREADS
+/*
+ * Thread function that reads from a pipe and pushes the data
+ * to the main application thread.
+ */
+/*
+ * XXX Right now we use async queues for basic signaling. The main thread
+ * sets cap_pipe_buf and cap_bytes_to_read, then pushes an item onto
+ * cap_pipe_pending_q which triggers a read in the cap_pipe_read thread.
+ * Iff the read is successful cap_pipe_read pushes an item onto
+ * cap_pipe_done_q, otherwise an error is signaled. No data is passed in
+ * the queues themselves (yet).
*
- * The Windows version calls WaitForSingleObject instead of
- * select().
+ * We might want to move some of the cap_pipe_dispatch logic here so that
+ * we can let cap_pipe_read run independently, queuing up multiple reads
+ * for the main thread (and possibly get rid of cap_pipe_read_mtx).
+ */
+static void *cap_pipe_read(void *ld_ptr) {
+ loop_data *ld = (loop_data *)ld_ptr;
+ int bytes_read;
+#ifdef _WIN32
+ BOOL res;
+ DWORD b, last_err;
+#else /* _WIN32 */
+ int b;
+#endif /* _WIN32 */
+
+ while (ld->cap_pipe_err == PIPOK) {
+ g_async_queue_pop(cap_pipe_pending_q); /* Wait for our cue (ahem) from the main thread */
+ g_mutex_lock(cap_pipe_read_mtx);
+ bytes_read = 0;
+ while (bytes_read < (int) ld->cap_pipe_bytes_to_read) {
+#ifdef _WIN32
+ /* If we try to use read() on a named pipe on Windows with partial
+ * data it appears to return EOF.
+ */
+ res = ReadFile(ld->cap_pipe_h, ld->cap_pipe_buf+bytes_read,
+ ld->cap_pipe_bytes_to_read - bytes_read,
+ &b, NULL);
+
+ bytes_read += b;
+ if (!res) {
+ last_err = GetLastError();
+ if (last_err == ERROR_MORE_DATA) {
+ continue;
+ } else if (last_err == ERROR_HANDLE_EOF || last_err == ERROR_BROKEN_PIPE || last_err == ERROR_PIPE_NOT_CONNECTED) {
+ ld->cap_pipe_err = PIPEOF;
+ bytes_read = 0;
+ break;
+ }
+ ld->cap_pipe_err = PIPERR;
+ bytes_read = -1;
+ break;
+ } else if (b == 0 && ld->cap_pipe_bytes_to_read > 0) {
+ ld->cap_pipe_err = PIPEOF;
+ bytes_read = 0;
+ break;
+ }
+#else /* _WIN32 */
+ b = read(ld->cap_pipe_fd, ld->cap_pipe_buf+bytes_read,
+ ld->cap_pipe_bytes_to_read - bytes_read);
+ if (b <= 0) {
+ if (b == 0) {
+ ld->cap_pipe_err = PIPEOF;
+ bytes_read = 0;
+ break;
+ } else {
+ ld->cap_pipe_err = PIPERR;
+ bytes_read = -1;
+ break;
+ }
+ } else {
+ bytes_read += b;
+ }
+#endif /*_WIN32 */
+ }
+ ld->cap_pipe_bytes_read = bytes_read;
+ if (ld->cap_pipe_bytes_read >= ld->cap_pipe_bytes_to_read) {
+ g_async_queue_push(cap_pipe_done_q, ld->cap_pipe_buf); /* Any non-NULL value will do */
+ }
+ g_mutex_unlock(cap_pipe_read_mtx);
+ }
+ return NULL;
+}
+#endif /* USE_THREADS */
+
+/* Provide select() functionality for a single file descriptor
+ * on UNIX/POSIX. Windows uses cap_pipe_read via a thread.
*
* Returns the same values as select. If an error is returned,
* the string cap_pipe_err_str should be used instead of errno.
*/
static int
cap_pipe_select(int pipe_fd) {
-#ifndef _WIN32
fd_set rfds;
struct timeval timeout, *pto;
int sel_ret;
cap_pipe_err_str = strerror(errno);
return sel_ret;
}
-#else
- /* XXX - Should we just use file handles exclusively under Windows?
- * Otherwise we have to convert between file handles and file descriptors
- * here and when we open a named pipe.
- */
- HANDLE hPipe = (HANDLE) _get_osfhandle(pipe_fd);
- wchar_t *err_str;
- DWORD wait_ret;
-
- if (hPipe == INVALID_HANDLE_VALUE) {
- cap_pipe_err_str = "Could not open standard input";
- return -1;
- }
-
- cap_pipe_err_str = "Unknown error";
-
- wait_ret = WaitForSingleObject(hPipe, CAP_READ_TIMEOUT);
- switch (wait_ret) {
- /* XXX - This probably isn't correct */
- case WAIT_ABANDONED:
- errno = EINTR;
- return -1;
- case WAIT_OBJECT_0:
- return 1;
- case WAIT_TIMEOUT:
- return 0;
- case WAIT_FAILED:
- FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
- NULL, GetLastError(), 0, (LPTSTR) &err_str, 0, NULL);
- cap_pipe_err_str = utf_16to8(err_str);
- LocalFree(err_str);
- return -1;
- default:
- g_assert_not_reached();
- return -1;
- }
-}
-#endif
/* Mimic pcap_open_live() for pipe captures
* header.
* N.B. : we can't read the libpcap formats used in RedHat 6.1 or SuSE 6.3
* because we can't seek on pipes (see wiretap/libpcap.c for details) */
-static int
+static void
cap_pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld,
char *errmsg, int errmsgl)
{
#ifndef _WIN32
struct stat pipe_stat;
-#else
+ int sel_ret;
+ int b;
+ unsigned int bytes_read;
+ int fd;
+#else /* _WIN32 */
#if 1
char *pncopy, *pos;
wchar_t *err_str;
#endif
- HANDLE hPipe = NULL;
#endif
- int sel_ret;
- int fd;
- int b;
- guint32 magic;
- unsigned int bytes_read;
+ guint32 magic = 0;
+#ifndef _WIN32
+ ld->cap_pipe_fd = -1;
+#else
+ ld->cap_pipe_h = INVALID_HANDLE_VALUE;
+#endif
g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "cap_pipe_open_live: %s", pipename);
/*
* XXX (T)Wireshark blocks until we return
*/
if (strcmp(pipename, "-") == 0) {
+#ifndef _WIN32
fd = 0; /* read from stdin */
-#ifdef _WIN32
- /*
- * This is needed to set the stdin pipe into binary mode, otherwise
- * CR/LF are mangled...
- */
- _setmode(0, _O_BINARY);
+#else /* _WIN32 */
+ ld->cap_pipe_h = GetStdHandle(STD_INPUT_HANDLE);
#endif /* _WIN32 */
} else {
#ifndef _WIN32
"due to error on pipe: %s", strerror(errno));
ld->cap_pipe_err = PIPERR;
}
- return -1;
+ return;
}
if (! S_ISFIFO(pipe_stat.st_mode)) {
if (S_ISCHR(pipe_stat.st_mode)) {
"\"%s\" is neither an interface nor a pipe", pipename);
ld->cap_pipe_err = PIPERR;
}
- return -1;
+ return;
}
fd = ws_open(pipename, O_RDONLY | O_NONBLOCK, 0000 /* no creation so don't matter */);
if (fd == -1) {
"The capture session could not be initiated "
"due to error on pipe open: %s", strerror(errno));
ld->cap_pipe_err = PIPERR;
- return -1;
+ return;
}
#else /* _WIN32 */
#define PIPE_STR "\\pipe\\"
"The capture session could not be initiated because\n"
"\"%s\" is neither an interface nor a pipe", pipename);
ld->cap_pipe_err = PIPNEXIST;
- return -1;
+ return;
}
/* Wait for the pipe to appear */
while (1) {
- hPipe = CreateFile(utf_8to16(pipename), GENERIC_READ, 0, NULL,
+ ld->cap_pipe_h = CreateFile(utf_8to16(pipename), GENERIC_READ, 0, NULL,
OPEN_EXISTING, 0, NULL);
- if (hPipe != INVALID_HANDLE_VALUE)
+ if (ld->cap_pipe_h != INVALID_HANDLE_VALUE)
break;
if (GetLastError() != ERROR_PIPE_BUSY) {
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL, GetLastError(), 0, (LPTSTR) &err_str, 0, NULL);
g_snprintf(errmsg, errmsgl,
- "The capture session on \"%s\" could not be initiated "
- "due to error on pipe open: pipe busy: %s (error %d)",
- pipename, utf_16to8(err_str), GetLastError());
+ "The capture session on \"%s\" could not be started "
+ "due to error on pipe open: %s (error %d)",
+ pipename, utf_16to8(err_str), GetLastError());
LocalFree(err_str);
ld->cap_pipe_err = PIPERR;
- return -1;
+ return;
}
if (!WaitNamedPipe(utf_8to16(pipename), 30 * 1000)) {
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL, GetLastError(), 0, (LPTSTR) &err_str, 0, NULL);
g_snprintf(errmsg, errmsgl,
- "The capture session could not be initiated "
- "due to error on named pipe open: %s (error %d)",
- utf_16to8(err_str), GetLastError());
+ "The capture session on \"%s\" timed out during "
+ "pipe open: %s (error %d)",
+ pipename, utf_16to8(err_str), GetLastError());
LocalFree(err_str);
ld->cap_pipe_err = PIPERR;
- return -1;
+ return;
}
}
- fd = _open_osfhandle((long) hPipe, _O_RDONLY);
- if (fd == -1) {
- g_snprintf(errmsg, errmsgl,
- "The capture session could not be initiated "
- "due to error on pipe open: %s", strerror(errno));
- ld->cap_pipe_err = PIPERR;
- return -1;
- }
#endif /* _WIN32 */
}
ld->from_cap_pipe = TRUE;
+#ifndef USE_THREADS
/* read the pcap header */
bytes_read = 0;
while (bytes_read < sizeof magic) {
b = read(fd, ((char *)&magic)+bytes_read, sizeof magic-bytes_read);
if (b <= 0) {
if (b == 0)
- g_snprintf(errmsg, errmsgl, "End of file on pipe during open");
+ g_snprintf(errmsg, errmsgl, "End of file on pipe magic during open");
else
- g_snprintf(errmsg, errmsgl, "Error on pipe during open: %s",
+ g_snprintf(errmsg, errmsgl, "Error on pipe magic during open: %s",
strerror(errno));
goto error;
}
bytes_read += b;
}
}
+#else /* USE_THREADS */
+ g_thread_create(&cap_pipe_read, ld, FALSE, NULL);
+
+ ld->cap_pipe_buf = (char *) &magic;
+ ld->cap_pipe_bytes_read = 0;
+ ld->cap_pipe_bytes_to_read = sizeof(magic);
+ /* We don't have to worry about cap_pipe_read_mtx here */
+ g_async_queue_push(cap_pipe_pending_q, ld->cap_pipe_buf);
+ g_async_queue_pop(cap_pipe_done_q);
+ if (ld->cap_pipe_bytes_read <= 0) {
+ if (ld->cap_pipe_bytes_read == 0)
+ g_snprintf(errmsg, errmsgl, "End of file on pipe magic during open");
+ else
+ g_snprintf(errmsg, errmsgl, "Error on pipe magic during open: %s",
+ strerror(errno));
+ goto error;
+ }
+#endif /* USE_THREADS */
switch (magic) {
case PCAP_MAGIC:
goto error;
}
+#ifndef USE_THREADS
/* Read the rest of the header */
bytes_read = 0;
while (bytes_read < sizeof(struct pcap_hdr)) {
sizeof(struct pcap_hdr) - bytes_read);
if (b <= 0) {
if (b == 0)
- g_snprintf(errmsg, errmsgl, "End of file on pipe during open");
+ g_snprintf(errmsg, errmsgl, "End of file on pipe header during open");
else
- g_snprintf(errmsg, errmsgl, "Error on pipe during open: %s",
+ g_snprintf(errmsg, errmsgl, "Error on pipe header during open: %s",
strerror(errno));
goto error;
}
bytes_read += b;
}
}
+#else /* USE_THREADS */
+ ld->cap_pipe_buf = (char *) hdr;
+ ld->cap_pipe_bytes_read = 0;
+ ld->cap_pipe_bytes_to_read = sizeof(struct pcap_hdr);
+ g_async_queue_push(cap_pipe_pending_q, ld->cap_pipe_buf);
+ g_async_queue_pop(cap_pipe_done_q);
+ if (ld->cap_pipe_bytes_read <= 0) {
+ if (ld->cap_pipe_bytes_read == 0)
+ g_snprintf(errmsg, errmsgl, "End of file on pipe header during open");
+ else
+ g_snprintf(errmsg, errmsgl, "Error on pipe header header during open: %s",
+ strerror(errno));
+ goto error;
+ }
+#endif /* USE_THREADS */
if (ld->cap_pipe_byte_swapped) {
/* Byte-swap the header fields about which we care. */
ld->cap_pipe_state = STATE_EXPECT_REC_HDR;
ld->cap_pipe_err = PIPOK;
- return fd;
+#ifndef _WIN32
+ ld->cap_pipe_fd = fd;
+#endif
+ return;
error:
g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "cap_pipe_open_live: error %s", errmsg);
ld->cap_pipe_err = PIPERR;
+#ifndef _WIN32
ws_close(fd);
- return -1;
+ ld->cap_pipe_fd = -1;
+#endif
+ return;
}
cap_pipe_dispatch(loop_data *ld, guchar *data, char *errmsg, int errmsgl)
{
struct pcap_pkthdr phdr;
- int b;
enum { PD_REC_HDR_READ, PD_DATA_READ, PD_PIPE_EOF, PD_PIPE_ERR,
- PD_ERR } result;
-
+ PD_ERR } result;
+#ifdef USE_THREADS
+ GTimeVal wait_time;
+ gpointer q_status;
+#else
+ int b;
+#endif
+#ifdef _WIN32
+ wchar_t *err_str;
+#endif
#ifdef LOG_CAPTURE_VERBOSE
g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "cap_pipe_dispatch");
switch (ld->cap_pipe_state) {
case STATE_EXPECT_REC_HDR:
+#ifdef USE_THREADS
+ if (g_mutex_trylock(cap_pipe_read_mtx)) {
+#endif
+
+ ld->cap_pipe_state = STATE_READ_REC_HDR;
ld->cap_pipe_bytes_to_read = ld->cap_pipe_modified ?
sizeof(struct pcaprec_modified_hdr) : sizeof(struct pcaprec_hdr);
ld->cap_pipe_bytes_read = 0;
- ld->cap_pipe_state = STATE_READ_REC_HDR;
+
+#ifdef USE_THREADS
+ ld->cap_pipe_buf = (char *) &ld->cap_pipe_rechdr;
+ g_async_queue_push(cap_pipe_pending_q, ld->cap_pipe_buf);
+ g_mutex_unlock(cap_pipe_read_mtx);
+ }
+#endif
/* Fall through */
case STATE_READ_REC_HDR:
+#ifndef USE_THREADS
b = read(ld->cap_pipe_fd, ((char *)&ld->cap_pipe_rechdr)+ld->cap_pipe_bytes_read,
ld->cap_pipe_bytes_to_read - ld->cap_pipe_bytes_read);
if (b <= 0) {
result = PD_PIPE_ERR;
break;
}
- if ((ld->cap_pipe_bytes_read += b) < ld->cap_pipe_bytes_to_read)
+ ld->cap_pipe_bytes_read += b;
+#else /* USE_THREADS */
+ g_get_current_time(&wait_time);
+ g_time_val_add(&wait_time, THREAD_READ_TIMEOUT);
+ q_status = g_async_queue_timed_pop(cap_pipe_done_q, &wait_time);
+ if (ld->cap_pipe_err == PIPEOF) {
+ result = PD_PIPE_EOF;
+ break;
+ } else if (ld->cap_pipe_err == PIPERR) {
+ result = PD_PIPE_ERR;
+ break;
+ }
+ if (!q_status) {
+ return 0;
+ }
+#endif /* USE_THREADS */
+ if ((ld->cap_pipe_bytes_read) < ld->cap_pipe_bytes_to_read)
return 0;
result = PD_REC_HDR_READ;
break;
case STATE_EXPECT_DATA:
- ld->cap_pipe_bytes_read = 0;
+#ifdef USE_THREADS
+ if (g_mutex_trylock(cap_pipe_read_mtx)) {
+#endif
+
ld->cap_pipe_state = STATE_READ_DATA;
+ ld->cap_pipe_bytes_to_read = ld->cap_pipe_rechdr.hdr.incl_len;
+ ld->cap_pipe_bytes_read = 0;
+
+#ifdef USE_THREADS
+ ld->cap_pipe_buf = (char *) data;
+ g_async_queue_push(cap_pipe_pending_q, ld->cap_pipe_buf);
+ g_mutex_unlock(cap_pipe_read_mtx);
+ }
+#endif
/* Fall through */
case STATE_READ_DATA:
+#ifndef USE_THREADS
b = read(ld->cap_pipe_fd, data+ld->cap_pipe_bytes_read,
- ld->cap_pipe_rechdr.hdr.incl_len - ld->cap_pipe_bytes_read);
+ ld->cap_pipe_bytes_to_read - ld->cap_pipe_bytes_read);
if (b <= 0) {
if (b == 0)
result = PD_PIPE_EOF;
result = PD_PIPE_ERR;
break;
}
- if ((ld->cap_pipe_bytes_read += b) < ld->cap_pipe_rechdr.hdr.incl_len)
+ ld->cap_pipe_bytes_read += b;
+#else /* USE_THREADS */
+ g_get_current_time(&wait_time);
+ g_time_val_add(&wait_time, THREAD_READ_TIMEOUT);
+ q_status = g_async_queue_timed_pop(cap_pipe_done_q, &wait_time);
+ if (ld->cap_pipe_err == PIPEOF) {
+ result = PD_PIPE_EOF;
+ break;
+ } else if (ld->cap_pipe_err == PIPERR) {
+ result = PD_PIPE_ERR;
+ break;
+ }
+ if (!q_status) {
return 0;
+ }
+#endif /* USE_THREADS */
+ if ((ld->cap_pipe_bytes_read) < ld->cap_pipe_bytes_to_read)
+ return 0;
result = PD_DATA_READ;
break;
return -1;
case PD_PIPE_ERR:
+#ifdef _WIN32
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ NULL, GetLastError(), 0, (LPTSTR) &err_str, 0, NULL);
+ g_snprintf(errmsg, errmsgl,
+ "Error reading from pipe: %s (error %d)",
+ utf_16to8(err_str), GetLastError());
+ LocalFree(err_str);
+#else
g_snprintf(errmsg, errmsgl, "Error reading from pipe: %s",
strerror(errno));
+#endif
/* Fall through */
case PD_ERR:
break;
switch (err) {
case WSASYSNOTREADY:
- g_snprintf(errmsg, errmsg_len,
+ g_snprintf(errmsg, (gulong) errmsg_len,
"Couldn't initialize Windows Sockets: Network system not ready for network communication");
break;
case WSAVERNOTSUPPORTED:
- g_snprintf(errmsg, errmsg_len,
+ g_snprintf(errmsg, (gulong) errmsg_len,
"Couldn't initialize Windows Sockets: Windows Sockets version %u.%u not supported",
LOBYTE(wVersionRequested), HIBYTE(wVersionRequested));
break;
case WSAEINPROGRESS:
- g_snprintf(errmsg, errmsg_len,
+ g_snprintf(errmsg, (gulong) errmsg_len,
"Couldn't initialize Windows Sockets: Blocking operation is in progress");
break;
case WSAEPROCLIM:
- g_snprintf(errmsg, errmsg_len,
+ g_snprintf(errmsg, (gulong) errmsg_len,
"Couldn't initialize Windows Sockets: Limit on the number of tasks supported by this WinSock implementation has been reached");
break;
case WSAEFAULT:
- g_snprintf(errmsg, errmsg_len,
+ g_snprintf(errmsg, (gulong) errmsg_len,
"Couldn't initialize Windows Sockets: Bad pointer passed to WSAStartup");
break;
default:
- g_snprintf(errmsg, errmsg_len,
+ g_snprintf(errmsg, (gulong) errmsg_len,
"Couldn't initialize Windows Sockets: error %d", err);
break;
}
- g_snprintf(secondary_errmsg, secondary_errmsg_len, please_report);
+ g_snprintf(secondary_errmsg, (gulong) secondary_errmsg_len, please_report);
return FALSE;
}
#endif
CAP_READ_TIMEOUT, &auth, open_err_str);
#else
ld->pcap_h = pcap_open_live(capture_opts->iface,
- capture_opts->has_snaplen ? capture_opts->snaplen :
- WTAP_MAX_PACKET_SIZE,
- capture_opts->promisc_mode, CAP_READ_TIMEOUT,
- open_err_str);
+ capture_opts->has_snaplen ? capture_opts->snaplen :
+ WTAP_MAX_PACKET_SIZE,
+ capture_opts->promisc_mode, CAP_READ_TIMEOUT,
+ open_err_str);
#endif
/* If not using libcap: we now can now set euid/egid to ruid/rgid */
#ifdef _WIN32
/* try to set the capture buffer size */
if (capture_opts->buffer_size > 1 &&
- pcap_setbuff(ld->pcap_h, capture_opts->buffer_size * 1024 * 1024) != 0) {
+ pcap_setbuff(ld->pcap_h, capture_opts->buffer_size * 1024 * 1024) != 0) {
sync_secondary_msg_str = g_strdup_printf(
"The capture buffer size of %luMB seems to be too high for your machine,\n"
"the default of 1MB will be used.\n"
/* setting the data link type only works on real interfaces */
if (capture_opts->linktype != -1) {
set_linktype_err_str = set_pcap_linktype(ld->pcap_h, capture_opts->iface,
- capture_opts->linktype);
+ capture_opts->linktype);
if (set_linktype_err_str != NULL) {
- g_snprintf(errmsg, errmsg_len, "Unable to set data link type (%s).",
- set_linktype_err_str);
- g_snprintf(secondary_errmsg, secondary_errmsg_len, please_report);
- return FALSE;
+ g_snprintf(errmsg, (gulong) errmsg_len, "Unable to set data link type (%s).",
+ set_linktype_err_str);
+ g_snprintf(secondary_errmsg, (gulong) secondary_errmsg_len, please_report);
+ return FALSE;
}
}
ld->linktype = get_pcap_linktype(ld->pcap_h, capture_opts->iface);
} else {
/* We couldn't open "iface" as a network device. */
/* Try to open it as a pipe */
- ld->cap_pipe_fd = cap_pipe_open_live(capture_opts->iface, &ld->cap_pipe_hdr, ld, errmsg, errmsg_len);
+ cap_pipe_open_live(capture_opts->iface, &ld->cap_pipe_hdr, ld, errmsg, (int) errmsg_len);
+#ifndef _WIN32
if (ld->cap_pipe_fd == -1) {
+#else
+ if (ld->cap_pipe_h == INVALID_HANDLE_VALUE) {
+#endif
if (ld->cap_pipe_err == PIPNEXIST) {
- /* Pipe doesn't exist, so output message for interface */
-
- /* If we got a "can't find PPA for X" message, warn the user (who
- is running (T)Wireshark on HP-UX) that they don't have a version
- of libpcap that properly handles HP-UX (libpcap 0.6.x and later
- versions, which properly handle HP-UX, say "can't find /dev/dlpi
- PPA for X" rather than "can't find PPA for X"). */
- if (strncmp(open_err_str, ppamsg, sizeof ppamsg - 1) == 0)
- libpcap_warn =
- "\n\n"
- "You are running (T)Wireshark with a version of the libpcap library\n"
- "that doesn't handle HP-UX network devices well; this means that\n"
- "(T)Wireshark may not be able to capture packets.\n"
- "\n"
- "To fix this, you should install libpcap 0.6.2, or a later version\n"
- "of libpcap, rather than libpcap 0.4 or 0.5.x. It is available in\n"
- "packaged binary form from the Software Porting And Archive Centre\n"
- "for HP-UX; the Centre is at http://hpux.connect.org.uk/ - the page\n"
- "at the URL lists a number of mirror sites.";
- else
- libpcap_warn = "";
- g_snprintf(errmsg, errmsg_len,
- "The capture session could not be initiated (%s).", open_err_str);
+ /* Pipe doesn't exist, so output message for interface */
+
+ /* If we got a "can't find PPA for X" message, warn the user (who
+ is running (T)Wireshark on HP-UX) that they don't have a version
+ of libpcap that properly handles HP-UX (libpcap 0.6.x and later
+ versions, which properly handle HP-UX, say "can't find /dev/dlpi
+ PPA for X" rather than "can't find PPA for X"). */
+ if (strncmp(open_err_str, ppamsg, sizeof ppamsg - 1) == 0)
+ libpcap_warn =
+ "\n\n"
+ "You are running (T)Wireshark with a version of the libpcap library\n"
+ "that doesn't handle HP-UX network devices well; this means that\n"
+ "(T)Wireshark may not be able to capture packets.\n"
+ "\n"
+ "To fix this, you should install libpcap 0.6.2, or a later version\n"
+ "of libpcap, rather than libpcap 0.4 or 0.5.x. It is available in\n"
+ "packaged binary form from the Software Porting And Archive Centre\n"
+ "for HP-UX; the Centre is at http://hpux.connect.org.uk/ - the page\n"
+ "at the URL lists a number of mirror sites.";
+ else
+ libpcap_warn = "";
+ g_snprintf(errmsg, (gulong) errmsg_len,
+ "The capture session could not be initiated (%s).", open_err_str);
#ifndef _WIN32
- g_snprintf(secondary_errmsg, secondary_errmsg_len,
+ 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, secondary_errmsg_len,
+ g_snprintf(secondary_errmsg, (gulong) secondary_errmsg_len,
"\n"
"Please check that \"%s\" is the proper interface.\n"
"\n"
g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_close_input");
/* if open, close the capture pipe "input file" */
+#ifndef _WIN32
if (ld->cap_pipe_fd >= 0) {
g_assert(ld->from_cap_pipe);
ws_close(ld->cap_pipe_fd);
ld->cap_pipe_fd = 0;
}
+#else
+ if (ld->cap_pipe_h != INVALID_HANDLE_VALUE) {
+ CloseHandle(ld->cap_pipe_h);
+ ld->cap_pipe_h = INVALID_HANDLE_VALUE;
+ }
+#endif
/* if open, close the pcap "input file" */
if(ld->pcap_h != NULL) {
- g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_close_input: closing %p", ld->pcap_h);
+ g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_close_input: closing %p", (void *)ld->pcap_h);
g_assert(!ld->from_cap_pipe);
pcap_close(ld->pcap_h);
ld->pcap_h = NULL;
/* set up to write to the already-opened capture output file/files */
static gboolean
capture_loop_init_output(capture_options *capture_opts, int save_file_fd, loop_data *ld, char *errmsg, int errmsg_len) {
- int file_snaplen;
int err;
/* get snaplen */
if (ld->from_cap_pipe) {
- file_snaplen = ld->cap_pipe_hdr.snaplen;
+ ld->file_snaplen = ld->cap_pipe_hdr.snaplen;
} else
{
- file_snaplen = pcap_snapshot(ld->pcap_h);
+ ld->file_snaplen = pcap_snapshot(ld->pcap_h);
}
/* Set up to write to the capture file. */
if (capture_opts->multi_files_on) {
- ld->pdh = ringbuf_init_libpcap_fdopen(ld->linktype, file_snaplen,
- &ld->bytes_written, &err);
+ ld->pdh = ringbuf_init_libpcap_fdopen(&err);
} else {
- ld->pdh = libpcap_fdopen(save_file_fd, ld->linktype, file_snaplen,
- &ld->bytes_written, &err);
+ ld->pdh = libpcap_fdopen(save_file_fd, &err);
+ }
+ if (ld->pdh) {
+ gboolean successful;
+
+ ld->bytes_written = 0;
+ if (capture_opts->use_pcapng) {
+ char appname[100];
+
+ g_snprintf(appname, sizeof(appname), "Dumpcap " VERSION "%s", wireshark_svnversion);
+ successful = libpcap_write_session_header_block(ld->pdh, appname, &ld->bytes_written, &err) &&
+ libpcap_write_interface_description_block(ld->pdh, capture_opts->iface, capture_opts->cfilter, ld->linktype, ld->file_snaplen, &ld->bytes_written, &err);
+ } else {
+ successful = libpcap_write_file_header(ld->pdh, ld->linktype, ld->file_snaplen,
+ &ld->bytes_written, &err);
+ }
+ if (!successful) {
+ fclose(ld->pdh);
+ ld->pdh = NULL;
+ }
}
if (ld->pdh == NULL) {
default:
if (err < 0) {
g_snprintf(errmsg, errmsg_len,
- "The file to which the capture would be"
- " saved (\"%s\") could not be opened: Error %d.",
- capture_opts->save_file, err);
+ "The file to which the capture would be"
+ " saved (\"%s\") could not be opened: Error %d.",
+ capture_opts->save_file, err);
} else {
g_snprintf(errmsg, errmsg_len,
- "The file to which the capture would be"
- " saved (\"%s\") could not be opened: %s.",
- capture_opts->save_file, strerror(err));
+ "The file to which the capture would be"
+ " saved (\"%s\") could not be opened: %s.",
+ capture_opts->save_file, strerror(err));
}
break;
}
if (capture_opts->multi_files_on) {
return ringbuf_libpcap_dump_close(&capture_opts->save_file, err_close);
} else {
+ if (capture_opts->use_pcapng) {
+ libpcap_write_interface_statistics_block(ld->pdh, 0, ld->pcap_h, &ld->bytes_written, err_close);
+ }
return libpcap_dump_close(ld->pdh, err_close);
}
}
char *errmsg, int errmsg_len)
{
int inpkts;
- int sel_ret;
gint packet_count_before;
guchar pcap_data[WTAP_MAX_PACKET_SIZE];
+#ifndef USE_THREADS
+ int sel_ret;
+#endif
packet_count_before = ld->packet_count;
if (ld->from_cap_pipe) {
#ifdef LOG_CAPTURE_VERBOSE
g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: from capture pipe");
#endif
+#ifndef USE_THREADS
sel_ret = cap_pipe_select(ld->cap_pipe_fd);
if (sel_ret <= 0) {
inpkts = 0;
/*
* "select()" says we can read from the pipe without blocking
*/
+#endif /* USE_THREADS */
inpkts = cap_pipe_dispatch(ld, pcap_data, errmsg, errmsg_len);
if (inpkts < 0) {
ld->go = FALSE;
}
+#ifndef USE_THREADS
}
+#endif
}
else
{
inpkts = pcap_dispatch(ld->pcap_h, 1, capture_loop_packet_cb,
(u_char *)ld);
if (inpkts < 0) {
- ld->pcap_err = TRUE;
+ if (inpkts == -1) {
+ /* Error, rather than pcap_breakloop(). */
+ ld->pcap_err = TRUE;
+ }
ld->go = FALSE; /* error or pcap_breakloop() - stop capturing */
}
} else {
capture_loop_open_output(capture_options *capture_opts, int *save_file_fd,
char *errmsg, int errmsg_len) {
- char tmpname[128+1];
+ char *tmpname;
gchar *capfile_name;
gboolean is_tempfile;
#ifndef _WIN32
is_tempfile = FALSE;
} else {
/* Choose a random name for the temporary capture buffer */
- *save_file_fd = create_tempfile(tmpname, sizeof tmpname, "wireshark");
+ *save_file_fd = create_tempfile(&tmpname, "wireshark");
capfile_name = g_strdup(tmpname);
is_tempfile = TRUE;
}
}
-static void
-capture_loop_stop_signal_handler(int signo _U_)
-{
- g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Signal: Stop capture");
- capture_loop_stop();
-}
-
#ifdef _WIN32
#define TIME_GET() GetTickCount()
#else
static gboolean
capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct pcap_stat *stats)
{
-#ifndef _WIN32
- struct sigaction act;
-#endif
time_t upd_time, cur_time;
time_t start_time;
int err_close;
global_ld.pcap_err = FALSE;
global_ld.from_cap_pipe = FALSE;
global_ld.pdh = NULL;
+#ifndef _WIN32
global_ld.cap_pipe_fd = -1;
+#else
+ global_ld.cap_pipe_h = INVALID_HANDLE_VALUE;
+#endif
#ifdef MUST_DO_SELECT
global_ld.pcap_fd = 0;
#endif
/* We haven't yet gotten the capture statistics. */
*stats_known = FALSE;
-#ifndef _WIN32
- /*
- * Catch SIGUSR1, so that we exit cleanly if the parent process
- * kills us with it due to the user selecting "Capture->Stop".
- */
- act.sa_handler = capture_loop_stop_signal_handler;
- /*
- * Arrange that system calls not get restarted, because when
- * our signal handler returns we don't want to restart
- * a call that was waiting for packets to arrive.
- */
- act.sa_flags = 0;
- sigemptyset(&act.sa_mask);
- sigaction(SIGUSR1, &act, NULL);
-#endif /* _WIN32 */
-
g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Capture loop starting ...");
capture_opts_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, capture_opts);
/* Switch to the next ringbuffer file */
if (ringbuf_switch_file(&global_ld.pdh, &capture_opts->save_file,
- &save_file_fd, &global_ld.bytes_written, &global_ld.err)) {
+ &save_file_fd, &global_ld.err)) {
+ gboolean successful;
+
/* File switch succeeded: reset the conditions */
+ global_ld.bytes_written = 0;
+ if (capture_opts->use_pcapng) {
+ char appname[100];
+
+ g_snprintf(appname, sizeof(appname), "Dumpcap " VERSION "%s", wireshark_svnversion);
+ successful = libpcap_write_session_header_block(global_ld.pdh, appname, &global_ld.bytes_written, &global_ld.err) &&
+ libpcap_write_interface_description_block(global_ld.pdh, capture_opts->iface, capture_opts->cfilter, global_ld.linktype, global_ld.file_snaplen, &global_ld.bytes_written, &global_ld.err);
+ } else {
+ successful = libpcap_write_file_header(global_ld.pdh, global_ld.linktype, global_ld.file_snaplen,
+ &global_ld.bytes_written, &global_ld.err);
+ }
+ if (!successful) {
+ fclose(global_ld.pdh);
+ global_ld.pdh = NULL;
+ global_ld.go = FALSE;
+ continue;
+ }
cnd_reset(cnd_autostop_size);
if (cnd_file_duration) {
cnd_reset(cnd_file_duration);
/* Switch to the next ringbuffer file */
if (ringbuf_switch_file(&global_ld.pdh, &capture_opts->save_file,
- &save_file_fd, &global_ld.bytes_written,
- &global_ld.err)) {
+ &save_file_fd, &global_ld.err)) {
+ gboolean successful;
+
/* file switch succeeded: reset the conditions */
+ global_ld.bytes_written = 0;
+ if (capture_opts->use_pcapng) {
+ char appname[100];
+
+ g_snprintf(appname, sizeof(appname), "Dumpcap " VERSION "%s", wireshark_svnversion);
+ successful = libpcap_write_session_header_block(global_ld.pdh, appname, &global_ld.bytes_written, &global_ld.err) &&
+ libpcap_write_interface_description_block(global_ld.pdh, capture_opts->iface, capture_opts->cfilter, global_ld.linktype, global_ld.file_snaplen, &global_ld.bytes_written, &global_ld.err);
+ } else {
+ successful = libpcap_write_file_header(global_ld.pdh, global_ld.linktype, global_ld.file_snaplen,
+ &global_ld.bytes_written, &global_ld.err);
+ }
+ if (!successful) {
+ fclose(global_ld.pdh);
+ global_ld.pdh = NULL;
+ global_ld.go = FALSE;
+ continue;
+ }
cnd_reset(cnd_file_duration);
if(cnd_autostop_size)
cnd_reset(cnd_autostop_size);
return;
if (ld->pdh) {
+ gboolean successful;
/* We're supposed to write the packet to a file; do so.
If this fails, set "ld->go" to FALSE, to stop the capture, and set
"ld->err" to the error. */
- if (!libpcap_write_packet(ld->pdh, phdr, pd, &ld->bytes_written, &err)) {
+ if (global_capture_opts.use_pcapng) {
+ successful = libpcap_write_enhanced_packet_block(ld->pdh, phdr, 0, pd, &ld->bytes_written, &err);
+ } else {
+ successful = libpcap_write_packet(ld->pdh, phdr, pd, &ld->bytes_written, &err);
+ }
+ if (!successful) {
ld->go = FALSE;
ld->err = err;
} else {
main(int argc, char *argv[])
{
int opt;
- extern char *optarg;
gboolean arg_error = FALSE;
#ifdef _WIN32
gint i;
#ifdef HAVE_PCAP_REMOTE
-#define OPTSTRING_INIT "a:A:b:c:Df:hi:Lm:MprSs:uvw:y:Z:"
+#define OPTSTRING_INIT "a:A:b:c:Df:hi:Lm:MnprSs:uvw:y:Z:"
#else
-#define OPTSTRING_INIT "a:b:c:Df:hi:LMpSs:vw:y:Z:"
+#define OPTSTRING_INIT "a:b:c:Df:hi:LMnpSs:vw:y:Z:"
#endif
#ifdef _WIN32
WSAStartup( MAKEWORD( 1, 1 ), &wsaData );
/* Set handler for Ctrl+C key */
- SetConsoleCtrlHandler(capture_cleanup, TRUE);
+ SetConsoleCtrlHandler(capture_cleanup_handler, TRUE);
+
+ /* Prepare to read from a pipe */
+ if (!g_thread_supported ())
+ g_thread_init (NULL);
+ cap_pipe_pending_q = g_async_queue_new();
+ cap_pipe_done_q = g_async_queue_new();
+ cap_pipe_read_mtx = g_mutex_new();
+
#else
/* Catch SIGINT and SIGTERM and, if we get either of them, clean up
and exit. */
- action.sa_handler = capture_cleanup;
+ action.sa_handler = capture_cleanup_handler;
+ /*
+ * Arrange that system calls not get restarted, because when
+ * our signal handler returns we don't want to restart
+ * a call that was waiting for packets to arrive.
+ */
action.sa_flags = 0;
sigemptyset(&action.sa_mask);
sigaction(SIGTERM, &action, NULL);
sigaction(SIGINT, &action, NULL);
+ sigaction(SIGPIPE, &action, NULL);
sigaction(SIGHUP, NULL, &oldaction);
if (oldaction.sa_handler == SIG_DFL)
sigaction(SIGHUP, &action, NULL);
/* */
/* It is therefore conceivable that if dumpcap somehow hangs */
/* in pcap_open_live or before that wireshark will not */
- /* be able to stop dumpcap using a signal (USR1, TERM, etc). */
+ /* be able to stop dumpcap using a signal (INT, TERM, etc). */
/* In this case, exiting wireshark will kill the child */
/* dumpcap process. */
/* */
case 'c': /* Capture x packets */
case 'f': /* capture filter */
case 'i': /* Use interface x */
+ case 'n': /* Use pcapng format */
case 'p': /* Don't capture in promiscuous mode */
case 's': /* Set the snapshot (capture) length */
case 'w': /* Write to capture file x */
}
}
#endif
+
+/*
+ * Editor modelines - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */