Add s1ap - needs more work, untested as no traces available yet.
[obnox/wireshark/wip.git] / capture.c
index f412e6c866a6478ea709995529c8ae95d2c0a488..b0efea16724d09c573c34e46c109b24158f106fd 100644 (file)
--- a/capture.c
+++ b/capture.c
 #include <fcntl.h>
 #endif
 
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>         /* needed to define AF_ values on UNIX */
+#endif
+
+#ifdef HAVE_WINSOCK2_H
+#include <winsock2.h>           /* needed to define AF_ values on Windows */
+#endif
+
+#ifdef NEED_INET_V6DEFS_H
+# include "inet_v6defs.h"
+#endif
+
 #include <signal.h>
 #include <errno.h>
 
@@ -47,6 +75,7 @@
 
 #include <epan/packet.h>
 #include <epan/dfilter/dfilter.h>
+#include <epan/ws_strsplit.h>
 #include "file.h"
 #include "capture.h"
 #include "capture_sync.h"
@@ -57,8 +86,6 @@
 #include "alert_box.h"
 #include "simple_dialog.h"
 #include <epan/prefs.h>
-#include "conditions.h"
-#include "ringbuffer.h"
 
 #ifdef _WIN32
 #include "capture-wpcap.h"
 #include "file_util.h"
 #include "log.h"
 
+typedef struct if_stat_cache_item_s {
+    char *name;
+    struct pcap_stat ps;
+} if_stat_cache_item_t;
 
+struct if_stat_cache_s {
+    int stat_fd;
+    int fork_child;
+    GList *cache_list;  /* List of if_stat_chache_entry_t */
+};
 
-/** 
+/**
  * Start a capture.
  *
  * @return TRUE if the capture starts successfully, FALSE otherwise.
@@ -142,7 +178,7 @@ capture_kill_child(capture_options *capture_opts)
   g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_INFO, "Capture Kill");
 
   /* kill the capture child */
-  sync_pipe_kill(capture_opts);
+  sync_pipe_kill(capture_opts->fork_child);
 }
 
 
@@ -209,7 +245,7 @@ guint32 drops)
     break;
 
   case CF_READ_ABORTED:
-    /* User wants to quit program. Exit by leaving the main loop, 
+    /* User wants to quit program. Exit by leaving the main loop,
        so that any quit functions we registered get called. */
     main_window_nested_quit();
     return FALSE;
@@ -217,7 +253,7 @@ guint32 drops)
 
   /* if we didn't captured even a single packet, close the file again */
   if(cf_get_packet_count(capture_opts->cf) == 0 && !capture_opts->restart) {
-    simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK, 
+    simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK,
 "%sNo packets captured!%s\n"
 "\n"
 "As no data was captured, closing the %scapture file!\n"
@@ -283,7 +319,7 @@ capture_input_new_file(capture_options *capture_opts, gchar *new_file)
     case CF_OK:
       break;
     case CF_ERROR:
-      /* Don't unlink (delete) the save file - leave it around, 
+      /* Don't unlink (delete) the save file - leave it around,
          for debugging purposes. */
       g_free(capture_opts->save_file);
       capture_opts->save_file = NULL;
@@ -307,7 +343,7 @@ capture_input_new_file(capture_options *capture_opts, gchar *new_file)
   return TRUE;
 }
 
-    
+
 /* capture child tells us we have new packets to read */
 void
 capture_input_new_packets(capture_options *capture_opts, int to_read)
