Improve error message when binding http port.
[darkstat] / localip.c
index 33f5a71..0c15409 100644 (file)
--- a/localip.c
+++ b/localip.c
@@ -1,5 +1,5 @@
 /* darkstat 3
- * copyright (c) 2001-2008 Emil Mikulic.
+ * copyright (c) 2001-2011 Emil Mikulic.
  *
  * localip.c: determine local IP of our capture interface
  *
@@ -7,19 +7,27 @@
  * GNU General Public License version 2. (see COPYING.GPL)
  */
 
-#include "darkstat.h"
 #include "addr.h"
-#include "conv.h" /* for strlcpy */
+#include "config.h" /* for HAVE_IFADDRS_H */
 #include "err.h"
 #include "localip.h"
+#include "bsd.h" /* for strlcpy */
 
 #include <sys/socket.h>
 #include <net/if.h>
-#include <ifaddrs.h>
 #include <errno.h>
 #include <string.h>
 #include <unistd.h>
 
+#ifdef HAVE_IFADDRS_H
+# include <ifaddrs.h>
+#else
+# ifdef HAVE_SYS_SOCKIO_H
+#  include <sys/sockio.h> /* for SIOCGIFADDR, especially on Solaris */
+# endif
+# include <sys/ioctl.h>
+#endif
+
 static const char *iface = NULL;
 struct addr localip4, localip6;
 static struct addr last_localip4, last_localip6;
@@ -28,71 +36,117 @@ void
 localip_init(const char *interface)
 {
    iface = interface;
+
+   /* defaults */
+   localip4.family = IPv4;
+   localip4.ip.v4 = 0;
+
+   localip6.family = IPv6;
+   memset(&(localip6.ip.v6), 0, sizeof(localip6.ip.v6));
+
+   last_localip4 = localip4;
+   last_localip6 = localip6;
+
+   /* initial update */
    localip_update();
 }
 
-void
-localip_update(void)
+static void
+localip_update_helper(void)
 {
-   struct ifaddrs *ifas, *ifa;
-   int got_v4 = 0, got_v6 = 0;
-
+   /* defaults */
    localip4.family = IPv4;
+   localip4.ip.v4 = 0;
+
    localip6.family = IPv6;
+   memset(&(localip6.ip.v6), 0, sizeof(localip6.ip.v6));
 
-   if (iface == NULL) {
-      /* reading from capfile */
-      localip4.ip.v4 = 0;
-      memset(&(localip6.ip.v6), 0, sizeof(localip6.ip.v6));
-      return;
-   }
+   if (iface == NULL)
+      return; /* reading from capfile */
 
-   if (getifaddrs(&ifas) < 0)
-      err(1, "can't get own IP address on interface \"%s\"", iface);
+#ifdef HAVE_IFADDRS_H
+   {
+      int got_v4 = 0, got_v6 = 0;
+      struct ifaddrs *ifas, *ifa;
 
-   for (ifa = ifas; ifa; ifa = ifa->ifa_next) {
-      if (got_v4 && got_v6)
-         break;   /* Task is already complete. */
+      if (getifaddrs(&ifas) < 0) {
+         warn("can't getifaddrs() on interface \"%s\"", iface);
+         return;
+      }
 
-      if (strncmp(ifa->ifa_name, iface, IFNAMSIZ))
-         continue;   /* Wrong interface. */
+      for (ifa = ifas; ifa; ifa = ifa->ifa_next) {
+         if (got_v4 && got_v6)
+            break;   /* Task is already complete. */
 
-      /* The first IPv4 name is always functional. */
-      if ((ifa->ifa_addr->sa_family == AF_INET) && !got_v4)
-      {
-         /* Good IPv4 address. */
-         localip4.ip.v4 =
-            ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
-         got_v4 = 1;
-         continue;
-      }
+         if (strncmp(ifa->ifa_name, iface, IFNAMSIZ))
+            continue;   /* Wrong interface. */
 
-      /* IPv6 needs some obvious exceptions. */
-      if ( ifa->ifa_addr->sa_family == AF_INET6 ) {
-         struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) ifa->ifa_addr;
+         if (!ifa->ifa_addr)
+            continue;   /* This can be NULL, e.g. for ppp0. */
 
-         if ( IN6_IS_ADDR_LINKLOCAL(&(sa6->sin6_addr))
-              || IN6_IS_ADDR_SITELOCAL(&(sa6->sin6_addr)) )
+         /* The first IPv4 name is always functional. */
+         if ((ifa->ifa_addr->sa_family == AF_INET) && !got_v4)
+         {
+            /* Good IPv4 address. */
+            localip4.ip.v4 =
+               ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
+            got_v4 = 1;
             continue;
+         }
+
+         /* IPv6 needs some obvious exceptions. */
+         if ( ifa->ifa_addr->sa_family == AF_INET6 ) {
+            struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) ifa->ifa_addr;
+
+            if ( IN6_IS_ADDR_LINKLOCAL(&(sa6->sin6_addr))
+                 || IN6_IS_ADDR_SITELOCAL(&(sa6->sin6_addr)) )
+               continue;
 
-         /* Only standard IPv6 can reach this point. */
-         memcpy(&(localip6.ip.v6), &sa6->sin6_addr, sizeof(localip6.ip.v6));
-         got_v6 = 1;
+            /* Only standard IPv6 can reach this point. */
+            memcpy(&(localip6.ip.v6), &sa6->sin6_addr, sizeof(localip6.ip.v6));
+            got_v6 = 1;
+         }
       }
-   }
 
-   freeifaddrs(ifas);
+      freeifaddrs(ifas);
+
+      if (!got_v4)
+          warnx("can't get own IPv4 address on interface \"%s\"", iface);
+   }
+#else /* don't HAVE_IFADDRS_H */
+   {
+      int tmp = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+      struct ifreq ifr;
+      struct sockaddr sa;
+
+      strlcpy(ifr.ifr_name, iface, IFNAMSIZ);
+      ifr.ifr_addr.sa_family = AF_INET;
+      if (ioctl(tmp, SIOCGIFADDR, &ifr) == -1) {
+         if (errno == EADDRNOTAVAIL) {
+            verbosef("lost local IP");
+         } else
+            warn("can't get own IP address on interface \"%s\"", iface);
+      } else {
+         /* success! */
+         sa = ifr.ifr_addr;
+         localip4.ip.v4 = ((struct sockaddr_in*)&sa)->sin_addr.s_addr;
+      }
+      close(tmp);
+   }
+#endif
+}
 
-   /* Report an error if IPv4 address could not be found. */
-   if (!got_v4)
-       err(1, "can't get own IPv4 address on interface \"%s\"", iface);
+void
+localip_update(void)
+{
+   localip_update_helper();
 
    if (!addr_equal(&last_localip4, &localip4)) {
-      verbosef("localip4 update(%s) = %s", iface, addr_to_str(&localip4));
+      verbosef("%s ip4 update: %s", iface, addr_to_str(&localip4));
       last_localip4 = localip4;
    }
    if (!addr_equal(&last_localip6, &localip6)) {
-      verbosef("localip6 update(%s) = %s", iface, addr_to_str(&localip6));
+      verbosef("%s ip6 update: %s", iface, addr_to_str(&localip6));
       last_localip6 = localip6;
    }
 }