Merge pull request #2979 from akoeplinger/tests-simplify
[mono.git] / mono / utils / networking-posix.c
index 57b3588ae69f86bcec5d47b983fe307056d784fc..ede69be85f35d83385d2d8acd2e6f5faf3fd44d2 100644 (file)
@@ -8,12 +8,38 @@
  */
 
 #include <config.h>
-#include <mono/utils/networking.h>
 #include <glib.h>
 
 #ifdef HAVE_NETDB_H
 #include <netdb.h>
 #endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_GETIFADDRS
+#include <ifaddrs.h>
+#endif
+
+#include <mono/utils/networking.h>
+#include <mono/utils/mono-threads-coop.h>
+
+static void*
+get_address_from_sockaddr (struct sockaddr *sa)
+{
+       switch (sa->sa_family) {
+       case AF_INET:
+               return &((struct sockaddr_in*)sa)->sin_addr;
+       case AF_INET6:
+               return &((struct sockaddr_in6*)sa)->sin6_addr;
+       }
+       return NULL;
+}
 
 #ifdef HAVE_GETADDRINFO
 
@@ -24,6 +50,7 @@ mono_get_address_info (const char *hostname, int port, int flags, MonoAddressInf
        struct addrinfo hints, *res = NULL, *info;
        MonoAddressEntry *cur = NULL, *prev = NULL;
        MonoAddressInfo *addr_info;
+       int ret;
 
        memset (&hints, 0, sizeof (struct addrinfo));
        *result = NULL;
@@ -45,7 +72,12 @@ mono_get_address_info (const char *hostname, int port, int flags, MonoAddressInf
                hints.ai_flags = AI_ADDRCONFIG;
 #endif
        sprintf (service_name, "%d", port);
-    if (getaddrinfo (hostname, service_name, &hints, &info))
+
+       MONO_ENTER_GC_SAFE;
+       ret = getaddrinfo (hostname, service_name, &hints, &info);
+       MONO_EXIT_GC_SAFE;
+
+       if (ret)
                return 1; /* FIXME propagate the error */
 
        res = info;
@@ -53,11 +85,6 @@ mono_get_address_info (const char *hostname, int port, int flags, MonoAddressInf
 
        while (res) {
                cur = g_new0 (MonoAddressEntry, 1);
-               if (prev)
-                       prev->next = cur;
-               else
-                       addr_info->entries = cur;
-
                cur->family = res->ai_family;
                cur->socktype = res->ai_socktype;
                cur->protocol = res->ai_protocol;
@@ -68,12 +95,20 @@ mono_get_address_info (const char *hostname, int port, int flags, MonoAddressInf
                        cur->address_len = sizeof (struct in6_addr);
                        cur->address.v6 = ((struct sockaddr_in6*)res->ai_addr)->sin6_addr;
                } else {
-                       g_error ("Cannot handle address family %d", cur->family);
+                       g_warning ("Cannot handle address family %d", cur->family);
+                       res = res->ai_next;
+                       g_free (cur);
+                       continue;
                }
 
                if (res->ai_canonname)
                        cur->canonical_name = g_strdup (res->ai_canonname);
 
+               if (prev)
+                       prev->next = cur;
+               else
+                       addr_info->entries = cur;
+                       
                prev = cur;
                res = res->ai_next;
        }
@@ -121,3 +156,200 @@ mono_networking_get_ipv6_protocol (void)
 }
 
 #endif
+
+#if defined (HAVE_SIOCGIFCONF)
+
+#define IFCONF_BUFF_SIZE 1024
+#ifndef _SIZEOF_ADDR_IFREQ
+#define _SIZEOF_ADDR_IFREQ(ifr) (sizeof (struct ifreq))
+#endif
+
+#define FOREACH_IFR(IFR, IFC) \
+       for (IFR = (IFC).ifc_req;       \
+       ifr < (struct ifreq*)((char*)(IFC).ifc_req + (IFC).ifc_len); \
+       ifr = (struct ifreq*)((char*)(IFR) + _SIZEOF_ADDR_IFREQ (*(IFR))))
+
+void *
+mono_get_local_interfaces (int family, int *interface_count)
+{
+       int fd;
+       struct ifconf ifc;
+       struct ifreq *ifr;
+       int if_count = 0;
+       gboolean ignore_loopback = FALSE;
+       void *result = NULL;
+       char *result_ptr;
+
+       *interface_count = 0;
+
+       if (!mono_address_size_for_family (family))
+               return NULL;
+
+       fd = socket (family, SOCK_STREAM, 0);
+       if (fd == -1)
+               return NULL;
+
+       memset (&ifc, 0, sizeof (ifc));
+       ifc.ifc_len = IFCONF_BUFF_SIZE;
+       ifc.ifc_buf = (char *)g_malloc (IFCONF_BUFF_SIZE); /* We can't have such huge buffers on the stack. */
+       if (ioctl (fd, SIOCGIFCONF, &ifc) < 0)
+               goto done;
+
+       FOREACH_IFR (ifr, ifc) {
+               struct ifreq iflags;
+
+               //only return addresses of the same type as @family
+               if (ifr->ifr_addr.sa_family != family) {
+                       ifr->ifr_name [0] = '\0';
+                       continue;
+               }
+
+               strcpy (iflags.ifr_name, ifr->ifr_name);
+
+               //ignore interfaces we can't get props for
+               if (ioctl (fd, SIOCGIFFLAGS, &iflags) < 0) {
+                       ifr->ifr_name [0] = '\0';
+                       continue;
+               }
+
+               //ignore interfaces that are down
+               if ((iflags.ifr_flags & IFF_UP) == 0) {
+                       ifr->ifr_name [0] = '\0';
+                       continue;
+               }
+
+               //If we have a non-loopback iface, don't return any loopback
+               if ((iflags.ifr_flags & IFF_LOOPBACK) == 0) {
+                       ignore_loopback = TRUE;
+                       ifr->ifr_name [0] = 1;//1 means non-loopback
+               } else {
+                       ifr->ifr_name [0] = 2; //2 means loopback
+               }
+               ++if_count;
+       }
+
+       result = (char *)g_malloc (if_count * mono_address_size_for_family (family));
+       result_ptr = (char *)result;
+       FOREACH_IFR (ifr, ifc) {
+               if (ifr->ifr_name [0] == '\0')
+                       continue;
+
+               if (ignore_loopback && ifr->ifr_name [0] == 2) {
+                       --if_count;
+                       continue;
+               }
+
+               memcpy (result_ptr, get_address_from_sockaddr (&ifr->ifr_addr), mono_address_size_for_family (family));
+               result_ptr += mono_address_size_for_family (family);
+       }
+       g_assert (result_ptr <= (char*)result + if_count * mono_address_size_for_family (family));
+
+done:
+       *interface_count = if_count;
+       g_free (ifc.ifc_buf);
+       close (fd);
+       return result;
+}
+
+#elif defined(HAVE_GETIFADDRS)
+
+void *
+mono_get_local_interfaces (int family, int *interface_count)
+{
+       struct ifaddrs *ifap = NULL, *cur;
+       int if_count = 0;
+       gboolean ignore_loopback = FALSE;
+       void *result;
+       char *result_ptr;
+
+       *interface_count = 0;
+
+       if (!mono_address_size_for_family (family))
+               return NULL;
+
+       if (getifaddrs (&ifap))
+               return NULL;
+
+       for (cur = ifap; cur; cur = cur->ifa_next) {
+               //ignore interfaces with no address assigned
+               if (!cur->ifa_addr)
+                       continue;
+
+               //ignore interfaces that don't belong to @family
+               if (cur->ifa_addr->sa_family != family)
+                       continue;
+
+               //ignore interfaces that are down
+               if ((cur->ifa_flags & IFF_UP) == 0)
+                       continue;
+
+               //If we have a non-loopback iface, don't return any loopback
+               if ((cur->ifa_flags & IFF_LOOPBACK) == 0)
+                       ignore_loopback = TRUE;
+
+               if_count++;
+       }
+
+       result_ptr = result = g_malloc (if_count * mono_address_size_for_family (family));
+       for (cur = ifap; cur; cur = cur->ifa_next) {
+               if (!cur->ifa_addr)
+                       continue;
+               if (cur->ifa_addr->sa_family != family)
+                       continue;
+               if ((cur->ifa_flags & IFF_UP) == 0)
+                       continue;
+
+               //we decrement if_count because it did not on the previous loop.
+               if (ignore_loopback && (cur->ifa_flags & IFF_LOOPBACK)) {
+                       --if_count;
+                       continue;
+               }
+
+               memcpy (result_ptr, get_address_from_sockaddr (cur->ifa_addr), mono_address_size_for_family (family));
+               result_ptr += mono_address_size_for_family (family);
+       }
+       g_assert (result_ptr <= (char*)result + if_count * mono_address_size_for_family (family));
+
+       freeifaddrs (ifap);
+       *interface_count = if_count;
+       return result;
+}
+
+#endif
+
+#ifdef HAVE_GETNAMEINFO
+
+gboolean
+mono_networking_addr_to_str (MonoAddress *address, char *buffer, socklen_t buflen)
+{
+       MonoSocketAddress saddr;
+       socklen_t len;
+       mono_socket_address_init (&saddr, &len, address->family, &address->addr, 0);
+
+       return getnameinfo (&saddr.addr, len, buffer, buflen, NULL, 0, NI_NUMERICHOST) == 0;
+}
+
+#elif HAVE_INET_NTOP
+
+gboolean
+mono_networking_addr_to_str (MonoAddress *address, char *buffer, socklen_t buflen)
+{
+       return inet_ntop (address->family, &address->addr, buffer, buflen) != NULL;
+}
+
+#endif
+
+#ifndef _WIN32
+// These are already defined in networking-windows.c for Windows
+void
+mono_networking_init (void)
+{
+       //nothing really
+}
+
+void
+mono_networking_shutdown (void)
+{
+       //nothing really
+}
+#endif