@@ -339,7 +375,7 @@ capture_input_new_packets(capture_options *capture_opts, int to_read)
     }
   } else {
     /* increase capture file packet counter by the number or incoming packets */
-    cf_set_packet_count(capture_opts->cf, 
+    cf_set_packet_count(capture_opts->cf,
         cf_get_packet_count(capture_opts->cf) + to_read);
 
     cf_callback_invoke(cf_cb_live_capture_fixed_continue, capture_opts->cf);
@@ -455,7 +491,7 @@ void
 capture_input_closed(capture_options *capture_opts)
 {
     int  err;
-
+    int  packet_count_save;
 
     g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture stopped!");
     g_assert(capture_opts->state == CAPTURE_PREPARING || capture_opts->state == CAPTURE_RUNNING);
@@ -471,13 +507,15 @@ capture_input_closed(capture_options *capture_opts)
     }
 
     if(capture_opts->real_time_mode) {
-               cf_read_status_t status;
+       cf_read_status_t status;
 
         /* Read what remains of the capture file. */
         status = cf_finish_tail(capture_opts->cf, &err);
 
+        /* XXX: If -Q (quit-after-cap) then cf->count clr'd below so save it first */
+       packet_count_save = cf_get_packet_count(capture_opts->cf);
         /* Tell the GUI, we are not doing a capture any more.
-                  Must be done after the cf_finish_tail(), so file lengths are displayed 
+                  Must be done after the cf_finish_tail(), so file lengths are displayed
                   correct. */
         cf_callback_invoke(cf_cb_live_capture_update_finished, capture_opts->cf);
 
@@ -485,8 +523,8 @@ capture_input_closed(capture_options *capture_opts)
         switch (status) {
 
         case CF_READ_OK:
-            if(cf_get_packet_count(capture_opts->cf) == 0 && !capture_opts->restart) {
-                simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK, 
+            if ((packet_count_save == 0) && !capture_opts->restart) {
+                simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK,
 "%sNo packets captured!%s\n"
 "\n"
 "As no data was captured, closing the %scapture file!\n"
@@ -524,7 +562,7 @@ capture_input_closed(capture_options *capture_opts)
 
         /* this is a normal mode capture and if no error happened, read in the capture file data */
         if(capture_opts->save_file != NULL) {
-            capture_input_read_all(capture_opts, cf_is_tempfile(capture_opts->cf), 
+            capture_input_read_all(capture_opts, cf_is_tempfile(capture_opts->cf),
                 cf_get_drops_known(capture_opts->cf), cf_get_drops(capture_opts->cf));
         }
     }
@@ -561,5 +599,270 @@ capture_input_closed(capture_options *capture_opts)
     }
 }
 
+/**
+ * Fetch the interface list from a child process (dumpcap).
+ *
+ * @return A GList containing if_info_t structs if successful, NULL otherwise.
+ */
+
+/* XXX - We parse simple text output to get our interface list.  Should
+ * we use "real" data serialization instead, e.g. via XML? */
+GList *
+capture_interface_list(int *err, char **err_str)
+{
+    GList     *if_list = NULL;
+    int        i, j;
+    gchar     *msg;
+    gchar    **raw_list, **if_parts, **addr_parts;
+    gchar     *name;
+    if_info_t *if_info;
+    if_addr_t *if_addr;
+
+    g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Interface List ...");
+
+    /* Try to get our interface list */
+    *err = sync_interface_list_open(&msg);
+    if (*err != 0) {
+        g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Interface List failed!");
+        if (err_str) {
+            if (*err_str)
+                *err_str = msg;
+            else
+                g_free(msg);
+        } else {
+            g_free(msg);
+        }
+        return NULL;
+    }
+
+    /* Split our lines */
+    raw_list = g_strsplit(msg, "\n", 0);
+    g_free(msg);
+
+    for (i = 0; raw_list[i] != NULL; i++) {
+        if_parts = g_strsplit(raw_list[i], "\t", 4);
+        if (if_parts[0] == NULL || if_parts[1] == NULL || if_parts[2] == NULL ||
+                if_parts[3] == NULL) {
+            g_strfreev(if_parts);
+            continue;
+        }
+
+        /* Number followed by the name, e.g "1. eth0" */
+        name = strchr(if_parts[0], ' ');
+        if (name) {
+            name++;
+        } else {
+            g_strfreev(if_parts);
+            continue;
+        }
+
+        if_info = g_malloc0(sizeof(if_info_t));
+        if_info->name = g_strdup(name);
+        if (strlen(if_parts[1]) > 0)
+            if_info->description = g_strdup(if_parts[1]);
+        addr_parts = g_strsplit(if_parts[2], ",", 0);
+        for (j = 0; addr_parts[j] != NULL; j++) {
+            if_addr = g_malloc0(sizeof(if_addr_t));
+            if (inet_pton(AF_INET, addr_parts[j], &if_addr->ip_addr.ip4_addr)) {
+                if_addr->type = AT_IPv4;
+            } else if (inet_pton(AF_INET6, addr_parts[j],
+                    &if_addr->ip_addr.ip6_addr)) {
+                if_addr->type = AT_IPv6;
+            } else {
+                g_free(if_addr);
+                if_addr = NULL;
+            }
+            if (if_addr) {
+                if_info->ip_addr = g_slist_append(if_info->ip_addr, if_addr);
+            }
+        }
+        if (strcmp(if_parts[3], "loopback") == 0)
+            if_info->loopback = TRUE;
+        g_strfreev(if_parts);
+        g_strfreev(addr_parts);
+        if_list = g_list_append(if_list, if_info);
+    }
+    g_strfreev(raw_list);
+
+    /* Check to see if we built a list */
+    if (if_list == NULL) {
+        if (err_str && *err_str)
+            *err_str = g_strdup("No interfaces found");
+        *err = NO_INTERFACES_FOUND;
+    }
+    return if_list;
+}
+
+/* XXX - We parse simple text output to get our interface list.  Should
+ * we use "real" data serialization instead, e.g. via XML? */
+GList *
+capture_pcap_linktype_list(gchar *ifname, char **err_str)
+{
+    GList     *linktype_list = NULL;
+    int        err, i;
+    gchar     *msg;
+    gchar    **raw_list, **lt_parts;
+    data_link_info_t *data_link_info;
+
+    g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Interface List ...");
+
+    /* Try to get our interface list */
+    err = sync_linktype_list_open(ifname, &msg);
+    if (err != 0) {
+        g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Interface List failed!");
+        if (err_str) {
+            *err_str = msg;
+        } else {
+            g_free(msg);
+        }
+        return NULL;
+    }
+
+    /* Split our lines */
+    raw_list = g_strsplit(msg, "\n", 0);
+    g_free(msg);
+
+    for (i = 0; raw_list[i] != NULL; i++) {
+        /* ...and what if the interface name has a tab in it, Mr. Clever Programmer? */
+        lt_parts = g_strsplit(raw_list[i], "\t", 3);
+        if (lt_parts[0] == NULL || lt_parts[1] == NULL || lt_parts[2] == NULL) {
+            g_strfreev(lt_parts);
+            continue;
+        }
+
+        data_link_info = g_malloc(sizeof (data_link_info_t));
+        data_link_info->dlt = (int) strtol(lt_parts[0], NULL, 10);
+        data_link_info->name = g_strdup(lt_parts[1]);
+        if (strcmp(lt_parts[2], "(not supported)") != 0)
+            data_link_info->description = g_strdup(lt_parts[2]);
+        else
+            data_link_info->description = NULL;
+
+        linktype_list = g_list_append(linktype_list, data_link_info);
+    }
+    g_strfreev(raw_list);
+
+    /* Check to see if we built a list */
+    if (linktype_list == NULL) {
+        if (err_str)
+            *err_str = NULL;
+    }
+    return linktype_list;
+}
+
+if_stat_cache_t *
+capture_stat_start(GList *if_list) {
+    int stat_fd, fork_child;
+    gchar *msg;
+    if_stat_cache_t *sc = NULL;
+    GList *if_entry;
+    if_info_t *if_info;
+    if_stat_cache_item_t *sc_item;
+
+    /* Fire up dumpcap. */
+    /*
+     * XXX - on systems with BPF, the number of BPF devices limits the
+     * number of devices on which you can capture simultaneously.
+     *
+     * This means that
+     *
+     * 1) this might fail if you run out of BPF devices
+     *
+     * and
+     *
+     * 2) opening every interface could leave too few BPF devices
+     *    for *other* programs.
+     *
+     * It also means the system could end up getting a lot of traffic
+     * that it has to pass through the networking stack and capture
+     * mechanism, so opening all the devices and presenting packet
+     * counts might not always be a good idea.
+     */
+     if (sync_interface_stats_open(&stat_fd, &fork_child, &msg) == 0) {
+        sc = g_malloc(sizeof(if_stat_cache_t));
+        sc->stat_fd = stat_fd;
+        sc->fork_child = fork_child;
+        sc->cache_list = NULL;
+
+        /* Initialize the cache */
+        for (if_entry = if_list; if_entry != NULL; if_entry = g_list_next(if_entry)) {
+            if_info = if_entry->data;
+            sc_item = g_malloc0(sizeof(if_stat_cache_item_t));
+            sc_item->name = g_strdup(if_info->name);
+            sc->cache_list = g_list_append(sc->cache_list, sc_item);
+        }
+    }
+    return sc;
+}
+
+#define MAX_STAT_LINE_LEN 500
+
+static void
+capture_stat_cache_update(if_stat_cache_t *sc) {
+    gchar stat_line[MAX_STAT_LINE_LEN];
+    gchar **stat_parts;
+    GList *sc_entry;
+    if_stat_cache_item_t *sc_item;
+
+    if (!sc)
+        return;
+
+    while (sync_pipe_gets_nonblock(sc->stat_fd, stat_line, MAX_STAT_LINE_LEN) > 0) {
+        g_strstrip(stat_line);
+        stat_parts = g_strsplit(stat_line, "\t", 3);
+        if (stat_parts[0] == NULL || stat_parts[1] == NULL ||
+            stat_parts[2] == NULL) {
+            g_strfreev(stat_parts);
+            continue;
+        }
+        for (sc_entry = sc->cache_list; sc_entry != NULL; sc_entry = g_list_next(sc_entry)) {
+            sc_item = sc_entry->data;
+            if (strcmp(sc_item->name, stat_parts[0]) == 0) {
+                sc_item->ps.ps_recv = (u_int) strtoul(stat_parts[1], NULL, 10);
+                sc_item->ps.ps_drop = (u_int) strtoul(stat_parts[2], NULL, 10);
+            }
+        }
+        g_strfreev(stat_parts);
+    }
+}
+
+gboolean
+capture_stats(if_stat_cache_t *sc, char *ifname, struct pcap_stat *ps) {
+    GList *sc_entry;
+    if_stat_cache_item_t *sc_item;
+
+    if (!sc || !ifname || !ps) {
+        return FALSE;
+    }
+
+    capture_stat_cache_update(sc);
+    for (sc_entry = sc->cache_list; sc_entry != NULL; sc_entry = g_list_next(sc_entry)) {
+        sc_item = sc_entry->data;
+        if (strcmp(sc_item->name, ifname) == 0) {
+            memcpy(ps, &sc_item->ps, sizeof(struct pcap_stat));
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
+
+void
+capture_stat_stop(if_stat_cache_t *sc) {
+    GList *sc_entry;
+    if_stat_cache_item_t *sc_item;
+    gchar *msg;
+
+    if (!sc)
+        return;
+
+    sync_interface_stats_close(&sc->stat_fd, &sc->fork_child, &msg);
+
+    for (sc_entry = sc->cache_list; sc_entry != NULL; sc_entry = g_list_next(sc_entry)) {
+        sc_item = sc_entry->data;
+        g_free(sc_item->name);
+        g_free(sc_item);
+    }
+    g_free(sc);
+}
 
 #endif /* HAVE_LIBPCAP */