X-Git-Url: http://git.samba.org/samba.git/?a=blobdiff_plain;f=capture_loop.c;h=92b626353b8f1d73b8d0a1d3464307ce36868098;hb=f3731f68b8138211b414a48cb95f0698b172ca6c;hp=4670f4be3c69e75214fae3a5dc052ca9f8c8500b;hpb=7a685d5c3c3f53dd7e395f42946b8a3dc06f65dc;p=obnox%2Fwireshark%2Fwip.git diff --git a/capture_loop.c b/capture_loop.c index 4670f4be3c..92b626353b 100644 --- a/capture_loop.c +++ b/capture_loop.c @@ -3,8 +3,8 @@ * * $Id$ * - * Ethereal - Network traffic analyzer - * By Gerald Combs + * Wireshark - Network traffic analyzer + * By Gerald Combs * Copyright 1998 Gerald Combs * * This program is free software; you can redistribute it and/or @@ -61,115 +61,43 @@ #include #include +#include -#include #include -#include +#include + +#include "pcapio.h" + +#include "capture-pcap-util.h" + #include "capture.h" #include "capture_sync.h" -#include "pcap-util.h" -#include "simple_dialog.h" -#include "globals.h" #include "conditions.h" #include "capture_stop_conditions.h" #include "ringbuffer.h" -#include "wiretap/libpcap.h" -#include "wiretap/wtap.h" -#include "wiretap/wtap-capture.h" - -#include -/* XXX - try to remove this later */ -#include "ui_util.h" -/* XXX - try to remove this later */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - +#include "simple_dialog.h" +#include "tempfile.h" +#include "log.h" +#include "file_util.h" +#include "capture_loop.h" /* - * We don't want to do a "select()" on the pcap_t's file descriptor 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.) - * - * We *do* want to do it on other platforms, as, on other platforms (with - * the possible exception of Ultrix and Digital UNIX), the read timeout - * doesn't expire if no packets have arrived, so a "pcap_dispatch()" call - * will block until packets arrive, causing the UI to hang. - * - * XXX - the various BSDs appear to define BSD in ; we don't - * want to include it if it's not present on this platform, however. + * Standard secondary message for unexpected errors. */ -#if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) && \ - !defined(__bsdi__) && !defined(__APPLE__) && !defined(_WIN32) && \ - !defined(__CYGWIN__) -# define MUST_DO_SELECT -#endif - - -typedef struct _loop_data { - /* common */ - gboolean go; /* TRUE as long as we're supposed to keep capturing */ - int err; /* if non-zero, error seen while capturing */ - gint packets_max; /* Number of packets we're supposed to capture - 0 means infinite */ - gint packets_sync_pipe; /* packets not already send out to the sync_pipe */ - packet_counts counts; /* several packet type counters */ - - /* 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 */ -#ifndef _WIN32 - struct pcap_hdr cap_pipe_hdr; - 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; -#endif +static const char please_report[] = + "Please report this to the Wireshark developers"; - /* wiretap (output file) */ - wtap_dumper *wtap_pdh; - gint wtap_linktype; - -} loop_data; +/* + * This needs to be static, so that the SIGUSR1 handler can clear the "go" + * flag. + */ +static loop_data ld; /* @@ -177,10 +105,10 @@ typedef struct _loop_data { */ #define CAP_READ_TIMEOUT 250 -static void capture_loop_packet_cb(guchar *user, const struct pcap_pkthdr *phdr, - const guchar *pd); -static void capture_loop_popup_errmsg(const char *); -static void capture_loop_get_errmsg(char *, int, const char *, int, gboolean); +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); @@ -188,9 +116,9 @@ static void capture_loop_get_errmsg(char *, int, const char *, int, gboolean); /* Take care of byte order in the libpcap headers read from pipes. * (function taken from wiretap/libpcap.c) */ static void -cap_pipe_adjust_header(loop_data *ld, struct pcap_hdr *hdr, struct pcaprec_hdr *rechdr) +cap_pipe_adjust_header(gboolean byte_swapped, struct pcap_hdr *hdr, struct pcaprec_hdr *rechdr) { - if (ld->cap_pipe_byte_swapped) { + if (byte_swapped) { /* Byte-swap the record header fields. */ rechdr->ts_sec = BSWAP32(rechdr->ts_sec); rechdr->ts_usec = BSWAP32(rechdr->ts_usec); @@ -233,13 +161,16 @@ cap_pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, fd_set rfds; struct timeval timeout; + + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "cap_pipe_open_live: %s", pipename); + /* - * XXX Ethereal blocks until we return + * XXX (T)Wireshark blocks until we return */ if (strcmp(pipename, "-") == 0) fd = 0; /* read from stdin */ else { - if (stat(pipename, &pipe_stat) < 0) { + if (eth_stat(pipename, &pipe_stat) < 0) { if (errno == ENOENT || errno == ENOTDIR) ld->cap_pipe_err = PIPNEXIST; else { @@ -265,7 +196,7 @@ cap_pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, } return -1; } - fd = open(pipename, O_RDONLY | O_NONBLOCK); + 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 " @@ -369,6 +300,7 @@ cap_pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, 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"); @@ -380,8 +312,9 @@ cap_pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, 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; - close(fd); + eth_close(fd); return -1; } @@ -389,16 +322,19 @@ error: /* 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(int fd, loop_data *ld, struct pcap_hdr *hdr, - struct pcaprec_modified_hdr *rechdr, guchar *data, - char *errmsg, int errmsgl) +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: @@ -409,8 +345,8 @@ cap_pipe_dispatch(int fd, loop_data *ld, struct pcap_hdr *hdr, /* Fall through */ case STATE_READ_REC_HDR: - b = read(fd, ((char *)rechdr)+ld->cap_pipe_bytes_read, - ld->cap_pipe_bytes_to_read - ld->cap_pipe_bytes_read); + 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; @@ -429,7 +365,8 @@ cap_pipe_dispatch(int fd, loop_data *ld, struct pcap_hdr *hdr, /* Fall through */ case STATE_READ_DATA: - b = read(fd, data+ld->cap_pipe_bytes_read, rechdr->hdr.incl_len - ld->cap_pipe_bytes_read); + 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; @@ -437,7 +374,7 @@ cap_pipe_dispatch(int fd, loop_data *ld, struct pcap_hdr *hdr, result = PD_PIPE_ERR; break; } - if ((ld->cap_pipe_bytes_read += b) < rechdr->hdr.incl_len) + if ((ld->cap_pipe_bytes_read += b) < ld->cap_pipe_rechdr.hdr.incl_len) return 0; result = PD_DATA_READ; break; @@ -455,10 +392,11 @@ cap_pipe_dispatch(int fd, loop_data *ld, struct pcap_hdr *hdr, case PD_REC_HDR_READ: /* We've read the header. Take care of byte order. */ - cap_pipe_adjust_header(ld, hdr, &rechdr->hdr); - if (rechdr->hdr.incl_len > WTAP_MAX_PACKET_SIZE) { + 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->counts.total+1, rechdr->hdr.incl_len); + ld->packet_count+1, ld->cap_pipe_rechdr.hdr.incl_len); break; } ld->cap_pipe_state = STATE_EXPECT_DATA; @@ -466,12 +404,12 @@ cap_pipe_dispatch(int fd, loop_data *ld, struct pcap_hdr *hdr, case PD_DATA_READ: /* Fill in a "struct pcap_pkthdr", and process the packet. */ - phdr.ts.tv_sec = rechdr->hdr.ts_sec; - phdr.ts.tv_usec = rechdr->hdr.ts_usec; - phdr.caplen = rechdr->hdr.incl_len; - phdr.len = rechdr->hdr.orig_len; + 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; - capture_loop_packet_cb((guchar *)ld, &phdr, data); + ld->packet_cb((u_char *)ld, &phdr, data); ld->cap_pipe_state = STATE_EXPECT_REC_HDR; return 1; @@ -496,8 +434,16 @@ cap_pipe_dispatch(int fd, loop_data *ld, struct pcap_hdr *hdr, /* open the capture input file (pcap or capture pipe) */ -static int capture_loop_open_input(loop_data *ld, char *errmsg, int errmsg_len) { +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; +#ifdef _WIN32 + gchar *sync_secondary_msg_str; +#endif const char *set_linktype_err_str; #ifdef _WIN32 int err; @@ -505,9 +451,15 @@ static int capture_loop_open_input(loop_data *ld, char *errmsg, int errmsg_len) WSADATA wsaData; #else static const char ppamsg[] = "can't find PPA for "; - char *libpcap_warn; + const char *libpcap_warn; #endif + + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_open_input : %s", capture_opts->iface); + + +/* XXX - opening Winsock on twireshark? */ + /* Initialize Windows Socket if we are in a WIN32 OS This needs to be done before querying the interface for network/netmask */ #ifdef _WIN32 @@ -549,6 +501,7 @@ static int capture_loop_open_input(loop_data *ld, char *errmsg, int errmsg_len) "Couldn't initialize Windows Sockets: error %d", err); break; } + g_snprintf(secondary_errmsg, secondary_errmsg_len, please_report); return FALSE; } #endif @@ -558,93 +511,97 @@ static int capture_loop_open_input(loop_data *ld, char *errmsg, int errmsg_len) 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'; - ld->pcap_h = pcap_open_live(cfile.iface, - capture_opts.has_snaplen ? capture_opts.snaplen : + 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, + capture_opts->promisc_mode, CAP_READ_TIMEOUT, open_err_str); if (ld->pcap_h != NULL) { - /* we've opened "cfile.iface" as a network device */ + /* 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) { - simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK, - "%sCouldn't set the capture buffer size!%s\n" - "\n" + 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", - simple_dialog_primary_start(), simple_dialog_primary_end(), capture_opts.buffer_size); + 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 /* 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, cfile.iface, - capture_opts.linktype); + 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); + 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 "cfile.iface" as a network device. */ + /* We couldn't open "iface" as a network device. */ #ifdef _WIN32 - /* On Windows, we don't support capturing on pipes, so we give up. - If this is a child process that does the capturing in sync - mode or fork mode, it shouldn't do any UI stuff until we pop up the - capture-progress window, and, since we couldn't start the - capture, we haven't popped it up. */ - if (!capture_child) { - main_window_update(); - } + /* On Windows, we don't support capturing on pipes, so we give up. */ /* On Win32 OSes, the capture devices are probably available to all users; don't warn about permissions problems. - Do, however, warn that WAN devices aren't supported. */ + Do, however, warn about the lack of 64-bit support, and warn that + WAN devices aren't supported. */ g_snprintf(errmsg, errmsg_len, - "The capture session could not be initiated (%s).\n" - "Please check that you have the proper interface specified.\n" - "\n" - "Note that the WinPcap 2.x version of the driver Ethereal uses for packet\n" - "capture on Windows doesn't support capturing on PPP/WAN interfaces in\n" - "Windows NT/2000/XP/2003 Server, and that the WinPcap 3.0 and later versions\n" - "don't support capturing on PPP/WAN interfaces at all.", - open_err_str); +"The capture session could not be initiated.\n" +"\"%s\"", + open_err_str); + 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); return FALSE; #else - /* try to open cfile.iface as a pipe */ - ld->cap_pipe_fd = cap_pipe_open_live(cfile.iface, &ld->cap_pipe_hdr, ld, errmsg, errmsg_len); + /* try to open iface 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 this is a child process that does the capturing in sync - * mode or fork mode, it shouldn't do any UI stuff until we pop up the - * capture-progress window, and, since we couldn't start the - * capture, we haven't popped it up. - */ - if (!capture_child) { - main_window_update(); - } - if (ld->cap_pipe_err == PIPNEXIST) { /* Pipe doesn't exist, so output message for interface */ - /* If we got a "can't find PPA for XXX" message, warn the user (who - is running Ethereal on HP-UX) that they don't have a version + /* 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 XXX" rather than "can't find PPA for XXX"). */ + 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 Ethereal with a version of the libpcap library\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" - "Ethereal may not be able to capture packets.\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" @@ -653,11 +610,11 @@ static int capture_loop_open_input(loop_data *ld, char *errmsg, int errmsg_len) "at the URL lists a number of mirror sites."; else libpcap_warn = ""; - g_snprintf(errmsg, sizeof errmsg, - "The capture session could not be initiated (%s).\n" - "Please check to make sure you have sufficient permissions, and that\n" - "you have the proper interface or pipe specified.%s", open_err_str, - libpcap_warn); + g_snprintf(errmsg, errmsg_len, + "The capture session could not be initiated (%s).", open_err_str); + g_snprintf(secondary_errmsg, secondary_errmsg_len, +"Please check to make sure you have sufficient permissions, and that you have\n" +"the proper interface or pipe specified.%s", libpcap_warn); } /* * Else pipe (or file) does exist and cap_pipe_open_live() has @@ -671,26 +628,39 @@ static int capture_loop_open_input(loop_data *ld, char *errmsg, int errmsg_len) #endif } +/* XXX - will this work for twireshark? */ #ifdef MUST_DO_SELECT - if (!ld->from_cap_pipe) ld->pcap_fd = pcap_fileno(ld->pcap_h); + 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') - g_warning("%s.", open_err_str); + 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; } -/* open the capture input file (pcap or capture pipe) */ +/* 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"); + #ifndef _WIN32 /* if open, close the capture pipe "input file" */ if (ld->cap_pipe_fd >= 0) { g_assert(ld->from_cap_pipe); - close(ld->cap_pipe_fd); + eth_close(ld->cap_pipe_fd); } #endif @@ -708,15 +678,18 @@ static void capture_loop_close_input(loop_data *ld) { /* init the capture filter */ -static int capture_loop_init_filter(loop_data *ld, char *errmsg, int errmsg_len) { +initfilter_status_t capture_loop_init_filter(pcap_t *pcap_h, gboolean from_cap_pipe, const 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 (cfile.cfilter && !ld->from_cap_pipe) { + if (cfilter && !from_cap_pipe) { /* A capture filter was specified; set it up. */ - if (pcap_lookupnet(cfile.iface, &netnum, &netmask, lookup_net_err_str) < 0) { + 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 @@ -726,83 +699,61 @@ static int capture_loop_init_filter(loop_data *ld, char *errmsg, int errmsg_len) * 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(ld->pcap_h, &fcode, cfile.cfilter, 1, netmask) < 0) { - dfilter_t *rfcode = NULL; - /* filter string invalid, did the user tried a display filter? */ - if (dfilter_compile(cfile.cfilter, &rfcode) && rfcode != NULL) { - g_snprintf(errmsg, errmsg_len, - "%sInvalid capture filter: \"%s\"!%s\n" - "\n" - "That string looks like a valid display filter; however, it is not a valid\n" - "capture filter (%s).\n" - "\n" - "Note that display filters and capture filters don't have the same syntax,\n" - "so you can't use most display filter expressions as capture filters.\n" - "\n" - "See the help for a description of the capture filter syntax.", - simple_dialog_primary_start(), cfile.cfilter, simple_dialog_primary_end(), - pcap_geterr(ld->pcap_h)); - dfilter_free(rfcode); - } else { - g_snprintf(errmsg, errmsg_len, - "%sInvalid capture filter: \"%s\"!%s\n" - "\n" - "That string is not a valid capture filter (%s).\n" - "See the help for a description of the capture filter syntax.", - simple_dialog_primary_start(), cfile.cfilter, simple_dialog_primary_end(), - pcap_geterr(ld->pcap_h)); - } - return FALSE; + 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(ld->pcap_h, &fcode) < 0) { - g_snprintf(errmsg, errmsg_len, "Can't install filter (%s).", - pcap_geterr(ld->pcap_h)); - return FALSE; + 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 TRUE; + return INITFILTER_NO_ERROR; } -/* open the capture output file (wiretap) */ -static int capture_loop_open_output(loop_data *ld, char *errmsg, int errmsg_len) { - int pcap_encap; +/* set up to write to the already-opened capture output file/files */ +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; - /* Set up to write to the capture file. */ + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_init_output"); + + /* get snaplen */ #ifndef _WIN32 if (ld->from_cap_pipe) { - pcap_encap = ld->cap_pipe_hdr.network; file_snaplen = ld->cap_pipe_hdr.snaplen; } else #endif { - pcap_encap = get_pcap_linktype(ld->pcap_h, cfile.iface); file_snaplen = pcap_snapshot(ld->pcap_h); } - ld->wtap_linktype = wtap_pcap_encap_to_wtap_encap(pcap_encap); - if (ld->wtap_linktype == WTAP_ENCAP_UNKNOWN) { - g_snprintf(errmsg, errmsg_len, - "The network you're capturing from is of a type" - " that Ethereal doesn't support (data link type %d).", pcap_encap); - return FALSE; - } - if (capture_opts.multi_files_on) { - ld->wtap_pdh = ringbuf_init_wtap_dump_fdopen(WTAP_FILE_PCAP, ld->wtap_linktype, - file_snaplen, &err); + /* 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->wtap_pdh = wtap_dump_fdopen(cfile.save_file_fd, WTAP_FILE_PCAP, - ld->wtap_linktype, file_snaplen, &err); + ld->pdh = libpcap_fdopen(save_file_fd, ld->linktype, file_snaplen, + &ld->bytes_written, &err); } - if (ld->wtap_pdh == NULL) { + if (ld->pdh == NULL) { /* We couldn't set up to write to the capture file. */ + /* XXX - use cf_open_error_message from twireshark instead? */ switch (err) { case WTAP_ERR_CANT_OPEN: @@ -820,12 +771,12 @@ static int capture_loop_open_output(loop_data *ld, char *errmsg, int errmsg_len) g_snprintf(errmsg, errmsg_len, "The file to which the capture would be" " saved (\"%s\") could not be opened: Error %d.", - cfile.save_file, err); + 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.", - cfile.save_file, strerror(err)); + capture_opts->save_file, strerror(err)); } break; } @@ -836,138 +787,296 @@ static int capture_loop_open_output(loop_data *ld, char *errmsg, int errmsg_len) return TRUE; } -static gboolean capture_loop_close_output(loop_data *ld, int *err_close) { - if (capture_opts.multi_files_on) { - return ringbuf_wtap_dump_close(&cfile, err_close); +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 wtap_dump_close(ld->wtap_pdh, err_close); + return libpcap_dump_close(ld->pdh, err_close); } } /* dispatch incoming packets (pcap or capture pipe) */ static int -capture_loop_dispatch(loop_data *ld, char *errmsg, int errmsg_len) { +capture_loop_dispatch(capture_options *capture_opts _U_, loop_data *ld, + char *errmsg, int errmsg_len) { int inpkts; #ifndef _WIN32 fd_set set1; struct timeval timeout; int sel_ret; - struct pcaprec_modified_hdr rechdr; guchar pcap_data[WTAP_MAX_PACKET_SIZE]; #endif - #ifndef _WIN32 + if (ld->from_cap_pipe) { /* dispatch from capture pipe */ - if (ld->from_cap_pipe) { - FD_ZERO(&set1); - FD_SET(ld->cap_pipe_fd, &set1); - timeout.tv_sec = 0; - timeout.tv_usec = CAP_READ_TIMEOUT*1000; - sel_ret = select(ld->cap_pipe_fd+1, &set1, NULL, NULL, &timeout); - if (sel_ret <= 0) { - inpkts = 0; - if (sel_ret < 0 && errno != EINTR) { - g_snprintf(errmsg, errmsg_len, - "Unexpected error from select: %s", strerror(errno)); - capture_loop_popup_errmsg(errmsg); - ld->go = FALSE; - } - } else { - /* - * "select()" says we can read from the pipe without blocking - */ - inpkts = cap_pipe_dispatch(ld->cap_pipe_fd, ld, &ld->cap_pipe_hdr, &rechdr, pcap_data, - errmsg, errmsg_len); - if (inpkts < 0) { - ld->go = FALSE; - } +#ifdef LOG_CAPTURE_VERBOSE + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: from capture pipe"); +#endif + FD_ZERO(&set1); + FD_SET(ld->cap_pipe_fd, &set1); + timeout.tv_sec = 0; + timeout.tv_usec = CAP_READ_TIMEOUT*1000; + sel_ret = select(ld->cap_pipe_fd+1, &set1, NULL, NULL, &timeout); + 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 + } + else #endif /* _WIN32 */ - { - /* dispatch from pcap using select */ + { + /* dispatch from pcap */ #ifdef MUST_DO_SELECT - /* - * Sigh. The semantics of the read timeout argument to - * "pcap_open_live()" aren't particularly well specified by - * the "pcap" man page - at least with the BSD BPF code, the - * intent appears to be, at least in part, a way of cutting - * down the number of reads done on a capture, by blocking - * until the buffer fills or a timer expires - and the Linux - * libpcap doesn't actually support it, so we can't use it - * to break out of the "pcap_dispatch()" every 1/4 of a second - * or so. Linux's libpcap is not the only libpcap that doesn't - * support the read timeout. - * - * Furthermore, at least on Solaris, the bufmod STREAMS module's - * read timeout won't go off if no data has arrived, i.e. it cannot - * be used to guarantee that a read from a DLPI stream will return - * within a specified amount of time regardless of whether any - * data arrives or not. - * - * Thus, on all platforms other than BSD, we do a "select()" on the - * file descriptor for the capture, with a timeout of CAP_READ_TIMEOUT - * milliseconds, or CAP_READ_TIMEOUT*1000 microseconds. - * - * "select()", on BPF devices, doesn't work as you might expect; - * at least on some versions of some flavors of BSD, the timer - * doesn't start until a read is done, so it won't expire if - * only a "select()" or "poll()" is posted. - */ + /* + * 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) { FD_ZERO(&set1); FD_SET(ld->pcap_fd, &set1); - timeout.tv_sec = 0; - timeout.tv_usec = CAP_READ_TIMEOUT*1000; - sel_ret = select(ld->pcap_fd+1, &set1, NULL, NULL, &timeout); + sel_ret = select(ld->pcap_fd+1, &set1, NULL, NULL, NULL); if (sel_ret > 0) { - /* - * "select()" says we can read from it without blocking; go for - * it. - */ - inpkts = pcap_dispatch(ld->pcap_h, 1, capture_loop_packet_cb, (gchar *)ld); - if (inpkts < 0) { - ld->pcap_err = TRUE; - ld->go = FALSE; - } + /* + * "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 { inpkts = 0; if (sel_ret < 0 && errno != EINTR) { g_snprintf(errmsg, errmsg_len, "Unexpected error from select: %s", strerror(errno)); - capture_loop_popup_errmsg(errmsg); + report_capture_error(errmsg, please_report); ld->go = FALSE; } } -#else + } + else +#endif /* MUST_DO_SELECT */ + { /* dispatch from pcap without select */ - inpkts = pcap_dispatch(ld->pcap_h, 1, capture_loop_packet_cb, (gchar *) ld); +#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) { - ld->pcap_err = TRUE; - ld->go = FALSE; + if (inpkts == -1) { + /* Error, rather than pcap_breakloop(). */ + ld->pcap_err = TRUE; + } + ld->go = FALSE; /* error or pcap_breakloop() - stop capturing */ } -#endif /* MUST_DO_SELECT */ +#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; + + inpkts = 0; + 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); + inpkts++; + } + + if(in < 0) { + ld->pcap_err = TRUE; + ld->go = FALSE; + inpkts = in; + } + } +#endif /* pcap_next_ex */ } + } - return inpkts; +#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 inpkts; } -/* - * This needs to be static, so that the SIGUSR1 handler can clear the "go" - * flag. - */ -static loop_data ld; +/* open the output file (temporary/specified name/ringbuffer/named pipe/stdout) */ +/* Returns TRUE if the file opened successfully, FALSE otherwise. */ +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 (strcmp(capfile_name, "-") == 0) { + /* Write to the standard output. */ + if (capture_opts->multi_files_on) { + /* ringbuffer is enabled; that doesn't work with standard output */ + g_snprintf(errmsg, errmsg_len, + "Ring buffer requested, but capture is being written to the standard output."); + g_free(capfile_name); + return FALSE; + } else { + *save_file_fd = 1; + } + } 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 = 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)". */ + + return TRUE; +} + + +#ifndef _WIN32 +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(); +} +#endif + +#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(gboolean *stats_known, struct pcap_stat *stats) +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, inpkts; + 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; @@ -975,141 +1084,177 @@ capture_loop_start(gboolean *stats_known, struct pcap_stat *stats) guint32 autostop_files = 0; gboolean write_ok; gboolean close_ok; - capture_info capture_ui; + gboolean cfilter_error = FALSE; char errmsg[4096+1]; - - gboolean show_info = capture_opts.show_info || !capture_opts.sync_mode; + char secondary_errmsg[4096+1]; + int save_file_fd = -1; /* init the loop data */ ld.go = TRUE; - if (capture_opts.has_autostop_packets) - ld.packets_max = capture_opts.autostop_packets; + ld.packet_count = 0; + if (capture_opts->has_autostop_packets) + ld.packet_max = capture_opts->autostop_packets; else - ld.packets_max = 0; /* no limit */ + 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.packets_sync_pipe = 0; - ld.counts.total = 0; - ld.counts.sctp = 0; - ld.counts.tcp = 0; - ld.counts.udp = 0; - ld.counts.icmp = 0; - ld.counts.ospf = 0; - ld.counts.gre = 0; - ld.counts.ipx = 0; - ld.counts.netbios = 0; - ld.counts.vines = 0; - ld.counts.other = 0; - ld.counts.arp = 0; - ld.wtap_pdh = NULL; + ld.pdh = NULL; #ifndef _WIN32 ld.cap_pipe_fd = -1; #endif #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(&ld, errmsg, sizeof(errmsg))) { + 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) */ - if (!capture_loop_init_filter(&ld, errmsg, sizeof(errmsg))) { + 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)); + *secondary_errmsg = '\0'; goto error; - } - /* open the (wiretap) output file */ - if (!capture_loop_open_output(&ld, errmsg, sizeof(errmsg))) { + 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))) { + *secondary_errmsg = '\0'; + 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))) { + *secondary_errmsg = '\0'; + 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. */ - if (capture_child) { /* Well, we should be able to start capturing. - This is the child process for a sync mode capture, so sync out - the capture file, so the header makes it to the file system, + 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. */ - fflush(wtap_dump_file(ld.wtap_pdh)); - sync_pipe_capstart_to_parent(); + 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) + if (capture_opts->has_autostop_filesize) cnd_autostop_size = - cnd_new(CND_CLASS_CAPTURESIZE,(long)capture_opts.autostop_filesize); - if (capture_opts.has_autostop_duration) + 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); + cnd_new(CND_CLASS_TIMEOUT,(gint32)capture_opts->autostop_duration); - if (capture_opts.multi_files_on) { - if (capture_opts.has_file_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); + cnd_new(CND_CLASS_TIMEOUT, capture_opts->file_duration); - if (capture_opts.has_autostop_files) + if (capture_opts->has_autostop_files) cnd_autostop_files = - cnd_new(CND_CLASS_CAPTURESIZE, capture_opts.autostop_files); - } - - /* start capture info dialog */ - if(show_info) { - capture_ui.callback_data = &ld; - capture_ui.counts = &ld.counts; - capture_info_create(&capture_ui, cfile.iface); + cnd_new(CND_CLASS_CAPTURESIZE, capture_opts->autostop_files); } /* init the time values */ - start_time = time(NULL); - upd_time = time(NULL); + 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) { - main_window_update(); - /* dispatch incoming packets */ - inpkts = capture_loop_dispatch(&ld, errmsg, sizeof(errmsg)); + 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) { - ld.packets_sync_pipe += inpkts; + inpkts_to_sync_pipe += inpkts; /* check capture size condition */ - if (cnd_autostop_size != NULL && cnd_eval(cnd_autostop_size, - (guint32)wtap_get_bytes_dumped(ld.wtap_pdh))){ + 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 */ + 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(&cfile, &ld.wtap_pdh, &ld.err)) { + 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; @@ -1121,38 +1266,34 @@ capture_loop_start(gboolean *stats_known, struct pcap_stat *stats) continue; } } /* cnd_autostop_size */ + if (capture_opts->output_to_pipe) { + libpcap_dump_flush(ld.pdh, NULL); + } } /* inpkts */ - /* Only update once a second so as not to overload slow displays */ - cur_time = time(NULL); - if (cur_time > upd_time) { - upd_time = cur_time; + /* Only update once a second (Win32: 500ms) so as not to overload slow displays */ + 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; }*/ - /* calculate and display running time */ - if(show_info) { - cur_time -= start_time; - capture_ui.running_time = cur_time; - capture_ui.new_packets = ld.packets_sync_pipe; - capture_info_update(&capture_ui); - } - /* Let the parent process know. */ - if (ld.packets_sync_pipe) { + if (inpkts_to_sync_pipe) { /* do sync here */ - fflush(wtap_dump_file(ld.wtap_pdh)); + libpcap_dump_flush(ld.pdh, NULL); - if (capture_child) { - /* This is the child process for a sync mode capture, so send - our parent a message saying we've written out "ld.sync_packets" - packets to the capture file. */ - sync_pipe_packet_count_to_parent(ld.packets_sync_pipe); - } + /* 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); - ld.packets_sync_pipe = 0; + inpkts_to_sync_pipe = 0; } /* check capture duration condition */ @@ -1165,22 +1306,28 @@ capture_loop_start(gboolean *stats_known, struct pcap_stat *stats) /* 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)) { + 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(&cfile, &ld.wtap_pdh, &ld.err)) { + 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; + ld.go = FALSE; continue; } } else { @@ -1193,10 +1340,7 @@ capture_loop_start(gboolean *stats_known, struct pcap_stat *stats) } /* while (ld.go) */ - /* close capture info dialog */ - if(show_info) { - capture_info_destroy(&capture_ui); - } + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Capture loop stopping ..."); /* delete stop conditions */ if (cnd_file_duration != NULL) @@ -1212,32 +1356,42 @@ capture_loop_start(gboolean *stats_known, struct pcap_stat *stats) if (ld.pcap_err) { g_snprintf(errmsg, sizeof(errmsg), "Error while capturing packets: %s", pcap_geterr(ld.pcap_h)); - capture_loop_popup_errmsg(errmsg); + report_capture_error(errmsg, please_report); } #ifndef _WIN32 else if (ld.from_cap_pipe && ld.cap_pipe_err == PIPERR) - capture_loop_popup_errmsg(errmsg); + report_capture_error(errmsg, ""); #endif /* did we had an error while capturing? */ if (ld.err == 0) { write_ok = TRUE; } else { - capture_loop_get_errmsg(errmsg, sizeof(errmsg), cfile.save_file, ld.err, + capture_loop_get_errmsg(errmsg, sizeof(errmsg), capture_opts->save_file, ld.err, FALSE); - capture_loop_popup_errmsg(errmsg); + report_capture_error(errmsg, please_report); write_ok = FALSE; } - /* close the wiretap (output) file */ - close_ok = capture_loop_close_output(&ld, &err_close); + 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), cfile.save_file, err_close, + capture_loop_get_errmsg(errmsg, sizeof(errmsg), capture_opts->save_file, err_close, TRUE); - capture_loop_popup_errmsg(errmsg); + report_capture_error(errmsg, ""); } /* @@ -1256,51 +1410,64 @@ capture_loop_start(gboolean *stats_known, struct pcap_stat *stats) dropped. */ if (pcap_stats(ld.pcap_h, stats) >= 0) { *stats_known = TRUE; - if (capture_child) { - /* Let the parent process know. */ - sync_pipe_drops_to_parent(stats->ps_drop); - } + /* 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)); - capture_loop_popup_errmsg(errmsg); + 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) { + if (capture_opts->multi_files_on) { /* cleanup ringbuffer */ ringbuf_error_cleanup(); } else { - /* We can't use the save file, and we have no wtap_dump stream + /* 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. */ - close(cfile.save_file_fd); + if(save_file_fd != -1) { + eth_close(save_file_fd); + } /* We couldn't even start the capture, so get rid of the capture file. */ - unlink(cfile.save_file); /* silently ignore error */ - g_free(cfile.save_file); + if(capture_opts->save_file != NULL) { + eth_unlink(capture_opts->save_file); + g_free(capture_opts->save_file); + } } - cfile.save_file = NULL; - capture_loop_popup_errmsg(errmsg); + 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) { - ld.go = FALSE; +#ifdef HAVE_PCAP_BREAKLOOP + pcap_breakloop(ld.pcap_h); +#else + ld.go = FALSE; +#endif } @@ -1362,117 +1529,31 @@ capture_loop_get_errmsg(char *errmsg, int errmsglen, const char *fname, } } -static void -capture_loop_popup_errmsg(const char *errmsg) -{ - if (capture_child) { - /* This is the child process for a sync mode capture. - Send the error message to our parent, so they can display a - dialog box containing it. */ - sync_pipe_errmsg_to_parent(errmsg); - } else { - /* Display the dialog box ourselves; there's no parent. */ - simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", errmsg); - } -} - /* one packet was captured, process it */ static void -capture_loop_packet_cb(guchar *user, const struct pcap_pkthdr *phdr, - const guchar *pd) +capture_loop_packet_cb(u_char *user, const struct pcap_pkthdr *phdr, + const u_char *pd) { - struct wtap_pkthdr whdr; - union wtap_pseudo_header pseudo_header; loop_data *ld = (loop_data *) user; int err; /* if the user told us to stop after x packets, do we have enough? */ - if ((ld->packets_max > 0) && (++ld->counts.total >= ld->packets_max)) + ld->packet_count++; + if ((ld->packet_max > 0) && (ld->packet_count >= ld->packet_max)) { ld->go = FALSE; } - /* Convert from libpcap to Wiretap format. - If that fails, set "ld->go" to FALSE, to stop the capture, and set - "ld->err" to the error. */ - pd = wtap_process_pcap_packet(ld->wtap_linktype, phdr, pd, &pseudo_header, - &whdr, &err); - if (pd == NULL) { - ld->go = FALSE; - ld->err = err; - return; - } - - if (ld->wtap_pdh) { + 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 (!wtap_dump(ld->wtap_pdh, &whdr, &pseudo_header, pd, &err)) { + if (!libpcap_write_packet(ld->pdh, phdr, pd, &ld->bytes_written, &err)) { ld->go = FALSE; ld->err = err; } } - - switch (ld->wtap_linktype) { - case WTAP_ENCAP_ETHERNET: - capture_eth(pd, 0, whdr.caplen, &ld->counts); - break; - case WTAP_ENCAP_FDDI: - case WTAP_ENCAP_FDDI_BITSWAPPED: - capture_fddi(pd, whdr.caplen, &ld->counts); - break; - case WTAP_ENCAP_PRISM_HEADER: - capture_prism(pd, 0, whdr.caplen, &ld->counts); - break; - case WTAP_ENCAP_TOKEN_RING: - capture_tr(pd, 0, whdr.caplen, &ld->counts); - break; - case WTAP_ENCAP_NULL: - capture_null(pd, whdr.caplen, &ld->counts); - break; - case WTAP_ENCAP_PPP: - capture_ppp_hdlc(pd, 0, whdr.caplen, &ld->counts); - break; - case WTAP_ENCAP_RAW_IP: - capture_raw(pd, whdr.caplen, &ld->counts); - break; - case WTAP_ENCAP_SLL: - capture_sll(pd, whdr.caplen, &ld->counts); - break; - case WTAP_ENCAP_LINUX_ATM_CLIP: - capture_clip(pd, whdr.caplen, &ld->counts); - break; - case WTAP_ENCAP_IEEE_802_11: - case WTAP_ENCAP_IEEE_802_11_WITH_RADIO: - capture_ieee80211(pd, 0, whdr.caplen, &ld->counts); - break; - case WTAP_ENCAP_CHDLC: - capture_chdlc(pd, 0, whdr.caplen, &ld->counts); - break; - case WTAP_ENCAP_LOCALTALK: - capture_llap(&ld->counts); - break; - case WTAP_ENCAP_ATM_PDUS: - capture_atm(&pseudo_header, pd, whdr.caplen, &ld->counts); - break; - case WTAP_ENCAP_IP_OVER_FC: - capture_ipfc(pd, whdr.caplen, &ld->counts); - break; - case WTAP_ENCAP_ARCNET: - capture_arcnet(pd, whdr.caplen, &ld->counts, FALSE, TRUE); - break; - case WTAP_ENCAP_ARCNET_LINUX: - capture_arcnet(pd, whdr.caplen, &ld->counts, TRUE, FALSE); - break; - case WTAP_ENCAP_APPLE_IP_OVER_IEEE1394: - capture_ap1394(pd, 0, whdr.caplen, &ld->counts); - break; - /* XXX - some ATM drivers on FreeBSD might prepend a 4-byte ATM - pseudo-header to DLT_ATM_RFC1483, with LLC header following; - we might have to implement that at some point. */ - } } #endif /* HAVE_LIBPCAP */ -