From Colin O'Flynn:
[obnox/wireshark/wip.git] / dumpcap.c
index 49a5fa655e54bf13fd846356a0bacfb5610a0855..21e342c1ffb33067d5881ea7c431eefb2404bcf7 100644 (file)
--- a/dumpcap.c
+++ b/dumpcap.c
@@ -79,6 +79,9 @@
 #include "version_info.h"
 
 #include "capture-pcap-util.h"
+#ifdef _WIN32
+#include "capture-wpcap.h"
+#endif /* _WIN32 */
 
 #include "pcapio.h"
 
@@ -235,7 +238,7 @@ typedef struct _pcap_options {
 #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 */
-#if defined(USE_THREADS) && defined(_WIN32)
+#if defined(_WIN32)
     char *         cap_pipe_buf;          /* Pointer to the data buffer we read into */
 #endif
     int            cap_pipe_bytes_to_read;/* Used by cap_pipe_dispatch */
@@ -247,7 +250,7 @@ typedef struct _pcap_options {
         STATE_READ_DATA
     } cap_pipe_state;
     enum { PIPOK, PIPEOF, PIPERR, PIPNEXIST } cap_pipe_err;
-#if defined(USE_THREADS) && defined(_WIN32)
+#if defined(_WIN32)
     GMutex *cap_pipe_read_mtx;
     GAsyncQueue *cap_pipe_pending_q, *cap_pipe_done_q;
 #endif
@@ -318,7 +321,7 @@ static gboolean need_timeout_workaround;
  * WaitForSingleObject. If it's less than 1000 WaitForSingleObject
  * will return immediately.
  */
-#if defined(USE_THREADS) && defined(_WIN32)
+#if defined(_WIN32)
 #define PIPE_READ_TIMEOUT   100000
 #else
 #define PIPE_READ_TIMEOUT   250000
@@ -361,7 +364,7 @@ print_usage(gboolean print_ver)
         output = stdout;
         fprintf(output,
                 "Dumpcap " VERSION "%s\n"
-                "Capture network packets and dump them into a libpcap file.\n"
+                "Capture network packets and dump them into a pcapng file.\n"
                 "See http://www.wireshark.org for more information.\n",
                 wireshark_svnversion);
     } else {
@@ -386,11 +389,11 @@ print_usage(gboolean print_ver)
 #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, "  -S                       print statistics for each interface once per second\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");
+    fprintf(output, "RPCAP options:\n");
     fprintf(output, "  -r                       don't ignore own RPCAP traffic in capture\n");
     fprintf(output, "  -u                       use UDP for RPCAP data transfer\n");
     fprintf(output, "  -A <user>:<password>     use RPCAP password authentication\n");
@@ -412,18 +415,17 @@ print_usage(gboolean print_ver)
     fprintf(output, "  -b <ringbuffer opt.> ... duration:NUM - switch to next file after NUM secs\n");
     fprintf(output, "                           filesize:NUM - switch to next file after NUM KB\n");
     fprintf(output, "                              files:NUM - ringbuffer: replace after NUM files\n");
-    fprintf(output, "  -n                       use pcapng format instead of pcap\n");
-    /*fprintf(output, "\n");*/
+    fprintf(output, "  -n                       use pcapng format instead of pcap (default)\n");
+    fprintf(output, "  -P                       use libpcap format instead of pcapng\n");
+    fprintf(output, "\n");
     fprintf(output, "Miscellaneous:\n");
-#ifdef USE_THREADS
     fprintf(output, "  -t                       use a separate thread per interface\n");
-#endif
     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");
-    fprintf(output, "Example: dumpcap -i eth0 -a duration:60 -w output.pcap\n");
-    fprintf(output, "\"Capture network packets from interface eth0 until 60s passed into output.pcap\"\n");
+    fprintf(output, "Example: dumpcap -i eth0 -a duration:60 -w output.pcapng\n");
+    fprintf(output, "\"Capture network packets from interface eth0 until 60s passed into output.pcapng\"\n");
     fprintf(output, "\n");
     fprintf(output, "Use Ctrl-C to stop capturing at any time.\n");
 }
