Add mono/metadata/mono-route.c for non-Linux ParseRouteInfo_local
authorBen Woods <woodsb02@gmail.com>
Sat, 15 Nov 2014 17:24:09 +0000 (01:24 +0800)
committerBen Woods <woodsb02@gmail.com>
Sun, 18 Jan 2015 23:05:32 +0000 (07:05 +0800)
Add #ifdef to ignore code in mono/metadata/mono-route.c if OS=linux
Also remove mono-route.c from msvc/libmonoruntime.vcxproj (Windows)

Add #ifdef to ensure mono/metadata/mono-route.h code is not included
on Linux

Change to using #if defined(PLATFORM_MACOSX) || defined(PLATFORM_BSD)

Use sizeof(addr) for maximum length of IP address string, instead of the hard-coded 16.
Should the size of addr ever change it will keep us from disaster in case if shrinking the array length.
Also, do not ignore snprintf return value - check for truncated output and act accordingly if it happens.

Add Tests for System.Net.NetworkInformation/IPInterfacePropertiesTest.cs and System.Net.NetworkInformation/NetworkInterfaceTest.cs

Remove accidental Copyright

add a space before the opening parenthesis, as shown in the coding guidelines.

Indent TestFixture to align with coding style of newer Test classes

Use G_N_ELEMENTS(mib) instead of sizeof(mib) / sizeof(mib[0])

mcs/class/System/System.Net.NetworkInformation/GatewayIPAddressInformationCollection.cs
mcs/class/System/System.Net.NetworkInformation/IPInterfaceProperties.cs
mcs/class/System/System_test.dll.sources
mcs/class/System/Test/System.Net.NetworkInformation/IPInterfacePropertiesTest.cs [new file with mode: 0644]
mcs/class/System/Test/System.Net.NetworkInformation/NetworkInterfaceTest.cs [new file with mode: 0644]
mono/metadata/Makefile.am
mono/metadata/icall-def.h
mono/metadata/icall.c
mono/metadata/mono-route.c [new file with mode: 0644]
mono/metadata/mono-route.h [new file with mode: 0644]

index 058c337c4af1a012e427a1f143a72d33c7163f97..07c8f478dee5dc33884824f8ff3db64433e6ddef 100644 (file)
@@ -128,18 +128,18 @@ namespace System.Net.NetworkInformation {
                }
        }
 
