diff --git a/Lidgren.Network/NetPeer.cs b/Lidgren.Network/NetPeer.cs index dec79af4..3ea06a91 100644 --- a/Lidgren.Network/NetPeer.cs +++ b/Lidgren.Network/NetPeer.cs @@ -271,9 +271,10 @@ internal void SendLibrary(NetOutgoingMessage msg, NetEndPoint recipient) Recycle(msg); } - static NetEndPoint GetNetEndPoint(string host, int port) + private NetEndPoint GetNetEndPoint(string host, int port) { - IPAddress address = NetUtility.Resolve(host); + var family = m_configuration.DualStack ? null : (AddressFamily?) m_configuration.LocalAddress.AddressFamily; + IPAddress address = NetUtility.Resolve(host, family); if (address == null) throw new NetException("Could not resolve host"); return new NetEndPoint(address, port); diff --git a/Lidgren.Network/NetUtility.cs b/Lidgren.Network/NetUtility.cs index 1120faa9..959e72c4 100644 --- a/Lidgren.Network/NetUtility.cs +++ b/Lidgren.Network/NetUtility.cs @@ -50,33 +50,43 @@ public static partial class NetUtility /// public delegate void ResolveAddressCallback(NetAddress adr); - /// - /// Get IPv4 endpoint from notation (xxx.xxx.xxx.xxx) or hostname and port number (asynchronous version) + /// + /// Get IPv4 or IPv6 address from notation (xxx.xxx.xxx.xxx or xxxx:xxxx:...:xxxx) or hostname (asynchronous version) /// public static void ResolveAsync(string ipOrHost, int port, ResolveEndPointCallback callback) { - ResolveAsync(ipOrHost, delegate(NetAddress adr) - { - if (adr == null) - { - callback(null); - } - else - { - callback(new NetEndPoint(adr, port)); - } - }); + ResolveAsync(ipOrHost, port, null, callback); } - /// - /// Get IPv4 endpoint from notation (xxx.xxx.xxx.xxx) or hostname and port number + public static void ResolveAsync(string ipOrHost, int port, AddressFamily? allowedFamily, ResolveEndPointCallback callback) + { + ResolveAsync(ipOrHost, allowedFamily, delegate(NetAddress adr) + { + if (adr == null) + { + callback(null); + } + else + { + callback(new NetEndPoint(adr, port)); + } + }); + } + + /// + /// Get IPv4 or IPv6 address from notation (xxx.xxx.xxx.xxx or xxxx:xxxx:...:xxxx) or hostname /// public static NetEndPoint Resolve(string ipOrHost, int port) { - var adr = Resolve(ipOrHost); - return adr == null ? null : new NetEndPoint(adr, port); + return Resolve(ipOrHost, port, null); } + public static NetEndPoint Resolve(string ipOrHost, int port, AddressFamily? allowedFamily) + { + var adr = Resolve(ipOrHost, allowedFamily); + return adr == null ? null : new NetEndPoint(adr, port); + } + private static IPAddress s_broadcastAddress; public static IPAddress GetCachedBroadcastAddress() { @@ -86,18 +96,38 @@ public static IPAddress GetCachedBroadcastAddress() } /// - /// Get IPv4 address from notation (xxx.xxx.xxx.xxx) or hostname (asynchronous version) + /// Get IPv4 or IPv6 address from notation (xxx.xxx.xxx.xxx or xxxx:xxxx:...:xxxx) or hostname (asynchronous version) /// public static void ResolveAsync(string ipOrHost, ResolveAddressCallback callback) + { + + } + + /// + /// Get IPv4 or IPv6 address from notation (xxx.xxx.xxx.xxx or xxxx:xxxx:...:xxxx) or hostname (asynchronous version) + /// + public static void ResolveAsync(string ipOrHost, AddressFamily? allowedFamily, ResolveAddressCallback callback) { if (string.IsNullOrEmpty(ipOrHost)) throw new ArgumentException("Supplied string must not be empty", "ipOrHost"); + if (allowedFamily != null && allowedFamily != AddressFamily.InterNetwork + && allowedFamily != AddressFamily.InterNetworkV6) + { + throw new ArgumentException("Address family must be either InterNetwork, InterNetworkV6 or null", + nameof(allowedFamily)); + } + ipOrHost = ipOrHost.Trim(); NetAddress ipAddress = null; if (NetAddress.TryParse(ipOrHost, out ipAddress)) { + if (allowedFamily != null && ipAddress.AddressFamily != allowedFamily) + { + callback(null); + return; + } if (ipAddress.AddressFamily == AddressFamily.InterNetwork || ipAddress.AddressFamily == AddressFamily.InterNetworkV6) { callback(ipAddress); @@ -139,11 +169,12 @@ public static void ResolveAsync(string ipOrHost, ResolveAddressCallback callback // check each entry for a valid IP address foreach (var ipCurrent in entry.AddressList) { - if (ipCurrent.AddressFamily == AddressFamily.InterNetwork || ipCurrent.AddressFamily == AddressFamily.InterNetworkV6) - { + var family = ipCurrent.AddressFamily; + if (family != AddressFamily.InterNetwork && family != AddressFamily.InterNetworkV6) + continue; + if (allowedFamily == null || allowedFamily == family) callback(ipCurrent); - return; - } + return; } callback(null); @@ -164,49 +195,85 @@ public static void ResolveAsync(string ipOrHost, ResolveAddressCallback callback } /// - /// Get IPv4 address from notation (xxx.xxx.xxx.xxx) or hostname + /// Get IPv4 or IPv6 address from notation (xxx.xxx.xxx.xxx or xxxx:xxxx:...:xxxx) or hostname /// public static NetAddress Resolve(string ipOrHost) - { - if (string.IsNullOrEmpty(ipOrHost)) - throw new ArgumentException("Supplied string must not be empty", "ipOrHost"); + { + return Resolve(ipOrHost, null); + } - ipOrHost = ipOrHost.Trim(); - NetAddress ipAddress = null; - if (NetAddress.TryParse(ipOrHost, out ipAddress)) - { - if (ipAddress.AddressFamily == AddressFamily.InterNetwork || ipAddress.AddressFamily == AddressFamily.InterNetworkV6) - return ipAddress; - throw new ArgumentException("This method will not currently resolve other than IPv4 or IPv6 addresses"); - } + /// + /// Get IPv4 or IPv6 address from notation (xxx.xxx.xxx.xxx or xxxx:xxxx:...:xxxx) or hostname, + /// taking in an allowed address family to filter resolved addresses by. + /// + /// + /// If is not null, the address returned will only be of the specified family. + /// + /// The hostname or IP address to parse. + /// If not null, the allowed address family to return. + /// + /// A resolved address matching the specified filter if it exists, + /// null if no such address exists or a lookup error occured. + /// + /// + /// is null or empty OR + /// is not one of null, + /// or + /// + public static NetAddress Resolve(string ipOrHost, AddressFamily? allowedFamily) + { + if (string.IsNullOrEmpty(ipOrHost)) + throw new ArgumentException("Supplied string must not be empty", "ipOrHost"); - // ok must be a host name - try + if (allowedFamily != null && allowedFamily != AddressFamily.InterNetwork + && allowedFamily != AddressFamily.InterNetworkV6) { - var addresses = Dns.GetHostAddresses(ipOrHost); - if (addresses == null) - return null; - foreach (var address in addresses) - { - if (address.AddressFamily == AddressFamily.InterNetwork || address.AddressFamily == AddressFamily.InterNetworkV6) - return address; - } - return null; - } - catch (SocketException ex) - { - if (ex.SocketErrorCode == SocketError.HostNotFound) - { - //LogWrite(string.Format(CultureInfo.InvariantCulture, "Failed to resolve host '{0}'.", ipOrHost)); - return null; - } - else - { - throw; - } + throw new ArgumentException("Address family must be either InterNetwork, InterNetworkV6 or null", + nameof(allowedFamily)); } - } + + ipOrHost = ipOrHost.Trim(); + + NetAddress ipAddress = null; + if (NetAddress.TryParse(ipOrHost, out ipAddress)) + { + if (allowedFamily != null && ipAddress.AddressFamily != allowedFamily) + return null; + if (ipAddress.AddressFamily == AddressFamily.InterNetwork || ipAddress.AddressFamily == AddressFamily.InterNetworkV6) + return ipAddress; + throw new ArgumentException("This method will not currently resolve other than IPv4 or IPv6 addresses"); + } + + // ok must be a host name + try + { + var addresses = Dns.GetHostAddresses(ipOrHost); + if (addresses == null) + return null; + foreach (var address in addresses) + { + var family = address.AddressFamily; + if (family != AddressFamily.InterNetwork && family != AddressFamily.InterNetworkV6) + continue; + if (allowedFamily == null || allowedFamily == family) + return address; + } + return null; + } + catch (SocketException ex) + { + if (ex.SocketErrorCode == SocketError.HostNotFound) + { + //LogWrite(string.Format(CultureInfo.InvariantCulture, "Failed to resolve host '{0}'.", ipOrHost)); + return null; + } + else + { + throw; + } + } + } /// /// Create a hex string from an Int64 value