Fix use-after-free in http_stop().
[darkstat] / cap.c
diff --git a/cap.c b/cap.c
index dda5cb8..2625655 100644 (file)
--- a/cap.c
+++ b/cap.c
@@ -1,5 +1,5 @@
 /* darkstat 3
- * copyright (c) 2001-2011 Emil Mikulic.
+ * copyright (c) 2001-2014 Emil Mikulic.
  *
  * cap.c: capture packets, and hand them off to decode and acct.
  *
@@ -112,6 +112,7 @@ static void cap_set_filter(pcap_t *pcap, const char *filter) {
    free(tmp_filter);
 }
 
+/* Start capturing on just one interface. Called from cap_start(). */
 static void cap_start_one(struct cap_iface *iface, const int promisc) {
    char errbuf[PCAP_ERRBUF_SIZE], *tmp_device;
    int linktype, snaplen, waited;
@@ -302,8 +303,10 @@ void cap_start(const int promisc) {
 
 #ifdef linux
 # define _unused_on_linux_ _unused_
+# define _unused_otherwise_
 #else
 # define _unused_on_linux_
+# define _unused_otherwise_ _unused_
 #endif
 
 /*
@@ -311,7 +314,7 @@ void cap_start(const int promisc) {
  */
 void cap_fd_set(fd_set *read_set _unused_on_linux_,
                 int *max_fd _unused_on_linux_,
-                struct timeval *timeout,
+                struct timeval *timeout _unused_otherwise_,
                 int *need_timeout) {
    assert(*need_timeout == 0); /* we're first to get a shot at the fd_set */
 
@@ -393,26 +396,30 @@ static void callback(u_char *user,
       acct_for(&sm, &iface->local_ips);
 }
 
-/* Process any packets currently in the capture buffer. */
-void cap_poll(fd_set *read_set _unused_on_linux_) {
-   int ret, premature = 1;
+/* Process any packets currently in the capture buffer.
+ * Returns 0 on error (usually means the interface went down).
+ */
+int cap_poll(fd_set *read_set _unused_on_linux_) {
    struct cap_iface *iface;
+   static int told = 0;
 
    STAILQ_FOREACH(iface, &cap_ifs, entries) {
-#ifndef linux /* We don't use select() on Linux. */
-      if (FD_ISSET(iface->fd, read_set))
-         premature = 0;
-      else
-         continue; /* skip this interface */
-#endif
-
       /* Once per capture poll, check our IP address.  It's used in accounting
        * for traffic graphs.
        */
       localip_update(iface->name, &iface->local_ips);
+      if (!told && iface->local_ips.num_addrs == 0) {
+         verbosef("interface '%s' has no addresses, "
+                  "your graphs will be blank",
+                  iface->name);
+         verbosef("please read the darkstat manpage, "
+                  "and consider using the -l option");
+         told = 1;
+      }
 
       for (;;) {
          struct timespec t;
+         int ret;
 
          timer_start(&t);
          ret = pcap_dispatch(
@@ -420,34 +427,32 @@ void cap_poll(fd_set *read_set _unused_on_linux_) {
                -1, /* count = entire buffer */
                callback,
                (u_char*)iface); /* user = struct to pass to callback */
+         timer_stop(&t,
+                    2 * CAP_TIMEOUT_MSEC * 1000000,
+                    "pcap_dispatch took too long");
 
          if (ret < 0) {
             warnx("pcap_dispatch('%s'): %s",
                iface->name, pcap_geterr(iface->pcap));
-            continue;
+            return 0;
          }
-         timer_stop(&t,
-                    2 * CAP_TIMEOUT_MSEC * 1000000,
-                    "pcap_dispatch took too long");
 
-         if (0) /* debugging */
-            verbosef("iface '%s' got %d pkts", iface->name, ret);
+#if 0 /* debugging */
+         verbosef("iface '%s' got %d pkts", iface->name, ret);
+#endif
 
 #ifdef linux
          /* keep looping until we've dispatched all the outstanding packets */
          if (ret == 0)
             break;
-         else
-            premature = 0;
 #else
          /* we get them all on the first shot */
          break;
 #endif
       }
    }
-   if (premature)
-      verbosef("cap_poll() premature");
    cap_stats_update();
+   return 1;
 }
 
 void cap_stop(void) {