-       class LinuxGatewayIPAddressInformationCollection : GatewayIPAddressInformationCollection
+       class UnixGatewayIPAddressInformationCollection : GatewayIPAddressInformationCollection
        {
-               public static readonly LinuxGatewayIPAddressInformationCollection Empty = new LinuxGatewayIPAddressInformationCollection (true);
+               public static readonly UnixGatewayIPAddressInformationCollection Empty = new UnixGatewayIPAddressInformationCollection (true);
 
                bool is_readonly;
 
-               private LinuxGatewayIPAddressInformationCollection (bool isReadOnly)
+               private UnixGatewayIPAddressInformationCollection (bool isReadOnly)
                {
                        this.is_readonly = isReadOnly;
                }
 
-               public LinuxGatewayIPAddressInformationCollection (IPAddressCollection col)
+               public UnixGatewayIPAddressInformationCollection (IPAddressCollection col)
                {
                        foreach (IPAddress a in col)
                                Add (new GatewayIPAddressInformationImpl (a));
index fec490398a26a7788f589ba4b25eca74d8fdc20b..13edcd33351156f76183ec5a80a4f931bd1c7443 100644 (file)
@@ -30,6 +30,7 @@ using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
 using System.Net.Sockets;
+using System.Runtime.CompilerServices;
 using System.Text.RegularExpressions;
 
 namespace System.Net.NetworkInformation {
@@ -59,7 +60,6 @@ namespace System.Net.NetworkInformation {
                protected UnixNetworkInterface iface;
                List <IPAddress> addresses;
                IPAddressCollection dns_servers;
-               IPAddressCollection gateways;
                string dns_suffix;
                DateTime last_parse;
                
@@ -74,38 +74,6 @@ namespace System.Net.NetworkInformation {
                        throw new NotImplementedException ();
                }
 
-               void ParseRouteInfo (string iface)
-               {
-                       try {
-                               gateways = new IPAddressCollection ();
-                               using (StreamReader reader = new StreamReader ("/proc/net/route")) {
-                                       string line;
-                                       reader.ReadLine (); // Ignore first line
-                                       while ((line = reader.ReadLine ()) != null) {
-                                               line = line.Trim ();
-                                               if (line.Length == 0)
-                                                       continue;
-
-                                               string [] parts = line.Split ('\t');
-                                               if (parts.Length < 3)
-                                                       continue;
-                                               string gw_address = parts [2].Trim ();
-                                               byte [] ipbytes = new byte [4];  
-                                               if (gw_address.Length == 8 && iface.Equals (parts [0], StringComparison.OrdinalIgnoreCase)) {
-                                                       for (int i = 0; i < 4; i++) {
-                                                               if (!Byte.TryParse (gw_address.Substring (i * 2, 2), NumberStyles.HexNumber, null, out ipbytes [3 - i]))
-                                                                       continue;
-                                                       }
-                                                       IPAddress ip = new IPAddress (ipbytes);
-                                                       if (!ip.Equals (IPAddress.Any))
-                                                               gateways.Add (ip);
-                                               }
-                                       }
-                               }
-                       } catch {
-                       }
-               }
-
                static Regex ns = new Regex (@"\s*nameserver\s+(?<address>.*)");
                static Regex search = new Regex (@"\s*search\s+(?<domain>.*)");
                void ParseResolvConf ()
@@ -188,16 +156,6 @@ namespace System.Net.NetworkInformation {
                                return dns_suffix;
                        }
                }
-     
-               public override GatewayIPAddressInformationCollection GatewayAddresses {
-                       get {
-                               ParseRouteInfo (this.iface.Name.ToString());
-                               if (gateways.Count > 0)
-                                       return new LinuxGatewayIPAddressInformationCollection (gateways);
-                               else
-                                       return LinuxGatewayIPAddressInformationCollection.Empty;
-                       }
-               }
 
                [MonoTODO ("Always returns true")]
                public override bool IsDnsEnabled {
@@ -260,6 +218,8 @@ namespace System.Net.NetworkInformation {
 
        class LinuxIPInterfaceProperties : UnixIPInterfaceProperties
        {
+               IPAddressCollection gateways;
+
                public LinuxIPInterfaceProperties (LinuxNetworkInterface iface, List <IPAddress> addresses)
                        : base (iface, addresses)
                {
@@ -272,10 +232,54 @@ namespace System.Net.NetworkInformation {
                        
                        return ipv4iface_properties;
                }
+
+               void ParseRouteInfo (string iface)
+               {
+                       try {
+                               using (StreamReader reader = new StreamReader ("/proc/net/route")) {
+                                       string line;
+                                       reader.ReadLine (); // Ignore first line
+                                       while ((line = reader.ReadLine ()) != null) {
+                                               line = line.Trim ();
+                                               if (line.Length == 0)
+                                                       continue;
+
+                                               string [] parts = line.Split ('\t');
+                                               if (parts.Length < 3)
+                                                       continue;
+                                               string gw_address = parts [2].Trim ();
+                                               byte [] ipbytes = new byte [4];
+                                               if (gw_address.Length == 8 && iface.Equals (parts [0], StringComparison.OrdinalIgnoreCase)) {
+                                                       for (int i = 0; i < 4; i++) {
+                                                               if (!Byte.TryParse (gw_address.Substring (i * 2, 2), NumberStyles.HexNumber, null, out ipbytes [3 - i]))
+                                                                       continue;
+                                                       }
+                                                       IPAddress ip = new IPAddress (ipbytes);
+                                                       if (!ip.Equals (IPAddress.Any) && !gateways.Contains (ip))
+                                                               gateways.Add (ip);
+                                               }
+                                       }
+                               }
+                       } catch {
+                       }
+               }
+
+               public override GatewayIPAddressInformationCollection GatewayAddresses {
+                       get {
+                               gateways = new IPAddressCollection ();
+                               ParseRouteInfo (this.iface.Name.ToString());
+                               if (gateways.Count > 0)
+                                       return new UnixGatewayIPAddressInformationCollection (gateways);
+                               else
+                                       return UnixGatewayIPAddressInformationCollection.Empty;
+                       }
+               }
        }
 
        class MacOsIPInterfaceProperties : UnixIPInterfaceProperties
        {
+               IPAddressCollection gateways;
+
                public MacOsIPInterfaceProperties (MacOsNetworkInterface iface, List <IPAddress> addresses)
                        : base (iface, addresses)
                {
@@ -288,6 +292,37 @@ namespace System.Net.NetworkInformation {
                        
                        return ipv4iface_properties;
                }
+
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               private extern static bool ParseRouteInfo_internal(string iface, out string[] gw_addr_list);
+
+               public override GatewayIPAddressInformationCollection GatewayAddresses {
+                       get {
+                               gateways = new IPAddressCollection ();
+                               string[] gw_addrlist;
+                               if (!ParseRouteInfo_internal (this.iface.Name.ToString(), out gw_addrlist))
+                                       return UnixGatewayIPAddressInformationCollection.Empty;
+
+                               for(int i=0; i<gw_addrlist.Length; i++) {
+                                       try {
+                                               IPAddress ip = IPAddress.Parse(gw_addrlist[i]);
+                                               if (!ip.Equals (IPAddress.Any) && !gateways.Contains (ip))
+                                                       gateways.Add (ip);
+                                       } catch (ArgumentNullException) {
+                                               /* Ignore this, as the
+                                                * internal call might have
+                                                * left some blank entries at
+                                                * the end of the array
+                                                */
+                                       }
+                               }
+
+                               if (gateways.Count > 0)
+                                       return new UnixGatewayIPAddressInformationCollection (gateways);
+                               else
+                                       return UnixGatewayIPAddressInformationCollection.Empty;
+                       }
+               }
        }
 
        class Win32IPInterfaceProperties2 : IPInterfaceProperties
index 6ff1a9488a353f1e50733f94204da4b360a64e8f..8898fd1cdccc25625b929107344c2093465d6dbf 100644 (file)
@@ -268,6 +268,8 @@ System.Net.Mail/SmtpPermissionAttributeTest.cs
 System.Net.Mail/SmtpServer.cs
 System.Net.Mime/ContentDispositionTest.cs
 System.Net.Mime/ContentTypeTest.cs
+System.Net.NetworkInformation/IPInterfacePropertiesTest.cs
+System.Net.NetworkInformation/NetworkInterfaceTest.cs
 System.Net.NetworkInformation/PhysicalAddressTest.cs
 System.Net.Security/SslStreamTest.cs
 System.Runtime.Versioning/FrameworkNameTest.cs
diff --git a/mcs/class/System/Test/System.Net.NetworkInformation/IPInterfacePropertiesTest.cs b/mcs/class/System/Test/System.Net.NetworkInformation/IPInterfacePropertiesTest.cs
new file mode 100644 (file)
index 0000000..f8ad5f1
--- /dev/null
@@ -0,0 +1,73 @@
+//
+// IPInterfacePropertiesTest.cs - NUnit Test Cases for System.Net.NetworkInformation.IPInterfaceProperties
+//
+// Authors:
+//   Ben Woods (woodsb02@gmail.com)
+//
+
+using NUnit.Framework;
+using System;
+using System.Net;
+using System.Net.NetworkInformation;
+
+namespace MonoTests.System.Net.NetworkInformation
+{
+
+       [TestFixture]
+       public class IPInterfacePropertiesTest
+       {
+               [Test]
+               public void AtLeastOneUnicastAddress ()
+               {
+                       int numUnicastAddresses = 0;
+                       NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces ();
+                       foreach (NetworkInterface adapter in adapters)
+                       {
+                               IPInterfaceProperties adapterProperties = adapter.GetIPProperties ();
+                               UnicastIPAddressInformationCollection unicastAddresses = adapterProperties.UnicastAddresses;
+                               numUnicastAddresses += unicastAddresses.Count;
+                       }
+                       Assert.Greater (numUnicastAddresses, 0);
+               }
+       
+               [Test]
+               public void AtLeastOneGatewayAddress ()
+               {
+                       int numGatewayAddresses = 0;
+                       NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces ();
+                       foreach (NetworkInterface adapter in adapters)
+                       {
+                               IPInterfaceProperties adapterProperties = adapter.GetIPProperties ();
+                               GatewayIPAddressInformationCollection gatewayAddresses = adapterProperties.GatewayAddresses;
+                               numGatewayAddresses += gatewayAddresses.Count;
+                       }
+                       Assert.Greater (numGatewayAddresses, 0);
+               }
+       
+               [Test]
+               public void DnsEnabled ()
+               {
+                       NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces ();
+                       foreach (NetworkInterface adapter in adapters)
+                       {
+                               IPInterfaceProperties adapterProperties = adapter.GetIPProperties ();
+                               Assert.IsTrue (adapterProperties.IsDnsEnabled);
+                       }
+               }
+       
+               [Test]
+               public void AtLeastOneDnsAddress ()
+               {
+                       int numDnsAddresses = 0;
+                       NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces ();
+                       foreach (NetworkInterface adapter in adapters)
+                       {
+                               IPInterfaceProperties adapterProperties = adapter.GetIPProperties ();
+                               IPAddressCollection dnsAddresses = adapterProperties.DnsAddresses;
+                               numDnsAddresses += dnsAddresses.Count;
+                       }
+                       Assert.Greater (numDnsAddresses, 0);
+               }
+       
+       }
+}
diff --git a/mcs/class/System/Test/System.Net.NetworkInformation/NetworkInterfaceTest.cs b/mcs/class/System/Test/System.Net.NetworkInformation/NetworkInterfaceTest.cs
new file mode 100644 (file)
index 0000000..315f356
--- /dev/null
@@ -0,0 +1,74 @@
+//
+// NetworkInterfaceTest.cs - NUnit Test Cases for System.Net.NetworkInformation.NetworkInterface
+//
+// Authors:
+//   Ben Woods (woodsb02@gmail.com)
+//
+
+using NUnit.Framework;
+using System;
+using System.Net;
+using System.Net.NetworkInformation;
+
+namespace MonoTests.System.Net.NetworkInformation
+{
+
+       [TestFixture]
+       public class NetworkInterfaceTest
+       {
+               [Test]
+               public void IsNetworkAvailable ()
+               {
+                       Assert.IsTrue (NetworkInterface.GetIsNetworkAvailable ());
+               }
+       
+               [Test]
+               public void LoopbackInterfaceIndex ()
+               {
+                       Assert.Greater (NetworkInterface.LoopbackInterfaceIndex, 0);
+               }
+       
+               [Test]
+               public void AtLeastOneInterface ()
+               {
+                       NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces ();
+                       Assert.Greater (adapters.Length, 0);
+               }
+       
+               [Test]
+               public void FirstInterfaceId ()
+               {
+                       NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces ();
+                       Assert.Greater (adapters[0].Id.Length, 0);
+               }
+       
+               [Test]
+               public void FirstInterfaceName ()
+               {
+                       NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces ();
+                       Assert.Greater (adapters[0].Name.Length, 0);
+               }
+       
+               [Test]
+               public void FirstInterfaceType ()
+               {
+                       NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces ();
+                       Assert.AreNotEqual (adapters[0].NetworkInterfaceType, NetworkInterfaceType.Unknown);
+               }
+       
+               [Test]
+               public void FirstInterfaceOperationalStatus ()
+               {
+                       NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces ();
+                       Assert.AreNotEqual (adapters[0].OperationalStatus, OperationalStatus.Unknown);
+               }
+       
+               [Test]
+               public void FirstInterfaceSpeed ()
+               {
+                       NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces ();
+                       Assert.Greater (adapters[0].Speed, 0);
+               }
+       
+       }
+}
index 335484ae676054522cb7b99975e7a95947ba7dfc..7ac4072e6c34625c767bc60d2e5bfa167611903f 100644 (file)
@@ -158,6 +158,8 @@ common_sources = \
        mono-perfcounters.h     \
        mono-perfcounters-def.h \
        mono-ptr-array.h        \
+       mono-route.c            \
+       mono-route.h            \
        mono-wsq.c              \
        mono-wsq.h              \
        monitor.h               \
index 257b8332f5f4ac9abaf33200788483a60daf44b7..1d1b7652dda2d7b56a94ff2126df45b0a78de7a6 100644 (file)
@@ -443,6 +443,11 @@ ICALL(NDNS_1, "GetHostByAddr_internal(string,string&,string[]&,string[]&)", ves_
 ICALL(NDNS_2, "GetHostByName_internal(string,string&,string[]&,string[]&)", ves_icall_System_Net_Dns_GetHostByName_internal)
 ICALL(NDNS_3, "GetHostName_internal(string&)", ves_icall_System_Net_Dns_GetHostName_internal)
 
+#if defined(PLATFORM_MACOSX) || defined(PLATFORM_BSD)
+ICALL_TYPE(MAC_IFACE_PROPS, "System.Net.NetworkInformation.MacOsIPInterfaceProperties", MAC_IFACE_PROPS_1)
+ICALL(MAC_IFACE_PROPS_1, "ParseRouteInfo_internal", ves_icall_System_Net_NetworkInformation_MacOsIPInterfaceProperties_ParseRouteInfo_internal)
+#endif
+
 ICALL_TYPE(SOCK, "System.Net.Sockets.Socket", SOCK_1)
 ICALL(SOCK_1, "Accept_internal(intptr,int&,bool)", ves_icall_System_Net_Sockets_Socket_Accept_internal)
 ICALL(SOCK_2, "Available_internal(intptr,int&)", ves_icall_System_Net_Sockets_Socket_Available_internal)
index 763b2f638bff16a7463b9ef207bf0ce2e33a2706..11ae25ddd99b03bbcb554c4b994f7124e109adf9 100644 (file)
@@ -45,6 +45,7 @@
 #include <mono/metadata/exception.h>
 #include <mono/metadata/file-io.h>
 #include <mono/metadata/console-io.h>
+#include <mono/metadata/mono-route.h>
 #include <mono/metadata/socket-io.h>
 #include <mono/metadata/mono-endian.h>
 #include <mono/metadata/tokentype.h>
diff --git a/mono/metadata/mono-route.c b/mono/metadata/mono-route.c
new file mode 100644 (file)
index 0000000..2acf7a8
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * mono-route.c: Read the network routing tables using sysctl(3) calls
+ *               Required for Unix-like systems that don't have Linux's /proc/net/route
+ *
+ * Author:
+ *   Ben Woods (woodsb02@gmail.com)
+ */
+
+#if defined(PLATFORM_MACOSX) || defined(PLATFORM_BSD)
+
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mono/metadata/object.h>
+#include <mono/metadata/mono-route.h>
+
+extern MonoBoolean ves_icall_System_Net_NetworkInformation_MacOsIPInterfaceProperties_ParseRouteInfo_internal(MonoString *iface, MonoArray **gw_addr_list)
+{
+       size_t needed;
+       in_addr_t in;
+       int mib[6];
+       int num_gws=0, gwnum=0;
+       unsigned int ifindex = 0;
+       char *buf, *next, *lim, *ifacename;
+       struct rt_msghdr *rtm;
+
+       MonoDomain *domain = mono_domain_get ();
+
+       ifacename = mono_string_to_utf8(iface);
+       if ((ifindex = if_nametoindex(ifacename)) == 0)
+               return FALSE;
+       g_free(ifacename);
+
+       // MIB array defining data to read from sysctl
+       mib[0] = CTL_NET;       // Networking
+       mib[1] = PF_ROUTE;      // Routing messages
+       mib[2] = 0;             // Protocol number (always zero)
+       mib[3] = AF_INET;       // Address family (IPv4)
+       mib[4] = NET_RT_DUMP;   // Dump routing table
+       mib[5] = 0;             //
+
+       // First sysctl call with oldp set to NULL to determine size of available data
+       if (sysctl(mib, G_N_ELEMENTS(mib), NULL, &needed, NULL, 0) < 0)
+               return FALSE;
+
+       // Allocate suffcient memory for available data based on the previous sysctl call
+       if ((buf = malloc(needed)) == NULL)
+               return FALSE;
+
+       // Second sysctl call to retrieve data into appropriately sized buffer
+       if (sysctl(mib, G_N_ELEMENTS(mib), buf, &needed, NULL, 0) < 0)
+               return FALSE;
+
+       lim = buf + needed;
+       for (next = buf; next < lim; next += rtm->rtm_msglen) {
+               rtm = (struct rt_msghdr *)next;
+               if (rtm->rtm_version != RTM_VERSION)
+                       continue;
+               if (rtm->rtm_index != ifindex)
+                       continue;
+               if((in = gateway_from_rtm(rtm)) == 0)
+                       continue;
+               num_gws++;
+       }
+
+       *gw_addr_list = mono_array_new(domain, mono_get_string_class (), num_gws);
+
+       for (next = buf; next < lim; next += rtm->rtm_msglen) {
+               rtm = (struct rt_msghdr *)next;
+               if (rtm->rtm_version != RTM_VERSION)
+                       continue;
+               if (rtm->rtm_index != ifindex)
+                       continue;
+               if ((in = gateway_from_rtm(rtm)) == 0)
+                       continue;
+
+               MonoString *addr_string;
+               char addr [16], *ptr;
+               int len;
+
+               ptr = (char *) &in;
+               len = snprintf(addr, sizeof(addr), "%u.%u.%u.%u",
+                       (unsigned char) ptr [0],
+                       (unsigned char) ptr [1],
+                       (unsigned char) ptr [2],
+                       (unsigned char) ptr [3]);
+
+               if ((len >= sizeof(addr)) || (len < 0))
+                       // snprintf output truncated
+                       continue;
+
+               addr_string = mono_string_new (domain, addr);
+               mono_array_setref (*gw_addr_list, gwnum, addr_string);
+               gwnum++;
+       }
+       free(buf);
+       return TRUE;
+}
+
+in_addr_t gateway_from_rtm(struct rt_msghdr *rtm)
+{
+       struct sockaddr *gw;
+       unsigned int l;
+
+       struct sockaddr *addr = (struct sockaddr *)(rtm + 1);
+       l = roundup(addr->sa_len, sizeof(long)); \
+       gw = (struct sockaddr *)((char *) addr + l); \
+
+       if (rtm->rtm_addrs & RTA_GATEWAY) {
+               if(gw->sa_family == AF_INET) {
+                       struct sockaddr_in *sockin = (struct sockaddr_in *)gw;
+                       return(sockin->sin_addr.s_addr);
+               }
+       }
+
+       return 0;
+}
+
+#endif /* #if defined(PLATFORM_MACOSX) || defined(PLATFORM_BSD) */
diff --git a/mono/metadata/mono-route.h b/mono/metadata/mono-route.h
new file mode 100644 (file)
index 0000000..45e39e4
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef __MONO_ROUTE_H__
+#define __MONO_ROUTE_H__
+
+#if defined(PLATFORM_MACOSX) || defined(PLATFORM_BSD)
+
+#include <sys/socket.h>
+#include <net/route.h>
+#include <mono/metadata/object-internals.h>
+
+in_addr_t gateway_from_rtm (struct rt_msghdr *rtm) MONO_INTERNAL;
+
+/* Category icalls */
+extern MonoBoolean ves_icall_System_Net_NetworkInformation_MacOsIPInterfaceProperties_ParseRouteInfo_internal (MonoString *iface, MonoArray **gw_addr_list) MONO_INTERNAL;
+
+#endif /* #if defined(PLATFORM_MACOSX) || defined(PLATFORM_BSD) */
+#endif /* __MONO_ROUTE_H__ */