Add s1ap - needs more work, untested as no traces available yet.
[obnox/wireshark/wip.git] / capture.c
index f3cdee6e16be4033bb6722518191da8a6516ffa0..b0efea16724d09c573c34e46c109b24158f106fd 100644 (file)
--- a/capture.c
+++ b/capture.c
 #include <sys/types.h>
 #endif
 
-#ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
+#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>
 
@@ -70,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.
@@ -594,20 +617,21 @@ capture_interface_list(int *err, char **err_str)
     gchar     *name;
     if_info_t *if_info;
     if_addr_t *if_addr;
-    struct addrinfo *ai;
-    struct sockaddr_in *sa4;
-    struct sockaddr_in6 *sa6;
 
     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) {
+    if (*err != 0) {
         g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Interface List failed!");
-        if (*err_str)
-            *err_str = msg;
-        else
+        if (err_str) {
+            if (*err_str)
+                *err_str = msg;
+            else
+                g_free(msg);
+        } else {
             g_free(msg);
+        }
         return NULL;
     }
 
@@ -638,27 +662,18 @@ capture_interface_list(int *err, char **err_str)
             if_info->description = g_strdup(if_parts[1]);
         addr_parts = g_strsplit(if_parts[2], ",", 0);
         for (j = 0; addr_parts[j] != NULL; j++) {
-            /* XXX - We're failing to convert IPv6 addresses (on Ubuntu, at least) */
-            if (getaddrinfo(addr_parts[j], NULL, NULL, &ai) == 0) {
+            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;
-                switch (ai->ai_family) {
-                    case AF_INET:
-                        if_addr = g_malloc0(sizeof(if_addr_t));
-                        if_addr->type = AT_IPv4;
-                        sa4 = (struct sockaddr_in *) ai->ai_addr;
-                        if_addr->ip_addr.ip4_addr = sa4->sin_addr.s_addr;
-                        break;
-                    case AF_INET6:
-                        if_addr = g_malloc0(sizeof(if_addr_t));
-                        if_addr->type = AT_IPv6;
-                        sa6 = (struct sockaddr_in6 *) ai->ai_addr;
-                        memcpy(&if_addr->ip_addr.ip6_addr, sa6->sin6_addr.s6_addr, 16);
-                        break;
-                }
-                if (if_addr) {
-                    if_info->ip_addr = g_slist_append(if_info->ip_addr, if_addr);
-                }
-                freeaddrinfo(ai);
+            }
+            if (if_addr) {
+                if_info->ip_addr = g_slist_append(if_info->ip_addr, if_addr);
             }
         }
         if (strcmp(if_parts[3], "loopback") == 0)
@@ -671,12 +686,183 @@ capture_interface_list(int *err, char **err_str)
 
     /* Check to see if we built a list */
     if (if_list == NULL) {
-        if (*err_str)
+        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 */