using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Net; using System.Net.Sockets; using System.Net.NetworkInformation; namespace LiteNetLib { /// /// Address type that you want to receive from NetUtils.GetLocalIp method /// [Flags] public enum LocalAddrType { IPv4 = 1, IPv6 = 2, All = IPv4 | IPv6 } /// /// Some specific network utilities /// public static class NetUtils { private static readonly NetworkSorter NetworkSorter = new NetworkSorter(); public static IPEndPoint MakeEndPoint(string hostStr, int port) { return new IPEndPoint(ResolveAddress(hostStr), port); } public static IPAddress ResolveAddress(string hostStr) { if(hostStr == "localhost") return IPAddress.Loopback; if (!IPAddress.TryParse(hostStr, out var ipAddress)) { if (NetManager.IPv6Support) ipAddress = ResolveAddress(hostStr, AddressFamily.InterNetworkV6); if (ipAddress == null) ipAddress = ResolveAddress(hostStr, AddressFamily.InterNetwork); } if (ipAddress == null) throw new ArgumentException("Invalid address: " + hostStr); return ipAddress; } public static IPAddress ResolveAddress(string hostStr, AddressFamily addressFamily) { IPAddress[] addresses = Dns.GetHostEntry(hostStr).AddressList; foreach (IPAddress ip in addresses) { if (ip.AddressFamily == addressFamily) { return ip; } } return null; } /// /// Get all local ip addresses /// /// type of address (IPv4, IPv6 or both) /// List with all local ip addresses public static List GetLocalIpList(LocalAddrType addrType) { List targetList = new List(); GetLocalIpList(targetList, addrType); return targetList; } /// /// Get all local ip addresses (non alloc version) /// /// result list /// type of address (IPv4, IPv6 or both) public static void GetLocalIpList(IList targetList, LocalAddrType addrType) { bool ipv4 = (addrType & LocalAddrType.IPv4) == LocalAddrType.IPv4; bool ipv6 = (addrType & LocalAddrType.IPv6) == LocalAddrType.IPv6; try { // Sort networks interfaces so it prefer Wifi over Cellular networks // Most cellulars networks seems to be incompatible with NAT Punch var networks = NetworkInterface.GetAllNetworkInterfaces(); Array.Sort(networks, NetworkSorter); foreach (NetworkInterface ni in networks) { //Skip loopback and disabled network interfaces if (ni.NetworkInterfaceType == NetworkInterfaceType.Loopback || ni.OperationalStatus != OperationalStatus.Up) continue; var ipProps = ni.GetIPProperties(); //Skip address without gateway if (ipProps.GatewayAddresses.Count == 0) continue; foreach (UnicastIPAddressInformation ip in ipProps.UnicastAddresses) { var address = ip.Address; if ((ipv4 && address.AddressFamily == AddressFamily.InterNetwork) || (ipv6 && address.AddressFamily == AddressFamily.InterNetworkV6)) targetList.Add(address.ToString()); } } //Fallback mode (unity android) if (targetList.Count == 0) { IPAddress[] addresses = Dns.GetHostEntry(Dns.GetHostName()).AddressList; foreach (IPAddress ip in addresses) { if((ipv4 && ip.AddressFamily == AddressFamily.InterNetwork) || (ipv6 && ip.AddressFamily == AddressFamily.InterNetworkV6)) targetList.Add(ip.ToString()); } } } catch { //ignored } if (targetList.Count == 0) { if(ipv4) targetList.Add("127.0.0.1"); if(ipv6) targetList.Add("::1"); } } private static readonly List IpList = new List(); /// /// Get first detected local ip address /// /// type of address (IPv4, IPv6 or both) /// IP address if available. Else - string.Empty public static string GetLocalIp(LocalAddrType addrType) { lock (IpList) { IpList.Clear(); GetLocalIpList(IpList, addrType); return IpList.Count == 0 ? string.Empty : IpList[0]; } } // =========================================== // Internal and debug log related stuff // =========================================== internal static void PrintInterfaceInfos() { NetDebug.WriteForce(NetLogLevel.Info, $"IPv6Support: { NetManager.IPv6Support}"); try { foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces()) { foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses) { if (ip.Address.AddressFamily == AddressFamily.InterNetwork || ip.Address.AddressFamily == AddressFamily.InterNetworkV6) { NetDebug.WriteForce( NetLogLevel.Info, $"Interface: {ni.Name}, Type: {ni.NetworkInterfaceType}, Ip: {ip.Address}, OpStatus: {ni.OperationalStatus}"); } } } } catch (Exception e) { NetDebug.WriteForce(NetLogLevel.Info, $"Error while getting interface infos: {e}"); } } internal static int RelativeSequenceNumber(int number, int expected) { return (number - expected + NetConstants.MaxSequence + NetConstants.HalfMaxSequence) % NetConstants.MaxSequence - NetConstants.HalfMaxSequence; } internal static T[] AllocatePinnedUninitializedArray(int count) where T : unmanaged { #if NET5_0_OR_GREATER || NET5_0 return GC.AllocateUninitializedArray(count, true); #else return new T[count]; #endif } } // Pick the most obvious choice for the local IP // Ethernet > Wifi > Others > Cellular internal class NetworkSorter : IComparer { [SuppressMessage("ReSharper", "PossibleNullReferenceException")] public int Compare(NetworkInterface a, NetworkInterface b) { var isCellularA = a.NetworkInterfaceType == NetworkInterfaceType.Wman || a.NetworkInterfaceType == NetworkInterfaceType.Wwanpp || a.NetworkInterfaceType == NetworkInterfaceType.Wwanpp2; var isCellularB = b.NetworkInterfaceType == NetworkInterfaceType.Wman || b.NetworkInterfaceType == NetworkInterfaceType.Wwanpp || b.NetworkInterfaceType == NetworkInterfaceType.Wwanpp2; var isWifiA = a.NetworkInterfaceType == NetworkInterfaceType.Wireless80211; var isWifiB = b.NetworkInterfaceType == NetworkInterfaceType.Wireless80211; var isEthernetA = a.NetworkInterfaceType == NetworkInterfaceType.Ethernet || a.NetworkInterfaceType == NetworkInterfaceType.Ethernet3Megabit || a.NetworkInterfaceType == NetworkInterfaceType.GigabitEthernet || a.NetworkInterfaceType == NetworkInterfaceType.FastEthernetFx || a.NetworkInterfaceType == NetworkInterfaceType.FastEthernetT; var isEthernetB = b.NetworkInterfaceType == NetworkInterfaceType.Ethernet || b.NetworkInterfaceType == NetworkInterfaceType.Ethernet3Megabit || b.NetworkInterfaceType == NetworkInterfaceType.GigabitEthernet || b.NetworkInterfaceType == NetworkInterfaceType.FastEthernetFx || b.NetworkInterfaceType == NetworkInterfaceType.FastEthernetT; var isOtherA = !isCellularA && !isWifiA && !isEthernetA; var isOtherB = !isCellularB && !isWifiB && !isEthernetB; var priorityA = isEthernetA ? 3 : isWifiA ? 2 : isOtherA ? 1 : 0; var priorityB = isEthernetB ? 3 : isWifiB ? 2 : isOtherB ? 1 : 0; return priorityA > priorityB ? -1 : priorityA < priorityB ? 1 : 0; } } }