using NatTypeTester_Console.Net.STUN.Client; using System; using System.Diagnostics; using System.Net; using System.Net.Sockets; namespace NatTypeTester_Console.Net { public static class NetUtils { #region static method CompareArray /// /// Compares if specified array items equals. /// /// Array 1. /// Array 2 /// Returns true if both arrays are equal. public static bool CompareArray(Array array1, Array array2) { return CompareArray(array1, array2, array2.Length); } /// /// Compares if specified array items equals. /// /// Array 1. /// Array 2 /// Number of bytes in array 2 used for compare. /// Returns true if both arrays are equal. public static bool CompareArray(Array array1, Array array2, int array2Count) { if (array1 == null && array2 == null) { return true; } if (array1 == null) { return false; } if (array2 == null) { return false; } if (array1.Length != array2Count) { return false; } for (var i = 0; i < array1.Length; i++) { if (!array1.GetValue(i).Equals(array2.GetValue(i))) { return false; } } return true; } #endregion #region static method IsPrivateIP /// /// Gets if specified IP address is private LAN IP address. For example 192.168.x.x is private ip. /// /// IP address to check. /// Returns true if IP is private IP. /// Is raised when ip is null reference. public static bool IsPrivateIP(IPAddress ip) { if (ip == null) { throw new ArgumentNullException(nameof(ip)); } if (ip.AddressFamily == AddressFamily.InterNetwork) { var ipBytes = ip.GetAddressBytes(); /* Private IPs: First Octet = 192 AND Second Octet = 168 (Example: 192.168.X.X) First Octet = 172 AND (Second Octet >= 16 AND Second Octet <= 31) (Example: 172.16.X.X - 172.31.X.X) First Octet = 10 (Example: 10.X.X.X) First Octet = 169 AND Second Octet = 254 (Example: 169.254.X.X) */ if (ipBytes[0] == 192 && ipBytes[1] == 168) { return true; } if (ipBytes[0] == 172 && ipBytes[1] >= 16 && ipBytes[1] <= 31) { return true; } if (ipBytes[0] == 10) { return true; } if (ipBytes[0] == 169 && ipBytes[1] == 254) { return true; } } return false; } #endregion #region static method CreateSocket /// /// Creates new socket for the specified end point. /// /// Local end point. /// Protocol type. /// Return newly created socket. /// Is raised when localEP is null reference. public static Socket CreateSocket(IPEndPoint localEP, ProtocolType protocolType) { if (localEP == null) { throw new ArgumentNullException(nameof(localEP)); } var socketType = SocketType.Stream; if (protocolType == ProtocolType.Udp) { socketType = SocketType.Dgram; } if (localEP.AddressFamily == AddressFamily.InterNetwork) { var socket = new Socket(AddressFamily.InterNetwork, socketType, protocolType); socket.Bind(localEP); return socket; } if (localEP.AddressFamily == AddressFamily.InterNetworkV6) { var socket = new Socket(AddressFamily.InterNetworkV6, socketType, protocolType); socket.Bind(localEP); return socket; } throw new ArgumentException(@"Invalid IPEndPoint address family."); } #endregion public const string DefaultLocalEnd = @"0.0.0.0:0"; public static IPEndPoint ParseEndpoint(string str) { var ipPort = str.Trim().Split(':'); if (ipPort.Length == 2) { if (IPAddress.TryParse(ipPort[0], out var ip)) { if (ushort.TryParse(ipPort[1], out var port)) { return new IPEndPoint(ip, port); } } } return null; } public static (string, string, string) NatTypeTestCore(string local, string server, int port) { try { if (string.IsNullOrWhiteSpace(server)) { Debug.WriteLine(@"[ERROR]: Please specify STUN server !"); return (string.Empty, DefaultLocalEnd, string.Empty); } using var socketV4 = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); var ipe = ParseEndpoint(local) ?? new IPEndPoint(IPAddress.Any, 0); socketV4.Bind(ipe); var result = StunClient.Query(server, port, socketV4); return ( result.NatType.ToString(), socketV4.LocalEndPoint.ToString(), result.NatType != NatType.UdpBlocked ? result.PublicEndPoint.ToString() : string.Empty ); } catch (Exception ex) { Debug.WriteLine($@"[ERROR]: {ex}"); return (string.Empty, DefaultLocalEnd, string.Empty); } } } }