X-Git-Url: http://git.samba.org/samba.git/?p=obnox%2Fwireshark%2Fwip.git;a=blobdiff_plain;f=dumpcap.c;h=4e2f1325e4320e519cf07a3fa8ff66e7d5d95f08;hp=43a74ac7e1a3bd9ee2a6a8d43d9e11e02d86fc5c;hb=0da36541d458c445f51633972348fef48e946616;hpb=bec4865b17e7e8ff7b2de85e09d06a062dbcdc02 diff --git a/dumpcap.c b/dumpcap.c index 43a74ac7e1..4e2f1325e4 100644 --- a/dumpcap.c +++ b/dumpcap.c @@ -25,6 +25,7 @@ # include "config.h" #endif +#include #include /* for exit() */ #include @@ -47,11 +48,21 @@ #include #endif +#ifdef HAVE_ARPA_INET_H +#include +#endif + +#if defined(__APPLE__) && defined(__LP64__) +#include +#endif + #include #include -#ifdef NEED_GETOPT_H -#include "getopt.h" +#ifdef HAVE_GETOPT_H +#include +#else +#include "wsutil/wsgetopt.h" #endif #ifdef HAVE_NETDB_H @@ -61,32 +72,38 @@ #ifdef HAVE_LIBCAP # include # include -# include #endif #include "ringbuffer.h" #include "clopts_common.h" +#include "console_io.h" #include "cmdarg_err.h" #include "version_info.h" -#include -#include "pcapio.h" - #include "capture-pcap-util.h" +#include "pcapio.h" + #ifdef _WIN32 #include "capture-wpcap.h" +#include #endif -#ifdef _WIN32 -#include "epan/unicode-utils.h" +#ifndef _WIN32 +#include +#include #endif -#include "epan/privileges.h" +#ifdef NEED_INET_V6DEFS_H +# include "wsutil/inet_v6defs.h" +#endif + +#include #include "sync_pipe.h" #include "capture_opts.h" +#include "capture_ifinfo.h" #include "capture_sync.h" #include "conditions.h" @@ -111,12 +128,27 @@ FILE *debug_log; /* for logging debug messages to */ /* is defined */ #endif +#ifdef _WIN32 +#define USE_THREADS +#endif + static gboolean capture_child = FALSE; /* FALSE: standalone call, TRUE: this is an Wireshark capture child */ #ifdef _WIN32 static gchar *sig_pipe_name = NULL; static HANDLE sig_pipe_handle = NULL; +static gboolean signal_pipe_check_running(void); #endif +#ifdef USE_THREADS +static GAsyncQueue *cap_pipe_pending_q, *cap_pipe_done_q; +static GMutex *cap_pipe_read_mtx; +#endif + +#ifdef SIGINFO +static gboolean infodelay; /* if TRUE, don't print capture info in SIGINFO handler */ +static gboolean infoprint; /* if TRUE, print capture info after clearing infodelay */ +#endif /* SIGINFO */ + /** Stop a low-level capture (stops the capture child). */ static void capture_loop_stop(void); @@ -129,7 +161,7 @@ static void capture_loop_stop(void); * is interrupted by a signal on UN*X, just go back and try again to * read again. * - * On UN*X, we catch SIGUSR1 as a "stop capturing" signal, and, in + * On UN*X, we catch SIGINT as a "stop capturing" signal, and, in * the signal handler, set a flag to stop capturing; however, without * a guarantee of that sort, we can't guarantee that we'll stop capturing * if the read will be retried and won't time out if no packets arrive. @@ -152,7 +184,7 @@ static void capture_loop_stop(void); * exit pcap_dispatch() with an indication that no packets have arrived, * and will break out of the capture loop at that point. * - * On Windows, we can't send a SIGUSR1 to stop capturing, so none of this + * On Windows, we can't send a SIGINT to stop capturing, so none of this * applies in any case. * * XXX - the various BSDs appear to define BSD in ; we don't @@ -185,6 +217,10 @@ typedef struct _loop_data { int err; /* if non-zero, error seen while capturing */ gint packet_count; /* Number of packets we have already captured */ gint packet_max; /* Number of packets we're supposed to capture - 0 means infinite */ + gint inpkts_to_sync_pipe; /* Packets not already send out to the sync_pipe */ +#ifdef SIGINFO + gboolean report_packet_count; /* Set by SIGINFO handler; print packet count */ +#endif /* pcap "input file" */ pcap_t *pcap_h; /* pcap handle */ @@ -197,11 +233,18 @@ typedef struct _loop_data { gboolean from_cap_pipe; /* TRUE if we are capturing data from a capture pipe */ struct pcap_hdr cap_pipe_hdr; /* Pcap header when capturing from a pipe */ struct pcaprec_modified_hdr cap_pipe_rechdr; /* Pcap record header when capturing from a pipe */ +#ifdef _WIN32 + HANDLE cap_pipe_h; /* The handle of the capture pipe */ +#else int cap_pipe_fd; /* the file descriptor of the capture pipe */ +#endif gboolean cap_pipe_modified; /* TRUE if data in the pipe uses modified pcap headers */ gboolean cap_pipe_byte_swapped; /* TRUE if data in the pipe is byte swapped */ - unsigned int cap_pipe_bytes_to_read;/* Used by cap_pipe_dispatch */ - unsigned int cap_pipe_bytes_read; /* Used by cap_pipe_dispatch */ +#ifdef USE_THREADS + char * cap_pipe_buf; /* Pointer to the data buffer we read into */ +#endif /* USE_THREADS */ + int cap_pipe_bytes_to_read;/* Used by cap_pipe_dispatch */ + int cap_pipe_bytes_read; /* Used by cap_pipe_dispatch */ enum { STATE_EXPECT_REC_HDR, STATE_READ_REC_HDR, @@ -210,12 +253,14 @@ typedef struct _loop_data { } cap_pipe_state; enum { PIPOK, PIPEOF, PIPERR, PIPNEXIST } cap_pipe_err; - /* output file */ + /* output file(s) */ FILE *pdh; + int save_file_fd; int linktype; + int file_snaplen; gint wtap_linktype; long bytes_written; - + guint32 autostop_files; } loop_data; /* @@ -226,17 +271,45 @@ static const char please_report[] = "(This is not a crash; please do not report it as such.)"; /* - * This needs to be static, so that the SIGUSR1 handler can clear the "go" + * This needs to be static, so that the SIGINT handler can clear the "go" * flag. */ -static loop_data ld; +static loop_data global_ld; /* - * Timeout, in milliseconds, for reads from the stream of captured packets. + * Timeout, in milliseconds, for reads from the stream of captured packets + * from a capture device. + * + * A bug in Mac OS X 10.6 and 10.6.1 causes calls to pcap_open_live(), in + * 64-bit applications, with sub-second timeouts not to work. The bug is + * fixed in 10.6.2, re-broken in 10.6.3, and again fixed in 10.6.5. */ +#if defined(__APPLE__) && defined(__LP64__) +static gboolean need_timeout_workaround; + +#define CAP_READ_TIMEOUT (need_timeout_workaround ? 1000 : 250) +#else #define CAP_READ_TIMEOUT 250 -static char *cap_pipe_err_str; +#endif + +/* + * Timeout, in microseconds, for reads from the stream of captured packets + * from a pipe. Pipes don't have the same problem that BPF devices do + * in OS X 10.6, 10.6.1, 10.6.3, and 10.6.4, so we always use a timeout + * of 250ms, i.e. the same value as CAP_READ_TIMEOUT when not on one + * of the offending versions of Snow Leopard. + * + * On Windows this value is converted to milliseconds and passed to + * WaitForSingleObject. If it's less than 1000 WaitForSingleObject + * will return immediately. + */ +#ifndef USE_THREADS +#define PIPE_READ_TIMEOUT 250000 +#else +#define PIPE_READ_TIMEOUT 100000 +#endif +static const char *cap_pipe_err_str; static void console_log_handler(const char *log_domain, GLogLevelFlags log_level, @@ -244,29 +317,22 @@ console_log_handler(const char *log_domain, GLogLevelFlags log_level, /* capture related options */ static capture_options global_capture_opts; -static capture_options *capture_opts = &global_capture_opts; +static gboolean quiet; 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 -static void exit_main(int err) __attribute__ ((noreturn)); -#else -static void exit_main(int err); -#endif +static void exit_main(int err) G_GNUC_NORETURN; static void report_new_capture_file(const char *filename); static void report_packet_count(int packet_count); -static void report_packet_drops(int drops); +static void report_packet_drops(guint32 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 +#define MSG_MAX_LENGTH 4096 static void print_usage(gboolean print_ver) { @@ -291,14 +357,20 @@ print_usage(gboolean print_ver) { fprintf(output, " -f packet filter in libpcap filter syntax\n"); fprintf(output, " -s packet snapshot length (def: 65535)\n"); fprintf(output, " -p don't capture in promiscuous mode\n"); -#ifdef _WIN32 +#ifdef HAVE_PCAP_CREATE + fprintf(output, " -I capture in monitor mode, if available\n"); +#endif +#if defined(_WIN32) || defined(HAVE_PCAP_CREATE) fprintf(output, " -B size of kernel buffer (def: 1MB)\n"); #endif fprintf(output, " -y link layer type (def: first appropriate)\n"); fprintf(output, " -D print list of interfaces and exit\n"); fprintf(output, " -L print list of link-layer types of iface and exit\n"); +#ifdef HAVE_BPF_IMAGE + fprintf(output, " -d print generated BPF code for capture filter\n"); +#endif fprintf(output, " -S print statistics for each interface once every second\n"); - fprintf(output, " -M for -D, -L, and -S produce machine-readable output\n"); + fprintf(output, " -M for -D, -L, and -S, produce machine-readable output\n"); fprintf(output, "\n"); #ifdef HAVE_PCAP_REMOTE fprintf(output, "\nRPCAP options:\n"); @@ -319,11 +391,14 @@ print_usage(gboolean print_ver) { /*fprintf(output, "\n");*/ fprintf(output, "Output (files):\n"); fprintf(output, " -w name of file to save (def: tempfile)\n"); + fprintf(output, " -g enable group read access on the output file(s)\n"); fprintf(output, " -b ... duration:NUM - switch to next file after NUM secs\n"); fprintf(output, " filesize:NUM - switch to next file after NUM KB\n"); fprintf(output, " files:NUM - ringbuffer: replace after NUM files\n"); + fprintf(output, " -n use pcapng format instead of pcap\n"); /*fprintf(output, "\n");*/ fprintf(output, "Miscellaneous:\n"); + fprintf(output, " -q don't report packet capture counts\n"); fprintf(output, " -v print version information and exit\n"); fprintf(output, " -h display this help and exit\n"); fprintf(output, "\n"); @@ -337,68 +412,901 @@ static void show_version(GString *comp_info_str, GString *runtime_info_str) { - printf( - "Dumpcap " VERSION "%s\n" - "\n" - "%s\n" - "%s\n" - "%s\n" - "See http://www.wireshark.org for more information.\n", - wireshark_svnversion, get_copyright_info() ,comp_info_str->str, runtime_info_str->str); + printf( + "Dumpcap " VERSION "%s\n" + "\n" + "%s\n" + "%s\n" + "%s\n" + "See http://www.wireshark.org for more information.\n", + wireshark_svnversion, get_copyright_info() ,comp_info_str->str, runtime_info_str->str); +} + +/* + * Print to the standard error. This is a command-line tool, so there's + * no need to pop up a console. + */ +void +vfprintf_stderr(const char *fmt, va_list ap) +{ + vfprintf(stderr, fmt, ap); +} + +void +fprintf_stderr(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf_stderr(fmt, ap); + va_end(ap); +} + +/* + * Report an error in command-line arguments. + */ +void +cmdarg_err(const char *fmt, ...) +{ + va_list ap; + + if(capture_child) { + gchar *msg; + /* Generate a 'special format' message back to parent */ + va_start(ap, fmt); + msg = g_strdup_vprintf(fmt, ap); + sync_pipe_errmsg_to_parent(2, msg, ""); + g_free(msg); + va_end(ap); + } else { + va_start(ap, fmt); + fprintf(stderr, "dumpcap: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + } +} + +/* + * Report additional information for an error in command-line arguments. + */ +void +cmdarg_err_cont(const char *fmt, ...) +{ + va_list ap; + + if(capture_child) { + gchar *msg; + va_start(ap, fmt); + msg = g_strdup_vprintf(fmt, ap); + sync_pipe_errmsg_to_parent(2, msg, ""); + g_free(msg); + va_end(ap); + } else { + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + } +} + +#ifdef HAVE_LIBCAP +static void +#if 0 /* Set to enable capability debugging */ +/* see 'man cap_to_text()' for explanation of output */ +/* '=' means 'all= ' ie: no capabilities */ +/* '=ip' means 'all=ip' ie: all capabilities are permissible and inheritable */ +/* .... */ +print_caps(const char *pfx) { + cap_t caps = cap_get_proc(); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, + "%s: EUID: %d Capabilities: %s", pfx, + geteuid(), cap_to_text(caps, NULL)); + cap_free(caps); +#else +print_caps(const char *pfx _U_) { +#endif +} + +static void +relinquish_all_capabilities(void) +{ + /* Drop any and all capabilities this process may have. */ + /* Allowed whether or not process has any privileges. */ + cap_t caps = cap_init(); /* all capabilities initialized to off */ + print_caps("Pre-clear"); + if (cap_set_proc(caps)) { + cmdarg_err("cap_set_proc() fail return: %s", strerror(errno)); + } + print_caps("Post-clear"); + cap_free(caps); +} +#endif + +static pcap_t * +open_capture_device(capture_options *capture_opts, + char (*open_err_str)[PCAP_ERRBUF_SIZE]) +{ + pcap_t *pcap_h; +#ifdef HAVE_PCAP_CREATE + int err; +#endif +#ifdef HAVE_PCAP_REMOTE + struct pcap_rmtauth auth; +#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 + /* + * If we're opening a remote device, use pcap_open(); that's currently + * the only open routine that supports remote devices. + */ + if (strncmp (capture_opts->iface, "rpcap://", 8) == 0) { + 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; + + 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 +#endif /* HAVE_PCAP_OPEN */ + { + /* + * If we're not opening a remote device, use pcap_create() and + * pcap_activate() if we have them, so that we can set the buffer + * size, otherwise use pcap_open_live(). + */ +#ifdef HAVE_PCAP_CREATE + pcap_h = pcap_create(capture_opts->iface, *open_err_str); + if (pcap_h != NULL) { + pcap_set_snaplen(pcap_h, capture_opts->has_snaplen ? capture_opts->snaplen : WTAP_MAX_PACKET_SIZE); + pcap_set_promisc(pcap_h, capture_opts->promisc_mode); + pcap_set_timeout(pcap_h, CAP_READ_TIMEOUT); + + if (capture_opts->buffer_size > 1) { + pcap_set_buffer_size(pcap_h, capture_opts->buffer_size * 1024 * 1024); + } + if (capture_opts->monitor_mode) + pcap_set_rfmon(pcap_h, 1); + err = pcap_activate(pcap_h); + if (err < 0) { + /* Failed to activate, set to NULL */ + if (err == PCAP_ERROR) + g_strlcpy(*open_err_str, pcap_geterr(pcap_h), sizeof *open_err_str); + else + g_strlcpy(*open_err_str, pcap_statustostr(err), sizeof *open_err_str); + pcap_close(pcap_h); + pcap_h = NULL; + } + } +#else + 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 not using libcap: we now can now set euid/egid to ruid/rgid */ + /* to remove any suid privileges. */ + /* If using libcap: we can now remove NET_RAW and NET_ADMIN capabilities */ + /* (euid/egid have already previously been set to ruid/rgid. */ + /* (See comment in main() for details) */ +#ifndef HAVE_LIBCAP + relinquish_special_privs_perm(); +#else + relinquish_all_capabilities(); +#endif + + return pcap_h; +} + +static void +get_capture_device_open_failure_messages(const char *open_err_str, + const char *iface +#ifndef _WIN32 + _U_ +#endif + , + char *errmsg, size_t errmsg_len, + char *secondary_errmsg, + size_t secondary_errmsg_len) +{ + const char *libpcap_warn; + static const char ppamsg[] = "can't find PPA for "; + + /* If we got a "can't find PPA for X" message, warn the user (who + is running dumcap on HP-UX) that they don't have a version of + libpcap that properly handles HP-UX (libpcap 0.6.x and later + versions, which properly handle HP-UX, say "can't find /dev/dlpi + PPA for X" rather than "can't find PPA for X"). */ + if (strncmp(open_err_str, ppamsg, sizeof ppamsg - 1) == 0) + libpcap_warn = + "\n\n" + "You are running (T)Wireshark with a version of the libpcap library\n" + "that doesn't handle HP-UX network devices well; this means that\n" + "(T)Wireshark may not be able to capture packets.\n" + "\n" + "To fix this, you should install libpcap 0.6.2, or a later version\n" + "of libpcap, rather than libpcap 0.4 or 0.5.x. It is available in\n" + "packaged binary form from the Software Porting And Archive Centre\n" + "for HP-UX; the Centre is at http://hpux.connect.org.uk/ - the page\n" + "at the URL lists a number of mirror sites."; + else + libpcap_warn = ""; + g_snprintf(errmsg, (gulong) errmsg_len, + "The capture session could not be initiated (%s).", open_err_str); +#ifndef _WIN32 + g_snprintf(secondary_errmsg, (gulong) secondary_errmsg_len, +"Please check to make sure you have sufficient permissions, and that you have " +"the proper interface or pipe specified.%s", libpcap_warn); +#else + g_snprintf(secondary_errmsg, (gulong) secondary_errmsg_len, +"\n" +"Please check that \"%s\" is the proper interface.\n" +"\n" +"\n" +"Help can be found at:\n" +"\n" +" http://wiki.wireshark.org/WinPcap\n" +" http://wiki.wireshark.org/CaptureSetup\n", + iface); +#endif /* _WIN32 */ +} + +/* Set the data link type on a pcap. */ +static gboolean +set_pcap_linktype(pcap_t *pcap_h, capture_options *capture_opts, + char *errmsg, size_t errmsg_len, + char *secondary_errmsg, size_t secondary_errmsg_len) +{ + char *set_linktype_err_str; + + if (capture_opts->linktype == -1) + return TRUE; /* just use the default */ +#ifdef HAVE_PCAP_SET_DATALINK + if (pcap_set_datalink(pcap_h, capture_opts->linktype) == 0) + return TRUE; /* no error */ + set_linktype_err_str = pcap_geterr(pcap_h); +#else + /* Let them set it to the type it is; reject any other request. */ + if (get_pcap_linktype(pcap_h, capture_opts->iface) == capture_opts->linktype) + return TRUE; /* no error */ + set_linktype_err_str = + "That DLT isn't one of the DLTs supported by this device"; +#endif + g_snprintf(errmsg, (gulong) errmsg_len, "Unable to set data link type (%s).", + set_linktype_err_str); + /* + * If the error isn't "XXX is not one of the DLTs supported by this device", + * tell the user to tell the Wireshark developers about it. + */ + if (strstr(set_linktype_err_str, "is not one of the DLTs supported by this device") == NULL) + g_snprintf(secondary_errmsg, (gulong) secondary_errmsg_len, please_report); + else + secondary_errmsg[0] = '\0'; + return FALSE; +} + +static gboolean +compile_capture_filter(const char *iface, pcap_t *pcap_h, + struct bpf_program *fcode, char *cfilter) +{ + bpf_u_int32 netnum, netmask; + gchar lookup_net_err_str[PCAP_ERRBUF_SIZE]; + + 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) + return FALSE; + return TRUE; +} + +#ifdef HAVE_BPF_IMAGE +static gboolean +show_filter_code(capture_options *capture_opts) +{ + pcap_t *pcap_h; + gchar open_err_str[PCAP_ERRBUF_SIZE]; + char errmsg[MSG_MAX_LENGTH+1]; + char secondary_errmsg[MSG_MAX_LENGTH+1]; + struct bpf_program fcode; + struct bpf_insn *insn; + u_int i; + + pcap_h = open_capture_device(capture_opts, &open_err_str); + if (pcap_h == NULL) { + /* Open failed; get messages */ + get_capture_device_open_failure_messages(open_err_str, + capture_opts->iface, + errmsg, sizeof errmsg, + secondary_errmsg, + sizeof secondary_errmsg); + /* And report them */ + report_capture_error(errmsg, secondary_errmsg); + return FALSE; + } + + /* Set the link-layer type. */ + if (!set_pcap_linktype(pcap_h, capture_opts, errmsg, sizeof errmsg, + secondary_errmsg, sizeof secondary_errmsg)) { + pcap_close(pcap_h); + report_capture_error(errmsg, secondary_errmsg); + return FALSE; + } + + /* OK, try to compile the capture filter. */ + if (!compile_capture_filter(capture_opts->iface, pcap_h, &fcode, + capture_opts->cfilter)) { + pcap_close(pcap_h); + report_cfilter_error(capture_opts->cfilter, errmsg); + return FALSE; + } + pcap_close(pcap_h); + + if (capture_child) { + /* Let our parent know we succeeded. */ + pipe_write_block(2, SP_SUCCESS, NULL); + } + + /* Now print the filter code. */ + insn = fcode.bf_insns; + + for (i = 0; i < fcode.bf_len; insn++, i++) + printf("%s\n", bpf_image(insn, i)); + return TRUE; +} +#endif + +/* + * capture_interface_list() is expected to do the right thing to get + * a list of interfaces. + * + * In most of the programs in the Wireshark suite, "the right thing" + * is to run dumpcap and ask it for the list, because dumpcap may + * be the only program in the suite with enough privileges to get + * the list. + * + * In dumpcap itself, however, we obviously can't run dumpcap to + * ask for the list. Therefore, our capture_interface_list() should + * just call get_interface_list(). + */ +GList * +capture_interface_list(int *err, char **err_str) +{ + return get_interface_list(err, err_str); +} + +/* + * Get the data-link type for a libpcap device. + * This works around AIX 5.x's non-standard and incompatible-with-the- + * rest-of-the-universe libpcap. + */ +static int +get_pcap_linktype(pcap_t *pch, const char *devname +#ifndef _AIX + _U_ +#endif +) +{ + int linktype; +#ifdef _AIX + const char *ifacename; +#endif + + linktype = pcap_datalink(pch); +#ifdef _AIX + + /* + * The libpcap that comes with AIX 5.x uses RFC 1573 ifType values + * rather than DLT_ values for link-layer types; the ifType values + * for LAN devices are: + * + * Ethernet 6 + * 802.3 7 + * Token Ring 9 + * FDDI 15 + * + * and the ifType value for a loopback device is 24. + * + * The AIX names for LAN devices begin with: + * + * Ethernet en + * 802.3 et + * Token Ring tr + * FDDI fi + * + * and the AIX names for loopback devices begin with "lo". + * + * (The difference between "Ethernet" and "802.3" is presumably + * whether packets have an Ethernet header, with a packet type, + * or an 802.3 header, with a packet length, followed by an 802.2 + * header and possibly a SNAP header.) + * + * If the device name matches "linktype" interpreted as an ifType + * value, rather than as a DLT_ value, we will assume this is AIX's + * non-standard, incompatible libpcap, rather than a standard libpcap, + * and will map the link-layer type to the standard DLT_ value for + * that link-layer type, as that's what the rest of Wireshark expects. + * + * (This means the capture files won't be readable by a tcpdump + * linked with AIX's non-standard libpcap, but so it goes. They + * *will* be readable by standard versions of tcpdump, Wireshark, + * and so on.) + * + * XXX - if we conclude we're using AIX libpcap, should we also + * set a flag to cause us to assume the time stamps are in + * seconds-and-nanoseconds form, and to convert them to + * seconds-and-microseconds form before processing them and + * writing them out? + */ + + /* + * Find the last component of the device name, which is the + * interface name. + */ + ifacename = strchr(devname, '/'); + if (ifacename == NULL) + ifacename = devname; + + /* See if it matches any of the LAN device names. */ + if (strncmp(ifacename, "en", 2) == 0) { + if (linktype == 6) { + /* + * That's the RFC 1573 value for Ethernet; map it to DLT_EN10MB. + */ + linktype = 1; + } + } else if (strncmp(ifacename, "et", 2) == 0) { + if (linktype == 7) { + /* + * That's the RFC 1573 value for 802.3; map it to DLT_EN10MB. + * (libpcap, tcpdump, Wireshark, etc. don't care if it's Ethernet + * or 802.3.) + */ + linktype = 1; + } + } else if (strncmp(ifacename, "tr", 2) == 0) { + if (linktype == 9) { + /* + * That's the RFC 1573 value for 802.5 (Token Ring); map it to + * DLT_IEEE802, which is what's used for Token Ring. + */ + linktype = 6; + } + } else if (strncmp(ifacename, "fi", 2) == 0) { + if (linktype == 15) { + /* + * That's the RFC 1573 value for FDDI; map it to DLT_FDDI. + */ + linktype = 10; + } + } else if (strncmp(ifacename, "lo", 2) == 0) { + if (linktype == 24) { + /* + * That's the RFC 1573 value for "software loopback" devices; map it + * to DLT_NULL, which is what's used for loopback devices on BSD. + */ + linktype = 0; + } + } +#endif + + return linktype; +} + +static data_link_info_t * +create_data_link_info(int dlt) +{ + data_link_info_t *data_link_info; + const char *text; + + data_link_info = (data_link_info_t *)g_malloc(sizeof (data_link_info_t)); + data_link_info->dlt = dlt; + text = pcap_datalink_val_to_name(dlt); + if (text != NULL) + data_link_info->name = g_strdup(text); + else + data_link_info->name = g_strdup_printf("DLT %d", dlt); + text = pcap_datalink_val_to_description(dlt); + if (text != NULL) + data_link_info->description = g_strdup(text); + else + data_link_info->description = NULL; + return data_link_info; +} + +/* + * Get the capabilities of a network device. + */ +static if_capabilities_t * +get_if_capabilities(const char *devname, gboolean monitor_mode +#ifndef HAVE_PCAP_CREATE + _U_ +#endif +, char **err_str) +{ + if_capabilities_t *caps; + char errbuf[PCAP_ERRBUF_SIZE]; + pcap_t *pch; +#ifdef HAVE_PCAP_CREATE + int status; +#endif + int deflt; +#ifdef HAVE_PCAP_LIST_DATALINKS + int *linktypes; + int i, nlt; +#endif + data_link_info_t *data_link_info; + + /* + * Allocate the interface capabilities structure. + */ + caps = g_malloc(sizeof *caps); + +#ifdef HAVE_PCAP_OPEN + pch = pcap_open(devname, MIN_PACKET_SIZE, 0, 0, NULL, errbuf); + caps->can_set_rfmon = FALSE; + if (pch == NULL) { + if (err_str != NULL) + *err_str = g_strdup(errbuf); + g_free(caps); + return NULL; + } +#elif defined(HAVE_PCAP_CREATE) + pch = pcap_create(devname, errbuf); + if (pch == NULL) { + if (err_str != NULL) + *err_str = g_strdup(errbuf); + g_free(caps); + return NULL; + } + status = pcap_can_set_rfmon(pch); + if (status < 0) { + /* Error. */ + if (status == PCAP_ERROR) + *err_str = g_strdup_printf("pcap_can_set_rfmon() failed: %s", + pcap_geterr(pch)); + else + *err_str = g_strdup(pcap_statustostr(status)); + pcap_close(pch); + g_free(caps); + return NULL; + } + if (status == 0) + caps->can_set_rfmon = FALSE; + else if (status == 1) { + caps->can_set_rfmon = TRUE; + if (monitor_mode) + pcap_set_rfmon(pch, 1); + } else { + if (err_str != NULL) { + *err_str = g_strdup_printf("pcap_can_set_rfmon() returned %d", + status); + } + pcap_close(pch); + g_free(caps); + return NULL; + } + + status = pcap_activate(pch); + if (status < 0) { + /* Error. We ignore warnings (status > 0). */ + if (err_str != NULL) { + if (status == PCAP_ERROR) + *err_str = g_strdup_printf("pcap_activate() failed: %s", + pcap_geterr(pch)); + else + *err_str = g_strdup(pcap_statustostr(status)); + } + pcap_close(pch); + g_free(caps); + return NULL; + } +#else + pch = pcap_open_live(devname, MIN_PACKET_SIZE, 0, 0, errbuf); + caps->can_set_rfmon = FALSE; + if (pch == NULL) { + if (err_str != NULL) + *err_str = g_strdup(errbuf); + g_free(caps); + return NULL; + } +#endif + deflt = get_pcap_linktype(pch, devname); +#ifdef HAVE_PCAP_LIST_DATALINKS + nlt = pcap_list_datalinks(pch, &linktypes); + if (nlt == 0 || linktypes == NULL) { + pcap_close(pch); + if (err_str != NULL) + *err_str = NULL; /* an empty list doesn't mean an error */ + return NULL; + } + caps->data_link_types = NULL; + for (i = 0; i < nlt; i++) { + data_link_info = create_data_link_info(linktypes[i]); + + /* + * XXX - for 802.11, make the most detailed 802.11 + * version the default, rather than the one the + * device has as the default? + */ + if (linktypes[i] == deflt) + caps->data_link_types = g_list_prepend(caps->data_link_types, + data_link_info); + else + caps->data_link_types = g_list_append(caps->data_link_types, + data_link_info); + } +#ifdef HAVE_PCAP_FREE_DATALINKS + pcap_free_datalinks(linktypes); +#else + /* + * In Windows, there's no guarantee that if you have a library + * built with one version of the MSVC++ run-time library, and + * it returns a pointer to allocated data, you can free that + * data from a program linked with another version of the + * MSVC++ run-time library. + * + * This is not an issue on UN*X. + * + * See the mail threads starting at + * + * http://www.winpcap.org/pipermail/winpcap-users/2006-September/001421.html + * + * and + * + * http://www.winpcap.org/pipermail/winpcap-users/2008-May/002498.html + */ +#ifndef _WIN32 +#define xx_free free /* hack so checkAPIs doesn't complain */ + xx_free(linktypes); +#endif /* _WIN32 */ +#endif /* HAVE_PCAP_FREE_DATALINKS */ +#else /* HAVE_PCAP_LIST_DATALINKS */ + + data_link_info = create_data_link_info(deflt); + caps->data_link_types = g_list_append(caps->data_link_types, + data_link_info); +#endif /* HAVE_PCAP_LIST_DATALINKS */ + + pcap_close(pch); + + if (err_str != NULL) + *err_str = NULL; + return caps; +} + +#define ADDRSTRLEN 46 /* Covers IPv4 & IPv6 */ +static void +print_machine_readable_interfaces(GList *if_list) +{ + int i; + GList *if_entry; + if_info_t *if_info; + GSList *addr; + if_addr_t *if_addr; + char addr_str[ADDRSTRLEN]; + + if (capture_child) { + /* Let our parent know we succeeded. */ + pipe_write_block(2, SP_SUCCESS, NULL); + } + + i = 1; /* Interface id number */ + for (if_entry = g_list_first(if_list); if_entry != NULL; + if_entry = g_list_next(if_entry)) { + if_info = (if_info_t *)if_entry->data; + printf("%d. %s", i++, if_info->name); + + /* + * Print the contents of the if_entry struct in a parseable format. + * Each if_entry element is tab-separated. Addresses are comma- + * separated. + */ + /* XXX - Make sure our description doesn't contain a tab */ + if (if_info->description != NULL) + printf("\t%s\t", if_info->description); + else + printf("\t\t"); + + for(addr = g_slist_nth(if_info->addrs, 0); addr != NULL; + addr = g_slist_next(addr)) { + if (addr != g_slist_nth(if_info->addrs, 0)) + printf(","); + + if_addr = (if_addr_t *)addr->data; + switch(if_addr->ifat_type) { + case IF_AT_IPv4: + if (inet_ntop(AF_INET, &if_addr->addr.ip4_addr, addr_str, + ADDRSTRLEN)) { + printf("%s", addr_str); + } else { + printf(""); + } + break; + case IF_AT_IPv6: + if (inet_ntop(AF_INET6, &if_addr->addr.ip6_addr, + addr_str, ADDRSTRLEN)) { + printf("%s", addr_str); + } else { + printf(""); + } + break; + default: + printf("", if_addr->ifat_type); + } + } + + if (if_info->loopback) + printf("\tloopback"); + else + printf("\tnetwork"); + + printf("\n"); + } } /* - * Report an error in command-line arguments. + * If you change the machine-readable output format of this function, + * you MUST update capture_ifinfo.c:capture_get_if_capabilities() accordingly! */ -void -cmdarg_err(const char *fmt, ...) +static void +print_machine_readable_if_capabilities(if_capabilities_t *caps) { - va_list ap; + GList *lt_entry; + data_link_info_t *data_link_info; + const gchar *desc_str; - if(capture_child) { - gchar *msg; - /* Generate a 'special format' message back to parent */ - va_start(ap, fmt); - msg = g_strdup_vprintf(fmt, ap); - sync_pipe_errmsg_to_parent(2, msg, ""); - g_free(msg); - va_end(ap); - } else { - va_start(ap, fmt); - fprintf(stderr, "dumpcap: "); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); - va_end(ap); - } + if (capture_child) { + /* Let our parent know we succeeded. */ + pipe_write_block(2, SP_SUCCESS, NULL); + } + + if (caps->can_set_rfmon) + printf("1\n"); + else + printf("0\n"); + for (lt_entry = caps->data_link_types; lt_entry != NULL; + lt_entry = g_list_next(lt_entry)) { + data_link_info = (data_link_info_t *)lt_entry->data; + if (data_link_info->description != NULL) + desc_str = data_link_info->description; + else + desc_str = "(not supported)"; + printf("%d\t%s\t%s\n", data_link_info->dlt, data_link_info->name, + desc_str); + } } -/* - * Report additional information for an error in command-line arguments. - */ -void -cmdarg_err_cont(const char *fmt, ...) +typedef struct { + char *name; + pcap_t *pch; +} if_stat_t; + +/* Print the number of packets captured for each interface until we're killed. */ +static int +print_statistics_loop(gboolean machine_readable) { - va_list ap; + GList *if_list, *if_entry, *stat_list = NULL, *stat_entry; + if_info_t *if_info; + if_stat_t *if_stat; + int err; + gchar *err_str; + pcap_t *pch; + char errbuf[PCAP_ERRBUF_SIZE]; + struct pcap_stat ps; + + if_list = get_interface_list(&err, &err_str); + if (if_list == NULL) { + switch (err) { + case CANT_GET_INTERFACE_LIST: + cmdarg_err("%s", err_str); + g_free(err_str); + break; + + case NO_INTERFACES_FOUND: + cmdarg_err("There are no interfaces on which a capture can be done"); + break; + } + return err; + } - if(capture_child) { - gchar *msg; - va_start(ap, fmt); - msg = g_strdup_vprintf(fmt, ap); - sync_pipe_errmsg_to_parent(2, msg, ""); - g_free(msg); - va_end(ap); - } else { - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); - va_end(ap); - } + for (if_entry = g_list_first(if_list); if_entry != NULL; if_entry = g_list_next(if_entry)) { + if_info = (if_info_t *)if_entry->data; +#ifdef HAVE_PCAP_OPEN + pch = pcap_open(if_info->name, MIN_PACKET_SIZE, 0, 0, NULL, errbuf); +#else + pch = pcap_open_live(if_info->name, MIN_PACKET_SIZE, 0, 0, errbuf); +#endif + + if (pch) { + if_stat = (if_stat_t *)g_malloc(sizeof(if_stat_t)); + if_stat->name = g_strdup(if_info->name); + if_stat->pch = pch; + stat_list = g_list_append(stat_list, if_stat); + } + } + + if (!stat_list) { + cmdarg_err("There are no interfaces on which a capture can be done"); + return 2; + } + + if (capture_child) { + /* Let our parent know we succeeded. */ + pipe_write_block(2, SP_SUCCESS, NULL); + } + + if (!machine_readable) { + printf("%-15s %10s %10s\n", "Interface", "Received", + "Dropped"); + } + + global_ld.go = TRUE; + while (global_ld.go) { + for (stat_entry = g_list_first(stat_list); stat_entry != NULL; stat_entry = g_list_next(stat_entry)) { + if_stat = (if_stat_t *)stat_entry->data; + pcap_stats(if_stat->pch, &ps); + + if (!machine_readable) { + printf("%-15s %10u %10u\n", if_stat->name, + ps.ps_recv, ps.ps_drop); + } else { + printf("%s\t%u\t%u\n", if_stat->name, + ps.ps_recv, ps.ps_drop); + fflush(stdout); + } + } +#ifdef _WIN32 + if (! global_ld.from_cap_pipe) + Sleep(1 * 1000); +#else + sleep(1); +#endif + } + + /* XXX - Not reached. Should we look for 'q' in stdin? */ + for (stat_entry = g_list_first(stat_list); stat_entry != NULL; stat_entry = g_list_next(stat_entry)) { + if_stat = (if_stat_t *)stat_entry->data; + pcap_close(if_stat->pch); + g_free(if_stat->name); + g_free(if_stat); + } + g_list_free(stat_list); + free_interface_list(if_list); + + return 0; } #ifdef _WIN32 static BOOL WINAPI -capture_cleanup(DWORD dwCtrlType) +capture_cleanup_handler(DWORD dwCtrlType) { /* CTRL_C_EVENT is sort of like SIGINT, CTRL_BREAK_EVENT is unique to Windows, CTRL_CLOSE_EVENT is sort of like SIGHUP, CTRL_LOGOFF_EVENT @@ -430,21 +1338,54 @@ capture_cleanup(DWORD dwCtrlType) } #else static void -capture_cleanup(int signum) +capture_cleanup_handler(int signum _U_) { /* On UN*X, we cleanly shut down the capture on SIGINT, SIGHUP, and SIGTERM. We assume that if the user wanted it to keep running after they logged out, they'd have nohupped it. */ - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, - "Console: Signal"); - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, - "Console: Signal, signal value: %u", signum); + /* Note: don't call g_log() in the signal handler: if we happened to be in + * g_log() in process context when the signal came in, g_log will detect + * the "recursion" and abort. + */ capture_loop_stop(); } #endif + +#ifdef SIGINFO +static void +report_counts(void) +{ + /* Don't print this if we're a capture child. */ + if (!capture_child) { + if (quiet) { + /* Report the count only if we aren't printing a packet count + as packets arrive. */ + fprintf(stderr, "%u packet%s captured\n", global_ld.packet_count, + plurality(global_ld.packet_count, "", "s")); + } + } + infoprint = FALSE; /* we just reported it */ +} + +static void +report_counts_siginfo(int signum _U_) +{ + int sav_errno = errno; + + /* If we've been told to delay printing, just set a flag asking + that we print counts (if we're supposed to), otherwise print + the count of packets captured (if we're supposed to). */ + if (infodelay) + infoprint = TRUE; + else + report_counts(); + errno = sav_errno; +} +#endif /* SIGINFO */ + static void exit_main(int status) { #ifdef _WIN32 @@ -468,24 +1409,6 @@ static void exit_main(int status) * CAP_NET_ADMIN and CAP_NET_RAW, then relinquish our permissions. * (See comment in main() for details) */ - -static void -#if 0 /* Set to enable capability debugging */ -/* see 'man cap_to_text()' for explanation of output */ -/* '=' means 'all= ' ie: no capabilities */ -/* '=ip' means 'all=ip' ie: all capabilities are permissible and inheritable */ -/* .... */ -print_caps(char *pfx) { - cap_t caps = cap_get_proc(); - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, - "%s: EUID: %d Capabilities: %s", pfx, - geteuid(), cap_to_text(caps, NULL)); - cap_free(caps); -#else -print_caps(char *pfx _U_) { -#endif -} - static void relinquish_privs_except_capture(void) { @@ -531,21 +1454,6 @@ relinquish_privs_except_capture(void) } } - -static void -relinquish_all_capabilities() -{ - /* Drop any and all capabilities this process may have. */ - /* Allowed whether or not process has any privileges. */ - cap_t caps = cap_init(); /* all capabilities initialized to off */ - print_caps("Pre-clear"); - if (cap_set_proc(caps)) { - cmdarg_err("cap_set_proc() fail return: %s", strerror(errno)); - } - print_caps("Post-clear"); - cap_free(caps); -} - #endif /* HAVE_LIBCAP */ /* Take care of byte order in the libpcap headers read from pipes. @@ -579,20 +1487,102 @@ cap_pipe_adjust_header(gboolean byte_swapped, struct pcap_hdr *hdr, struct pcapr } } -/* Provide select() functionality for a single file descriptor - * on both UNIX/POSIX and Windows. +#ifdef USE_THREADS +/* + * Thread function that reads from a pipe and pushes the data + * to the main application thread. + */ +/* + * XXX Right now we use async queues for basic signaling. The main thread + * sets cap_pipe_buf and cap_bytes_to_read, then pushes an item onto + * cap_pipe_pending_q which triggers a read in the cap_pipe_read thread. + * Iff the read is successful cap_pipe_read pushes an item onto + * cap_pipe_done_q, otherwise an error is signaled. No data is passed in + * the queues themselves (yet). * - * The Windows version calls WaitForSingleObject instead of - * select(). + * We might want to move some of the cap_pipe_dispatch logic here so that + * we can let cap_pipe_read run independently, queuing up multiple reads + * for the main thread (and possibly get rid of cap_pipe_read_mtx). + */ +static void *cap_pipe_read(void *ld_ptr) { + loop_data *ld = (loop_data *)ld_ptr; + int bytes_read; +#ifdef _WIN32 + BOOL res; + DWORD b, last_err; +#else /* _WIN32 */ + int b; +#endif /* _WIN32 */ + + while (ld->cap_pipe_err == PIPOK) { + g_async_queue_pop(cap_pipe_pending_q); /* Wait for our cue (ahem) from the main thread */ + g_mutex_lock(cap_pipe_read_mtx); + bytes_read = 0; + while (bytes_read < (int) ld->cap_pipe_bytes_to_read) { +#ifdef _WIN32 + /* If we try to use read() on a named pipe on Windows with partial + * data it appears to return EOF. + */ + res = ReadFile(ld->cap_pipe_h, ld->cap_pipe_buf+bytes_read, + ld->cap_pipe_bytes_to_read - bytes_read, + &b, NULL); + + bytes_read += b; + if (!res) { + last_err = GetLastError(); + if (last_err == ERROR_MORE_DATA) { + continue; + } else if (last_err == ERROR_HANDLE_EOF || last_err == ERROR_BROKEN_PIPE || last_err == ERROR_PIPE_NOT_CONNECTED) { + ld->cap_pipe_err = PIPEOF; + bytes_read = 0; + break; + } + ld->cap_pipe_err = PIPERR; + bytes_read = -1; + break; + } else if (b == 0 && ld->cap_pipe_bytes_to_read > 0) { + ld->cap_pipe_err = PIPEOF; + bytes_read = 0; + break; + } +#else /* _WIN32 */ + b = read(ld->cap_pipe_fd, ld->cap_pipe_buf+bytes_read, + ld->cap_pipe_bytes_to_read - bytes_read); + if (b <= 0) { + if (b == 0) { + ld->cap_pipe_err = PIPEOF; + bytes_read = 0; + break; + } else { + ld->cap_pipe_err = PIPERR; + bytes_read = -1; + break; + } + } else { + bytes_read += b; + } +#endif /*_WIN32 */ + } + ld->cap_pipe_bytes_read = bytes_read; + if (ld->cap_pipe_bytes_read >= ld->cap_pipe_bytes_to_read) { + g_async_queue_push(cap_pipe_done_q, ld->cap_pipe_buf); /* Any non-NULL value will do */ + } + g_mutex_unlock(cap_pipe_read_mtx); + } + return NULL; +} +#endif /* USE_THREADS */ + +/* Provide select() functionality for a single file descriptor + * on UNIX/POSIX. Windows uses cap_pipe_read via a thread. * * Returns the same values as select. If an error is returned, * the string cap_pipe_err_str should be used instead of errno. */ static int cap_pipe_select(int pipe_fd) { -#ifndef _WIN32 fd_set rfds; - struct timeval timeout, *pto; + struct timeval timeout; int sel_ret; cap_pipe_err_str = "Unknown error"; @@ -600,92 +1590,58 @@ cap_pipe_select(int pipe_fd) { FD_ZERO(&rfds); FD_SET(pipe_fd, &rfds); - timeout.tv_sec = 0; - timeout.tv_usec = CAP_READ_TIMEOUT * 1000; - pto = &timeout; + timeout.tv_sec = PIPE_READ_TIMEOUT / 1000000; + timeout.tv_usec = PIPE_READ_TIMEOUT % 1000000; - sel_ret = select(pipe_fd+1, &rfds, NULL, NULL, pto); + sel_ret = select(pipe_fd+1, &rfds, NULL, NULL, &timeout); 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. + + * We check if "pipename" is "-" (stdin), a AF_UNIX socket, 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 +static void cap_pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, - char *errmsg, int errmsgl) + char *errmsg, int errmsgl) { #ifndef _WIN32 struct stat pipe_stat; -#else + struct sockaddr_un sa; + int sel_ret; + int b; + unsigned int bytes_read; + int fd; +#else /* _WIN32 */ #if 1 char *pncopy, *pos; wchar_t *err_str; #endif - HANDLE hPipe = NULL; #endif - int sel_ret; - int fd; - int b; - guint32 magic; - unsigned int bytes_read; + guint32 magic = 0; +#ifndef _WIN32 + ld->cap_pipe_fd = -1; +#else + ld->cap_pipe_h = INVALID_HANDLE_VALUE; +#endif g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "cap_pipe_open_live: %s", pipename); /* - * XXX (T)Wireshark blocks until we return + * XXX - this blocks until a pcap per-file header has been written to + * the pipe, so it could block indefinitely. */ if (strcmp(pipename, "-") == 0) { +#ifndef _WIN32 fd = 0; /* read from stdin */ -#ifdef _WIN32 - /* - * This is needed to set the stdin pipe into binary mode, otherwise - * CR/LF are mangled... - */ - _setmode(0, _O_BINARY); +#else /* _WIN32 */ + ld->cap_pipe_h = GetStdHandle(STD_INPUT_HANDLE); #endif /* _WIN32 */ } else { #ifndef _WIN32 @@ -695,12 +1651,70 @@ cap_pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, else { g_snprintf(errmsg, errmsgl, "The capture session could not be initiated " - "due to error on pipe: %s", strerror(errno)); + "due to error getting information on pipe/socket: %s", strerror(errno)); ld->cap_pipe_err = PIPERR; } - return -1; + return; } - if (! S_ISFIFO(pipe_stat.st_mode)) { + if (S_ISFIFO(pipe_stat.st_mode)) { + fd = ws_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; + } + } else if (S_ISSOCK(pipe_stat.st_mode)) { + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd == -1) { + g_snprintf(errmsg, errmsgl, + "The capture session could not be initiated " + "due to error on socket create: %s", strerror(errno)); + ld->cap_pipe_err = PIPERR; + return; + } + sa.sun_family = AF_UNIX; + /* + * The Single UNIX Specification says: + * + * The size of sun_path has intentionally been left undefined. + * This is because different implementations use different sizes. + * For example, 4.3 BSD uses a size of 108, and 4.4 BSD uses a size + * of 104. Since most implementations originate from BSD versions, + * the size is typically in the range 92 to 108. + * + * Applications should not assume a particular length for sun_path + * or assume that it can hold {_POSIX_PATH_MAX} bytes (256). + * + * It also says + * + * The header shall define the sockaddr_un structure, + * which shall include at least the following members: + * + * sa_family_t sun_family Address family. + * char sun_path[] Socket pathname. + * + * so we assume that it's an array, with a specified size, + * and that the size reflects the maximum path length. + */ + if (g_strlcpy(sa.sun_path, pipename, sizeof sa.sun_path) > sizeof sa.sun_path) { + /* Path name too long */ + g_snprintf(errmsg, errmsgl, + "The capture session coud not be initiated " + "due to error on socket connect: Path name too long"); + ld->cap_pipe_err = PIPERR; + return; + } + b = connect(fd, (struct sockaddr *)&sa, sizeof sa); + if (b == -1) { + g_snprintf(errmsg, errmsgl, + "The capture session coud not be initiated " + "due to error on socket connect: %s", strerror(errno)); + ld->cap_pipe_err = PIPERR; + return; + } + } else { if (S_ISCHR(pipe_stat.st_mode)) { /* * Assume the user specified an interface on a system where @@ -711,18 +1725,10 @@ cap_pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, { g_snprintf(errmsg, errmsgl, "The capture session could not be initiated because\n" - "\"%s\" is neither an interface nor a pipe", pipename); + "\"%s\" is neither an interface nor a socket nor a pipe", pipename); ld->cap_pipe_err = PIPERR; } - return -1; - } - fd = ws_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; + return; } #else /* _WIN32 */ #define PIPE_STR "\\pipe\\" @@ -743,55 +1749,47 @@ cap_pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, "The capture session could not be initiated because\n" "\"%s\" is neither an interface nor a pipe", pipename); ld->cap_pipe_err = PIPNEXIST; - return -1; + return; } /* Wait for the pipe to appear */ while (1) { - hPipe = CreateFile(utf_8to16(pipename), GENERIC_READ, 0, NULL, + ld->cap_pipe_h = CreateFile(utf_8to16(pipename), GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); - if (hPipe != INVALID_HANDLE_VALUE) + if (ld->cap_pipe_h != INVALID_HANDLE_VALUE) break; if (GetLastError() != ERROR_PIPE_BUSY) { FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, GetLastError(), 0, (LPTSTR) &err_str, 0, NULL); g_snprintf(errmsg, errmsgl, - "The capture session on \"%s\" could not be initiated " - "due to error on pipe open: pipe busy: %s (error %d)", - pipename, utf_16to8(err_str), GetLastError()); + "The capture session on \"%s\" could not be started " + "due to error on pipe open: %s (error %d)", + pipename, utf_16to8(err_str), GetLastError()); LocalFree(err_str); ld->cap_pipe_err = PIPERR; - return -1; + return; } if (!WaitNamedPipe(utf_8to16(pipename), 30 * 1000)) { FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, GetLastError(), 0, (LPTSTR) &err_str, 0, NULL); g_snprintf(errmsg, errmsgl, - "The capture session could not be initiated " - "due to error on named pipe open: %s (error %d)", - utf_16to8(err_str), GetLastError()); + "The capture session on \"%s\" timed out during " + "pipe open: %s (error %d)", + pipename, utf_16to8(err_str), GetLastError()); LocalFree(err_str); ld->cap_pipe_err = PIPERR; - return -1; + return; } } - - fd = _open_osfhandle((long) hPipe, _O_RDONLY); - if (fd == -1) { - g_snprintf(errmsg, errmsgl, - "The capture session could not be initiated " - "due to error on pipe open: %s", strerror(errno)); - ld->cap_pipe_err = PIPERR; - return -1; - } #endif /* _WIN32 */ } ld->from_cap_pipe = TRUE; +#ifndef USE_THREADS /* read the pcap header */ bytes_read = 0; while (bytes_read < sizeof magic) { @@ -804,15 +1802,34 @@ cap_pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, b = read(fd, ((char *)&magic)+bytes_read, sizeof magic-bytes_read); if (b <= 0) { if (b == 0) - g_snprintf(errmsg, errmsgl, "End of file on pipe during open"); + g_snprintf(errmsg, errmsgl, "End of file on pipe magic during open"); else - g_snprintf(errmsg, errmsgl, "Error on pipe during open: %s", + g_snprintf(errmsg, errmsgl, "Error on pipe magic during open: %s", strerror(errno)); goto error; } bytes_read += b; } } +#else /* USE_THREADS */ + g_thread_create(&cap_pipe_read, ld, FALSE, NULL); + + ld->cap_pipe_buf = (char *) &magic; + ld->cap_pipe_bytes_read = 0; + ld->cap_pipe_bytes_to_read = sizeof(magic); + /* We don't have to worry about cap_pipe_read_mtx here */ + g_async_queue_push(cap_pipe_pending_q, ld->cap_pipe_buf); + g_async_queue_pop(cap_pipe_done_q); + if (ld->cap_pipe_bytes_read <= 0) { + if (ld->cap_pipe_bytes_read == 0) + g_snprintf(errmsg, errmsgl, "End of file on pipe magic during open"); + else + g_snprintf(errmsg, errmsgl, "Error on pipe magic during open: %s", + strerror(errno)); + goto error; + } + +#endif /* USE_THREADS */ switch (magic) { case PCAP_MAGIC: @@ -847,6 +1864,7 @@ cap_pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, goto error; } +#ifndef USE_THREADS /* Read the rest of the header */ bytes_read = 0; while (bytes_read < sizeof(struct pcap_hdr)) { @@ -860,15 +1878,30 @@ cap_pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, sizeof(struct pcap_hdr) - bytes_read); if (b <= 0) { if (b == 0) - g_snprintf(errmsg, errmsgl, "End of file on pipe during open"); + g_snprintf(errmsg, errmsgl, "End of file on pipe header during open"); else - g_snprintf(errmsg, errmsgl, "Error on pipe during open: %s", + g_snprintf(errmsg, errmsgl, "Error on pipe header during open: %s", strerror(errno)); goto error; } bytes_read += b; } } +#else /* USE_THREADS */ + ld->cap_pipe_buf = (char *) hdr; + ld->cap_pipe_bytes_read = 0; + ld->cap_pipe_bytes_to_read = sizeof(struct pcap_hdr); + g_async_queue_push(cap_pipe_pending_q, ld->cap_pipe_buf); + g_async_queue_pop(cap_pipe_done_q); + if (ld->cap_pipe_bytes_read <= 0) { + if (ld->cap_pipe_bytes_read == 0) + g_snprintf(errmsg, errmsgl, "End of file on pipe header during open"); + else + g_snprintf(errmsg, errmsgl, "Error on pipe header header during open: %s", + strerror(errno)); + goto error; + } +#endif /* USE_THREADS */ if (ld->cap_pipe_byte_swapped) { /* Byte-swap the header fields about which we care. */ @@ -886,13 +1919,19 @@ cap_pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, ld->cap_pipe_state = STATE_EXPECT_REC_HDR; ld->cap_pipe_err = PIPOK; - return fd; +#ifndef _WIN32 + ld->cap_pipe_fd = fd; +#endif + return; error: g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "cap_pipe_open_live: error %s", errmsg); ld->cap_pipe_err = PIPERR; +#ifndef _WIN32 ws_close(fd); - return -1; + ld->cap_pipe_fd = -1; +#endif + return; } @@ -903,10 +1942,17 @@ 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; - + PD_ERR } result; +#ifdef USE_THREADS + GTimeVal wait_time; + gpointer q_status; +#else + int b; +#endif +#ifdef _WIN32 + wchar_t *err_str; +#endif #ifdef LOG_CAPTURE_VERBOSE g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "cap_pipe_dispatch"); @@ -915,13 +1961,25 @@ cap_pipe_dispatch(loop_data *ld, guchar *data, char *errmsg, int errmsgl) switch (ld->cap_pipe_state) { case STATE_EXPECT_REC_HDR: +#ifdef USE_THREADS + if (g_mutex_trylock(cap_pipe_read_mtx)) { +#endif + + ld->cap_pipe_state = STATE_READ_REC_HDR; ld->cap_pipe_bytes_to_read = ld->cap_pipe_modified ? sizeof(struct pcaprec_modified_hdr) : sizeof(struct pcaprec_hdr); ld->cap_pipe_bytes_read = 0; - ld->cap_pipe_state = STATE_READ_REC_HDR; + +#ifdef USE_THREADS + ld->cap_pipe_buf = (char *) &ld->cap_pipe_rechdr; + g_async_queue_push(cap_pipe_pending_q, ld->cap_pipe_buf); + g_mutex_unlock(cap_pipe_read_mtx); + } +#endif /* Fall through */ case STATE_READ_REC_HDR: +#ifndef USE_THREADS b = read(ld->cap_pipe_fd, ((char *)&ld->cap_pipe_rechdr)+ld->cap_pipe_bytes_read, ld->cap_pipe_bytes_to_read - ld->cap_pipe_bytes_read); if (b <= 0) { @@ -931,19 +1989,48 @@ cap_pipe_dispatch(loop_data *ld, guchar *data, char *errmsg, int errmsgl) result = PD_PIPE_ERR; break; } - if ((ld->cap_pipe_bytes_read += b) < ld->cap_pipe_bytes_to_read) + ld->cap_pipe_bytes_read += b; +#else /* USE_THREADS */ + g_get_current_time(&wait_time); + g_time_val_add(&wait_time, PIPE_READ_TIMEOUT); + q_status = g_async_queue_timed_pop(cap_pipe_done_q, &wait_time); + if (ld->cap_pipe_err == PIPEOF) { + result = PD_PIPE_EOF; + break; + } else if (ld->cap_pipe_err == PIPERR) { + result = PD_PIPE_ERR; + break; + } + if (!q_status) { + return 0; + } +#endif /* USE_THREADS */ + if ((ld->cap_pipe_bytes_read) < ld->cap_pipe_bytes_to_read) return 0; result = PD_REC_HDR_READ; break; case STATE_EXPECT_DATA: - ld->cap_pipe_bytes_read = 0; +#ifdef USE_THREADS + if (g_mutex_trylock(cap_pipe_read_mtx)) { +#endif + ld->cap_pipe_state = STATE_READ_DATA; + ld->cap_pipe_bytes_to_read = ld->cap_pipe_rechdr.hdr.incl_len; + ld->cap_pipe_bytes_read = 0; + +#ifdef USE_THREADS + ld->cap_pipe_buf = (char *) data; + g_async_queue_push(cap_pipe_pending_q, ld->cap_pipe_buf); + g_mutex_unlock(cap_pipe_read_mtx); + } +#endif /* Fall through */ case STATE_READ_DATA: +#ifndef USE_THREADS b = read(ld->cap_pipe_fd, data+ld->cap_pipe_bytes_read, - ld->cap_pipe_rechdr.hdr.incl_len - ld->cap_pipe_bytes_read); + ld->cap_pipe_bytes_to_read - ld->cap_pipe_bytes_read); if (b <= 0) { if (b == 0) result = PD_PIPE_EOF; @@ -951,8 +2038,24 @@ cap_pipe_dispatch(loop_data *ld, guchar *data, char *errmsg, int errmsgl) result = PD_PIPE_ERR; break; } - if ((ld->cap_pipe_bytes_read += b) < ld->cap_pipe_rechdr.hdr.incl_len) + ld->cap_pipe_bytes_read += b; +#else /* USE_THREADS */ + g_get_current_time(&wait_time); + g_time_val_add(&wait_time, PIPE_READ_TIMEOUT); + q_status = g_async_queue_timed_pop(cap_pipe_done_q, &wait_time); + if (ld->cap_pipe_err == PIPEOF) { + result = PD_PIPE_EOF; + break; + } else if (ld->cap_pipe_err == PIPERR) { + result = PD_PIPE_ERR; + break; + } + if (!q_status) { return 0; + } +#endif /* USE_THREADS */ + if ((ld->cap_pipe_bytes_read) < ld->cap_pipe_bytes_to_read) + return 0; result = PD_DATA_READ; break; @@ -996,8 +2099,17 @@ cap_pipe_dispatch(loop_data *ld, guchar *data, char *errmsg, int errmsgl) return -1; case PD_PIPE_ERR: +#ifdef _WIN32 + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, + NULL, GetLastError(), 0, (LPTSTR) &err_str, 0, NULL); + g_snprintf(errmsg, errmsgl, + "Error reading from pipe: %s (error %d)", + utf_16to8(err_str), GetLastError()); + LocalFree(err_str); +#else g_snprintf(errmsg, errmsgl, "Error reading from pipe: %s", strerror(errno)); +#endif /* Fall through */ case PD_ERR: break; @@ -1018,19 +2130,12 @@ capture_loop_open_input(capture_options *capture_opts, loop_data *ld, { 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; + gchar *sync_secondary_msg_str; 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); @@ -1048,86 +2153,51 @@ capture_loop_open_input(capture_options *capture_opts, loop_data *ld, switch (err) { case WSASYSNOTREADY: - g_snprintf(errmsg, errmsg_len, + g_snprintf(errmsg, (gulong) errmsg_len, "Couldn't initialize Windows Sockets: Network system not ready for network communication"); break; case WSAVERNOTSUPPORTED: - g_snprintf(errmsg, errmsg_len, + g_snprintf(errmsg, (gulong) errmsg_len, "Couldn't initialize Windows Sockets: Windows Sockets version %u.%u not supported", LOBYTE(wVersionRequested), HIBYTE(wVersionRequested)); break; case WSAEINPROGRESS: - g_snprintf(errmsg, errmsg_len, + g_snprintf(errmsg, (gulong) errmsg_len, "Couldn't initialize Windows Sockets: Blocking operation is in progress"); break; case WSAEPROCLIM: - g_snprintf(errmsg, errmsg_len, + g_snprintf(errmsg, (gulong) errmsg_len, "Couldn't initialize Windows Sockets: Limit on the number of tasks supported by this WinSock implementation has been reached"); break; case WSAEFAULT: - g_snprintf(errmsg, errmsg_len, + g_snprintf(errmsg, (gulong) errmsg_len, "Couldn't initialize Windows Sockets: Bad pointer passed to WSAStartup"); break; default: - g_snprintf(errmsg, errmsg_len, + g_snprintf(errmsg, (gulong) errmsg_len, "Couldn't initialize Windows Sockets: error %d", err); break; } - g_snprintf(secondary_errmsg, secondary_errmsg_len, please_report); + g_snprintf(secondary_errmsg, (gulong) secondary_errmsg_len, please_report); return FALSE; } #endif - /* 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 not using libcap: we now can now set euid/egid to ruid/rgid */ -/* to remove any suid privileges. */ -/* If using libcap: we can now remove NET_RAW and NET_ADMIN capabilities */ -/* (euid/egid have already previously been set to ruid/rgid. */ -/* (See comment in main() for details) */ -#ifndef HAVE_LIBCAP - relinquish_special_privs_perm(); -#else - relinquish_all_capabilities(); -#endif + ld->pcap_h = open_capture_device(capture_opts, &open_err_str); 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) { + if (capture_opts->buffer_size > 1 && + 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 capture buffer size of %dMB seems to be too high for your machine,\n" "the default of 1MB will be used.\n" "\n" "Nonetheless, the capture is started.\n", @@ -1139,7 +2209,8 @@ capture_loop_open_input(capture_options *capture_opts, loop_data *ld, #endif #if defined(HAVE_PCAP_REMOTE) && defined(HAVE_PCAP_SETSAMPLING) - if (capture_opts->sampling_method != CAPTURE_SAMP_NONE) + if ((capture_opts->sampling_method != CAPTURE_SAMP_NONE) && + (strncmp (capture_opts->iface, "rpcap://", 8) == 0)) { struct pcap_samp *samp; @@ -1176,75 +2247,29 @@ capture_loop_open_input(capture_options *capture_opts, loop_data *ld, #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; - } - } + if (!set_pcap_linktype(ld->pcap_h, capture_opts, errmsg, errmsg_len, + secondary_errmsg, secondary_errmsg_len)) + return FALSE; ld->linktype = get_pcap_linktype(ld->pcap_h, capture_opts->iface); } else { /* We couldn't open "iface" as a network device. */ /* Try to open it as a pipe */ - ld->cap_pipe_fd = cap_pipe_open_live(capture_opts->iface, &ld->cap_pipe_hdr, ld, errmsg, errmsg_len); + cap_pipe_open_live(capture_opts->iface, &ld->cap_pipe_hdr, ld, errmsg, (int) errmsg_len); +#ifndef _WIN32 if (ld->cap_pipe_fd == -1) { +#else + if (ld->cap_pipe_h == INVALID_HANDLE_VALUE) { +#endif if (ld->cap_pipe_err == PIPNEXIST) { - /* Pipe doesn't exist, so output message for interface */ - - /* If we got a "can't find PPA for X" message, warn the user (who - is running (T)Wireshark on HP-UX) that they don't have a version - of libpcap that properly handles HP-UX (libpcap 0.6.x and later - versions, which properly handle HP-UX, say "can't find /dev/dlpi - PPA for X" rather than "can't find PPA for X"). */ - if (strncmp(open_err_str, ppamsg, sizeof ppamsg - 1) == 0) - libpcap_warn = - "\n\n" - "You are running (T)Wireshark with a version of the libpcap library\n" - "that doesn't handle HP-UX network devices well; this means that\n" - "(T)Wireshark may not be able to capture packets.\n" - "\n" - "To fix this, you should install libpcap 0.6.2, or a later version\n" - "of libpcap, rather than libpcap 0.4 or 0.5.x. It is available in\n" - "packaged binary form from the Software Porting And Archive Centre\n" - "for HP-UX; the Centre is at http://hpux.connect.org.uk/ - the page\n" - "at the URL lists a number of mirror sites."; - else - libpcap_warn = ""; - g_snprintf(errmsg, errmsg_len, - "The capture session could not be initiated (%s).", open_err_str); -#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 */ + /* Pipe doesn't exist, so output message for interface */ + get_capture_device_open_failure_messages(open_err_str, + capture_opts->iface, + errmsg, + errmsg_len, + secondary_errmsg, + secondary_errmsg_len); } /* * Else pipe (or file) does exist and cap_pipe_open_live() has @@ -1279,22 +2304,28 @@ capture_loop_open_input(capture_options *capture_opts, loop_data *ld, 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" */ +#ifndef _WIN32 if (ld->cap_pipe_fd >= 0) { g_assert(ld->from_cap_pipe); ws_close(ld->cap_pipe_fd); ld->cap_pipe_fd = 0; } +#else + if (ld->cap_pipe_h != INVALID_HANDLE_VALUE) { + CloseHandle(ld->cap_pipe_h); + ld->cap_pipe_h = INVALID_HANDLE_VALUE; + } +#endif /* if open, close the pcap "input file" */ if(ld->pcap_h != NULL) { - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_close_input: closing %p", ld->pcap_h); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_close_input: closing %p", (void *)ld->pcap_h); g_assert(!ld->from_cap_pipe); pcap_close(ld->pcap_h); ld->pcap_h = NULL; @@ -1311,32 +2342,17 @@ static void capture_loop_close_input(loop_data *ld) { /* init the capture filter */ static initfilter_status_t -capture_loop_init_filter(pcap_t *pcap_h, gboolean from_cap_pipe, gchar * iface, gchar * cfilter) { - bpf_u_int32 netnum, netmask; - gchar lookup_net_err_str[PCAP_ERRBUF_SIZE]; +capture_loop_init_filter(pcap_t *pcap_h, gboolean from_cap_pipe, + gchar * iface, gchar * cfilter) +{ 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) { + if (!compile_capture_filter(iface, pcap_h, &fcode, cfilter)) { /* 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. */ @@ -1359,8 +2375,7 @@ capture_loop_init_filter(pcap_t *pcap_h, gboolean from_cap_pipe, gchar * iface, /* 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; +capture_loop_init_output(capture_options *capture_opts, loop_data *ld, char *errmsg, int errmsg_len) { int err; @@ -1368,19 +2383,36 @@ capture_loop_init_output(capture_options *capture_opts, int save_file_fd, loop_d /* get snaplen */ if (ld->from_cap_pipe) { - file_snaplen = ld->cap_pipe_hdr.snaplen; + ld->file_snaplen = ld->cap_pipe_hdr.snaplen; } else { - file_snaplen = pcap_snapshot(ld->pcap_h); + ld->file_snaplen = pcap_snapshot(ld->pcap_h); } /* Set up to write to the capture file. */ if (capture_opts->multi_files_on) { - ld->pdh = ringbuf_init_libpcap_fdopen(ld->linktype, file_snaplen, - &ld->bytes_written, &err); + ld->pdh = ringbuf_init_libpcap_fdopen(&err); } else { - ld->pdh = libpcap_fdopen(save_file_fd, ld->linktype, file_snaplen, - &ld->bytes_written, &err); + ld->pdh = libpcap_fdopen(ld->save_file_fd, &err); + } + if (ld->pdh) { + gboolean successful; + + ld->bytes_written = 0; + if (capture_opts->use_pcapng) { + char appname[100]; + + g_snprintf(appname, sizeof(appname), "Dumpcap " VERSION "%s", wireshark_svnversion); + successful = libpcap_write_session_header_block(ld->pdh, appname, &ld->bytes_written, &err) && + libpcap_write_interface_description_block(ld->pdh, capture_opts->iface, capture_opts->cfilter, ld->linktype, ld->file_snaplen, &ld->bytes_written, &err); + } else { + successful = libpcap_write_file_header(ld->pdh, ld->linktype, ld->file_snaplen, + &ld->bytes_written, &err); + } + if (!successful) { + fclose(ld->pdh); + ld->pdh = NULL; + } } if (ld->pdh == NULL) { @@ -1401,14 +2433,14 @@ capture_loop_init_output(capture_options *capture_opts, int save_file_fd, loop_d default: if (err < 0) { g_snprintf(errmsg, errmsg_len, - "The file to which the capture would be" - " saved (\"%s\") could not be opened: Error %d.", - capture_opts->save_file, err); + "The file to which the capture would be" + " saved (\"%s\") could not be opened: Error %d.", + capture_opts->save_file, err); } else { g_snprintf(errmsg, errmsg_len, - "The file to which the capture would be" - " saved (\"%s\") could not be opened: %s.", - capture_opts->save_file, strerror(err)); + "The file to which the capture would be" + " saved (\"%s\") could not be opened: %s.", + capture_opts->save_file, strerror(err)); } break; } @@ -1427,6 +2459,9 @@ capture_loop_close_output(capture_options *capture_opts, loop_data *ld, int *err if (capture_opts->multi_files_on) { return ringbuf_libpcap_dump_close(&capture_opts->save_file, err_close); } else { + if (capture_opts->use_pcapng) { + libpcap_write_interface_statistics_block(ld->pdh, 0, ld->pcap_h, &ld->bytes_written, err_close); + } return libpcap_dump_close(ld->pdh, err_close); } } @@ -1447,9 +2482,11 @@ 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]; +#ifndef USE_THREADS + int sel_ret; +#endif packet_count_before = ld->packet_count; if (ld->from_cap_pipe) { @@ -1457,6 +2494,7 @@ capture_loop_dispatch(capture_options *capture_opts _U_, loop_data *ld, #ifdef LOG_CAPTURE_VERBOSE g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: from capture pipe"); #endif +#ifndef USE_THREADS sel_ret = cap_pipe_select(ld->cap_pipe_fd); if (sel_ret <= 0) { inpkts = 0; @@ -1470,11 +2508,14 @@ capture_loop_dispatch(capture_options *capture_opts _U_, loop_data *ld, /* * "select()" says we can read from the pipe without blocking */ +#endif /* USE_THREADS */ inpkts = cap_pipe_dispatch(ld, pcap_data, errmsg, errmsg_len); if (inpkts < 0) { ld->go = FALSE; } +#ifndef USE_THREADS } +#endif } else { @@ -1511,7 +2552,10 @@ capture_loop_dispatch(capture_options *capture_opts _U_, loop_data *ld, inpkts = pcap_dispatch(ld->pcap_h, 1, capture_loop_packet_cb, (u_char *)ld); if (inpkts < 0) { - ld->pcap_err = TRUE; + if (inpkts == -1) { + /* Error, rather than pcap_breakloop(). */ + ld->pcap_err = TRUE; + } ld->go = FALSE; /* error or pcap_breakloop() - stop capturing */ } } else { @@ -1591,6 +2635,29 @@ capture_loop_dispatch(capture_options *capture_opts _U_, loop_data *ld, return ld->packet_count - packet_count_before; } +#ifdef _WIN32 +/* Isolate the Universally Unique Identifier from the interface. Basically, we + * want to grab only the characters between the '{' and '}' delimiters. + * + * Returns a GString that must be freed with g_string_free(). */ +static GString *isolate_uuid(const char *iface) +{ + gchar *ptr; + GString *gstr; + + ptr = strchr(iface, '{'); + if (ptr == NULL) + return g_string_new(iface); + gstr = g_string_new(ptr + 1); + + ptr = strchr(gstr->str, '}'); + if (ptr == NULL) + return gstr; + + gstr = g_string_truncate(gstr, ptr - gstr->str); + return gstr; +} +#endif /* open the output file (temporary/specified name/ringbuffer/named pipe/stdout) */ /* Returns TRUE if the file opened successfully, FALSE otherwise. */ @@ -1598,10 +2665,13 @@ static gboolean capture_loop_open_output(capture_options *capture_opts, int *save_file_fd, char *errmsg, int errmsg_len) { - char tmpname[128+1]; + char *tmpname; gchar *capfile_name; + gchar *prefix; gboolean is_tempfile; - +#ifndef _WIN32 + int ret; +#endif g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_open_output: %s", (capture_opts->save_file) ? capture_opts->save_file : ""); @@ -1635,7 +2705,8 @@ capture_loop_open_output(capture_options *capture_opts, int *save_file_fd, 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); + (capture_opts->has_ring_num_files) ? capture_opts->ring_num_files : 0, + capture_opts->group_read_access); /* we need the ringbuf name */ if(*save_file_fd != -1) { @@ -1645,13 +2716,23 @@ capture_loop_open_output(capture_options *capture_opts, int *save_file_fd, } else { /* Try to open/create the specified file for use as a capture buffer. */ *save_file_fd = ws_open(capfile_name, O_RDWR|O_BINARY|O_TRUNC|O_CREAT, - 0600); + (capture_opts->group_read_access) ? 0640 : 0600); } } is_tempfile = FALSE; } else { /* Choose a random name for the temporary capture buffer */ - *save_file_fd = create_tempfile(tmpname, sizeof tmpname, "ether"); +#ifdef _WIN32 + GString *iface; + + iface = isolate_uuid(capture_opts->iface); + prefix = g_strconcat("wireshark_", g_basename(iface->str), NULL); + g_string_free(iface, TRUE); +#else + prefix = g_strconcat("wireshark_", g_basename(capture_opts->iface), NULL); +#endif + *save_file_fd = create_tempfile(&tmpname, prefix); + g_free(prefix); capfile_name = g_strdup(tmpname); is_tempfile = TRUE; } @@ -1683,114 +2764,164 @@ capture_loop_open_output(capture_options *capture_opts, int *save_file_fd, /* 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); + ret = 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 work of handling either the file size or file duration capture + conditions being reached, and switching files or stopping. */ +static gboolean +do_file_switch_or_stop(capture_options *capture_opts, + condition *cnd_autostop_files, + condition *cnd_autostop_size, + condition *cnd_file_duration) +{ + if (capture_opts->multi_files_on) { + if (cnd_autostop_files != NULL && + cnd_eval(cnd_autostop_files, ++global_ld.autostop_files)) { + /* no files left: stop here */ + global_ld.go = FALSE; + return FALSE; + } + + /* Switch to the next ringbuffer file */ + if (ringbuf_switch_file(&global_ld.pdh, &capture_opts->save_file, + &global_ld.save_file_fd, &global_ld.err)) { + gboolean successful; +#ifndef _WIN32 + int ret; +#endif + + /* File switch succeeded: reset the conditions */ + global_ld.bytes_written = 0; + if (capture_opts->use_pcapng) { + char appname[100]; + + g_snprintf(appname, sizeof(appname), "Dumpcap " VERSION "%s", wireshark_svnversion); + successful = libpcap_write_session_header_block(global_ld.pdh, appname, &global_ld.bytes_written, &global_ld.err) && + libpcap_write_interface_description_block(global_ld.pdh, capture_opts->iface, capture_opts->cfilter, global_ld.linktype, global_ld.file_snaplen, &global_ld.bytes_written, &global_ld.err); + } else { + successful = libpcap_write_file_header(global_ld.pdh, global_ld.linktype, global_ld.file_snaplen, + &global_ld.bytes_written, &global_ld.err); + } + if (!successful) { + fclose(global_ld.pdh); + global_ld.pdh = NULL; + global_ld.go = FALSE; + return FALSE; + } + if(cnd_autostop_size) + cnd_reset(cnd_autostop_size); + if(cnd_file_duration) + cnd_reset(cnd_file_duration); + libpcap_dump_flush(global_ld.pdh, NULL); + if (!quiet) + report_packet_count(global_ld.inpkts_to_sync_pipe); + global_ld.inpkts_to_sync_pipe = 0; + report_new_capture_file(capture_opts->save_file); + +#ifndef _WIN32 + ret = fchown(global_ld.save_file_fd, capture_opts->owner, capture_opts->group); +#endif + } else { + /* File switch failed: stop here */ + global_ld.go = FALSE; + return FALSE; + } + } else { + /* single file, stop now */ + global_ld.go = FALSE; + return FALSE; + } + return TRUE; +} + /* 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; + global_ld.go = TRUE; + global_ld.packet_count = 0; +#ifdef SIGINFO + global_ld.report_packet_count = FALSE; +#endif if (capture_opts->has_autostop_packets) - ld.packet_max = capture_opts->autostop_packets; + global_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; + global_ld.packet_max = 0; /* no limit */ + global_ld.inpkts_to_sync_pipe = 0; + global_ld.err = 0; /* no error seen yet */ + global_ld.wtap_linktype = WTAP_ENCAP_UNKNOWN; + global_ld.pcap_err = FALSE; + global_ld.from_cap_pipe = FALSE; + global_ld.pdh = NULL; +#ifndef _WIN32 + global_ld.cap_pipe_fd = -1; +#else + global_ld.cap_pipe_h = INVALID_HANDLE_VALUE; +#endif #ifdef MUST_DO_SELECT - ld.pcap_fd = 0; + global_ld.pcap_fd = 0; #endif + global_ld.autostop_files = 0; + global_ld.save_file_fd = -1; /* 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), + if (!capture_loop_open_input(capture_opts, &global_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)) { + switch (capture_loop_init_filter(global_ld.pcap_h, global_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)); + g_snprintf(errmsg, sizeof(errmsg), "%s", pcap_geterr(global_ld.pcap_h)); goto error; case INITFILTER_OTHER_ERROR: g_snprintf(errmsg, sizeof(errmsg), "Can't install filter (%s).", - pcap_geterr(ld.pcap_h)); + pcap_geterr(global_ld.pcap_h)); g_snprintf(secondary_errmsg, sizeof(secondary_errmsg), "%s", please_report); goto error; } @@ -1798,12 +2929,14 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct /* 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))) { + if (!capture_loop_open_output(capture_opts, &global_ld.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))) { + if (!capture_loop_init_output(capture_opts, &global_ld, errmsg, + sizeof(errmsg))) { goto error; } @@ -1820,7 +2953,7 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct 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); + libpcap_dump_flush(global_ld.pdh, NULL); report_new_capture_file(capture_opts->save_file); } @@ -1852,57 +2985,40 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct /* WOW, everything is prepared! */ /* please fasten your seat belts, we will enter now the actual capture loop */ - while (ld.go) { + while (global_ld.go) { /* dispatch incoming packets */ - inpkts = capture_loop_dispatch(capture_opts, &ld, errmsg, sizeof(errmsg)); + inpkts = capture_loop_dispatch(capture_opts, &global_ld, errmsg, + sizeof(errmsg)); + +#ifdef SIGINFO + /* Were we asked to print packet counts by the SIGINFO handler? */ + if (global_ld.report_packet_count) { + fprintf(stderr, "%u packet%s captured\n", global_ld.packet_count, + plurality(global_ld.packet_count, "", "s")); + global_ld.report_packet_count = FALSE; + } +#endif #ifdef _WIN32 /* any news from our parent (signal pipe)? -> just stop the capture */ if (!signal_pipe_check_running()) { - ld.go = FALSE; + global_ld.go = FALSE; } #endif if (inpkts > 0) { - inpkts_to_sync_pipe += inpkts; + global_ld.inpkts_to_sync_pipe += inpkts; /* check capture size condition */ if (cnd_autostop_size != NULL && - cnd_eval(cnd_autostop_size, (guint32)ld.bytes_written)){ + cnd_eval(cnd_autostop_size, (guint32)global_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; + if (!do_file_switch_or_stop(capture_opts, cnd_autostop_files, + cnd_autostop_size, cnd_file_duration)) continue; - } } /* cnd_autostop_size */ if (capture_opts->output_to_pipe) { - libpcap_dump_flush(ld.pdh, NULL); + libpcap_dump_flush(global_ld.pdh, NULL); } } /* inpkts */ @@ -1922,60 +3038,35 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct }*/ /* Let the parent process know. */ - if (inpkts_to_sync_pipe) { + if (global_ld.inpkts_to_sync_pipe) { /* do sync here */ - libpcap_dump_flush(ld.pdh, NULL); + libpcap_dump_flush(global_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); + /* Send our parent a message saying we've written out + "global_ld.inpkts_to_sync_pipe" packets to the capture file. */ + if (!quiet) + report_packet_count(global_ld.inpkts_to_sync_pipe); - inpkts_to_sync_pipe = 0; + global_ld.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; + global_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; + if (!do_file_switch_or_stop(capture_opts, cnd_autostop_files, + cnd_autostop_size, cnd_file_duration)) continue; - } } /* cnd_file_duration */ } - } /* while (ld.go) */ + } /* while (global_ld.go) */ g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Capture loop stopping ..."); @@ -1990,7 +3081,7 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct cnd_delete(cnd_autostop_duration); /* did we had a pcap (input) error? */ - if (ld.pcap_err) { + if (global_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 @@ -2005,10 +3096,11 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct These should *not* be reported to the Wireshark developers. */ char *cap_err_str; - cap_err_str = pcap_geterr(ld.pcap_h); + cap_err_str = pcap_geterr(global_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) { + strcmp(cap_err_str, "read: I/O error") == 0 || + strcmp(cap_err_str, "read error: PacketReceivePacket failed") == 0) { report_capture_error("The network adapter on which the capture was being done " "is no longer running; the capture has stopped.", ""); @@ -2018,30 +3110,31 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct report_capture_error(errmsg, please_report); } } - else if (ld.from_cap_pipe && ld.cap_pipe_err == PIPERR) + else if (global_ld.from_cap_pipe && global_ld.cap_pipe_err == PIPERR) report_capture_error(errmsg, ""); /* did we had an error while capturing? */ - if (ld.err == 0) { + if (global_ld.err == 0) { write_ok = TRUE; } else { - capture_loop_get_errmsg(errmsg, sizeof(errmsg), capture_opts->save_file, ld.err, - FALSE); + capture_loop_get_errmsg(errmsg, sizeof(errmsg), capture_opts->save_file, + global_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); + close_ok = capture_loop_close_output(capture_opts, &global_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(global_ld.inpkts_to_sync_pipe) { + if (!quiet) + report_packet_count(global_ld.inpkts_to_sync_pipe); + global_ld.inpkts_to_sync_pipe = 0; } /* If we've displayed a message about a write error, there's no point @@ -2062,24 +3155,24 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct */ /* get packet drop statistics from pcap */ - if(ld.pcap_h != NULL) { - g_assert(!ld.from_cap_pipe); + if(global_ld.pcap_h != NULL) { + g_assert(!global_ld.from_cap_pipe); /* Get the capture statistics, so we know how many packets were dropped. */ - if (pcap_stats(ld.pcap_h, stats) >= 0) { + if (pcap_stats(global_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)); + pcap_geterr(global_ld.pcap_h)); report_capture_error(errmsg, please_report); } } /* close the input file (pcap or capture pipe) */ - capture_loop_close_input(&ld); + capture_loop_close_input(&global_ld); g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Capture loop stopped!"); @@ -2093,8 +3186,8 @@ error: } 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) { - ws_close(save_file_fd); + if(global_ld.save_file_fd != -1) { + ws_close(global_ld.save_file_fd); } /* We couldn't even start the capture, so get rid of the capture @@ -2111,7 +3204,7 @@ error: report_capture_error(errmsg, secondary_errmsg); /* close the input file (pcap or cap_pipe) */ - capture_loop_close_input(&ld); + capture_loop_close_input(&global_ld); g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Capture loop stopped with error"); @@ -2122,10 +3215,10 @@ error: static void capture_loop_stop(void) { #ifdef HAVE_PCAP_BREAKLOOP - if(ld.pcap_h != NULL) - pcap_breakloop(ld.pcap_h); + if(global_ld.pcap_h != NULL) + pcap_breakloop(global_ld.pcap_h); #endif - ld.go = FALSE; + global_ld.go = FALSE; } @@ -2193,16 +3286,9 @@ static void capture_loop_packet_cb(u_char *user, const struct pcap_pkthdr *phdr, const u_char *pd) { - loop_data *ld = (void *) user; + loop_data *ld = (loop_data *) (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. */ @@ -2210,14 +3296,26 @@ capture_loop_packet_cb(u_char *user, const struct pcap_pkthdr *phdr, return; if (ld->pdh) { + gboolean successful; /* We're supposed to write the packet to a file; do so. If this fails, set "ld->go" to FALSE, to stop the capture, and set "ld->err" to the error. */ - if (!libpcap_write_packet(ld->pdh, phdr, pd, &ld->bytes_written, &err)) { + if (global_capture_opts.use_pcapng) { + successful = libpcap_write_enhanced_packet_block(ld->pdh, phdr, 0, pd, &ld->bytes_written, &err); + } else { + successful = libpcap_write_packet(ld->pdh, phdr, pd, &ld->bytes_written, &err); + } + if (!successful) { ld->go = FALSE; ld->err = err; - } else + } else { ld->packet_count++; + /* 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; + } + } } } @@ -2227,13 +3325,11 @@ int main(int argc, char *argv[]) { int opt; - extern char *optarg; gboolean arg_error = FALSE; #ifdef _WIN32 WSADATA wsaData; -#endif /* _WIN32 */ -#ifndef _WIN32 +#else struct sigaction action, oldaction; #endif @@ -2243,25 +3339,60 @@ main(int argc, char *argv[]) GLogLevelFlags log_flags; gboolean list_interfaces = FALSE; gboolean list_link_layer_types = FALSE; +#ifdef HAVE_BPF_IMAGE + gboolean print_bpf_code = FALSE; +#endif gboolean machine_readable = FALSE; gboolean print_statistics = FALSE; int status, run_once_args = 0; gint i; +#if defined(__APPLE__) && defined(__LP64__) + struct utsname osinfo; +#endif + +#ifdef _WIN32 + /* + * Initialize our DLL search path. MUST be called before LoadLibrary + * or g_module_open. + */ + ws_init_dll_search_path(); +#endif #ifdef HAVE_PCAP_REMOTE -#define OPTSTRING_INIT "a:A:b:c:Df:hi:Lm:MprSs:uvw:y:Z:" +#define OPTSTRING_A "A:" +#define OPTSTRING_r "r" +#define OPTSTRING_u "u" #else -#define OPTSTRING_INIT "a:b:c:Df:hi:LMpSs:vw:y:Z:" +#define OPTSTRING_A "" +#define OPTSTRING_r "" +#define OPTSTRING_u "" #endif -#ifdef _WIN32 -#define OPTSTRING_WIN32 "B:" +#ifdef HAVE_PCAP_SETSAMPLING +#define OPTSTRING_m "m:" #else -#define OPTSTRING_WIN32 "" -#endif /* _WIN32 */ +#define OPTSTRING_m "" +#endif + +#if defined(_WIN32) || defined(HAVE_PCAP_CREATE) +#define OPTSTRING_B "B:" +#else +#define OPTSTRING_B "" +#endif /* _WIN32 or HAVE_PCAP_CREATE */ + +#ifdef HAVE_PCAP_CREATE +#define OPTSTRING_I "I" +#else +#define OPTSTRING_I "" +#endif + +#ifdef HAVE_BPF_IMAGE +#define OPTSTRING_d "d" +#else +#define OPTSTRING_d "" +#endif - char optstring[sizeof(OPTSTRING_INIT) + sizeof(OPTSTRING_WIN32) - 1] = - OPTSTRING_INIT OPTSTRING_WIN32; +#define OPTSTRING "a:" OPTSTRING_A "b:" OPTSTRING_B "c:" OPTSTRING_d "Df:ghi:" OPTSTRING_I "L" OPTSTRING_m "Mnpq" OPTSTRING_r "Ss:" OPTSTRING_u "vw:y:Z:" #ifdef DEBUG_CHILD_DUMPCAP if ((debug_log = ws_fopen("dumpcap_debug_log.tmp","w")) == NULL) { @@ -2270,24 +3401,58 @@ main(int argc, char *argv[]) } #endif - /* Determine if dumpcap is being requested to run in a special */ - /* capture_child mode by going thru the command line args to see if */ - /* a -Z is present. (-Z is a hidden option). */ - /* The primary result of running in capture_child mode is that */ - /* all messages sent out on stderr are in a special type/len/string */ - /* format to allow message processing by type. */ - /* These messages include various 'status' messages which are sent */ - /* when an actual capture is in progress. Capture_child mode */ - /* would normally be requested by a parent process which invokes */ - /* dumpcap and obtains dumpcap stderr output via a pipe to which */ - /* dumpcap stderr has been redirected. */ - /* Capture_child mode needs to be determined immediately upon */ - /* startup so that any messages generated by dumpcap in this mode */ - /* (eg: during initialization) will be formatted properly. */ +#if defined(__APPLE__) && defined(__LP64__) + /* + * Is this Mac OS X 10.6.0, 10.6.1, 10.6.3, or 10.6.4? If so, we need + * a bug workaround - timeouts less than 1 second don't work with libpcap + * in 64-bit code. (The bug was introduced in 10.6, fixed in 10.6.2, + * re-introduced in 10.6.3, not fixed in 10.6.4, and fixed in 10.6.5. + * The problem is extremely unlikely to be reintroduced in a future + * release.) + */ + if (uname(&osinfo) == 0) { + /* + * Mac OS X 10.x uses Darwin {x+4}.0.0. Mac OS X 10.x.y uses Darwin + * {x+4}.y.0 (except that 10.6.1 appears to have a uname version + * number of 10.0.0, not 10.1.0 - go figure). + */ + if (strcmp(osinfo.release, "10.0.0") == 0 || /* 10.6, 10.6.1 */ + strcmp(osinfo.release, "10.3.0") == 0 || /* 10.6.3 */ + strcmp(osinfo.release, "10.4.0") == 0) /* 10.6.4 */ + need_timeout_workaround = TRUE; + } +#endif + + /* + * Determine if dumpcap is being requested to run in a special + * capture_child mode by going thru the command line args to see if + * a -Z is present. (-Z is a hidden option). + * + * The primary result of running in capture_child mode is that + * all messages sent out on stderr are in a special type/len/string + * format to allow message processing by type. These messages include + * error messages if dumpcap fails to start the operation it was + * requested to do, as well as various "status" messages which are sent + * when an actual capture is in progress, and a "success" message sent + * if dumpcap was requested to perform an operation other than a + * capture. + * + * Capture_child mode would normally be requested by a parent process + * which invokes dumpcap and obtains dumpcap stderr output via a pipe + * to which dumpcap stderr has been redirected. It might also have + * another pipe to obtain dumpcap stdout output; for operations other + * than a capture, that information is formatted specially for easier + * parsing by the parent process. + * + * Capture_child mode needs to be determined immediately upon + * startup so that any messages generated by dumpcap in this mode + * (eg: during initialization) will be formatted properly. + */ for (i=1; isnaplen = WTAP_MAX_PACKET_SIZE; + global_capture_opts.snaplen = WTAP_MAX_PACKET_SIZE; /* We always save to a file - if no file was specified, we save to a temporary file. */ - capture_opts->saving_to_file = TRUE; - capture_opts->has_ring_num_files = TRUE; + global_capture_opts.saving_to_file = TRUE; + global_capture_opts.has_ring_num_files = TRUE; /* Now get our args */ - while ((opt = getopt(argc, argv, optstring)) != -1) { + while ((opt = getopt(argc, argv, OPTSTRING)) != -1) { switch (opt) { case 'h': /* Print help and exit */ print_usage(TRUE); @@ -2457,7 +3645,7 @@ main(int argc, char *argv[]) GString *runtime_info_str; /* Assemble the compile-time version information string */ comp_info_str = g_string_new("Compiled "); - get_compiled_version_info(comp_info_str, NULL); + get_compiled_version_info(comp_info_str, NULL, NULL); /* Assemble the run-time version information string */ runtime_info_str = g_string_new("Running "); @@ -2474,9 +3662,11 @@ main(int argc, char *argv[]) case 'c': /* Capture x packets */ case 'f': /* capture filter */ case 'i': /* Use interface x */ + case 'n': /* Use pcapng format */ case 'p': /* Don't capture in promiscuous mode */ case 's': /* Set the snapshot (capture) length */ case 'w': /* Write to capture file x */ + case 'g': /* enable group read accesson file(s) */ case 'y': /* Set the pcap data link type */ #ifdef HAVE_PCAP_REMOTE case 'u': /* Use UDP for data transfer */ @@ -2486,10 +3676,13 @@ main(int argc, char *argv[]) #ifdef HAVE_PCAP_SETSAMPLING case 'm': /* Sampling */ #endif -#ifdef _WIN32 +#if defined(_WIN32) || defined(HAVE_PCAP_CREATE) case 'B': /* Buffer size */ -#endif /* _WIN32 */ - status = capture_opts_add_opt(capture_opts, opt, optarg, &start_capture); +#endif /* _WIN32 or HAVE_PCAP_CREATE */ +#ifdef HAVE_PCAP_CREATE + case 'I': /* Monitor mode */ +#endif + status = capture_opts_add_opt(&global_capture_opts, opt, optarg, &start_capture); if(status != 0) { exit_main(status); } @@ -2519,6 +3712,10 @@ main(int argc, char *argv[]) #endif break; + case 'q': /* Quiet */ + quiet = TRUE; + break; + /*** all non capture option specific ***/ case 'D': /* Print a list of capture devices and exit */ list_interfaces = TRUE; @@ -2528,11 +3725,17 @@ main(int argc, char *argv[]) list_link_layer_types = TRUE; run_once_args++; break; +#ifdef HAVE_BPF_IMAGE + case 'd': /* Print BPF code for capture filter and exit */ + print_bpf_code = TRUE; + run_once_args++; + break; +#endif case 'S': /* Print interface statistics once a second */ print_statistics = TRUE; run_once_args++; break; - case 'M': /* For -D and -L, print machine-readable output */ + case 'M': /* For -D, -L, and -S, print machine-readable output */ machine_readable = TRUE; break; default: @@ -2568,60 +3771,141 @@ main(int argc, char *argv[]) if (run_once_args > 1) { cmdarg_err("Only one of -D, -L, or -S may be supplied."); exit_main(1); - } else if (list_link_layer_types) { - /* We're supposed to list the link-layer types for an interface; - did the user also specify a capture file to be read? */ - /* No - did they specify a ring buffer option? */ - if (capture_opts->multi_files_on) { + } else if (run_once_args == 1) { + /* We're supposed to print some information, rather than + to capture traffic; did they specify a ring buffer option? */ + if (global_capture_opts.multi_files_on) { cmdarg_err("Ring buffer requested, but a capture isn't being done."); exit_main(1); } } else { - /* No - was the ring buffer option specified and, if so, does it make - sense? */ - if (capture_opts->multi_files_on) { + /* We're supposed to capture traffic; was the ring buffer option + specified and, if so, does it make sense? */ + if (global_capture_opts.multi_files_on) { /* Ring buffer works only under certain conditions: a) ring buffer does not work with temporary files; b) it makes no sense to enable the ring buffer if the maximum file size is set to "infinite". */ - if (capture_opts->save_file == NULL) { + if (global_capture_opts.save_file == NULL) { cmdarg_err("Ring buffer requested, but capture isn't being saved to a permanent file."); - capture_opts->multi_files_on = FALSE; + global_capture_opts.multi_files_on = FALSE; } - if (!capture_opts->has_autostop_filesize && !capture_opts->has_file_duration) { + if (!global_capture_opts.has_autostop_filesize && !global_capture_opts.has_file_duration) { cmdarg_err("Ring buffer requested, but no maximum capture file size or duration were specified."); /* XXX - this must be redesigned as the conditions changed */ -/* capture_opts->multi_files_on = FALSE;*/ +/* global_capture_opts.multi_files_on = FALSE;*/ } } } - if (capture_opts_trim_iface(capture_opts, NULL) == FALSE) { + /* + * "-D" requires no interface to be selected; it's supposed to list + * all interfaces. + */ + if (list_interfaces) { + /* Get the list of interfaces */ + GList *if_list; + int err; + gchar *err_str; + + if_list = capture_interface_list(&err, &err_str); + if (if_list == NULL) { + switch (err) { + case CANT_GET_INTERFACE_LIST: + cmdarg_err("%s", err_str); + g_free(err_str); + exit_main(2); + break; + + case NO_INTERFACES_FOUND: + /* + * If we're being run by another program, just give them + * an empty list of interfaces, don't report this as + * an error; that lets them decide whether to report + * this as an error or not. + */ + if (!machine_readable) { + cmdarg_err("There are no interfaces on which a capture can be done"); + exit_main(2); + } + break; + } + } + + if (machine_readable) /* tab-separated values to stdout */ + print_machine_readable_interfaces(if_list); + else + capture_opts_print_interfaces(if_list); + free_interface_list(if_list); + exit_main(0); + } + + /* + * "-S" requires no interface to be selected; it gives statistics + * for all interfaces. + */ + if (print_statistics) { + status = print_statistics_loop(machine_readable); + exit_main(status); + } + + /* + * "-L", "-d", and capturing act on a particular interface, so we have to + * have an interface; if none was specified, pick a default. + */ + if (capture_opts_trim_iface(&global_capture_opts, NULL) == FALSE) { /* cmdarg_err() already called .... */ exit_main(1); } /* Let the user know what interface was chosen. */ /* get_interface_descriptive_name() is not available! */ - g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Interface: %s\n", capture_opts->iface); + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Interface: %s\n", global_capture_opts.iface); + + if (list_link_layer_types) { + /* Get the list of link-layer types for the capture device. */ + if_capabilities_t *caps; + gchar *err_str; + + caps = get_if_capabilities(global_capture_opts.iface, + global_capture_opts.monitor_mode, &err_str); + if (caps == NULL) { + cmdarg_err("The capabilities of the capture device \"%s\" could not be obtained (%s).\n" + "Please check to make sure you have sufficient permissions, and that\n" + "you have the proper interface or pipe specified.", global_capture_opts.iface, err_str); + g_free(err_str); + exit_main(2); + } + if (caps->data_link_types == NULL) { + cmdarg_err("The capture device \"%s\" has no data link types.", global_capture_opts.iface); + exit_main(2); + } + if (machine_readable) /* tab-separated values to stdout */ + print_machine_readable_if_capabilities(caps); + else + capture_opts_print_if_capabilities(caps, + global_capture_opts.monitor_mode); + free_if_capabilities(caps); + exit_main(0); + } - if (list_interfaces) { - status = capture_opts_list_interfaces(machine_readable); - exit_main(status); - } else if (list_link_layer_types) { - status = capture_opts_list_link_layer_types(capture_opts, machine_readable); - exit_main(status); - } else if (print_statistics) { - status = capture_opts_print_statistics(machine_readable); - exit_main(status); + /* We're supposed to do a capture, or print the BPF code for a filter. + Process the snapshot length, as that affects the generated BPF code. */ + capture_opts_trim_snaplen(&global_capture_opts, MIN_PACKET_SIZE); + +#ifdef HAVE_BPF_IMAGE + if (print_bpf_code) { + show_filter_code(&global_capture_opts); + exit_main(0); } +#endif - capture_opts_trim_snaplen(capture_opts, MIN_PACKET_SIZE); - capture_opts_trim_ring_num_files(capture_opts); + /* We're supposed to do a capture. Process the ring buffer arguments. */ + capture_opts_trim_ring_num_files(&global_capture_opts); /* Now start the capture. */ - if(capture_loop_start(capture_opts, &stats_known, &stats) == TRUE) { + if(capture_loop_start(&global_capture_opts, &stats_known, &stats) == TRUE) { /* capture ok */ exit_main(0); } else { @@ -2720,7 +4004,7 @@ console_log_handler(const char *log_domain, GLogLevelFlags log_level, /* indication report routines */ -void +static void report_packet_count(int packet_count) { char tmp[SP_DECISIZE+1+1]; @@ -2745,9 +4029,30 @@ report_new_capture_file(const char *filename) g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "File: %s", filename); pipe_write_block(2, SP_FILE, filename); } else { +#ifdef SIGINFO + /* + * Prevent a SIGINFO handler from writing to the standard error + * while we're doing so; instead, have it just set a flag telling + * us to print that information when we're done. + */ + infodelay = TRUE; +#endif /* SIGINFO */ fprintf(stderr, "File: %s\n", filename); /* stderr could be line buffered */ fflush(stderr); + +#ifdef SIGINFO + /* + * Allow SIGINFO handlers to write. + */ + infodelay = FALSE; + + /* + * If a SIGINFO handler asked us to write out capture counts, do so. + */ + if (infoprint) + report_counts(); +#endif /* SIGINFO */ } } @@ -2777,16 +4082,18 @@ report_capture_error(const char *error_msg, const char *secondary_error_msg) "Secondary Error: %s", secondary_error_msg); sync_pipe_errmsg_to_parent(2, error_msg, secondary_error_msg); } else { - fprintf(stderr, "%s\n%s\n", error_msg, secondary_error_msg); + fprintf(stderr, "%s\n", error_msg); + if (secondary_error_msg[0] != '\0') + fprintf(stderr, "%s\n", secondary_error_msg); } } void -report_packet_drops(int drops) +report_packet_drops(guint32 drops) { char tmp[SP_DECISIZE+1+1]; - g_snprintf(tmp, sizeof(tmp), "%d", drops); + g_snprintf(tmp, sizeof(tmp), "%u", drops); if(capture_child) { g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Packets dropped: %s", tmp); @@ -2846,3 +4153,16 @@ signal_pipe_check_running(void) } } #endif + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab + * :indentSize=4:tabSize=8:noTabs=true: + */