diff options
author | guy <guy@f5534014-38df-0310-8fa8-9805f1628bb7> | 2008-02-16 02:39:58 +0000 |
---|---|---|
committer | guy <guy@f5534014-38df-0310-8fa8-9805f1628bb7> | 2008-02-16 02:39:58 +0000 |
commit | 56a4a86233951c2d3b08036817bca9bc7026955b (patch) | |
tree | 208d76ec65d5ad787a464bbf1313e4de59dd3e5e /dumpcap.c | |
parent | c63b924aaec0d6ced0744ec5f62235fd8f324509 (diff) |
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.)
git-svn-id: http://anonsvn.wireshark.org/wireshark/trunk@24345 f5534014-38df-0310-8fa8-9805f1628bb7
Diffstat (limited to 'dumpcap.c')
-rw-r--r-- | dumpcap.c | 1858 |
1 files changed, 1844 insertions, 14 deletions
@@ -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 */ |