@@ -678,21 +680,40 @@ get_capture_device_open_failure_messages(const char *open_err_str,
         libpcap_warn = "";
     g_snprintf(errmsg, (gulong) errmsg_len,
                "The capture session could not be initiated (%s).", open_err_str);
-#ifndef _WIN32
+#ifdef _WIN32
+    if (!has_wpcap) {
+      g_snprintf(secondary_errmsg, (gulong) secondary_errmsg_len,
+                 "\n"
+                 "In order to capture packets, WinPcap must be installed; see\n"
+                 "\n"
+                 "        http://www.winpcap.org/\n"
+                 "\n"
+                 "or the mirror at\n"
+                 "\n"
+                 "        http://www.mirrors.wiretapped.net/security/packet-capture/winpcap/\n"
+                 "\n"
+                 "or the mirror at\n"
+                 "\n"
+                 "        http://winpcap.cs.pu.edu.tw/\n"
+                 "\n"
+                 "for a downloadable version of WinPcap and for instructions on how to install\n"
+                 "WinPcap.");
+    } 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);
+    }
+#else
     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 */
 }
 
@@ -1281,6 +1302,7 @@ print_statistics_loop(gboolean machine_readable)
     if (if_list == NULL) {
         switch (err) {
         case CANT_GET_INTERFACE_LIST:
+        case DONT_HAVE_PCAP:
             cmdarg_err("%s", err_str);
             g_free(err_str);
             break;
@@ -1547,7 +1569,7 @@ cap_pipe_adjust_header(gboolean byte_swapped, struct pcap_hdr *hdr, struct pcapr
     }
 }
 
-#if defined(USE_THREADS) && defined(_WIN32)
+#if defined(_WIN32)
 /*
  * Thread function that reads from a pipe and pushes the data
  * to the main application thread.
@@ -1635,7 +1657,7 @@ static void *cap_pipe_read(void *arg)
 }
 #endif
 
-#if (!(defined(USE_THREADS) && defined(_WIN32))) || defined(MUST_DO_SELECT)
+#if !defined(_WIN32) || defined(MUST_DO_SELECT)
 /* Provide select() functionality for a single file descriptor
  * on UNIX/POSIX. Windows uses cap_pipe_read via a thread.
  *
@@ -1682,7 +1704,7 @@ cap_pipe_open_live(char *pipename,
     wchar_t *err_str;
 #endif
 #endif
-#if !(defined(USE_THREADS) && defined(_WIN32))
+#ifndef _WIN32
     int          sel_ret;
     unsigned int bytes_read;
 #endif
@@ -1851,7 +1873,7 @@ cap_pipe_open_live(char *pipename,
 
     pcap_opts->from_cap_pipe = TRUE;
 
-#if !(defined(USE_THREADS) && defined(_WIN32))
+#ifndef _WIN32
     /* read the pcap header */
     bytes_read = 0;
     while (bytes_read < sizeof magic) {
@@ -1926,7 +1948,7 @@ cap_pipe_open_live(char *pipename,
         goto error;
     }
 
-#if !(defined(USE_THREADS) && defined(_WIN32))
+#ifndef _WIN32
     /* Read the rest of the header */
     bytes_read = 0;
     while (bytes_read < sizeof(struct pcap_hdr)) {
@@ -2006,7 +2028,7 @@ cap_pipe_dispatch(loop_data *ld, pcap_options *pcap_opts, guchar *data, char *er
     struct pcap_pkthdr phdr;
     enum { PD_REC_HDR_READ, PD_DATA_READ, PD_PIPE_EOF, PD_PIPE_ERR,
            PD_ERR } result;
-#if defined(USE_THREADS) && defined(_WIN32)
+#ifdef _WIN32
     GTimeVal wait_time;
     gpointer q_status;
 #else
@@ -2023,7 +2045,7 @@ cap_pipe_dispatch(loop_data *ld, pcap_options *pcap_opts, guchar *data, char *er
     switch (pcap_opts->cap_pipe_state) {
 
     case STATE_EXPECT_REC_HDR:
-#if defined(USE_THREADS) && defined(_WIN32)
+#ifdef _WIN32
         if (g_mutex_trylock(pcap_opts->cap_pipe_read_mtx)) {
 #endif
 
@@ -2032,7 +2054,7 @@ cap_pipe_dispatch(loop_data *ld, pcap_options *pcap_opts, guchar *data, char *er
                 sizeof(struct pcaprec_modified_hdr) : sizeof(struct pcaprec_hdr);
             pcap_opts->cap_pipe_bytes_read = 0;
 
-#if defined(USE_THREADS) && defined(_WIN32)
+#ifdef _WIN32
             pcap_opts->cap_pipe_buf = (char *) &pcap_opts->cap_pipe_rechdr;
             g_async_queue_push(pcap_opts->cap_pipe_pending_q, pcap_opts->cap_pipe_buf);
             g_mutex_unlock(pcap_opts->cap_pipe_read_mtx);
@@ -2041,7 +2063,7 @@ cap_pipe_dispatch(loop_data *ld, pcap_options *pcap_opts, guchar *data, char *er
         /* Fall through */
 
     case STATE_READ_REC_HDR:
-#if !(defined(USE_THREADS) && defined(_WIN32))
+#ifndef _WIN32
         b = read(pcap_opts->cap_pipe_fd, ((char *)&pcap_opts->cap_pipe_rechdr)+pcap_opts->cap_pipe_bytes_read,
                  pcap_opts->cap_pipe_bytes_to_read - pcap_opts->cap_pipe_bytes_read);
         if (b <= 0) {
@@ -2073,7 +2095,7 @@ cap_pipe_dispatch(loop_data *ld, pcap_options *pcap_opts, guchar *data, char *er
         break;
 
     case STATE_EXPECT_DATA:
-#if defined(USE_THREADS) && defined(_WIN32)
+#ifdef _WIN32
         if (g_mutex_trylock(pcap_opts->cap_pipe_read_mtx)) {
 #endif
 
@@ -2081,7 +2103,7 @@ cap_pipe_dispatch(loop_data *ld, pcap_options *pcap_opts, guchar *data, char *er
             pcap_opts->cap_pipe_bytes_to_read = pcap_opts->cap_pipe_rechdr.hdr.incl_len;
             pcap_opts->cap_pipe_bytes_read = 0;
 
-#if defined(USE_THREADS) && defined(_WIN32)
+#ifdef _WIN32
             pcap_opts->cap_pipe_buf = (char *) data;
             g_async_queue_push(pcap_opts->cap_pipe_pending_q, pcap_opts->cap_pipe_buf);
             g_mutex_unlock(pcap_opts->cap_pipe_read_mtx);
@@ -2090,7 +2112,7 @@ cap_pipe_dispatch(loop_data *ld, pcap_options *pcap_opts, guchar *data, char *er
         /* Fall through */
 
     case STATE_READ_DATA:
-#if !(defined(USE_THREADS) && defined(_WIN32))
+#ifndef _WIN32
         b = read(pcap_opts->cap_pipe_fd, data+pcap_opts->cap_pipe_bytes_read,
                  pcap_opts->cap_pipe_bytes_to_read - pcap_opts->cap_pipe_bytes_read);
         if (b <= 0) {
@@ -2292,15 +2314,20 @@ capture_loop_open_input(capture_options *capture_opts, loop_data *ld,
 #endif
         pcap_opts->cap_pipe_modified = FALSE;
         pcap_opts->cap_pipe_byte_swapped = FALSE;
-#if defined(USE_THREADS) && defined(_WIN32)
+#ifdef _WIN32
         pcap_opts->cap_pipe_buf = NULL;
 #endif
         pcap_opts->cap_pipe_bytes_to_read = 0;
         pcap_opts->cap_pipe_bytes_read = 0;
         pcap_opts->cap_pipe_state = 0;
         pcap_opts->cap_pipe_err = PIPOK;
-#if defined(USE_THREADS) && defined(_WIN32)
+#ifdef _WIN32
+#if GLIB_CHECK_VERSION(2,31,0)
+        pcap_opts->cap_pipe_read_mtx = g_malloc(sizeof(GMutex));
+        g_mutex_init(pcap_opts->cap_pipe_read_mtx);
+#else
         pcap_opts->cap_pipe_read_mtx = g_mutex_new();
+#endif
         pcap_opts->cap_pipe_pending_q = g_async_queue_new();
         pcap_opts->cap_pipe_done_q = g_async_queue_new();
 #endif
@@ -2552,7 +2579,6 @@ capture_loop_init_output(capture_options *capture_opts, loop_data *ld, char *err
                                                                        &err);
             }
         } else {
-            interface_opts = g_array_index(capture_opts->ifaces, interface_options, 0);
             pcap_opts = g_array_index(ld->pcaps, pcap_options *, 0);
             if (pcap_opts->from_cap_pipe) {
                 pcap_opts->snaplen = pcap_opts->cap_pipe_hdr.snaplen;
@@ -2636,7 +2662,7 @@ capture_loop_dispatch(loop_data *ld,
     int       inpkts;
     gint      packet_count_before;
     guchar    pcap_data[WTAP_MAX_PACKET_SIZE];
-#if !(defined(USE_THREADS) && defined(_WIN32))
+#ifndef _WIN32
     int       sel_ret;
 #endif
 
@@ -2646,7 +2672,7 @@ capture_loop_dispatch(loop_data *ld,
 #ifdef LOG_CAPTURE_VERBOSE
         g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: from capture pipe");
 #endif
-#if !(defined(USE_THREADS) && defined(_WIN32))
+#ifndef _WIN32
         sel_ret = cap_pipe_select(pcap_opts->cap_pipe_fd);
         if (sel_ret <= 0) {
             if (sel_ret < 0 && errno != EINTR) {
@@ -2664,7 +2690,7 @@ capture_loop_dispatch(loop_data *ld,
             if (inpkts < 0) {
                 ld->go = FALSE;
             }
-#if !(defined(USE_THREADS) && defined(_WIN32))
+#ifndef _WIN32
         }
 #endif
     }
@@ -2983,7 +3009,6 @@ do_file_switch_or_stop(capture_options *capture_opts,
                                                                            &global_ld.err);
                 }
             } else {
-                interface_opts = g_array_index(capture_opts->ifaces, interface_options, 0);
                 pcap_opts = g_array_index(global_ld.pcaps, pcap_options *, 0);
                 successful = libpcap_write_file_header(global_ld.pdh, pcap_opts->linktype, pcap_opts->snaplen,
                                                        &global_ld.bytes_written, &global_ld.err);
@@ -3062,7 +3087,6 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
     interface_options interface_opts;
     guint i, error_index = 0;
 
-    interface_opts = capture_opts->default_options;
     *errmsg           = '\0';
     *secondary_errmsg = '\0';
 
@@ -3191,7 +3215,12 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
         pcap_queue_packets = 0;
         for (i = 0; i < global_ld.pcaps->len; i++) {
             pcap_opts = g_array_index(global_ld.pcaps, pcap_options *, i);
+#if GLIB_CHECK_VERSION(2,31,0)
+            /* XXX - Add an interface name here? */
+            pcap_opts->tid = g_thread_new("Capture read", pcap_read_handler, pcap_opts);
+#else
             pcap_opts->tid = g_thread_create(pcap_read_handler, pcap_opts, TRUE, NULL);
+#endif
         }
     }
     while (global_ld.go) {
@@ -3703,6 +3732,7 @@ main(int argc, char *argv[])
 #if defined(__APPLE__) && defined(__LP64__)
     struct utsname       osinfo;
 #endif
+    GString             *str;
 
 #ifdef _WIN32
     arg_list_utf_16to8(argc, argv);
@@ -3750,12 +3780,7 @@ main(int argc, char *argv[])
 #define OPTSTRING_d ""
 #endif
 
-#ifdef USE_THREADS
-#define OPTSTRING_t "t"
-#else
-#define OPTSTRING_t ""
-#endif
-#define OPTSTRING "a:" OPTSTRING_A "b:" OPTSTRING_B "c:" OPTSTRING_d "Df:ghi:" OPTSTRING_I "L" OPTSTRING_m "MnpPq" OPTSTRING_r "Ss:" OPTSTRING_t OPTSTRING_u "vw:y:Z:"
+#define OPTSTRING "a:" OPTSTRING_A "b:" OPTSTRING_B "c:" OPTSTRING_d "Df:ghi:" OPTSTRING_I "L" OPTSTRING_m "MnpPq" OPTSTRING_r "Ss:t" OPTSTRING_u "vw:y:Z:"
 
 #ifdef DEBUG_CHILD_DUMPCAP
     if ((debug_log = ws_fopen("dumpcap_debug_log.tmp","w")) == NULL) {
@@ -3855,11 +3880,11 @@ main(int argc, char *argv[])
     /* Initialize the pcaps list */
     global_ld.pcaps = g_array_new(FALSE, FALSE, sizeof(pcap_options *));
 
+#if !GLIB_CHECK_VERSION(2,31,0)
     /* Initialize the thread system */
-#ifdef USE_THREADS
-    if (!g_thread_supported())
-        g_thread_init(NULL);
+    g_thread_init(NULL);
 #endif
+
 #ifdef _WIN32
     /* Load wpcap if possible. Do this before collecting the run-time version information */
     load_wpcap();
@@ -4076,11 +4101,9 @@ main(int argc, char *argv[])
         case 'q':        /* Quiet */
             quiet = TRUE;
             break;
-#ifdef USE_THREADS
         case 't':
             use_threads = TRUE;
             break;
-#endif
             /*** all non capture option specific ***/
         case 'D':        /* Print a list of capture devices and exit */
             list_interfaces = TRUE;
@@ -4148,12 +4171,10 @@ main(int argc, char *argv[])
     } else {
         /* We're supposed to capture traffic; */
         /* Are we capturing on multiple interface? If so, use threads and pcapng. */
-#ifdef USE_THREADS
         if (global_capture_opts.ifaces->len > 1) {
             use_threads = TRUE;
             global_capture_opts.use_pcapng = TRUE;
         }
-#endif
         /* 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:
@@ -4188,6 +4209,7 @@ main(int argc, char *argv[])
         if (if_list == NULL) {
             switch (err) {
             case CANT_GET_INTERFACE_LIST:
+            case DONT_HAVE_PCAP:
                 cmdarg_err("%s", err_str);
                 g_free(err_str);
                 exit_main(2);
@@ -4236,11 +4258,41 @@ main(int argc, char *argv[])
 
     /* Let the user know what interfaces were chosen. */
     /* get_interface_descriptive_name() is not available! */
-    for (j = 0; j < global_capture_opts.ifaces->len; j++) {
-        interface_options interface_opts;
+    if (capture_child) {
+        for (j = 0; j < global_capture_opts.ifaces->len; j++) {
+            interface_options interface_opts;
 
-        interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, j);
-        g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Interface: %s", interface_opts.name);
+            interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, j);
+            g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Interface: %s\n",
+                  interface_opts.name);
+        }
+    } else {
+        str = g_string_new("");
+#ifdef _WIN32
+        if (global_capture_opts.ifaces->len < 2) {
+#else
+        if (global_capture_opts.ifaces->len < 4) {
+#endif
+            for (j = 0; j < global_capture_opts.ifaces->len; j++) {
+                interface_options interface_opts;
+
+                interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, j);
+                if (j > 0) {
+                    if (global_capture_opts.ifaces->len > 2) {
+                        g_string_append_printf(str, ",");
+                    }
+                    g_string_append_printf(str, " ");
+                    if (j == global_capture_opts.ifaces->len - 1) {
+                        g_string_append_printf(str, "and ");
+                    }
+                }
+                g_string_append_printf(str, "%s", interface_opts.name);
+            }
+        } else {
+            g_string_append_printf(str, "%u interfaces", global_capture_opts.ifaces->len);
+        }
+        fprintf(stderr, "Capturing on %s\n", str->str);
+        g_string_free(str, TRUE);
     }
 
     if (list_link_layer_types) {
@@ -4463,11 +4515,11 @@ report_cfilter_error(capture_options *capture_opts, guint i, const char *errmsg)
              * the error message below.
              */
             interface_opts = g_array_index(capture_opts->ifaces, interface_options, i);
-            fprintf(stderr,
-              "Invalid capture filter \"%s\" for interface %s!\n"
+            cmdarg_err(
+              "Invalid capture filter: \"%s\" for interface %s!\n"
               "\n"
               "That string isn't a valid capture filter (%s).\n"
-              "See the User's Guide for a description of the capture filter syntax.\n",
+              "See the User's Guide for a description of the capture filter syntax.",
               interface_opts.cfilter, interface_opts.name, errmsg);
         }
     }
@@ -4483,9 +4535,9 @@ 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", error_msg);
+        cmdarg_err("%s", error_msg);
         if (secondary_error_msg[0] != '\0')
-          fprintf(stderr, "%s\n", secondary_error_msg);
+          cmdarg_err_cont("%s", secondary_error_msg);
     }
 }
 
@@ -4570,6 +4622,6 @@ signal_pipe_check_running(void)
  * indent-tabs-mode: nil
  * End:
  *
- * vi: set shiftwidth=4 tabstop=8 expandtab
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
  * :indentSize=4:tabSize=8:noTabs=true:
  */