With Android 7.0 (Nougat) it is no longer possible to read some
[mono.git] / mcs / class / System / System.Net.NetworkInformation / NetworkInterface.cs
index f25525711d6450919e8323f98ea7a43af080c187..56d0e4d61f9453ecd233301ebdcf936cb30b354c 100644 (file)
@@ -41,15 +41,11 @@ using System.IO;
 using System.Globalization;
 
 namespace System.Net.NetworkInformation {
-       public abstract class NetworkInterface {
+       static class SystemNetworkInterface {
 
                static readonly NetworkInterfaceFactory nif = NetworkInterfaceFactory.Create ();
 
-               protected NetworkInterface ()
-               {
-               }
-
-               public static NetworkInterface [] GetAllNetworkInterfaces ()
+               public static NetworkInterface [] GetNetworkInterfaces ()
                {
                        try {
                                return nif.GetAllNetworkInterfaces ();
@@ -58,31 +54,28 @@ namespace System.Net.NetworkInformation {
                        }
                }
 
-               [MonoTODO("Always returns true")]
-               public static bool GetIsNetworkAvailable ()
+               public static bool InternalGetIsNetworkAvailable ()
                {
+                       // TODO:
                        return true;
                }
-               
-               public static int LoopbackInterfaceIndex {
+
+               public static int InternalLoopbackInterfaceIndex {
                        get {
                                return nif.GetLoopbackInterfaceIndex ();
                        }
                }
 
-               public abstract IPInterfaceProperties GetIPProperties ();
-               public abstract IPv4InterfaceStatistics GetIPv4Statistics ();
-               public abstract PhysicalAddress GetPhysicalAddress ();
-               public abstract bool Supports (NetworkInterfaceComponent networkInterfaceComponent);
+               public static int InternalIPv6LoopbackInterfaceIndex {
+                       get {
+                               throw new NotImplementedException ();
+                       }
+               }
 
-               public abstract string Description { get; }
-               public abstract string Id { get; }
-               public abstract bool IsReceiveOnly { get; }
-               public abstract string Name { get; }
-               public abstract NetworkInterfaceType NetworkInterfaceType { get; }
-               public abstract OperationalStatus OperationalStatus { get; }
-               public abstract long Speed { get; }
-               public abstract bool SupportsMulticast { get; }
+               public static IPAddress GetNetMask (IPAddress address)
+               {
+                       return nif.GetNetMask (address);
+               }
        }
 
        abstract class NetworkInterfaceFactory
@@ -101,12 +94,12 @@ namespace System.Net.NetworkInformation {
 
                class MacOsNetworkInterfaceAPI : UnixNetworkInterfaceAPI
                {
+                       const int AF_INET  = 2;
+                       const int AF_INET6 = 30;
+                       const int AF_LINK  = 18;
+
                        public override NetworkInterface [] GetAllNetworkInterfaces ()
                        {
-                               const int AF_INET  = 2;
-                               const int AF_INET6 = 30;
-                               const int AF_LINK  = 18;
-
                                var interfaces = new Dictionary <string, MacOsNetworkInterface> ();
                                IntPtr ifap;
                                if (getifaddrs (out ifap) != 0)
@@ -210,10 +203,45 @@ namespace System.Net.NetworkInformation {
                        {
                                return if_nametoindex ("lo0");
                        }
+
+                       public override IPAddress GetNetMask (IPAddress address)
+                       {
+                               IntPtr ifap;
+                               if (getifaddrs (out ifap) != 0)
+                                       throw new SystemException ("getifaddrs() failed");
+
+                               try {
+                                       IntPtr next = ifap;
+                                       while (next != IntPtr.Zero) {
+                                               MacOsStructs.ifaddrs addr = (MacOsStructs.ifaddrs) Marshal.PtrToStructure (next, typeof (MacOsStructs.ifaddrs));
+
+                                               if (addr.ifa_addr != IntPtr.Zero) {
+                                                       // optain IPAddress
+                                                       MacOsStructs.sockaddr sockaddr = (MacOsStructs.sockaddr) Marshal.PtrToStructure (addr.ifa_addr, typeof (MacOsStructs.sockaddr));
+
+                                                       if (sockaddr.sa_family == AF_INET) {
+                                                               MacOsStructs.sockaddr_in sockaddrin = (MacOsStructs.sockaddr_in) Marshal.PtrToStructure (addr.ifa_addr, typeof (MacOsStructs.sockaddr_in));
+                                                               var saddress = new IPAddress (sockaddrin.sin_addr);
+                                                               if (address.Equals (saddress))
+                                                                       return new IPAddress(((sockaddr_in)Marshal.PtrToStructure(addr.ifa_netmask, typeof(sockaddr_in))).sin_addr);
+                                                       }
+                                               }
+                                               next = addr.ifa_next;
+                                       }
+                               } finally {
+                                       freeifaddrs (ifap);
+                               }
+
+                               return null;
+                       }
                }
 
                class LinuxNetworkInterfaceAPI : UnixNetworkInterfaceAPI
                {
+                       const int AF_INET = 2;
+                       const int AF_INET6 = 10;
+                       const int AF_PACKET = 17;
+
                        static void FreeInterfaceAddresses (IntPtr ifap)
                        {
 #if MONODROID
@@ -234,9 +262,6 @@ namespace System.Net.NetworkInformation {
 
                        public override NetworkInterface [] GetAllNetworkInterfaces ()
                        {
-                               const int AF_INET   = 2;
-                               const int AF_INET6  = 10;
-                               const int AF_PACKET = 17;
 
                                var interfaces = new Dictionary <string, LinuxNetworkInterface> ();
                                IntPtr ifap;
@@ -367,13 +392,60 @@ namespace System.Net.NetworkInformation {
                        {
                                return if_nametoindex ("lo");
                        }
+
+                       public override IPAddress GetNetMask (IPAddress address)
+                       {
+                               foreach (ifaddrs networkInteface in GetNetworkInterfaces()) {
+                                       if (networkInteface.ifa_addr == IntPtr.Zero)
+                                               continue;
+
+                                       var sockaddr = (sockaddr_in)Marshal.PtrToStructure(networkInteface.ifa_addr, typeof(sockaddr_in));
+
+                                       if (sockaddr.sin_family != AF_INET)
+                                               continue;
+
+                                       if (!address.Equals(new IPAddress(sockaddr.sin_addr)))
+                                               continue;
+
+                                       var netmask = (sockaddr_in)Marshal.PtrToStructure(networkInteface.ifa_netmask, typeof(sockaddr_in));
+                                       return new IPAddress(netmask.sin_addr);
+                               }
+
+                               return null;
+                       }
+
+                       private static IEnumerable<ifaddrs> GetNetworkInterfaces()
+                       {
+                               IntPtr ifap = IntPtr.Zero;
+
+                               try {
+                                       if (GetInterfaceAddresses(out ifap) != 0)
+                                               yield break;
+
+                                       var next = ifap;
+                                       while (next != IntPtr.Zero) {
+                                               var addr = (ifaddrs)Marshal.PtrToStructure(next, typeof(ifaddrs));
+                                               yield return addr;
+                                               next = addr.ifa_next;
+                                       }
+                               } finally {
+                                       if (ifap != IntPtr.Zero)
+                                               FreeInterfaceAddresses(ifap);
+                               }
+                       }
                }
 
+#if !MOBILE
                class Win32NetworkInterfaceAPI : NetworkInterfaceFactory
                {
-                       [DllImport ("iphlpapi.dll", SetLastError = true)]
+                       private const string IPHLPAPI = "iphlpapi.dll";
+
+                       [DllImport (IPHLPAPI, SetLastError = true)]
                        static extern int GetAdaptersAddresses (uint family, uint flags, IntPtr reserved, byte [] info, ref int size);
 
+                       [DllImport (IPHLPAPI)]
+                       static extern uint GetBestInterfaceEx (byte[] ipAddress, out int index);
+
                        unsafe static Win32_IP_ADAPTER_ADDRESSES [] GetAdaptersAddresses ()
                        {
                                byte [] bytes = null;
@@ -406,18 +478,36 @@ namespace System.Net.NetworkInformation {
                                return ret;
                        }
 
+                       private static int GetBestInterfaceForAddress (IPAddress addr) {
+                               int index;
+                               SocketAddress address = new SocketAddress (addr);
+                               int error = (int) GetBestInterfaceEx (address.m_Buffer, out index);
+                               if (error != 0) {
+                                       throw new NetworkInformationException (error);
+                               }
+
+                               return index;
+                       }
+
                        public override int GetLoopbackInterfaceIndex ()
+                       {
+                               return GetBestInterfaceForAddress (IPAddress.Loopback);
+                       }
+
+                       public override IPAddress GetNetMask (IPAddress address)
                        {
                                throw new NotImplementedException ();
                        }
                }
+#endif
 
                public abstract NetworkInterface [] GetAllNetworkInterfaces ();
                public abstract int GetLoopbackInterfaceIndex ();
+               public abstract IPAddress GetNetMask (IPAddress address);
 
                public static NetworkInterfaceFactory Create ()
                {
-#if MONOTOUCH
+#if MONOTOUCH || XAMMAC
                        return new MacOsNetworkInterfaceAPI ();
 #else
                        Version windowsVer51 = new Version (5, 1);
@@ -430,8 +520,10 @@ namespace System.Net.NetworkInformation {
                                return new LinuxNetworkInterfaceAPI ();
                        }
 
+#if !MOBILE
                        if (Environment.OSVersion.Version >= windowsVer51)
                                return new Win32NetworkInterfaceAPI ();
+#endif
 
                        throw new NotImplementedException ();
 #endif
@@ -539,6 +631,19 @@ namespace System.Net.NetworkInformation {
                string               iface_operstate_path;
                string               iface_flags_path;          
 
+#if MONODROID
+               [DllImport ("__Internal")]
+               protected static extern int _monodroid_get_android_api_level ();
+
+               [DllImport ("__Internal")]
+               protected static extern bool _monodroid_get_network_interface_up_state (string ifname, ref bool is_up);
+
+               [DllImport ("__Internal")]
+               protected static extern bool _monodroid_get_network_interface_supports_multicast (string ifname, ref bool supports_multicast);
+
+               bool android_use_java_api;
+#endif
+
                internal string IfacePath {
                        get { return iface_path; }
                }
@@ -549,6 +654,9 @@ namespace System.Net.NetworkInformation {
                        iface_path = "/sys/class/net/" + name + "/";
                        iface_operstate_path = iface_path + "operstate";
                        iface_flags_path = iface_path + "flags";
+#if MONODROID
+                       android_use_java_api = _monodroid_get_android_api_level () >= 24;
+#endif
                }
 
                public override IPInterfaceProperties GetIPProperties ()
@@ -567,6 +675,23 @@ namespace System.Net.NetworkInformation {
 
                public override OperationalStatus OperationalStatus {
                        get {
+#if MONODROID
+                               if (android_use_java_api) {
+                                       // Starting from API 24 (Android 7 "Nougat") Android restricts access to many
+                                       // files in the /sys filesystem (see https://code.google.com/p/android/issues/detail?id=205565
+                                       // for more information) and therefore we are forced to call into Java API in
+                                       // order to get the information. Alas, what we can obtain in this way is quite
+                                       // limited. In the case of OperationalStatus we can only determine whether the
+                                       // interface is up or down. There is a way to get more detailed information but
+                                       // it requires an instance of the Android Context class which is not available
+                                       // to us here.
+                                       bool is_up = false;
+                                       if (_monodroid_get_network_interface_up_state (Name, ref is_up))
+                                               return is_up ? OperationalStatus.Up : OperationalStatus.Down;
+                                       else
+                                               return OperationalStatus.Unknown;
+                               }
+#endif
                                if (!Directory.Exists (iface_path))
                                        return OperationalStatus.Unknown;
                                
@@ -603,6 +728,17 @@ namespace System.Net.NetworkInformation {
 
                public override bool SupportsMulticast {
                        get {
+#if MONODROID
+                               if (android_use_java_api) {
+                                       // Starting from API 24 (Android 7 "Nougat") Android restricts access to many
+                                       // files in the /sys filesystem (see https://code.google.com/p/android/issues/detail?id=205565
+                                       // for more information) and therefore we are forced to call into Java API in
+                                       // order to get the information.
+                                       bool supports_multicast = false;
+                                       _monodroid_get_network_interface_supports_multicast (Name, ref supports_multicast);
+                                       return supports_multicast;
+                               }
+#endif
                                if (!Directory.Exists (iface_path))
                                        return false;
                                
@@ -671,6 +807,7 @@ namespace System.Net.NetworkInformation {
                }
        }
 
+#if !MOBILE
        class Win32NetworkInterface2 : NetworkInterface
        {
                [DllImport ("iphlpapi.dll", SetLastError = true)]
@@ -783,5 +920,6 @@ namespace System.Net.NetworkInformation {
                        get { return !addr.NoMulticast; }
                }
        }
+#endif
 }