diff options
author | Guy Harris <guy@alum.mit.edu> | 2008-02-16 02:39:58 +0000 |
---|---|---|
committer | Guy Harris <guy@alum.mit.edu> | 2008-02-16 02:39:58 +0000 |
commit | 4aac1e2c0b3f202b1355595b8c2eec1f1dac004b (patch) | |
tree | 208d76ec65d5ad787a464bbf1313e4de59dd3e5e | |
parent | f10febad71a1185a4e8666f8323f142e80e83d15 (diff) | |
download | wireshark-4aac1e2c0b3f202b1355595b8c2eec1f1dac004b.tar.gz wireshark-4aac1e2c0b3f202b1355595b8c2eec1f1dac004b.tar.bz2 wireshark-4aac1e2c0b3f202b1355595b8c2eec1f1dac004b.zip |
Pull capture_loop.c into dumpcap.c, as dumpcap is the only program that
does capturing any more. (We will be inserting a call to give up
privileges after the pcap_open_live(), which should fix 2273; we're
currently only giving up privileges on platforms with libcap.)
svn path=/trunk/; revision=24345
-rw-r--r-- | Makefile.common | 2 | ||||
-rw-r--r-- | capture_loop.c | 1826 | ||||
-rw-r--r-- | capture_loop.h | 137 | ||||
-rw-r--r-- | capture_sync.h | 2 | ||||
-rw-r--r-- | dumpcap.c | 1858 |
5 files changed, 1844 insertions, 1981 deletions
diff --git a/Makefile.common b/Makefile.common index a3ed735537..cab75da558 100644 --- a/Makefile.common +++ b/Makefile.common @@ -230,7 +230,6 @@ randpkt_SOURCES = \ dumpcap_SOURCES = \ $(PLATFORM_SRC) \ capture_opts.c \ - capture_loop.c \ capture-pcap-util.c \ capture_stop_conditions.c \ clopts_common.c \ @@ -246,7 +245,6 @@ dumpcap_SOURCES = \ # corresponding headers dumpcap_INCLUDES = \ - capture_loop.h \ capture_stop_conditions.h \ conditions.h \ pcapio.h \ diff --git a/capture_loop.c b/capture_loop.c deleted file mode 100644 index 38f7e34674..0000000000 --- a/capture_loop.c +++ /dev/null @@ -1,1826 +0,0 @@ -/* capture_loop.c - * The actual capturing loop, getting packets and storing it - * - * $Id$ - * - * Wireshark - Network traffic analyzer - * By Gerald Combs <gerald@wireshark.org> - * Copyright 1998 Gerald Combs - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - - -/** @file - * - * Capture loop (internal interface). - * - * It will open the input and output files, capture the packets, - * change ringbuffer output files while capturing and close all files again. - * - * The input file can be a network interface or capture pipe (unix only). - * The output file can be a single or a ringbuffer file handled by wiretap. - * - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#ifdef HAVE_LIBPCAP - -#include <string.h> - -#ifdef HAVE_FCNTL_H -#include <fcntl.h> -#endif - -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif - -#ifdef HAVE_SYS_TYPES_H -# include <sys/types.h> -#endif - -#ifdef HAVE_SYS_STAT_H -# include <sys/stat.h> -#endif - -#include <signal.h> -#include <errno.h> -#include <setjmp.h> - - -#include <glib.h> - -#include <pcap.h> - -#include "pcapio.h" - -#include "capture-pcap-util.h" - -#include "capture.h" -#include "capture_sync.h" - -#include "conditions.h" -#include "capture_stop_conditions.h" -#include "ringbuffer.h" - -#include "simple_dialog.h" -#include "tempfile.h" -#include "log.h" -#include "file_util.h" - -#include "epan/unicode-utils.h" - -#include "capture_loop.h" - -#ifdef NEED_G_ASCII_STRCASECMP_H -#include "epan/g_ascii_strcasecmp.h" -#endif - -/* E: capture_loop.c only (Wireshark/dumpcap) T: tshark only */ -typedef struct _loop_data { - /* common */ - gboolean go; /* TRUE as long as we're supposed to keep capturing */ - int err; /* E: if non-zero, error seen while capturing */ - gint packet_count; /* Number of packets we have already captured */ - gint packet_max; /* E: Number of packets we're supposed to capture - 0 means infinite */ - - capture_packet_cb_fct packet_cb; /* callback for a single captured packet */ - - /* pcap "input file" */ - pcap_t *pcap_h; /* pcap handle */ - gboolean pcap_err; /* E: TRUE if error from pcap */ -#ifdef MUST_DO_SELECT - int pcap_fd; /* pcap file descriptor */ -#endif - - /* capture pipe (unix only "input file") */ - 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 */ - 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 */ - unsigned int cap_pipe_bytes_to_read;/* Used by cap_pipe_dispatch */ - unsigned int cap_pipe_bytes_read; /* Used by cap_pipe_dispatch */ - enum { - STATE_EXPECT_REC_HDR, - STATE_READ_REC_HDR, - STATE_EXPECT_DATA, - STATE_READ_DATA - } cap_pipe_state; - enum { PIPOK, PIPEOF, PIPERR, PIPNEXIST } cap_pipe_err; - - /* output file */ - FILE *pdh; - int linktype; - gint wtap_linktype; - long bytes_written; - -} loop_data; - -/* - * Standard secondary message for unexpected errors. - */ -static const char please_report[] = - "Please report this to the Wireshark developers.\n" - "(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" - * flag. - */ -static loop_data ld; - - -/* - * Timeout, in milliseconds, for reads from the stream of captured packets. - */ -#define CAP_READ_TIMEOUT 250 -static char *cap_pipe_err_str; - -static void capture_loop_packet_cb(u_char *user, const struct pcap_pkthdr *phdr, - const u_char *pd); -static void capture_loop_get_errmsg(char *errmsg, int errmsglen, const char *fname, - int err, gboolean is_close); - - - -/* Take care of byte order in the libpcap headers read from pipes. - * (function taken from wiretap/libpcap.c) */ -static void -cap_pipe_adjust_header(gboolean byte_swapped, struct pcap_hdr *hdr, struct pcaprec_hdr *rechdr) -{ - if (byte_swapped) { - /* Byte-swap the record header fields. */ - rechdr->ts_sec = BSWAP32(rechdr->ts_sec); - rechdr->ts_usec = BSWAP32(rechdr->ts_usec); - rechdr->incl_len = BSWAP32(rechdr->incl_len); - rechdr->orig_len = BSWAP32(rechdr->orig_len); - } - - /* In file format version 2.3, the "incl_len" and "orig_len" fields were - swapped, in order to match the BPF header layout. - - Unfortunately, some files were, according to a comment in the "libpcap" - source, written with version 2.3 in their headers but without the - interchanged fields, so if "incl_len" is greater than "orig_len" - which - would make no sense - we assume that we need to swap them. */ - if (hdr->version_major == 2 && - (hdr->version_minor < 3 || - (hdr->version_minor == 3 && rechdr->incl_len > rechdr->orig_len))) { - guint32 temp; - - temp = rechdr->orig_len; - rechdr->orig_len = rechdr->incl_len; - rechdr->incl_len = temp; - } -} - -/* Provide select() functionality for a single file descriptor - * on both UNIX/POSIX and Windows. - * - * The Windows version calls WaitForSingleObject instead of - * select(). - * - * 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 = "Unknown error"; - - FD_ZERO(&rfds); - FD_SET(pipe_fd, &rfds); - - timeout.tv_sec = 0; - timeout.tv_usec = CAP_READ_TIMEOUT * 1000; - pto = &timeout; - - sel_ret = select(pipe_fd+1, &rfds, NULL, NULL, pto); - if (sel_ret < 0) - cap_pipe_err_str = strerror(errno); - return sel_ret; -} -#else - /* XXX - Should we just use file handles exclusively under Windows? - * Otherwise we have to convert between file handles and file descriptors - * here and when we open a named pipe. - */ - HANDLE hPipe = (HANDLE) _get_osfhandle(pipe_fd); - wchar_t *err_str; - DWORD wait_ret; - - if (hPipe == INVALID_HANDLE_VALUE) { - cap_pipe_err_str = "Could not open standard input"; - return -1; - } - - cap_pipe_err_str = "Unknown error"; - - wait_ret = WaitForSingleObject(hPipe, CAP_READ_TIMEOUT); - switch (wait_ret) { - /* XXX - This probably isn't correct */ - case WAIT_ABANDONED: - errno = EINTR; - return -1; - case WAIT_OBJECT_0: - return 1; - case WAIT_TIMEOUT: - return 0; - case WAIT_FAILED: - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, - NULL, GetLastError(), 0, (LPTSTR) &err_str, 0, NULL); - cap_pipe_err_str = utf_16to8(err_str); - LocalFree(err_str); - return -1; - default: - g_assert_not_reached(); - return -1; - } -} -#endif - - -/* Mimic pcap_open_live() for pipe captures - * We check if "pipename" is "-" (stdin) or a FIFO, open it, and read the - * header. - * 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 -cap_pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, - char *errmsg, int errmsgl) -{ -#ifndef _WIN32 - struct stat pipe_stat; -#else -#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; - - 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) { - 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); -#endif /* _WIN32 */ - } else { -#ifndef _WIN32 - if (eth_stat(pipename, &pipe_stat) < 0) { - if (errno == ENOENT || errno == ENOTDIR) - ld->cap_pipe_err = PIPNEXIST; - else { - g_snprintf(errmsg, errmsgl, - "The capture session could not be initiated " - "due to error on pipe: %s", strerror(errno)); - ld->cap_pipe_err = PIPERR; - } - return -1; - } - if (! S_ISFIFO(pipe_stat.st_mode)) { - if (S_ISCHR(pipe_stat.st_mode)) { - /* - * Assume the user specified an interface on a system where - * interfaces are in /dev. Pretend we haven't seen it. - */ - ld->cap_pipe_err = PIPNEXIST; - } else - { - g_snprintf(errmsg, errmsgl, - "The capture session could not be initiated because\n" - "\"%s\" is neither an interface nor a pipe", pipename); - ld->cap_pipe_err = PIPERR; - } - return -1; - } - fd = eth_open(pipename, O_RDONLY | O_NONBLOCK, 0000 /* no creation so don't matter */); - if (fd == -1) { - g_snprintf(errmsg, errmsgl, - "The capture session could not be initiated " - "due to error on pipe open: %s", strerror(errno)); - ld->cap_pipe_err = PIPERR; - return -1; - } -#else /* _WIN32 */ -#define PIPE_STR "\\pipe\\" - /* Under Windows, named pipes _must_ have the form - * "\\<server>\pipe\<pipename>". <server> may be "." for localhost. - */ - pncopy = g_strdup(pipename); - if ( (pos=strstr(pncopy, "\\\\")) == pncopy) { - pos = strchr(pncopy + 3, '\\'); - if (pos && g_ascii_strncasecmp(pos, PIPE_STR, strlen(PIPE_STR)) != 0) - pos = NULL; - } - - g_free(pncopy); - - if (!pos) { - g_snprintf(errmsg, errmsgl, - "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; - } - - /* Wait for the pipe to appear */ - while (1) { - hPipe = CreateFile(utf_8to16(pipename), GENERIC_READ, 0, NULL, - OPEN_EXISTING, 0, NULL); - - if (hPipe != 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()); - LocalFree(err_str); - ld->cap_pipe_err = PIPERR; - return -1; - } - - 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()); - LocalFree(err_str); - ld->cap_pipe_err = PIPERR; - return -1; - } - } - - 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; - - /* 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 during open"); - else - g_snprintf(errmsg, errmsgl, "Error on pipe during open: %s", - strerror(errno)); - goto error; - } - bytes_read += b; - } - } - - switch (magic) { - case PCAP_MAGIC: - /* Host that wrote it has our byte order, and was running - a program using either standard or ss990417 libpcap. */ - ld->cap_pipe_byte_swapped = FALSE; - ld->cap_pipe_modified = FALSE; - break; - case PCAP_MODIFIED_MAGIC: - /* Host that wrote it has our byte order, but was running - a program using either ss990915 or ss991029 libpcap. */ - ld->cap_pipe_byte_swapped = FALSE; - ld->cap_pipe_modified = TRUE; - break; - case PCAP_SWAPPED_MAGIC: - /* Host that wrote it has a byte order opposite to ours, - and was running a program using either standard or - ss990417 libpcap. */ - ld->cap_pipe_byte_swapped = TRUE; - ld->cap_pipe_modified = FALSE; - break; - case PCAP_SWAPPED_MODIFIED_MAGIC: - /* Host that wrote it out has a byte order opposite to - ours, and was running a program using either ss990915 - or ss991029 libpcap. */ - ld->cap_pipe_byte_swapped = TRUE; - ld->cap_pipe_modified = TRUE; - break; - default: - /* Not a "libpcap" type we know about. */ - g_snprintf(errmsg, errmsgl, "Unrecognized libpcap format"); - goto error; - } - - /* 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 during open"); - else - g_snprintf(errmsg, errmsgl, "Error on pipe during open: %s", - strerror(errno)); - goto error; - } - bytes_read += b; - } - } - - if (ld->cap_pipe_byte_swapped) { - /* Byte-swap the header fields about which we care. */ - hdr->version_major = BSWAP16(hdr->version_major); - hdr->version_minor = BSWAP16(hdr->version_minor); - hdr->snaplen = BSWAP32(hdr->snaplen); - hdr->network = BSWAP32(hdr->network); - } - ld->linktype = hdr->network; - - if (hdr->version_major < 2) { - g_snprintf(errmsg, errmsgl, "Unable to read old libpcap format"); - goto error; - } - - ld->cap_pipe_state = STATE_EXPECT_REC_HDR; - ld->cap_pipe_err = PIPOK; - return fd; - -error: - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "cap_pipe_open_live: error %s", errmsg); - ld->cap_pipe_err = PIPERR; - eth_close(fd); - return -1; - -} - - -/* We read one record from the pipe, take care of byte order in the record - * header, write the record to the capture file, and update capture statistics. */ -static int -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; - - -#ifdef LOG_CAPTURE_VERBOSE - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "cap_pipe_dispatch"); -#endif - - switch (ld->cap_pipe_state) { - - case STATE_EXPECT_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; - /* Fall through */ - - case STATE_READ_REC_HDR: - 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) { - if (b == 0) - result = PD_PIPE_EOF; - else - result = PD_PIPE_ERR; - break; - } - if ((ld->cap_pipe_bytes_read += b) < ld->cap_pipe_bytes_to_read) - return 0; - result = PD_REC_HDR_READ; - break; - - case STATE_EXPECT_DATA: - ld->cap_pipe_bytes_read = 0; - ld->cap_pipe_state = STATE_READ_DATA; - /* Fall through */ - - case STATE_READ_DATA: - b = read(ld->cap_pipe_fd, data+ld->cap_pipe_bytes_read, - ld->cap_pipe_rechdr.hdr.incl_len - ld->cap_pipe_bytes_read); - if (b <= 0) { - if (b == 0) - result = PD_PIPE_EOF; - else - result = PD_PIPE_ERR; - break; - } - if ((ld->cap_pipe_bytes_read += b) < ld->cap_pipe_rechdr.hdr.incl_len) - return 0; - result = PD_DATA_READ; - break; - - default: - g_snprintf(errmsg, errmsgl, "cap_pipe_dispatch: invalid state"); - result = PD_ERR; - - } /* switch (ld->cap_pipe_state) */ - - /* - * We've now read as much data as we were expecting, so process it. - */ - switch (result) { - - case PD_REC_HDR_READ: - /* We've read the header. Take care of byte order. */ - cap_pipe_adjust_header(ld->cap_pipe_byte_swapped, &ld->cap_pipe_hdr, - &ld->cap_pipe_rechdr.hdr); - if (ld->cap_pipe_rechdr.hdr.incl_len > WTAP_MAX_PACKET_SIZE) { - g_snprintf(errmsg, errmsgl, "Frame %u too long (%d bytes)", - ld->packet_count+1, ld->cap_pipe_rechdr.hdr.incl_len); - break; - } - ld->cap_pipe_state = STATE_EXPECT_DATA; - return 0; - - case PD_DATA_READ: - /* Fill in a "struct pcap_pkthdr", and process the packet. */ - phdr.ts.tv_sec = ld->cap_pipe_rechdr.hdr.ts_sec; - phdr.ts.tv_usec = ld->cap_pipe_rechdr.hdr.ts_usec; - phdr.caplen = ld->cap_pipe_rechdr.hdr.incl_len; - phdr.len = ld->cap_pipe_rechdr.hdr.orig_len; - - ld->packet_cb((u_char *)ld, &phdr, data); - - ld->cap_pipe_state = STATE_EXPECT_REC_HDR; - return 1; - - case PD_PIPE_EOF: - ld->cap_pipe_err = PIPEOF; - return -1; - - case PD_PIPE_ERR: - g_snprintf(errmsg, errmsgl, "Error reading from pipe: %s", - strerror(errno)); - /* Fall through */ - case PD_ERR: - break; - } - - ld->cap_pipe_err = PIPERR; - /* Return here rather than inside the switch to prevent GCC warning */ - return -1; -} - - -/* open the capture input file (pcap or capture pipe) */ -static gboolean -capture_loop_open_input(capture_options *capture_opts, loop_data *ld, - char *errmsg, size_t errmsg_len, - char *secondary_errmsg, size_t secondary_errmsg_len) -{ - gchar open_err_str[PCAP_ERRBUF_SIZE]; - gchar *sync_msg_str; - static const char ppamsg[] = "can't find PPA for "; - const char *set_linktype_err_str; - const char *libpcap_warn; -#ifdef _WIN32 - gchar *sync_secondary_msg_str; - int err; - WORD wVersionRequested; - WSADATA wsaData; -#endif -#ifdef HAVE_PCAP_REMOTE - struct pcap_rmtauth auth; -#endif - - - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_open_input : %s", capture_opts->iface); - - -/* XXX - opening Winsock on tshark? */ - - /* Initialize Windows Socket if we are in a WIN32 OS - This needs to be done before querying the interface for network/netmask */ -#ifdef _WIN32 - /* XXX - do we really require 1.1 or earlier? - Are there any versions that support only 2.0 or higher? */ - wVersionRequested = MAKEWORD(1, 1); - err = WSAStartup(wVersionRequested, &wsaData); - if (err != 0) { - switch (err) { - - case WSASYSNOTREADY: - g_snprintf(errmsg, errmsg_len, - "Couldn't initialize Windows Sockets: Network system not ready for network communication"); - break; - - case WSAVERNOTSUPPORTED: - g_snprintf(errmsg, 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, - "Couldn't initialize Windows Sockets: Blocking operation is in progress"); - break; - - case WSAEPROCLIM: - g_snprintf(errmsg, 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, - "Couldn't initialize Windows Sockets: Bad pointer passed to WSAStartup"); - break; - - default: - g_snprintf(errmsg, errmsg_len, - "Couldn't initialize Windows Sockets: error %d", err); - break; - } - g_snprintf(secondary_errmsg, secondary_errmsg_len, please_report); - return FALSE; - } -#endif - - /* Open the network interface to capture from it. - Some versions of libpcap may put warnings into the error buffer - if they succeed; to tell if that's happened, we have to clear - the error buffer, and check if it's still a null string. */ - open_err_str[0] = '\0'; -#ifdef HAVE_PCAP_OPEN - auth.type = capture_opts->auth_type == CAPTURE_AUTH_PWD ? - RPCAP_RMTAUTH_PWD : RPCAP_RMTAUTH_NULL; - auth.username = capture_opts->auth_username; - auth.password = capture_opts->auth_password; - - ld->pcap_h = pcap_open(capture_opts->iface, - capture_opts->has_snaplen ? capture_opts->snaplen : - WTAP_MAX_PACKET_SIZE, - /* flags */ - (capture_opts->promisc_mode ? PCAP_OPENFLAG_PROMISCUOUS : 0) | - (capture_opts->datatx_udp ? PCAP_OPENFLAG_DATATX_UDP : 0) | - (capture_opts->nocap_rpcap ? PCAP_OPENFLAG_NOCAPTURE_RPCAP : 0), - CAP_READ_TIMEOUT, &auth, open_err_str); -#else - ld->pcap_h = pcap_open_live(capture_opts->iface, - capture_opts->has_snaplen ? capture_opts->snaplen : - WTAP_MAX_PACKET_SIZE, - capture_opts->promisc_mode, CAP_READ_TIMEOUT, - open_err_str); -#endif - - if (ld->pcap_h != NULL) { - /* we've opened "iface" as a network device */ -#ifdef _WIN32 - /* try to set the capture buffer size */ - if (pcap_setbuff(ld->pcap_h, capture_opts->buffer_size * 1024 * 1024) != 0) { - 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" - "\n" - "Nonetheless, the capture is started.\n", - capture_opts->buffer_size); - report_capture_error("Couldn't set the capture buffer size!", - sync_secondary_msg_str); - g_free(sync_secondary_msg_str); - } -#endif - -#if defined(HAVE_PCAP_REMOTE) && defined(HAVE_PCAP_SETSAMPLING) - if (capture_opts->sampling_method != CAPTURE_SAMP_NONE) - { - struct pcap_samp *samp; - - if ((samp = pcap_setsampling(ld->pcap_h)) != NULL) - { - switch (capture_opts->sampling_method) - { - case CAPTURE_SAMP_BY_COUNT: - samp->method = PCAP_SAMP_1_EVERY_N; - break; - - case CAPTURE_SAMP_BY_TIMER: - samp->method = PCAP_SAMP_FIRST_AFTER_N_MS; - break; - - default: - sync_msg_str = g_strdup_printf( - "Unknown sampling method %d specified,\n" - "continue without packet sampling", - capture_opts->sampling_method); - report_capture_error("Couldn't set the capture " - "sampling", sync_msg_str); - g_free(sync_msg_str); - } - samp->value = capture_opts->sampling_param; - } - else - { - report_capture_error("Couldn't set the capture sampling", - "Cannot get packet sampling data structure"); - } - - } -#endif - - /* setting the data link type only works on real interfaces */ - if (capture_opts->linktype != -1) { - set_linktype_err_str = set_pcap_linktype(ld->pcap_h, capture_opts->iface, - capture_opts->linktype); - if (set_linktype_err_str != NULL) { - g_snprintf(errmsg, errmsg_len, "Unable to set data link type (%s).", - set_linktype_err_str); - g_snprintf(secondary_errmsg, secondary_errmsg_len, please_report); - return FALSE; - } - } - 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); - - if (ld->cap_pipe_fd == -1) { - - if (ld->cap_pipe_err == PIPNEXIST) { - /* Pipe doesn't exist, so output message for interface */ - - /* If we got a "can't find PPA for X" message, warn the user (who - is running (T)Wireshark on HP-UX) that they don't have a version - of libpcap that properly handles HP-UX (libpcap 0.6.x and later - versions, which properly handle HP-UX, say "can't find /dev/dlpi - PPA for X" rather than "can't find PPA for X"). */ - if (strncmp(open_err_str, ppamsg, sizeof ppamsg - 1) == 0) - libpcap_warn = - "\n\n" - "You are running (T)Wireshark with a version of the libpcap library\n" - "that doesn't handle HP-UX network devices well; this means that\n" - "(T)Wireshark may not be able to capture packets.\n" - "\n" - "To fix this, you should install libpcap 0.6.2, or a later version\n" - "of libpcap, rather than libpcap 0.4 or 0.5.x. It is available in\n" - "packaged binary form from the Software Porting And Archive Centre\n" - "for HP-UX; the Centre is at http://hpux.connect.org.uk/ - the page\n" - "at the URL lists a number of mirror sites."; - else - libpcap_warn = ""; - g_snprintf(errmsg, errmsg_len, - "The capture session could not be initiated (%s).", open_err_str); -#ifndef _WIN32 - g_snprintf(secondary_errmsg, secondary_errmsg_len, -"Please check to make sure you have sufficient permissions, and that you have " -"the proper interface or pipe specified.%s", libpcap_warn); -#else - g_snprintf(secondary_errmsg, secondary_errmsg_len, -"\n" -"Please check that \"%s\" is the proper interface.\n" -"\n" -"\n" -"Help can be found at:\n" -"\n" -" http://wiki.wireshark.org/CaptureSetup\n" -"\n" -"64-bit Windows:\n" -"WinPcap does not support 64-bit Windows; you will have to use some other\n" -"tool to capture traffic, such as netcap.\n" -"For netcap details see: http://support.microsoft.com/?id=310875\n" -"\n" -"Modem (PPP/WAN):\n" -"Note that version 3.0 of WinPcap, and earlier versions of WinPcap, don't\n" -"support capturing on PPP/WAN interfaces on Windows NT 4.0 / 2000 / XP /\n" -"Server 2003.\n" -"WinPcap 3.1 has support for it on Windows 2000 / XP / Server 2003, but has no\n" -"support for it on Windows NT 4.0 or Windows Vista (Beta 1).", - capture_opts->iface); -#endif /* _WIN32 */ - } - /* - * Else pipe (or file) does exist and cap_pipe_open_live() has - * filled in errmsg - */ - return FALSE; - } else - /* cap_pipe_open_live() succeeded; don't want - error message from pcap_open_live() */ - open_err_str[0] = '\0'; - } - -/* XXX - will this work for tshark? */ -#ifdef MUST_DO_SELECT - if (!ld->from_cap_pipe) { -#ifdef HAVE_PCAP_GET_SELECTABLE_FD - ld->pcap_fd = pcap_get_selectable_fd(ld->pcap_h); -#else - ld->pcap_fd = pcap_fileno(ld->pcap_h); -#endif - } -#endif - - /* Does "open_err_str" contain a non-empty string? If so, "pcap_open_live()" - returned a warning; print it, but keep capturing. */ - if (open_err_str[0] != '\0') { - sync_msg_str = g_strdup_printf("%s.", open_err_str); - report_capture_error(sync_msg_str, ""); - g_free(sync_msg_str); - } - - return TRUE; -} - - -/* close the capture input file (pcap or capture pipe) */ -static void capture_loop_close_input(loop_data *ld) { - - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_close_input"); - - /* if open, close the capture pipe "input file" */ - if (ld->cap_pipe_fd >= 0) { - g_assert(ld->from_cap_pipe); - eth_close(ld->cap_pipe_fd); - ld->cap_pipe_fd = 0; - } - - /* 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_assert(!ld->from_cap_pipe); - pcap_close(ld->pcap_h); - ld->pcap_h = NULL; - } - - ld->go = FALSE; - -#ifdef _WIN32 - /* Shut down windows sockets */ - WSACleanup(); -#endif -} - - -/* init the capture filter */ -static initfilter_status_t -capture_loop_init_filter(pcap_t *pcap_h, gboolean from_cap_pipe, gchar * iface, gchar * cfilter) { - bpf_u_int32 netnum, netmask; - gchar lookup_net_err_str[PCAP_ERRBUF_SIZE]; - struct bpf_program fcode; - - - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_init_filter: %s", cfilter); - - /* capture filters only work on real interfaces */ - if (cfilter && !from_cap_pipe) { - /* A capture filter was specified; set it up. */ - if (pcap_lookupnet(iface, &netnum, &netmask, lookup_net_err_str) < 0) { - /* - * Well, we can't get the netmask for this interface; it's used - * only for filters that check for broadcast IP addresses, so - * we just punt and use 0. It might be nice to warn the user, - * but that's a pain in a GUI application, as it'd involve popping - * up a message box, and it's not clear how often this would make - * a difference (only filters that check for IP broadcast addresses - * use the netmask). - */ - /*cmdarg_err( - "Warning: Couldn't obtain netmask info (%s).", lookup_net_err_str);*/ - netmask = 0; - } - if (pcap_compile(pcap_h, &fcode, cfilter, 1, netmask) < 0) { - /* Treat this specially - our caller might try to compile this - as a display filter and, if that succeeds, warn the user that - the display and capture filter syntaxes are different. */ - return INITFILTER_BAD_FILTER; - } - if (pcap_setfilter(pcap_h, &fcode) < 0) { -#ifdef HAVE_PCAP_FREECODE - pcap_freecode(&fcode); -#endif - return INITFILTER_OTHER_ERROR; - } -#ifdef HAVE_PCAP_FREECODE - pcap_freecode(&fcode); -#endif - } - - return INITFILTER_NO_ERROR; -} - - -/* 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; - - - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_init_output"); - - /* get snaplen */ - if (ld->from_cap_pipe) { - file_snaplen = ld->cap_pipe_hdr.snaplen; - } else - { - 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); - } else { - ld->pdh = libpcap_fdopen(save_file_fd, ld->linktype, file_snaplen, - &ld->bytes_written, &err); - } - - if (ld->pdh == NULL) { - /* We couldn't set up to write to the capture file. */ - /* XXX - use cf_open_error_message from tshark instead? */ - switch (err) { - - case WTAP_ERR_CANT_OPEN: - g_snprintf(errmsg, errmsg_len, "The file to which the capture would be saved" - " couldn't be created for some unknown reason."); - break; - - case WTAP_ERR_SHORT_WRITE: - g_snprintf(errmsg, errmsg_len, "A full header couldn't be written to the file" - " to which the capture would be saved."); - break; - - 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); - } 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)); - } - break; - } - - return FALSE; - } - - return TRUE; -} - -static gboolean -capture_loop_close_output(capture_options *capture_opts, loop_data *ld, int *err_close) { - - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_close_output"); - - if (capture_opts->multi_files_on) { - return ringbuf_libpcap_dump_close(&capture_opts->save_file, err_close); - } else { - return libpcap_dump_close(ld->pdh, err_close); - } -} - -/* dispatch incoming packets (pcap or capture pipe) - * - * Waits for incoming packets to be available, and calls pcap_dispatch() - * to cause them to be processed. - * - * Returns the number of packets which were processed. - * - * Times out (returning zero) after CAP_READ_TIMEOUT ms; this ensures that the - * packet-batching behaviour does not cause packets to get held back - * indefinitely. - */ -static int -capture_loop_dispatch(capture_options *capture_opts _U_, loop_data *ld, - char *errmsg, int errmsg_len) -{ - int inpkts; - int sel_ret; - gint packet_count_before; - guchar pcap_data[WTAP_MAX_PACKET_SIZE]; - - packet_count_before = ld->packet_count; - if (ld->from_cap_pipe) { - /* dispatch from capture pipe */ -#ifdef LOG_CAPTURE_VERBOSE - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: from capture pipe"); -#endif - sel_ret = cap_pipe_select(ld->cap_pipe_fd); - if (sel_ret <= 0) { - inpkts = 0; - if (sel_ret < 0 && errno != EINTR) { - g_snprintf(errmsg, errmsg_len, - "Unexpected error from select: %s", strerror(errno)); - report_capture_error(errmsg, please_report); - ld->go = FALSE; - } - } else { - /* - * "select()" says we can read from the pipe without blocking - */ - inpkts = cap_pipe_dispatch(ld, pcap_data, errmsg, errmsg_len); - if (inpkts < 0) { - ld->go = FALSE; - } - } - } - else - { - /* dispatch from pcap */ -#ifdef MUST_DO_SELECT - /* - * If we have "pcap_get_selectable_fd()", we use it to get the - * descriptor on which to select; if that's -1, it means there - * is no descriptor on which you can do a "select()" (perhaps - * because you're capturing on a special device, and that device's - * driver unfortunately doesn't support "select()", in which case - * we don't do the select - which means it might not be possible - * to stop a capture until a packet arrives. If that's unacceptable, - * plead with whoever supplies the software for that device to add - * "select()" support, or upgrade to libpcap 0.8.1 or later, and - * rebuild Wireshark or get a version built with libpcap 0.8.1 or - * later, so it can use pcap_breakloop(). - */ -#ifdef LOG_CAPTURE_VERBOSE - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: from pcap_dispatch with select"); -#endif - if (ld->pcap_fd != -1) { - sel_ret = cap_pipe_select(ld->pcap_fd); - if (sel_ret > 0) { - /* - * "select()" says we can read from it without blocking; go for - * it. - * - * We don't have pcap_breakloop(), so we only process one packet - * per pcap_dispatch() call, to allow a signal to stop the - * processing immediately, rather than processing all packets - * in a batch before quitting. - */ - inpkts = pcap_dispatch(ld->pcap_h, 1, ld->packet_cb, (u_char *)ld); - if (inpkts < 0) { - ld->pcap_err = TRUE; - ld->go = FALSE; /* error or pcap_breakloop() - stop capturing */ - } - } else { - if (sel_ret < 0 && errno != EINTR) { - g_snprintf(errmsg, errmsg_len, - "Unexpected error from select: %s", strerror(errno)); - report_capture_error(errmsg, please_report); - ld->go = FALSE; - } - } - } - else -#endif /* MUST_DO_SELECT */ - { - /* dispatch from pcap without select */ -#if 1 -#ifdef LOG_CAPTURE_VERBOSE - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: from pcap_dispatch"); -#endif -#ifdef _WIN32 - /* - * On Windows, we don't support asynchronously telling a process to - * stop capturing; instead, we check for an indication on a pipe - * after processing packets. We therefore process only one packet - * at a time, so that we can check the pipe after every packet. - */ - inpkts = pcap_dispatch(ld->pcap_h, 1, ld->packet_cb, (u_char *) ld); -#else - inpkts = pcap_dispatch(ld->pcap_h, -1, ld->packet_cb, (u_char *) ld); -#endif - if (inpkts < 0) { - if (inpkts == -1) { - /* Error, rather than pcap_breakloop(). */ - ld->pcap_err = TRUE; - } - ld->go = FALSE; /* error or pcap_breakloop() - stop capturing */ - } -#else /* pcap_next_ex */ -#ifdef LOG_CAPTURE_VERBOSE - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: from pcap_next_ex"); -#endif - /* XXX - this is currently unused, as there is some confusion with pcap_next_ex() vs. pcap_dispatch() */ - - /* - * WinPcap's remote capturing feature doesn't work with pcap_dispatch(), - * see http://wiki.wireshark.org/CaptureSetup_2fWinPcapRemote - * This should be fixed in the WinPcap 4.0 alpha release. - * - * For reference, an example remote interface: - * rpcap://[1.2.3.4]/\Device\NPF_{39993D68-7C9B-4439-A329-F2D888DA7C5C} - */ - - /* emulate dispatch from pcap */ - { - int in; - struct pcap_pkthdr *pkt_header; - u_char *pkt_data; - - in = 0; - while(ld->go && - (in = pcap_next_ex(ld->pcap_h, &pkt_header, &pkt_data)) == 1) - ld->packet_cb( (u_char *) ld, pkt_header, pkt_data); - - if(in < 0) { - ld->pcap_err = TRUE; - ld->go = FALSE; - } - } -#endif /* pcap_next_ex */ - } - } - -#ifdef LOG_CAPTURE_VERBOSE - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: %d new packet%s", inpkts, plurality(inpkts, "", "s")); -#endif - - return ld->packet_count - packet_count_before; -} - - -/* open the output file (temporary/specified name/ringbuffer/named pipe/stdout) */ -/* Returns TRUE if the file opened successfully, FALSE otherwise. */ -static gboolean -capture_loop_open_output(capture_options *capture_opts, int *save_file_fd, - char *errmsg, int errmsg_len) { - - char tmpname[128+1]; - gchar *capfile_name; - gboolean is_tempfile; - - - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_open_output: %s", - (capture_opts->save_file) ? capture_opts->save_file : ""); - - if (capture_opts->save_file != NULL) { - /* We return to the caller while the capture is in progress. - * Therefore we need to take a copy of save_file in - * case the caller destroys it after we return. - */ - capfile_name = g_strdup(capture_opts->save_file); - - if (capture_opts->output_to_pipe == TRUE) { /* either "-" or named pipe */ - if (capture_opts->multi_files_on) { - /* ringbuffer is enabled; that doesn't work with standard output or a named pipe */ - g_snprintf(errmsg, errmsg_len, - "Ring buffer requested, but capture is being written to standard output or to a named pipe."); - g_free(capfile_name); - return FALSE; - } - if (strcmp(capfile_name, "-") == 0) { - /* write to stdout */ - *save_file_fd = 1; -#ifdef _WIN32 - /* set output pipe to binary mode to avoid Windows text-mode processing (eg: for CR/LF) */ - _setmode(1, O_BINARY); -#endif - } - } /* if (...output_to_pipe ... */ - - else { - if (capture_opts->multi_files_on) { - /* ringbuffer is enabled */ - *save_file_fd = ringbuf_init(capfile_name, - (capture_opts->has_ring_num_files) ? capture_opts->ring_num_files : 0); - - /* we need the ringbuf name */ - if(*save_file_fd != -1) { - g_free(capfile_name); - capfile_name = g_strdup(ringbuf_current_filename()); - } - } else { - /* Try to open/create the specified file for use as a capture buffer. */ - *save_file_fd = eth_open(capfile_name, O_RDWR|O_BINARY|O_TRUNC|O_CREAT, - 0600); - } - } - is_tempfile = FALSE; - } else { - /* Choose a random name for the temporary capture buffer */ - *save_file_fd = create_tempfile(tmpname, sizeof tmpname, "ether"); - capfile_name = g_strdup(tmpname); - is_tempfile = TRUE; - } - - /* did we fail to open the output file? */ - if (*save_file_fd == -1) { - 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)); - } else { - if (capture_opts->multi_files_on) { - ringbuf_error_cleanup(); - } - - 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_free(capfile_name); - return FALSE; - } - - if(capture_opts->save_file != NULL) { - g_free(capture_opts->save_file); - } - capture_opts->save_file = capfile_name; - /* capture_opts.save_file is "g_free"ed later, which is equivalent to - "g_free(capfile_name)". */ -#ifndef _WIN32 - fchown(*save_file_fd, capture_opts->owner, capture_opts->group); -#endif - - return TRUE; -} - - -static void -capture_loop_stop_signal_handler(int signo _U_) -{ - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Signal: Stop capture"); - capture_loop_stop(); -} - -#ifdef _WIN32 -#define TIME_GET() GetTickCount() -#else -#define TIME_GET() time(NULL) -#endif - -/* Do the low-level work of a capture. - Returns TRUE if it succeeds, FALSE otherwise. */ -int -capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct pcap_stat *stats) -{ -#ifndef _WIN32 - struct sigaction act; -#endif - time_t upd_time, cur_time; - time_t start_time; - int err_close; - int inpkts; - gint inpkts_to_sync_pipe = 0; /* packets not already send out to the sync_pipe */ - condition *cnd_file_duration = NULL; - condition *cnd_autostop_files = NULL; - condition *cnd_autostop_size = NULL; - condition *cnd_autostop_duration = NULL; - guint32 autostop_files = 0; - gboolean write_ok; - gboolean close_ok; - gboolean cfilter_error = FALSE; -#define MSG_MAX_LENGTH 4096 - char errmsg[MSG_MAX_LENGTH+1]; - char secondary_errmsg[MSG_MAX_LENGTH+1]; - int save_file_fd = -1; - - *errmsg = '\0'; - *secondary_errmsg = '\0'; - - /* init the loop data */ - ld.go = TRUE; - ld.packet_count = 0; - if (capture_opts->has_autostop_packets) - ld.packet_max = capture_opts->autostop_packets; - else - ld.packet_max = 0; /* no limit */ - ld.err = 0; /* no error seen yet */ - ld.wtap_linktype = WTAP_ENCAP_UNKNOWN; - ld.pcap_err = FALSE; - ld.from_cap_pipe = FALSE; - ld.pdh = NULL; - ld.cap_pipe_fd = -1; -#ifdef MUST_DO_SELECT - ld.pcap_fd = 0; -#endif - ld.packet_cb = capture_loop_packet_cb; - - - /* We haven't yet gotten the capture statistics. */ - *stats_known = FALSE; - -#ifndef _WIN32 - /* - * Catch SIGUSR1, so that we exit cleanly if the parent process - * kills us with it due to the user selecting "Capture->Stop". - */ - act.sa_handler = capture_loop_stop_signal_handler; - /* - * Arrange that system calls not get restarted, because when - * our signal handler returns we don't want to restart - * a call that was waiting for packets to arrive. - */ - act.sa_flags = 0; - sigemptyset(&act.sa_mask); - sigaction(SIGUSR1, &act, NULL); -#endif /* _WIN32 */ - - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Capture loop starting ..."); - capture_opts_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, capture_opts); - - /* open the "input file" from network interface or capture pipe */ - if (!capture_loop_open_input(capture_opts, &ld, errmsg, sizeof(errmsg), - secondary_errmsg, sizeof(secondary_errmsg))) { - goto error; - } - - /* init the input filter from the network interface (capture pipe will do nothing) */ - switch (capture_loop_init_filter(ld.pcap_h, ld.from_cap_pipe, capture_opts->iface, capture_opts->cfilter)) { - - case INITFILTER_NO_ERROR: - break; - - case INITFILTER_BAD_FILTER: - cfilter_error = TRUE; - g_snprintf(errmsg, sizeof(errmsg), "%s", pcap_geterr(ld.pcap_h)); - goto error; - - case INITFILTER_OTHER_ERROR: - g_snprintf(errmsg, sizeof(errmsg), "Can't install filter (%s).", - pcap_geterr(ld.pcap_h)); - g_snprintf(secondary_errmsg, sizeof(secondary_errmsg), "%s", please_report); - goto error; - } - - /* If we're supposed to write to a capture file, open it for output - (temporary/specified name/ringbuffer) */ - if (capture_opts->saving_to_file) { - if (!capture_loop_open_output(capture_opts, &save_file_fd, errmsg, sizeof(errmsg))) { - goto error; - } - - /* set up to write to the already-opened capture output file/files */ - if (!capture_loop_init_output(capture_opts, save_file_fd, &ld, errmsg, sizeof(errmsg))) { - goto error; - } - - /* XXX - capture SIGTERM and close the capture, in case we're on a - Linux 2.0[.x] system and you have to explicitly close the capture - stream in order to turn promiscuous mode off? We need to do that - in other places as well - and I don't think that works all the - time in any case, due to libpcap bugs. */ - - /* Well, we should be able to start capturing. - - Sync out the capture file, so the header makes it to the file system, - and send a "capture started successfully and capture file created" - message to our parent so that they'll open the capture file and - update its windows to indicate that we have a live capture in - progress. */ - libpcap_dump_flush(ld.pdh, NULL); - report_new_capture_file(capture_opts->save_file); - } - - /* initialize capture stop (and alike) conditions */ - init_capture_stop_conditions(); - /* create stop conditions */ - if (capture_opts->has_autostop_filesize) - cnd_autostop_size = - cnd_new(CND_CLASS_CAPTURESIZE,(long)capture_opts->autostop_filesize * 1024); - if (capture_opts->has_autostop_duration) - cnd_autostop_duration = - cnd_new(CND_CLASS_TIMEOUT,(gint32)capture_opts->autostop_duration); - - if (capture_opts->multi_files_on) { - if (capture_opts->has_file_duration) - cnd_file_duration = - cnd_new(CND_CLASS_TIMEOUT, capture_opts->file_duration); - - if (capture_opts->has_autostop_files) - cnd_autostop_files = - cnd_new(CND_CLASS_CAPTURESIZE, capture_opts->autostop_files); - } - - /* init the time values */ - start_time = TIME_GET(); - upd_time = TIME_GET(); - - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Capture loop running!"); - - /* WOW, everything is prepared! */ - /* please fasten your seat belts, we will enter now the actual capture loop */ - while (ld.go) { - /* dispatch incoming packets */ - inpkts = capture_loop_dispatch(capture_opts, &ld, errmsg, sizeof(errmsg)); - -#ifdef _WIN32 - /* any news from our parent (signal pipe)? -> just stop the capture */ - if (!signal_pipe_check_running()) { - ld.go = FALSE; - } -#endif - - if (inpkts > 0) { - inpkts_to_sync_pipe += inpkts; - - /* check capture size condition */ - if (cnd_autostop_size != NULL && - cnd_eval(cnd_autostop_size, (guint32)ld.bytes_written)){ - /* Capture size limit reached, do we have another file? */ - if (capture_opts->multi_files_on) { - if (cnd_autostop_files != NULL && - cnd_eval(cnd_autostop_files, ++autostop_files)) { - /* no files left: stop here */ - ld.go = FALSE; - continue; - } - - /* Switch to the next ringbuffer file */ - if (ringbuf_switch_file(&ld.pdh, &capture_opts->save_file, - &save_file_fd, &ld.bytes_written, &ld.err)) { - /* File switch succeeded: reset the conditions */ - cnd_reset(cnd_autostop_size); - if (cnd_file_duration) { - cnd_reset(cnd_file_duration); - } - libpcap_dump_flush(ld.pdh, NULL); - report_packet_count(inpkts_to_sync_pipe); - inpkts_to_sync_pipe = 0; - report_new_capture_file(capture_opts->save_file); - } else { - /* File switch failed: stop here */ - ld.go = FALSE; - continue; - } - } else { - /* single file, stop now */ - ld.go = FALSE; - continue; - } - } /* cnd_autostop_size */ - if (capture_opts->output_to_pipe) { - libpcap_dump_flush(ld.pdh, NULL); - } - } /* inpkts */ - - /* Only update once a second (Win32: 500ms) so as not to overload slow - * displays. This also prevents too much context-switching between the - * dumpcap and wireshark processes */ - cur_time = TIME_GET(); -#ifdef _WIN32 - if ( (cur_time - upd_time) > 500) { -#else - if (cur_time - upd_time > 0) { -#endif - upd_time = cur_time; - - /*if (pcap_stats(pch, stats) >= 0) { - *stats_known = TRUE; - }*/ - - /* Let the parent process know. */ - if (inpkts_to_sync_pipe) { - /* do sync here */ - libpcap_dump_flush(ld.pdh, NULL); - - /* Send our parent a message saying we've written out "inpkts_to_sync_pipe" - packets to the capture file. */ - report_packet_count(inpkts_to_sync_pipe); - - inpkts_to_sync_pipe = 0; - } - - /* check capture duration condition */ - if (cnd_autostop_duration != NULL && cnd_eval(cnd_autostop_duration)) { - /* The maximum capture time has elapsed; stop the capture. */ - ld.go = FALSE; - continue; - } - - /* check capture file duration condition */ - if (cnd_file_duration != NULL && cnd_eval(cnd_file_duration)) { - /* duration limit reached, do we have another file? */ - if (capture_opts->multi_files_on) { - if (cnd_autostop_files != NULL && - cnd_eval(cnd_autostop_files, ++autostop_files)) { - /* no files left: stop here */ - ld.go = FALSE; - continue; - } - - /* Switch to the next ringbuffer file */ - if (ringbuf_switch_file(&ld.pdh, &capture_opts->save_file, - &save_file_fd, &ld.bytes_written, &ld.err)) { - /* file switch succeeded: reset the conditions */ - cnd_reset(cnd_file_duration); - if(cnd_autostop_size) - cnd_reset(cnd_autostop_size); - libpcap_dump_flush(ld.pdh, NULL); - report_packet_count(inpkts_to_sync_pipe); - inpkts_to_sync_pipe = 0; - report_new_capture_file(capture_opts->save_file); - } else { - /* File switch failed: stop here */ - ld.go = FALSE; - continue; - } - } else { - /* single file, stop now */ - ld.go = FALSE; - continue; - } - } /* cnd_file_duration */ - } - - } /* while (ld.go) */ - - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Capture loop stopping ..."); - - /* delete stop conditions */ - if (cnd_file_duration != NULL) - cnd_delete(cnd_file_duration); - if (cnd_autostop_files != NULL) - cnd_delete(cnd_autostop_files); - if (cnd_autostop_size != NULL) - cnd_delete(cnd_autostop_size); - if (cnd_autostop_duration != NULL) - cnd_delete(cnd_autostop_duration); - - /* did we had a pcap (input) error? */ - if (ld.pcap_err) { - /* On Linux, if an interface goes down while you're capturing on it, - you'll get a "recvfrom: Network is down" error (ENETDOWN). - (At least you will if strerror() doesn't show a local translation - of the error.) - - On FreeBSD and OS X, if a network adapter disappears while - you're capturing on it, you'll get a "read: Device not configured" - error (ENXIO). (See previous parenthetical note.) - - On OpenBSD, you get "read: I/O error" (EIO) in the same case. - - These should *not* be reported to the Wireshark developers. */ - char *cap_err_str; - - cap_err_str = pcap_geterr(ld.pcap_h); - if (strcmp(cap_err_str, "recvfrom: Network is down") == 0 || - strcmp(cap_err_str, "read: Device not configured") == 0 || - strcmp(cap_err_str, "read: I/O error") == 0) { - report_capture_error("The network adapter on which the capture was being done " - "is no longer running; the capture has stopped.", - ""); - } else { - g_snprintf(errmsg, sizeof(errmsg), "Error while capturing packets: %s", - cap_err_str); - report_capture_error(errmsg, please_report); - } - } - else if (ld.from_cap_pipe && ld.cap_pipe_err == PIPERR) - report_capture_error(errmsg, ""); - - /* did we had an error while capturing? */ - if (ld.err == 0) { - write_ok = TRUE; - } else { - capture_loop_get_errmsg(errmsg, sizeof(errmsg), capture_opts->save_file, ld.err, - FALSE); - report_capture_error(errmsg, please_report); - write_ok = FALSE; - } - - if (capture_opts->saving_to_file) { - /* close the wiretap (output) file */ - close_ok = capture_loop_close_output(capture_opts, &ld, &err_close); - } else - close_ok = TRUE; - - /* there might be packets not yet notified to the parent */ - /* (do this after closing the file, so all packets are already flushed) */ - if(inpkts_to_sync_pipe) { - report_packet_count(inpkts_to_sync_pipe); - inpkts_to_sync_pipe = 0; - } - - /* If we've displayed a message about a write error, there's no point - in displaying another message about an error on close. */ - if (!close_ok && write_ok) { - capture_loop_get_errmsg(errmsg, sizeof(errmsg), capture_opts->save_file, err_close, - TRUE); - report_capture_error(errmsg, ""); - } - - /* - * XXX We exhibit different behaviour between normal mode and sync mode - * when the pipe is stdin and not already at EOF. If we're a child, the - * parent's stdin isn't closed, so if the user starts another capture, - * cap_pipe_open_live() will very likely not see the expected magic bytes and - * will say "Unrecognized libpcap format". On the other hand, in normal - * mode, cap_pipe_open_live() will say "End of file on pipe during open". - */ - - /* get packet drop statistics from pcap */ - if(ld.pcap_h != NULL) { - g_assert(!ld.from_cap_pipe); - /* Get the capture statistics, so we know how many packets were - dropped. */ - if (pcap_stats(ld.pcap_h, stats) >= 0) { - *stats_known = TRUE; - /* Let the parent process know. */ - report_packet_drops(stats->ps_drop); - } else { - g_snprintf(errmsg, sizeof(errmsg), - "Can't get packet-drop statistics: %s", - pcap_geterr(ld.pcap_h)); - report_capture_error(errmsg, please_report); - } - } - - /* close the input file (pcap or capture pipe) */ - capture_loop_close_input(&ld); - - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Capture loop stopped!"); - - /* ok, if the write and the close were successful. */ - return write_ok && close_ok; - -error: - if (capture_opts->multi_files_on) { - /* cleanup ringbuffer */ - ringbuf_error_cleanup(); - } else { - /* We can't use the save file, and we have no FILE * for the stream - to close in order to close it, so close the FD directly. */ - if(save_file_fd != -1) { - eth_close(save_file_fd); - } - - /* We couldn't even start the capture, so get rid of the capture - file. */ - if(capture_opts->save_file != NULL) { - eth_unlink(capture_opts->save_file); - g_free(capture_opts->save_file); - } - } - capture_opts->save_file = NULL; - if (cfilter_error) - report_cfilter_error(capture_opts->cfilter, errmsg); - else - report_capture_error(errmsg, secondary_errmsg); - - /* close the input file (pcap or cap_pipe) */ - capture_loop_close_input(&ld); - - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Capture loop stopped with error"); - - return FALSE; -} - - -void capture_loop_stop(void) -{ -#ifdef HAVE_PCAP_BREAKLOOP - if(ld.pcap_h != NULL) - pcap_breakloop(ld.pcap_h); -#endif - ld.go = FALSE; -} - - -static void -capture_loop_get_errmsg(char *errmsg, int errmsglen, const char *fname, - int err, gboolean is_close) -{ - switch (err) { - - case ENOSPC: - g_snprintf(errmsg, errmsglen, - "Not all the packets could be written to the file" - " to which the capture was being saved\n" - "(\"%s\") because there is no space left on the file system\n" - "on which that file resides.", - fname); - break; - -#ifdef EDQUOT - case EDQUOT: - g_snprintf(errmsg, errmsglen, - "Not all the packets could be written to the file" - " to which the capture was being saved\n" - "(\"%s\") because you are too close to, or over," - " your disk quota\n" - "on the file system on which that file resides.", - fname); - break; -#endif - - case WTAP_ERR_CANT_CLOSE: - g_snprintf(errmsg, errmsglen, - "The file to which the capture was being saved" - " couldn't be closed for some unknown reason."); - break; - - case WTAP_ERR_SHORT_WRITE: - g_snprintf(errmsg, errmsglen, - "Not all the packets could be written to the file" - " to which the capture was being saved\n" - "(\"%s\").", - fname); - break; - - default: - if (is_close) { - g_snprintf(errmsg, errmsglen, - "The file to which the capture was being saved\n" - "(\"%s\") could not be closed: %s.", - fname, wtap_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, wtap_strerror(err)); - } - break; - } -} - - -/* one packet was captured, process it */ -static void -capture_loop_packet_cb(u_char *user, const struct pcap_pkthdr *phdr, - const u_char *pd) -{ - loop_data *ld = (void *) user; - int err; - - /* if the user told us to stop after x packets, do we already have enough? */ - if ((ld->packet_max > 0) && (ld->packet_count >= ld->packet_max)) - { - ld->go = FALSE; - return; - } - - /* We may be called multiple times from pcap_dispatch(); if we've set - the "stop capturing" flag, ignore this packet, as we're not - supposed to be saving any more packets. */ - if (!ld->go) - return; - - if (ld->pdh) { - /* 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)) { - ld->go = FALSE; - ld->err = err; - } else - ld->packet_count++; - } -} - -#endif /* HAVE_LIBPCAP */ diff --git a/capture_loop.h b/capture_loop.h deleted file mode 100644 index 77d0720d71..0000000000 --- a/capture_loop.h +++ /dev/null @@ -1,137 +0,0 @@ -/* capture_loop.h - * Do the low-level work of a capture - * - * $Id$ - * - * Wireshark - Network traffic analyzer - * By Gerald Combs <gerald@wireshark.org> - * Copyright 1998 Gerald Combs - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - - -/** @file - * - * Do the low-level work of a capture. - * - */ - -#ifndef __CAPTURE_LOOP_H__ -#define __CAPTURE_LOOP_H__ - -/* - * Get information about libpcap format from "wiretap/libpcap.h". - * XXX - can we just use pcap_open_offline() to read the pipe? - */ -#include "wiretap/libpcap.h" - -/** Do the low-level work of a capture. - * Returns TRUE if it succeeds, FALSE otherwise. */ -extern int capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct pcap_stat *stats); - -/** Stop a low-level capture (stops the capture child). */ -extern void capture_loop_stop(void); - - -/*** the following is internal only (should be moved to capture_loop_int.h) ***/ - -#if !defined (__linux__) -#ifndef HAVE_PCAP_BREAKLOOP -/* - * We don't have pcap_breakloop(), which is the only way to ensure that - * pcap_dispatch(), pcap_loop(), or even pcap_next() or pcap_next_ex() - * won't, if the call to read the next packet or batch of packets is - * 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 - * 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. - * - * Therefore, on at least some platforms, we work around the lack of - * pcap_breakloop() by doing a select() on the pcap_t's file descriptor - * to wait for packets to arrive, so that we're probably going to be - * blocked in the select() when the signal arrives, and can just bail - * out of the loop at that point. - * - * However, we don't want to that on BSD (because "select()" doesn't work - * correctly on BPF devices on at least some releases of some flavors of - * BSD), and we don't want to do it on Windows (because "select()" is - * something for sockets, not for arbitrary handles). (Note that "Windows" - * here includes Cygwin; even in its pretend-it's-UNIX environment, we're - * using WinPcap, not a UNIX libpcap.) - * - * Fortunately, we don't need to do it on BSD, because the libpcap timeout - * on BSD times out even if no packets have arrived, so we'll eventually - * 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 - * applies in any case. - * - * XXX - the various BSDs appear to define BSD in <sys/param.h>; we don't - * want to include it if it's not present on this platform, however. - */ -# if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) && \ - !defined(__bsdi__) && !defined(__APPLE__) && !defined(_WIN32) && \ - !defined(__CYGWIN__) -# define MUST_DO_SELECT -# endif /* avoid select */ -#endif /* HAVE_PCAP_BREAKLOOP */ -#else /* linux */ -/* whatever the deal with pcap_breakloop, linux doesn't support timeouts - * in pcap_dispatch(); on the other hand, select() works just fine there. - * Hence we use a select for that come what may. - */ -#define MUST_DO_SELECT -#endif - -typedef void (*capture_packet_cb_fct)(u_char *, const struct pcap_pkthdr *, const u_char *); - -/** init the capture filter */ -typedef enum { - INITFILTER_NO_ERROR, - INITFILTER_BAD_FILTER, - INITFILTER_OTHER_ERROR -} initfilter_status_t; - -/* - * Routines called by the capture loop code to report things. - */ - -/** Report a new capture file having been opened. */ -extern void -report_new_capture_file(const char *filename); - -/** Report a number of new packets captured. */ -extern void -report_packet_count(int packet_count); - -/** Report the packet drops once the capture finishes. */ -extern void -report_packet_drops(int drops); - -/** Report an error in the capture. */ -extern void -report_capture_error(const char *error_msg, const char *secondary_error_msg); - -/** Report an error with a capture filter. */ -extern void -report_cfilter_error(const char *cfilter, const char *errmsg); - - -#endif /* capture_loop.h */ diff --git a/capture_sync.h b/capture_sync.h index 46eff4a715..4abbfb1758 100644 --- a/capture_sync.h +++ b/capture_sync.h @@ -60,8 +60,6 @@ sync_pipe_kill(int fork_child); /** Has the parent signalled the child to stop? */ #define SIGNAL_PIPE_CTRL_ID_NONE "none" #ifdef _WIN32 -extern gboolean -signal_pipe_check_running(void); #define SIGNAL_PIPE_FORMAT "\\\\.\\pipe\\wireshark.%s.signal" #endif @@ -31,11 +31,24 @@ #include <string.h> #include <ctype.h> +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#ifdef HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif + +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + #ifdef HAVE_UNISTD_H #include <unistd.h> #endif #include <signal.h> +#include <errno.h> #ifdef NEED_GETOPT_H #include "getopt.h" @@ -57,6 +70,8 @@ #include "version_info.h" #include <pcap.h> +#include "pcapio.h" + #include "capture-pcap-util.h" #ifdef _WIN32 @@ -74,37 +89,189 @@ #include "sync_pipe.h" #include "capture.h" -#include "capture_loop.h" #include "capture_sync.h" -#include "simple_dialog.h" -#include "util.h" +#include "conditions.h" +#include "capture_stop_conditions.h" + +#include "tempfile.h" #include "log.h" #include "file_util.h" +#include "epan/unicode-utils.h" + +#ifdef NEED_G_ASCII_STRCASECMP_H +#include "epan/g_ascii_strcasecmp.h" +#endif + +/* + * Get information about libpcap format from "wiretap/libpcap.h". + * XXX - can we just use pcap_open_offline() to read the pipe? + */ +#include "wiretap/libpcap.h" /*#define DEBUG_DUMPCAP*/ -gboolean capture_child = FALSE; /* FALSE: standalone call, TRUE: this is an Wireshark capture child */ +static gboolean capture_child = FALSE; /* FALSE: standalone call, TRUE: this is an Wireshark capture child */ #ifdef _WIN32 -gchar *sig_pipe_name = NULL; -HANDLE sig_pipe_handle = NULL; +static gchar *sig_pipe_name = NULL; +static HANDLE sig_pipe_handle = NULL; +#endif + +/** Stop a low-level capture (stops the capture child). */ +static void capture_loop_stop(void); + +#if !defined (__linux__) +#ifndef HAVE_PCAP_BREAKLOOP +/* + * We don't have pcap_breakloop(), which is the only way to ensure that + * pcap_dispatch(), pcap_loop(), or even pcap_next() or pcap_next_ex() + * won't, if the call to read the next packet or batch of packets is + * 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 + * 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. + * + * Therefore, on at least some platforms, we work around the lack of + * pcap_breakloop() by doing a select() on the pcap_t's file descriptor + * to wait for packets to arrive, so that we're probably going to be + * blocked in the select() when the signal arrives, and can just bail + * out of the loop at that point. + * + * However, we don't want to that on BSD (because "select()" doesn't work + * correctly on BPF devices on at least some releases of some flavors of + * BSD), and we don't want to do it on Windows (because "select()" is + * something for sockets, not for arbitrary handles). (Note that "Windows" + * here includes Cygwin; even in its pretend-it's-UNIX environment, we're + * using WinPcap, not a UNIX libpcap.) + * + * Fortunately, we don't need to do it on BSD, because the libpcap timeout + * on BSD times out even if no packets have arrived, so we'll eventually + * 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 + * applies in any case. + * + * XXX - the various BSDs appear to define BSD in <sys/param.h>; we don't + * want to include it if it's not present on this platform, however. + */ +# if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) && \ + !defined(__bsdi__) && !defined(__APPLE__) && !defined(_WIN32) && \ + !defined(__CYGWIN__) +# define MUST_DO_SELECT +# endif /* avoid select */ +#endif /* HAVE_PCAP_BREAKLOOP */ +#else /* linux */ +/* whatever the deal with pcap_breakloop, linux doesn't support timeouts + * in pcap_dispatch(); on the other hand, select() works just fine there. + * Hence we use a select for that come what may. + */ +#define MUST_DO_SELECT +#endif + +typedef void (*capture_packet_cb_fct)(u_char *, const struct pcap_pkthdr *, const u_char *); + +/** init the capture filter */ +typedef enum { + INITFILTER_NO_ERROR, + INITFILTER_BAD_FILTER, + INITFILTER_OTHER_ERROR +} initfilter_status_t; + +typedef struct _loop_data { + /* common */ + gboolean go; /* TRUE as long as we're supposed to keep capturing */ + int err; /* if non-zero, error seen while capturing */ + gint packet_count; /* Number of packets we have already captured */ + gint packet_max; /* Number of packets we're supposed to capture - 0 means infinite */ + + capture_packet_cb_fct packet_cb; /* callback for a single captured packet */ + + /* pcap "input file" */ + pcap_t *pcap_h; /* pcap handle */ + gboolean pcap_err; /* TRUE if error from pcap */ +#ifdef MUST_DO_SELECT + int pcap_fd; /* pcap file descriptor */ #endif + /* capture pipe (unix only "input file") */ + 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 */ + 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 */ + unsigned int cap_pipe_bytes_to_read;/* Used by cap_pipe_dispatch */ + unsigned int cap_pipe_bytes_read; /* Used by cap_pipe_dispatch */ + enum { + STATE_EXPECT_REC_HDR, + STATE_READ_REC_HDR, + STATE_EXPECT_DATA, + STATE_READ_DATA + } cap_pipe_state; + enum { PIPOK, PIPEOF, PIPERR, PIPNEXIST } cap_pipe_err; + + /* output file */ + FILE *pdh; + int linktype; + gint wtap_linktype; + long bytes_written; + +} loop_data; + +/* + * Standard secondary message for unexpected errors. + */ +static const char please_report[] = + "Please report this to the Wireshark developers.\n" + "(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" + * flag. + */ +static loop_data ld; + + +/* + * Timeout, in milliseconds, for reads from the stream of captured packets. + */ +#define CAP_READ_TIMEOUT 250 +static char *cap_pipe_err_str; + static void console_log_handler(const char *log_domain, GLogLevelFlags log_level, const char *message, gpointer user_data _U_); /* capture related options */ -capture_options global_capture_opts; -capture_options *capture_opts = &global_capture_opts; +static capture_options global_capture_opts; +static capture_options *capture_opts = &global_capture_opts; + +static void capture_loop_packet_cb(u_char *user, const struct pcap_pkthdr *phdr, + const u_char *pd); +static void capture_loop_get_errmsg(char *errmsg, int errmsglen, const char *fname, + int err, gboolean is_close); + #if __GNUC__ >= 2 -void exit_main(int err) __attribute__ ((noreturn)); +static void exit_main(int err) __attribute__ ((noreturn)); #else -void exit_main(int err); +static void exit_main(int err); #endif +static void report_new_capture_file(const char *filename); +static void report_packet_count(int packet_count); +static void report_packet_drops(int drops); +static void report_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) { @@ -275,7 +442,7 @@ capture_cleanup(int signum) } #endif -void exit_main(int status) +static void exit_main(int status) { #ifdef _WIN32 /* Shutdown windows sockets */ @@ -298,7 +465,7 @@ void exit_main(int status) * CAP_NET_ADMIN and CAP_NET_RAW, then relinquish our permissions. */ -void +static void #if 0 /* Set to enable capability debugging */ print_caps(char *pfx) { cap_t caps = cap_get_proc(); @@ -310,7 +477,7 @@ print_caps(char *pfx _U_) { #endif } -void +static void relinquish_privs_except_capture(void) { /* CAP_NET_ADMIN: Promiscuous mode and a truckload of other @@ -348,6 +515,1669 @@ relinquish_privs_except_capture(void) } #endif /* HAVE_LIBCAP */ +/* Take care of byte order in the libpcap headers read from pipes. + * (function taken from wiretap/libpcap.c) */ +static void +cap_pipe_adjust_header(gboolean byte_swapped, struct pcap_hdr *hdr, struct pcaprec_hdr *rechdr) +{ + if (byte_swapped) { + /* Byte-swap the record header fields. */ + rechdr->ts_sec = BSWAP32(rechdr->ts_sec); + rechdr->ts_usec = BSWAP32(rechdr->ts_usec); + rechdr->incl_len = BSWAP32(rechdr->incl_len); + rechdr->orig_len = BSWAP32(rechdr->orig_len); + } + + /* In file format version 2.3, the "incl_len" and "orig_len" fields were + swapped, in order to match the BPF header layout. + + Unfortunately, some files were, according to a comment in the "libpcap" + source, written with version 2.3 in their headers but without the + interchanged fields, so if "incl_len" is greater than "orig_len" - which + would make no sense - we assume that we need to swap them. */ + if (hdr->version_major == 2 && + (hdr->version_minor < 3 || + (hdr->version_minor == 3 && rechdr->incl_len > rechdr->orig_len))) { + guint32 temp; + + temp = rechdr->orig_len; + rechdr->orig_len = rechdr->incl_len; + rechdr->incl_len = temp; + } +} + +/* Provide select() functionality for a single file descriptor + * on both UNIX/POSIX and Windows. + * + * The Windows version calls WaitForSingleObject instead of + * select(). + * + * 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 = "Unknown error"; + + FD_ZERO(&rfds); + FD_SET(pipe_fd, &rfds); + + timeout.tv_sec = 0; + timeout.tv_usec = CAP_READ_TIMEOUT * 1000; + pto = &timeout; + + sel_ret = select(pipe_fd+1, &rfds, NULL, NULL, pto); + if (sel_ret < 0) + cap_pipe_err_str = strerror(errno); + return sel_ret; +} +#else + /* XXX - Should we just use file handles exclusively under Windows? + * Otherwise we have to convert between file handles and file descriptors + * here and when we open a named pipe. + */ + HANDLE hPipe = (HANDLE) _get_osfhandle(pipe_fd); + wchar_t *err_str; + DWORD wait_ret; + + if (hPipe == INVALID_HANDLE_VALUE) { + cap_pipe_err_str = "Could not open standard input"; + return -1; + } + + cap_pipe_err_str = "Unknown error"; + + wait_ret = WaitForSingleObject(hPipe, CAP_READ_TIMEOUT); + switch (wait_ret) { + /* XXX - This probably isn't correct */ + case WAIT_ABANDONED: + errno = EINTR; + return -1; + case WAIT_OBJECT_0: + return 1; + case WAIT_TIMEOUT: + return 0; + case WAIT_FAILED: + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, + NULL, GetLastError(), 0, (LPTSTR) &err_str, 0, NULL); + cap_pipe_err_str = utf_16to8(err_str); + LocalFree(err_str); + return -1; + default: + g_assert_not_reached(); + return -1; + } +} +#endif + + +/* Mimic pcap_open_live() for pipe captures + * We check if "pipename" is "-" (stdin) or a FIFO, open it, and read the + * header. + * 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 +cap_pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, + char *errmsg, int errmsgl) +{ +#ifndef _WIN32 + struct stat pipe_stat; +#else +#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; + + 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) { + 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); +#endif /* _WIN32 */ + } else { +#ifndef _WIN32 + if (eth_stat(pipename, &pipe_stat) < 0) { + if (errno == ENOENT || errno == ENOTDIR) + ld->cap_pipe_err = PIPNEXIST; + else { + g_snprintf(errmsg, errmsgl, + "The capture session could not be initiated " + "due to error on pipe: %s", strerror(errno)); + ld->cap_pipe_err = PIPERR; + } + return -1; + } + if (! S_ISFIFO(pipe_stat.st_mode)) { + if (S_ISCHR(pipe_stat.st_mode)) { + /* + * Assume the user specified an interface on a system where + * interfaces are in /dev. Pretend we haven't seen it. + */ + ld->cap_pipe_err = PIPNEXIST; + } else + { + g_snprintf(errmsg, errmsgl, + "The capture session could not be initiated because\n" + "\"%s\" is neither an interface nor a pipe", pipename); + ld->cap_pipe_err = PIPERR; + } + return -1; + } + fd = eth_open(pipename, O_RDONLY | O_NONBLOCK, 0000 /* no creation so don't matter */); + if (fd == -1) { + g_snprintf(errmsg, errmsgl, + "The capture session could not be initiated " + "due to error on pipe open: %s", strerror(errno)); + ld->cap_pipe_err = PIPERR; + return -1; + } +#else /* _WIN32 */ +#define PIPE_STR "\\pipe\\" + /* Under Windows, named pipes _must_ have the form + * "\\<server>\pipe\<pipename>". <server> may be "." for localhost. + */ + pncopy = g_strdup(pipename); + if ( (pos=strstr(pncopy, "\\\\")) == pncopy) { + pos = strchr(pncopy + 3, '\\'); + if (pos && g_ascii_strncasecmp(pos, PIPE_STR, strlen(PIPE_STR)) != 0) + pos = NULL; + } + + g_free(pncopy); + + if (!pos) { + g_snprintf(errmsg, errmsgl, + "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; + } + + /* Wait for the pipe to appear */ + while (1) { + hPipe = CreateFile(utf_8to16(pipename), GENERIC_READ, 0, NULL, + OPEN_EXISTING, 0, NULL); + + if (hPipe != 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()); + LocalFree(err_str); + ld->cap_pipe_err = PIPERR; + return -1; + } + + 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()); + LocalFree(err_str); + ld->cap_pipe_err = PIPERR; + return -1; + } + } + + 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; + + /* 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 during open"); + else + g_snprintf(errmsg, errmsgl, "Error on pipe during open: %s", + strerror(errno)); + goto error; + } + bytes_read += b; + } + } + + switch (magic) { + case PCAP_MAGIC: + /* Host that wrote it has our byte order, and was running + a program using either standard or ss990417 libpcap. */ + ld->cap_pipe_byte_swapped = FALSE; + ld->cap_pipe_modified = FALSE; + break; + case PCAP_MODIFIED_MAGIC: + /* Host that wrote it has our byte order, but was running + a program using either ss990915 or ss991029 libpcap. */ + ld->cap_pipe_byte_swapped = FALSE; + ld->cap_pipe_modified = TRUE; + break; + case PCAP_SWAPPED_MAGIC: + /* Host that wrote it has a byte order opposite to ours, + and was running a program using either standard or + ss990417 libpcap. */ + ld->cap_pipe_byte_swapped = TRUE; + ld->cap_pipe_modified = FALSE; + break; + case PCAP_SWAPPED_MODIFIED_MAGIC: + /* Host that wrote it out has a byte order opposite to + ours, and was running a program using either ss990915 + or ss991029 libpcap. */ + ld->cap_pipe_byte_swapped = TRUE; + ld->cap_pipe_modified = TRUE; + break; + default: + /* Not a "libpcap" type we know about. */ + g_snprintf(errmsg, errmsgl, "Unrecognized libpcap format"); + goto error; + } + + /* 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 during open"); + else + g_snprintf(errmsg, errmsgl, "Error on pipe during open: %s", + strerror(errno)); + goto error; + } + bytes_read += b; + } + } + + if (ld->cap_pipe_byte_swapped) { + /* Byte-swap the header fields about which we care. */ + hdr->version_major = BSWAP16(hdr->version_major); + hdr->version_minor = BSWAP16(hdr->version_minor); + hdr->snaplen = BSWAP32(hdr->snaplen); + hdr->network = BSWAP32(hdr->network); + } + ld->linktype = hdr->network; + + if (hdr->version_major < 2) { + g_snprintf(errmsg, errmsgl, "Unable to read old libpcap format"); + goto error; + } + + ld->cap_pipe_state = STATE_EXPECT_REC_HDR; + ld->cap_pipe_err = PIPOK; + return fd; + +error: + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "cap_pipe_open_live: error %s", errmsg); + ld->cap_pipe_err = PIPERR; + eth_close(fd); + return -1; + +} + + +/* We read one record from the pipe, take care of byte order in the record + * header, write the record to the capture file, and update capture statistics. */ +static int +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; + + +#ifdef LOG_CAPTURE_VERBOSE + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "cap_pipe_dispatch"); +#endif + + switch (ld->cap_pipe_state) { + + case STATE_EXPECT_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; + /* Fall through */ + + case STATE_READ_REC_HDR: + 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) { + if (b == 0) + result = PD_PIPE_EOF; + else + result = PD_PIPE_ERR; + break; + } + if ((ld->cap_pipe_bytes_read += b) < ld->cap_pipe_bytes_to_read) + return 0; + result = PD_REC_HDR_READ; + break; + + case STATE_EXPECT_DATA: + ld->cap_pipe_bytes_read = 0; + ld->cap_pipe_state = STATE_READ_DATA; + /* Fall through */ + + case STATE_READ_DATA: + b = read(ld->cap_pipe_fd, data+ld->cap_pipe_bytes_read, + ld->cap_pipe_rechdr.hdr.incl_len - ld->cap_pipe_bytes_read); + if (b <= 0) { + if (b == 0) + result = PD_PIPE_EOF; + else + result = PD_PIPE_ERR; + break; + } + if ((ld->cap_pipe_bytes_read += b) < ld->cap_pipe_rechdr.hdr.incl_len) + return 0; + result = PD_DATA_READ; + break; + + default: + g_snprintf(errmsg, errmsgl, "cap_pipe_dispatch: invalid state"); + result = PD_ERR; + + } /* switch (ld->cap_pipe_state) */ + + /* + * We've now read as much data as we were expecting, so process it. + */ + switch (result) { + + case PD_REC_HDR_READ: + /* We've read the header. Take care of byte order. */ + cap_pipe_adjust_header(ld->cap_pipe_byte_swapped, &ld->cap_pipe_hdr, + &ld->cap_pipe_rechdr.hdr); + if (ld->cap_pipe_rechdr.hdr.incl_len > WTAP_MAX_PACKET_SIZE) { + g_snprintf(errmsg, errmsgl, "Frame %u too long (%d bytes)", + ld->packet_count+1, ld->cap_pipe_rechdr.hdr.incl_len); + break; + } + ld->cap_pipe_state = STATE_EXPECT_DATA; + return 0; + + case PD_DATA_READ: + /* Fill in a "struct pcap_pkthdr", and process the packet. */ + phdr.ts.tv_sec = ld->cap_pipe_rechdr.hdr.ts_sec; + phdr.ts.tv_usec = ld->cap_pipe_rechdr.hdr.ts_usec; + phdr.caplen = ld->cap_pipe_rechdr.hdr.incl_len; + phdr.len = ld->cap_pipe_rechdr.hdr.orig_len; + + ld->packet_cb((u_char *)ld, &phdr, data); + + ld->cap_pipe_state = STATE_EXPECT_REC_HDR; + return 1; + + case PD_PIPE_EOF: + ld->cap_pipe_err = PIPEOF; + return -1; + + case PD_PIPE_ERR: + g_snprintf(errmsg, errmsgl, "Error reading from pipe: %s", + strerror(errno)); + /* Fall through */ + case PD_ERR: + break; + } + + ld->cap_pipe_err = PIPERR; + /* Return here rather than inside the switch to prevent GCC warning */ + return -1; +} + + +/** Open the capture input file (pcap or capture pipe). + * Returns TRUE if it succeeds, FALSE otherwise. */ +static gboolean +capture_loop_open_input(capture_options *capture_opts, loop_data *ld, + char *errmsg, size_t errmsg_len, + char *secondary_errmsg, size_t secondary_errmsg_len) +{ + gchar open_err_str[PCAP_ERRBUF_SIZE]; + gchar *sync_msg_str; + static const char ppamsg[] = "can't find PPA for "; + const char *set_linktype_err_str; + const char *libpcap_warn; +#ifdef _WIN32 + gchar *sync_secondary_msg_str; + int err; + WORD wVersionRequested; + WSADATA wsaData; +#endif +#ifdef HAVE_PCAP_REMOTE + struct pcap_rmtauth auth; +#endif + + + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_open_input : %s", capture_opts->iface); + + +/* XXX - opening Winsock on tshark? */ + + /* Initialize Windows Socket if we are in a WIN32 OS + This needs to be done before querying the interface for network/netmask */ +#ifdef _WIN32 + /* XXX - do we really require 1.1 or earlier? + Are there any versions that support only 2.0 or higher? */ + wVersionRequested = MAKEWORD(1, 1); + err = WSAStartup(wVersionRequested, &wsaData); + if (err != 0) { + switch (err) { + + case WSASYSNOTREADY: + g_snprintf(errmsg, errmsg_len, + "Couldn't initialize Windows Sockets: Network system not ready for network communication"); + break; + + case WSAVERNOTSUPPORTED: + g_snprintf(errmsg, 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, + "Couldn't initialize Windows Sockets: Blocking operation is in progress"); + break; + + case WSAEPROCLIM: + g_snprintf(errmsg, 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, + "Couldn't initialize Windows Sockets: Bad pointer passed to WSAStartup"); + break; + + default: + g_snprintf(errmsg, errmsg_len, + "Couldn't initialize Windows Sockets: error %d", err); + break; + } + g_snprintf(secondary_errmsg, secondary_errmsg_len, please_report); + return FALSE; + } +#endif + + /* Open the network interface to capture from it. + Some versions of libpcap may put warnings into the error buffer + if they succeed; to tell if that's happened, we have to clear + the error buffer, and check if it's still a null string. */ + open_err_str[0] = '\0'; +#ifdef HAVE_PCAP_OPEN + auth.type = capture_opts->auth_type == CAPTURE_AUTH_PWD ? + RPCAP_RMTAUTH_PWD : RPCAP_RMTAUTH_NULL; + auth.username = capture_opts->auth_username; + auth.password = capture_opts->auth_password; + + ld->pcap_h = pcap_open(capture_opts->iface, + capture_opts->has_snaplen ? capture_opts->snaplen : + WTAP_MAX_PACKET_SIZE, + /* flags */ + (capture_opts->promisc_mode ? PCAP_OPENFLAG_PROMISCUOUS : 0) | + (capture_opts->datatx_udp ? PCAP_OPENFLAG_DATATX_UDP : 0) | + (capture_opts->nocap_rpcap ? PCAP_OPENFLAG_NOCAPTURE_RPCAP : 0), + CAP_READ_TIMEOUT, &auth, open_err_str); +#else + ld->pcap_h = pcap_open_live(capture_opts->iface, + capture_opts->has_snaplen ? capture_opts->snaplen : + WTAP_MAX_PACKET_SIZE, + capture_opts->promisc_mode, CAP_READ_TIMEOUT, + open_err_str); +#endif + + if (ld->pcap_h != NULL) { + /* we've opened "iface" as a network device */ +#ifdef _WIN32 + /* try to set the capture buffer size */ + if (pcap_setbuff(ld->pcap_h, capture_opts->buffer_size * 1024 * 1024) != 0) { + 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" + "\n" + "Nonetheless, the capture is started.\n", + capture_opts->buffer_size); + report_capture_error("Couldn't set the capture buffer size!", + sync_secondary_msg_str); + g_free(sync_secondary_msg_str); + } +#endif + +#if defined(HAVE_PCAP_REMOTE) && defined(HAVE_PCAP_SETSAMPLING) + if (capture_opts->sampling_method != CAPTURE_SAMP_NONE) + { + struct pcap_samp *samp; + + if ((samp = pcap_setsampling(ld->pcap_h)) != NULL) + { + switch (capture_opts->sampling_method) + { + case CAPTURE_SAMP_BY_COUNT: + samp->method = PCAP_SAMP_1_EVERY_N; + break; + + case CAPTURE_SAMP_BY_TIMER: + samp->method = PCAP_SAMP_FIRST_AFTER_N_MS; + break; + + default: + sync_msg_str = g_strdup_printf( + "Unknown sampling method %d specified,\n" + "continue without packet sampling", + capture_opts->sampling_method); + report_capture_error("Couldn't set the capture " + "sampling", sync_msg_str); + g_free(sync_msg_str); + } + samp->value = capture_opts->sampling_param; + } + else + { + report_capture_error("Couldn't set the capture sampling", + "Cannot get packet sampling data structure"); + } + + } +#endif + + /* setting the data link type only works on real interfaces */ + if (capture_opts->linktype != -1) { + set_linktype_err_str = set_pcap_linktype(ld->pcap_h, capture_opts->iface, + capture_opts->linktype); + if (set_linktype_err_str != NULL) { + g_snprintf(errmsg, errmsg_len, "Unable to set data link type (%s).", + set_linktype_err_str); + g_snprintf(secondary_errmsg, secondary_errmsg_len, please_report); + return FALSE; + } + } + 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); + + if (ld->cap_pipe_fd == -1) { + + if (ld->cap_pipe_err == PIPNEXIST) { + /* Pipe doesn't exist, so output message for interface */ + + /* If we got a "can't find PPA for X" message, warn the user (who + is running (T)Wireshark on HP-UX) that they don't have a version + of libpcap that properly handles HP-UX (libpcap 0.6.x and later + versions, which properly handle HP-UX, say "can't find /dev/dlpi + PPA for X" rather than "can't find PPA for X"). */ + if (strncmp(open_err_str, ppamsg, sizeof ppamsg - 1) == 0) + libpcap_warn = + "\n\n" + "You are running (T)Wireshark with a version of the libpcap library\n" + "that doesn't handle HP-UX network devices well; this means that\n" + "(T)Wireshark may not be able to capture packets.\n" + "\n" + "To fix this, you should install libpcap 0.6.2, or a later version\n" + "of libpcap, rather than libpcap 0.4 or 0.5.x. It is available in\n" + "packaged binary form from the Software Porting And Archive Centre\n" + "for HP-UX; the Centre is at http://hpux.connect.org.uk/ - the page\n" + "at the URL lists a number of mirror sites."; + else + libpcap_warn = ""; + g_snprintf(errmsg, errmsg_len, + "The capture session could not be initiated (%s).", open_err_str); +#ifndef _WIN32 + g_snprintf(secondary_errmsg, secondary_errmsg_len, +"Please check to make sure you have sufficient permissions, and that you have " +"the proper interface or pipe specified.%s", libpcap_warn); +#else + g_snprintf(secondary_errmsg, secondary_errmsg_len, +"\n" +"Please check that \"%s\" is the proper interface.\n" +"\n" +"\n" +"Help can be found at:\n" +"\n" +" http://wiki.wireshark.org/CaptureSetup\n" +"\n" +"64-bit Windows:\n" +"WinPcap does not support 64-bit Windows; you will have to use some other\n" +"tool to capture traffic, such as netcap.\n" +"For netcap details see: http://support.microsoft.com/?id=310875\n" +"\n" +"Modem (PPP/WAN):\n" +"Note that version 3.0 of WinPcap, and earlier versions of WinPcap, don't\n" +"support capturing on PPP/WAN interfaces on Windows NT 4.0 / 2000 / XP /\n" +"Server 2003.\n" +"WinPcap 3.1 has support for it on Windows 2000 / XP / Server 2003, but has no\n" +"support for it on Windows NT 4.0 or Windows Vista (Beta 1).", + capture_opts->iface); +#endif /* _WIN32 */ + } + /* + * Else pipe (or file) does exist and cap_pipe_open_live() has + * filled in errmsg + */ + return FALSE; + } else + /* cap_pipe_open_live() succeeded; don't want + error message from pcap_open_live() */ + open_err_str[0] = '\0'; + } + +/* XXX - will this work for tshark? */ +#ifdef MUST_DO_SELECT + if (!ld->from_cap_pipe) { +#ifdef HAVE_PCAP_GET_SELECTABLE_FD + ld->pcap_fd = pcap_get_selectable_fd(ld->pcap_h); +#else + ld->pcap_fd = pcap_fileno(ld->pcap_h); +#endif + } +#endif + + /* Does "open_err_str" contain a non-empty string? If so, "pcap_open_live()" + returned a warning; print it, but keep capturing. */ + if (open_err_str[0] != '\0') { + sync_msg_str = g_strdup_printf("%s.", open_err_str); + report_capture_error(sync_msg_str, ""); + g_free(sync_msg_str); + } + + return TRUE; +} + + +/* close the capture input file (pcap or capture pipe) */ +static void capture_loop_close_input(loop_data *ld) { + + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_close_input"); + + /* if open, close the capture pipe "input file" */ + if (ld->cap_pipe_fd >= 0) { + g_assert(ld->from_cap_pipe); + eth_close(ld->cap_pipe_fd); + ld->cap_pipe_fd = 0; + } + + /* 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_assert(!ld->from_cap_pipe); + pcap_close(ld->pcap_h); + ld->pcap_h = NULL; + } + + ld->go = FALSE; + +#ifdef _WIN32 + /* Shut down windows sockets */ + WSACleanup(); +#endif +} + + +/* init the capture filter */ +static initfilter_status_t +capture_loop_init_filter(pcap_t *pcap_h, gboolean from_cap_pipe, gchar * iface, gchar * cfilter) { + bpf_u_int32 netnum, netmask; + gchar lookup_net_err_str[PCAP_ERRBUF_SIZE]; + struct bpf_program fcode; + + + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_init_filter: %s", cfilter); + + /* capture filters only work on real interfaces */ + if (cfilter && !from_cap_pipe) { + /* A capture filter was specified; set it up. */ + if (pcap_lookupnet(iface, &netnum, &netmask, lookup_net_err_str) < 0) { + /* + * Well, we can't get the netmask for this interface; it's used + * only for filters that check for broadcast IP addresses, so + * we just punt and use 0. It might be nice to warn the user, + * but that's a pain in a GUI application, as it'd involve popping + * up a message box, and it's not clear how often this would make + * a difference (only filters that check for IP broadcast addresses + * use the netmask). + */ + /*cmdarg_err( + "Warning: Couldn't obtain netmask info (%s).", lookup_net_err_str);*/ + netmask = 0; + } + if (pcap_compile(pcap_h, &fcode, cfilter, 1, netmask) < 0) { + /* Treat this specially - our caller might try to compile this + as a display filter and, if that succeeds, warn the user that + the display and capture filter syntaxes are different. */ + return INITFILTER_BAD_FILTER; + } + if (pcap_setfilter(pcap_h, &fcode) < 0) { +#ifdef HAVE_PCAP_FREECODE + pcap_freecode(&fcode); +#endif + return INITFILTER_OTHER_ERROR; + } +#ifdef HAVE_PCAP_FREECODE + pcap_freecode(&fcode); +#endif + } + + return INITFILTER_NO_ERROR; +} + + +/* 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; + + + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_init_output"); + + /* get snaplen */ + if (ld->from_cap_pipe) { + file_snaplen = ld->cap_pipe_hdr.snaplen; + } else + { + 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); + } else { + ld->pdh = libpcap_fdopen(save_file_fd, ld->linktype, file_snaplen, + &ld->bytes_written, &err); + } + + if (ld->pdh == NULL) { + /* We couldn't set up to write to the capture file. */ + /* XXX - use cf_open_error_message from tshark instead? */ + switch (err) { + + case WTAP_ERR_CANT_OPEN: + g_snprintf(errmsg, errmsg_len, "The file to which the capture would be saved" + " couldn't be created for some unknown reason."); + break; + + case WTAP_ERR_SHORT_WRITE: + g_snprintf(errmsg, errmsg_len, "A full header couldn't be written to the file" + " to which the capture would be saved."); + break; + + 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); + } 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)); + } + break; + } + + return FALSE; + } + + return TRUE; +} + +static gboolean +capture_loop_close_output(capture_options *capture_opts, loop_data *ld, int *err_close) { + + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_close_output"); + + if (capture_opts->multi_files_on) { + return ringbuf_libpcap_dump_close(&capture_opts->save_file, err_close); + } else { + return libpcap_dump_close(ld->pdh, err_close); + } +} + +/* dispatch incoming packets (pcap or capture pipe) + * + * Waits for incoming packets to be available, and calls pcap_dispatch() + * to cause them to be processed. + * + * Returns the number of packets which were processed. + * + * Times out (returning zero) after CAP_READ_TIMEOUT ms; this ensures that the + * packet-batching behaviour does not cause packets to get held back + * indefinitely. + */ +static int +capture_loop_dispatch(capture_options *capture_opts _U_, loop_data *ld, + char *errmsg, int errmsg_len) +{ + int inpkts; + int sel_ret; + gint packet_count_before; + guchar pcap_data[WTAP_MAX_PACKET_SIZE]; + + packet_count_before = ld->packet_count; + if (ld->from_cap_pipe) { + /* dispatch from capture pipe */ +#ifdef LOG_CAPTURE_VERBOSE + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: from capture pipe"); +#endif + sel_ret = cap_pipe_select(ld->cap_pipe_fd); + if (sel_ret <= 0) { + inpkts = 0; + if (sel_ret < 0 && errno != EINTR) { + g_snprintf(errmsg, errmsg_len, + "Unexpected error from select: %s", strerror(errno)); + report_capture_error(errmsg, please_report); + ld->go = FALSE; + } + } else { + /* + * "select()" says we can read from the pipe without blocking + */ + inpkts = cap_pipe_dispatch(ld, pcap_data, errmsg, errmsg_len); + if (inpkts < 0) { + ld->go = FALSE; + } + } + } + else + { + /* dispatch from pcap */ +#ifdef MUST_DO_SELECT + /* + * If we have "pcap_get_selectable_fd()", we use it to get the + * descriptor on which to select; if that's -1, it means there + * is no descriptor on which you can do a "select()" (perhaps + * because you're capturing on a special device, and that device's + * driver unfortunately doesn't support "select()", in which case + * we don't do the select - which means it might not be possible + * to stop a capture until a packet arrives. If that's unacceptable, + * plead with whoever supplies the software for that device to add + * "select()" support, or upgrade to libpcap 0.8.1 or later, and + * rebuild Wireshark or get a version built with libpcap 0.8.1 or + * later, so it can use pcap_breakloop(). + */ +#ifdef LOG_CAPTURE_VERBOSE + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: from pcap_dispatch with select"); +#endif + if (ld->pcap_fd != -1) { + sel_ret = cap_pipe_select(ld->pcap_fd); + if (sel_ret > 0) { + /* + * "select()" says we can read from it without blocking; go for + * it. + * + * We don't have pcap_breakloop(), so we only process one packet + * per pcap_dispatch() call, to allow a signal to stop the + * processing immediately, rather than processing all packets + * in a batch before quitting. + */ + inpkts = pcap_dispatch(ld->pcap_h, 1, ld->packet_cb, (u_char *)ld); + if (inpkts < 0) { + ld->pcap_err = TRUE; + ld->go = FALSE; /* error or pcap_breakloop() - stop capturing */ + } + } else { + if (sel_ret < 0 && errno != EINTR) { + g_snprintf(errmsg, errmsg_len, + "Unexpected error from select: %s", strerror(errno)); + report_capture_error(errmsg, please_report); + ld->go = FALSE; + } + } + } + else +#endif /* MUST_DO_SELECT */ + { + /* dispatch from pcap without select */ +#if 1 +#ifdef LOG_CAPTURE_VERBOSE + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: from pcap_dispatch"); +#endif +#ifdef _WIN32 + /* + * On Windows, we don't support asynchronously telling a process to + * stop capturing; instead, we check for an indication on a pipe + * after processing packets. We therefore process only one packet + * at a time, so that we can check the pipe after every packet. + */ + inpkts = pcap_dispatch(ld->pcap_h, 1, ld->packet_cb, (u_char *) ld); +#else + inpkts = pcap_dispatch(ld->pcap_h, -1, ld->packet_cb, (u_char *) ld); +#endif + if (inpkts < 0) { + if (inpkts == -1) { + /* Error, rather than pcap_breakloop(). */ + ld->pcap_err = TRUE; + } + ld->go = FALSE; /* error or pcap_breakloop() - stop capturing */ + } +#else /* pcap_next_ex */ +#ifdef LOG_CAPTURE_VERBOSE + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: from pcap_next_ex"); +#endif + /* XXX - this is currently unused, as there is some confusion with pcap_next_ex() vs. pcap_dispatch() */ + + /* + * WinPcap's remote capturing feature doesn't work with pcap_dispatch(), + * see http://wiki.wireshark.org/CaptureSetup_2fWinPcapRemote + * This should be fixed in the WinPcap 4.0 alpha release. + * + * For reference, an example remote interface: + * rpcap://[1.2.3.4]/\Device\NPF_{39993D68-7C9B-4439-A329-F2D888DA7C5C} + */ + + /* emulate dispatch from pcap */ + { + int in; + struct pcap_pkthdr *pkt_header; + u_char *pkt_data; + + in = 0; + while(ld->go && + (in = pcap_next_ex(ld->pcap_h, &pkt_header, &pkt_data)) == 1) + ld->packet_cb( (u_char *) ld, pkt_header, pkt_data); + + if(in < 0) { + ld->pcap_err = TRUE; + ld->go = FALSE; + } + } +#endif /* pcap_next_ex */ + } + } + +#ifdef LOG_CAPTURE_VERBOSE + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: %d new packet%s", inpkts, plurality(inpkts, "", "s")); +#endif + + return ld->packet_count - packet_count_before; +} + + +/* open the output file (temporary/specified name/ringbuffer/named pipe/stdout) */ +/* Returns TRUE if the file opened successfully, FALSE otherwise. */ +static gboolean +capture_loop_open_output(capture_options *capture_opts, int *save_file_fd, + char *errmsg, int errmsg_len) { + + char tmpname[128+1]; + gchar *capfile_name; + gboolean is_tempfile; + + + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_open_output: %s", + (capture_opts->save_file) ? capture_opts->save_file : ""); + + if (capture_opts->save_file != NULL) { + /* We return to the caller while the capture is in progress. + * Therefore we need to take a copy of save_file in + * case the caller destroys it after we return. + */ + capfile_name = g_strdup(capture_opts->save_file); + + if (capture_opts->output_to_pipe == TRUE) { /* either "-" or named pipe */ + if (capture_opts->multi_files_on) { + /* ringbuffer is enabled; that doesn't work with standard output or a named pipe */ + g_snprintf(errmsg, errmsg_len, + "Ring buffer requested, but capture is being written to standard output or to a named pipe."); + g_free(capfile_name); + return FALSE; + } + if (strcmp(capfile_name, "-") == 0) { + /* write to stdout */ + *save_file_fd = 1; +#ifdef _WIN32 + /* set output pipe to binary mode to avoid Windows text-mode processing (eg: for CR/LF) */ + _setmode(1, O_BINARY); +#endif + } + } /* if (...output_to_pipe ... */ + + else { + if (capture_opts->multi_files_on) { + /* ringbuffer is enabled */ + *save_file_fd = ringbuf_init(capfile_name, + (capture_opts->has_ring_num_files) ? capture_opts->ring_num_files : 0); + + /* we need the ringbuf name */ + if(*save_file_fd != -1) { + g_free(capfile_name); + capfile_name = g_strdup(ringbuf_current_filename()); + } + } else { + /* Try to open/create the specified file for use as a capture buffer. */ + *save_file_fd = eth_open(capfile_name, O_RDWR|O_BINARY|O_TRUNC|O_CREAT, + 0600); + } + } + is_tempfile = FALSE; + } else { + /* Choose a random name for the temporary capture buffer */ + *save_file_fd = create_tempfile(tmpname, sizeof tmpname, "ether"); + capfile_name = g_strdup(tmpname); + is_tempfile = TRUE; + } + + /* did we fail to open the output file? */ + if (*save_file_fd == -1) { + 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)); + } else { + if (capture_opts->multi_files_on) { + ringbuf_error_cleanup(); + } + + 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_free(capfile_name); + return FALSE; + } + + if(capture_opts->save_file != NULL) { + g_free(capture_opts->save_file); + } + capture_opts->save_file = capfile_name; + /* capture_opts.save_file is "g_free"ed later, which is equivalent to + "g_free(capfile_name)". */ +#ifndef _WIN32 + fchown(*save_file_fd, capture_opts->owner, capture_opts->group); +#endif + + return TRUE; +} + + +static void +capture_loop_stop_signal_handler(int signo _U_) +{ + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Signal: Stop capture"); + capture_loop_stop(); +} + +#ifdef _WIN32 +#define TIME_GET() GetTickCount() +#else +#define TIME_GET() time(NULL) +#endif + +/* Do the low-level work of a capture. + Returns TRUE if it succeeds, FALSE otherwise. */ +static gboolean +capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct pcap_stat *stats) +{ +#ifndef _WIN32 + struct sigaction act; +#endif + time_t upd_time, cur_time; + time_t start_time; + int err_close; + int inpkts; + gint inpkts_to_sync_pipe = 0; /* packets not already send out to the sync_pipe */ + condition *cnd_file_duration = NULL; + condition *cnd_autostop_files = NULL; + condition *cnd_autostop_size = NULL; + condition *cnd_autostop_duration = NULL; + guint32 autostop_files = 0; + gboolean write_ok; + gboolean close_ok; + gboolean cfilter_error = FALSE; +#define MSG_MAX_LENGTH 4096 + char errmsg[MSG_MAX_LENGTH+1]; + char secondary_errmsg[MSG_MAX_LENGTH+1]; + int save_file_fd = -1; + + *errmsg = '\0'; + *secondary_errmsg = '\0'; + + /* init the loop data */ + ld.go = TRUE; + ld.packet_count = 0; + if (capture_opts->has_autostop_packets) + ld.packet_max = capture_opts->autostop_packets; + else + ld.packet_max = 0; /* no limit */ + ld.err = 0; /* no error seen yet */ + ld.wtap_linktype = WTAP_ENCAP_UNKNOWN; + ld.pcap_err = FALSE; + ld.from_cap_pipe = FALSE; + ld.pdh = NULL; + ld.cap_pipe_fd = -1; +#ifdef MUST_DO_SELECT + ld.pcap_fd = 0; +#endif + ld.packet_cb = capture_loop_packet_cb; + + + /* We haven't yet gotten the capture statistics. */ + *stats_known = FALSE; + +#ifndef _WIN32 + /* + * Catch SIGUSR1, so that we exit cleanly if the parent process + * kills us with it due to the user selecting "Capture->Stop". + */ + act.sa_handler = capture_loop_stop_signal_handler; + /* + * Arrange that system calls not get restarted, because when + * our signal handler returns we don't want to restart + * a call that was waiting for packets to arrive. + */ + act.sa_flags = 0; + sigemptyset(&act.sa_mask); + sigaction(SIGUSR1, &act, NULL); +#endif /* _WIN32 */ + + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Capture loop starting ..."); + capture_opts_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, capture_opts); + + /* open the "input file" from network interface or capture pipe */ + if (!capture_loop_open_input(capture_opts, &ld, errmsg, sizeof(errmsg), + secondary_errmsg, sizeof(secondary_errmsg))) { + goto error; + } + + /* init the input filter from the network interface (capture pipe will do nothing) */ + switch (capture_loop_init_filter(ld.pcap_h, ld.from_cap_pipe, capture_opts->iface, capture_opts->cfilter)) { + + case INITFILTER_NO_ERROR: + break; + + case INITFILTER_BAD_FILTER: + cfilter_error = TRUE; + g_snprintf(errmsg, sizeof(errmsg), "%s", pcap_geterr(ld.pcap_h)); + goto error; + + case INITFILTER_OTHER_ERROR: + g_snprintf(errmsg, sizeof(errmsg), "Can't install filter (%s).", + pcap_geterr(ld.pcap_h)); + g_snprintf(secondary_errmsg, sizeof(secondary_errmsg), "%s", please_report); + goto error; + } + + /* If we're supposed to write to a capture file, open it for output + (temporary/specified name/ringbuffer) */ + if (capture_opts->saving_to_file) { + if (!capture_loop_open_output(capture_opts, &save_file_fd, errmsg, sizeof(errmsg))) { + goto error; + } + + /* set up to write to the already-opened capture output file/files */ + if (!capture_loop_init_output(capture_opts, save_file_fd, &ld, errmsg, sizeof(errmsg))) { + goto error; + } + + /* XXX - capture SIGTERM and close the capture, in case we're on a + Linux 2.0[.x] system and you have to explicitly close the capture + stream in order to turn promiscuous mode off? We need to do that + in other places as well - and I don't think that works all the + time in any case, due to libpcap bugs. */ + + /* Well, we should be able to start capturing. + + Sync out the capture file, so the header makes it to the file system, + and send a "capture started successfully and capture file created" + message to our parent so that they'll open the capture file and + update its windows to indicate that we have a live capture in + progress. */ + libpcap_dump_flush(ld.pdh, NULL); + report_new_capture_file(capture_opts->save_file); + } + + /* initialize capture stop (and alike) conditions */ + init_capture_stop_conditions(); + /* create stop conditions */ + if (capture_opts->has_autostop_filesize) + cnd_autostop_size = + cnd_new(CND_CLASS_CAPTURESIZE,(long)capture_opts->autostop_filesize * 1024); + if (capture_opts->has_autostop_duration) + cnd_autostop_duration = + cnd_new(CND_CLASS_TIMEOUT,(gint32)capture_opts->autostop_duration); + + if (capture_opts->multi_files_on) { + if (capture_opts->has_file_duration) + cnd_file_duration = + cnd_new(CND_CLASS_TIMEOUT, capture_opts->file_duration); + + if (capture_opts->has_autostop_files) + cnd_autostop_files = + cnd_new(CND_CLASS_CAPTURESIZE, capture_opts->autostop_files); + } + + /* init the time values */ + start_time = TIME_GET(); + upd_time = TIME_GET(); + + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Capture loop running!"); + + /* WOW, everything is prepared! */ + /* please fasten your seat belts, we will enter now the actual capture loop */ + while (ld.go) { + /* dispatch incoming packets */ + inpkts = capture_loop_dispatch(capture_opts, &ld, errmsg, sizeof(errmsg)); + +#ifdef _WIN32 + /* any news from our parent (signal pipe)? -> just stop the capture */ + if (!signal_pipe_check_running()) { + ld.go = FALSE; + } +#endif + + if (inpkts > 0) { + inpkts_to_sync_pipe += inpkts; + + /* check capture size condition */ + if (cnd_autostop_size != NULL && + cnd_eval(cnd_autostop_size, (guint32)ld.bytes_written)){ + /* Capture size limit reached, do we have another file? */ + if (capture_opts->multi_files_on) { + if (cnd_autostop_files != NULL && + cnd_eval(cnd_autostop_files, ++autostop_files)) { + /* no files left: stop here */ + ld.go = FALSE; + continue; + } + + /* Switch to the next ringbuffer file */ + if (ringbuf_switch_file(&ld.pdh, &capture_opts->save_file, + &save_file_fd, &ld.bytes_written, &ld.err)) { + /* File switch succeeded: reset the conditions */ + cnd_reset(cnd_autostop_size); + if (cnd_file_duration) { + cnd_reset(cnd_file_duration); + } + libpcap_dump_flush(ld.pdh, NULL); + report_packet_count(inpkts_to_sync_pipe); + inpkts_to_sync_pipe = 0; + report_new_capture_file(capture_opts->save_file); + } else { + /* File switch failed: stop here */ + ld.go = FALSE; + continue; + } + } else { + /* single file, stop now */ + ld.go = FALSE; + continue; + } + } /* cnd_autostop_size */ + if (capture_opts->output_to_pipe) { + libpcap_dump_flush(ld.pdh, NULL); + } + } /* inpkts */ + + /* Only update once a second (Win32: 500ms) so as not to overload slow + * displays. This also prevents too much context-switching between the + * dumpcap and wireshark processes */ + cur_time = TIME_GET(); +#ifdef _WIN32 + if ( (cur_time - upd_time) > 500) { +#else + if (cur_time - upd_time > 0) { +#endif + upd_time = cur_time; + + /*if (pcap_stats(pch, stats) >= 0) { + *stats_known = TRUE; + }*/ + + /* Let the parent process know. */ + if (inpkts_to_sync_pipe) { + /* do sync here */ + libpcap_dump_flush(ld.pdh, NULL); + + /* Send our parent a message saying we've written out "inpkts_to_sync_pipe" + packets to the capture file. */ + report_packet_count(inpkts_to_sync_pipe); + + inpkts_to_sync_pipe = 0; + } + + /* check capture duration condition */ + if (cnd_autostop_duration != NULL && cnd_eval(cnd_autostop_duration)) { + /* The maximum capture time has elapsed; stop the capture. */ + ld.go = FALSE; + continue; + } + + /* check capture file duration condition */ + if (cnd_file_duration != NULL && cnd_eval(cnd_file_duration)) { + /* duration limit reached, do we have another file? */ + if (capture_opts->multi_files_on) { + if (cnd_autostop_files != NULL && + cnd_eval(cnd_autostop_files, ++autostop_files)) { + /* no files left: stop here */ + ld.go = FALSE; + continue; + } + + /* Switch to the next ringbuffer file */ + if (ringbuf_switch_file(&ld.pdh, &capture_opts->save_file, + &save_file_fd, &ld.bytes_written, &ld.err)) { + /* file switch succeeded: reset the conditions */ + cnd_reset(cnd_file_duration); + if(cnd_autostop_size) + cnd_reset(cnd_autostop_size); + libpcap_dump_flush(ld.pdh, NULL); + report_packet_count(inpkts_to_sync_pipe); + inpkts_to_sync_pipe = 0; + report_new_capture_file(capture_opts->save_file); + } else { + /* File switch failed: stop here */ + ld.go = FALSE; + continue; + } + } else { + /* single file, stop now */ + ld.go = FALSE; + continue; + } + } /* cnd_file_duration */ + } + + } /* while (ld.go) */ + + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Capture loop stopping ..."); + + /* delete stop conditions */ + if (cnd_file_duration != NULL) + cnd_delete(cnd_file_duration); + if (cnd_autostop_files != NULL) + cnd_delete(cnd_autostop_files); + if (cnd_autostop_size != NULL) + cnd_delete(cnd_autostop_size); + if (cnd_autostop_duration != NULL) + cnd_delete(cnd_autostop_duration); + + /* did we had a pcap (input) error? */ + if (ld.pcap_err) { + /* On Linux, if an interface goes down while you're capturing on it, + you'll get a "recvfrom: Network is down" error (ENETDOWN). + (At least you will if strerror() doesn't show a local translation + of the error.) + + On FreeBSD and OS X, if a network adapter disappears while + you're capturing on it, you'll get a "read: Device not configured" + error (ENXIO). (See previous parenthetical note.) + + On OpenBSD, you get "read: I/O error" (EIO) in the same case. + + These should *not* be reported to the Wireshark developers. */ + char *cap_err_str; + + cap_err_str = pcap_geterr(ld.pcap_h); + if (strcmp(cap_err_str, "recvfrom: Network is down") == 0 || + strcmp(cap_err_str, "read: Device not configured") == 0 || + strcmp(cap_err_str, "read: I/O error") == 0) { + report_capture_error("The network adapter on which the capture was being done " + "is no longer running; the capture has stopped.", + ""); + } else { + g_snprintf(errmsg, sizeof(errmsg), "Error while capturing packets: %s", + cap_err_str); + report_capture_error(errmsg, please_report); + } + } + else if (ld.from_cap_pipe && ld.cap_pipe_err == PIPERR) + report_capture_error(errmsg, ""); + + /* did we had an error while capturing? */ + if (ld.err == 0) { + write_ok = TRUE; + } else { + capture_loop_get_errmsg(errmsg, sizeof(errmsg), capture_opts->save_file, ld.err, + FALSE); + report_capture_error(errmsg, please_report); + write_ok = FALSE; + } + + if (capture_opts->saving_to_file) { + /* close the wiretap (output) file */ + close_ok = capture_loop_close_output(capture_opts, &ld, &err_close); + } else + close_ok = TRUE; + + /* there might be packets not yet notified to the parent */ + /* (do this after closing the file, so all packets are already flushed) */ + if(inpkts_to_sync_pipe) { + report_packet_count(inpkts_to_sync_pipe); + inpkts_to_sync_pipe = 0; + } + + /* If we've displayed a message about a write error, there's no point + in displaying another message about an error on close. */ + if (!close_ok && write_ok) { + capture_loop_get_errmsg(errmsg, sizeof(errmsg), capture_opts->save_file, err_close, + TRUE); + report_capture_error(errmsg, ""); + } + + /* + * XXX We exhibit different behaviour between normal mode and sync mode + * when the pipe is stdin and not already at EOF. If we're a child, the + * parent's stdin isn't closed, so if the user starts another capture, + * cap_pipe_open_live() will very likely not see the expected magic bytes and + * will say "Unrecognized libpcap format". On the other hand, in normal + * mode, cap_pipe_open_live() will say "End of file on pipe during open". + */ + + /* get packet drop statistics from pcap */ + if(ld.pcap_h != NULL) { + g_assert(!ld.from_cap_pipe); + /* Get the capture statistics, so we know how many packets were + dropped. */ + if (pcap_stats(ld.pcap_h, stats) >= 0) { + *stats_known = TRUE; + /* Let the parent process know. */ + report_packet_drops(stats->ps_drop); + } else { + g_snprintf(errmsg, sizeof(errmsg), + "Can't get packet-drop statistics: %s", + pcap_geterr(ld.pcap_h)); + report_capture_error(errmsg, please_report); + } + } + + /* close the input file (pcap or capture pipe) */ + capture_loop_close_input(&ld); + + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Capture loop stopped!"); + + /* ok, if the write and the close were successful. */ + return write_ok && close_ok; + +error: + if (capture_opts->multi_files_on) { + /* cleanup ringbuffer */ + ringbuf_error_cleanup(); + } else { + /* We can't use the save file, and we have no FILE * for the stream + to close in order to close it, so close the FD directly. */ + if(save_file_fd != -1) { + eth_close(save_file_fd); + } + + /* We couldn't even start the capture, so get rid of the capture + file. */ + if(capture_opts->save_file != NULL) { + eth_unlink(capture_opts->save_file); + g_free(capture_opts->save_file); + } + } + capture_opts->save_file = NULL; + if (cfilter_error) + report_cfilter_error(capture_opts->cfilter, errmsg); + else + report_capture_error(errmsg, secondary_errmsg); + + /* close the input file (pcap or cap_pipe) */ + capture_loop_close_input(&ld); + + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Capture loop stopped with error"); + + return FALSE; +} + + +static void capture_loop_stop(void) +{ +#ifdef HAVE_PCAP_BREAKLOOP + if(ld.pcap_h != NULL) + pcap_breakloop(ld.pcap_h); +#endif + ld.go = FALSE; +} + + +static void +capture_loop_get_errmsg(char *errmsg, int errmsglen, const char *fname, + int err, gboolean is_close) +{ + switch (err) { + + case ENOSPC: + g_snprintf(errmsg, errmsglen, + "Not all the packets could be written to the file" + " to which the capture was being saved\n" + "(\"%s\") because there is no space left on the file system\n" + "on which that file resides.", + fname); + break; + +#ifdef EDQUOT + case EDQUOT: + g_snprintf(errmsg, errmsglen, + "Not all the packets could be written to the file" + " to which the capture was being saved\n" + "(\"%s\") because you are too close to, or over," + " your disk quota\n" + "on the file system on which that file resides.", + fname); + break; +#endif + + case WTAP_ERR_CANT_CLOSE: + g_snprintf(errmsg, errmsglen, + "The file to which the capture was being saved" + " couldn't be closed for some unknown reason."); + break; + + case WTAP_ERR_SHORT_WRITE: + g_snprintf(errmsg, errmsglen, + "Not all the packets could be written to the file" + " to which the capture was being saved\n" + "(\"%s\").", + fname); + break; + + default: + if (is_close) { + g_snprintf(errmsg, errmsglen, + "The file to which the capture was being saved\n" + "(\"%s\") could not be closed: %s.", + fname, wtap_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, wtap_strerror(err)); + } + break; + } +} + + +/* one packet was captured, process it */ +static void +capture_loop_packet_cb(u_char *user, const struct pcap_pkthdr *phdr, + const u_char *pd) +{ + loop_data *ld = (void *) user; + int err; + + /* if the user told us to stop after x packets, do we already have enough? */ + if ((ld->packet_max > 0) && (ld->packet_count >= ld->packet_max)) + { + ld->go = FALSE; + return; + } + + /* We may be called multiple times from pcap_dispatch(); if we've set + the "stop capturing" flag, ignore this packet, as we're not + supposed to be saving any more packets. */ + if (!ld->go) + return; + + if (ld->pdh) { + /* 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)) { + ld->go = FALSE; + ld->err = err; + } else + ld->packet_count++; + } +} + /* And now our feature presentation... [ fade to music ] */ int @@ -794,7 +2624,7 @@ report_packet_drops(int drops) #ifdef _WIN32 -gboolean +static gboolean signal_pipe_check_running(void) { /* any news from our parent? -> just stop the capture */ |