libcollectdclient: Implement if_nametoindex() for Windows.
[collectd.git] / src / libcollectdclient / network.c
index 55f2aff..7105774 100644 (file)
@@ -1,25 +1,44 @@
 /**
- * collectd - src/libcollectdclient/network.h
- * Copyright (C) 2005-2010  Florian octo Forster
+ * collectd - src/libcollectdclient/network.c
+ * Copyright (C) 2005-2014  Florian Forster
+ * Copyright (C) 2010       Max Henkel
  *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; only version 2.1 of the License is
- * applicable.
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
  *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
  *
  * Authors:
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian Forster <octo at collectd.org>
+ *   Max Henkel <henkel at gmx.at>
  **/
 
+#if WIN32
+
+#include <winsock2.h>
+#include <wspiapi.h>
+#include <iphlpapi.h>
+#include <windows.h>
+#include <io.h>
+#include <assert.h>
+
+#else
+
+#include "collectd.h"
+
 #include <stdlib.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <sys/socket.h>
 #include <netdb.h>
 
+#if HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+
+#if HAVE_NET_IF_H
+# include <net/if.h>
+#endif
+
+#endif /* !WIN32 */
+
 #include "collectd/network.h"
 #include "collectd/network_buffer.h"
 
@@ -148,7 +177,7 @@ static int server_open_socket (lcc_server_t *srv) /* {{{ */
         optname = IP_TTL;
 
       setsockopt (srv->fd, IPPROTO_IP, optname,
-          &srv->ttl,
+          (void *) &srv->ttl,
           sizeof (srv->ttl));
     }
     else if (ai_ptr->ai_family == AF_INET6)
@@ -163,7 +192,7 @@ static int server_open_socket (lcc_server_t *srv) /* {{{ */
         optname = IPV6_UNICAST_HOPS;
 
       setsockopt (srv->fd, IPPROTO_IPV6, optname,
-          &srv->ttl,
+          (void *) &srv->ttl,
           sizeof (srv->ttl));
     }
 
@@ -369,46 +398,125 @@ int lcc_server_set_ttl (lcc_server_t *srv, uint8_t ttl) /* {{{ */
   return (0);
 } /* }}} int lcc_server_set_ttl */
 
-int lcc_server_set_security_level (lcc_server_t *srv, /* {{{ */
-    lcc_security_level_t level)
+#if WIN32
+static unsigned if_nametoindex (LPCSTR pszIfName) /* {{{ */
 {
-  if ((srv == NULL)
-      || ((level != NONE) && (level != SIGN) && (level != ENCRYPT)))
-    return (EINVAL);
+  IP_ADAPTER_ADDRESSES *pAddrList;
+  IP_ADAPTER_ADDRESSES *pAddrPtr;
+  BYTE bBuffer[8192];
+  ULONG dwSize;
+  ULONG dwStatus;
+  unsigned dwIndex = 0;
+
+  dwSize = (ULONG) sizeof (bBuffer);
+  pAddrList = (IP_ADAPTER_ADDRESSES *) &bBuffer[0];
+
+  dwStatus = GetAdaptersAddresses (
+      /* Family           = */ AF_UNSPEC,
+      /* Flags            = */ GAA_FLAG_SKIP_ANYCAST,
+      /* Reserved         = */ NULL,
+      /* AdapterAddresses = */ pAddrList,
+      /* SizePointer      = */ &dwSize);
+  if (dwStatus != ERROR_SUCCESS)
+    return (0);
 
-  srv->security_level = level;
+  for (pAddrPtr = pAddrList;
+      pAddrPtr != NULL;
+      pAddrPtr = pAddrPtr->Next)
+  {
+    if (strcmp (pszIfName, pAddrPtr->AdapterName) != 0)
+      continue;
 
-  return (0);
-} /* }}} int lcc_server_set_security_level */
+    dwIndex = (unsigned) pAddrPtr->IfIndex;
+    break;
+  }
 
-int lcc_server_set_credentials (lcc_server_t *srv, /* {{{ */
-    const char *username, const char *password)
+  return (dwIndex);
+} /* }}} unsigned if_nametoindex */
+#endif /* WIN32 */
+
+int lcc_server_set_interface (lcc_server_t *srv, char const *ifname) /* {{{ */
 {
-  char *tmp_username;
-  char *tmp_password;
+  int if_index;
+  int status;
 
-  if ((srv == NULL) || (username == NULL) || (password == NULL))
+  if ((srv == NULL) || (ifname == NULL))
     return (EINVAL);
 
-  tmp_username = strdup (username);
-  if (tmp_username == NULL)
-    return (ENOMEM);
+  if_index = if_nametoindex (ifname);
+  if (if_index == 0)
+    return (ENOENT);
 
-  tmp_password = strdup (password);
-  if (tmp_password == NULL)
+  /* IPv4 multicast */
+  if (srv->sa->sa_family == AF_INET)
   {
-    free (tmp_username);
-    return (ENOMEM);
+    struct sockaddr_in *addr = (struct sockaddr_in *) srv->sa;
+
+    if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
+    {
+#if HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
+      /* If possible, use the "ip_mreqn" structure which has
+       * an "interface index" member. Using the interface
+       * index is preferred here, because of its similarity
+       * to the way IPv6 handles this. Unfortunately, it
+       * appears not to be portable. */
+      struct ip_mreqn mreq;
+
+      memset (&mreq, 0, sizeof (mreq));
+      mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
+      mreq.imr_address.s_addr = ntohl (INADDR_ANY);
+      mreq.imr_ifindex = if_index;
+#else
+      struct ip_mreq mreq;
+
+      memset (&mreq, 0, sizeof (mreq));
+      mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
+      mreq.imr_interface.s_addr = ntohl (INADDR_ANY);
+#endif
+
+      status = setsockopt (srv->fd, IPPROTO_IP, IP_MULTICAST_IF,
+          (void *) &mreq, sizeof (mreq));
+      if (status != 0)
+        return (status);
+
+      return (0);
+    }
   }
 
-  free (srv->username);
-  free (srv->password);
+  /* IPv6 multicast */
+  if (srv->sa->sa_family == AF_INET6)
+  {
+    struct sockaddr_in6 *addr = (struct sockaddr_in6 *) srv->sa;
+
+    if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
+    {
+      status = setsockopt (srv->fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
+          (void *) &if_index, sizeof (if_index));
+      if (status != 0)
+        return (status);
 
-  srv->username = tmp_username;
-  srv->password = tmp_password;
+      return (0);
+    }
+  }
+
+  /* else: Not a multicast interface. */
+#if defined(SO_BINDTODEVICE)
+  status = setsockopt (srv->fd, SOL_SOCKET, SO_BINDTODEVICE,
+      ifname, strlen (ifname) + 1);
+  if (status != 0)
+    return (-1);
+#endif
 
   return (0);
-} /* }}} int lcc_server_set_credentials */
+} /* }}} int lcc_server_set_interface */
+
+int lcc_server_set_security_level (lcc_server_t *srv, /* {{{ */
+    lcc_security_level_t level,
+    const char *username, const char *password)
+{
+  return (lcc_network_buffer_set_security_level (srv->buffer,
+        level, username, password));
+} /* }}} int lcc_server_set_security_level */
 
 int lcc_network_values_send (lcc_network_t *net, /* {{{ */
     const lcc_value_list_t *vl)