239 lines
9.5 KiB
C#
239 lines
9.5 KiB
C#
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Diagnostics.CodeAnalysis;
|
|||
|
using System.Net;
|
|||
|
using System.Net.Sockets;
|
|||
|
using System.Net.NetworkInformation;
|
|||
|
|
|||
|
namespace LiteNetLib
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// Address type that you want to receive from NetUtils.GetLocalIp method
|
|||
|
/// </summary>
|
|||
|
[Flags]
|
|||
|
public enum LocalAddrType
|
|||
|
{
|
|||
|
IPv4 = 1,
|
|||
|
IPv6 = 2,
|
|||
|
All = IPv4 | IPv6
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Some specific network utilities
|
|||
|
/// </summary>
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Get all local ip addresses
|
|||
|
/// </summary>
|
|||
|
/// <param name="addrType">type of address (IPv4, IPv6 or both)</param>
|
|||
|
/// <returns>List with all local ip addresses</returns>
|
|||
|
public static List<string> GetLocalIpList(LocalAddrType addrType)
|
|||
|
{
|
|||
|
List<string> targetList = new List<string>();
|
|||
|
GetLocalIpList(targetList, addrType);
|
|||
|
return targetList;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Get all local ip addresses (non alloc version)
|
|||
|
/// </summary>
|
|||
|
/// <param name="targetList">result list</param>
|
|||
|
/// <param name="addrType">type of address (IPv4, IPv6 or both)</param>
|
|||
|
public static void GetLocalIpList(IList<string> 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<string> IpList = new List<string>();
|
|||
|
/// <summary>
|
|||
|
/// Get first detected local ip address
|
|||
|
/// </summary>
|
|||
|
/// <param name="addrType">type of address (IPv4, IPv6 or both)</param>
|
|||
|
/// <returns>IP address if available. Else - string.Empty</returns>
|
|||
|
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<T>(int count) where T : unmanaged
|
|||
|
{
|
|||
|
#if NET5_0_OR_GREATER || NET5_0
|
|||
|
return GC.AllocateUninitializedArray<T>(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<NetworkInterface>
|
|||
|
{
|
|||
|
[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;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|