-
Notifications
You must be signed in to change notification settings - Fork 84
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ServiceDiscovery): Only return reachable address records
- Loading branch information
1 parent
cd9b00c
commit 723b659
Showing
3 changed files
with
197 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Net; | ||
using System.Net.NetworkInformation; | ||
using System.Net.Sockets; | ||
using System.Text; | ||
|
||
namespace Makaretu.Dns | ||
{ | ||
/// <summary> | ||
/// Extensions for <see cref="IPAddress"/>. | ||
/// </summary> | ||
public static class IPAddressExtensions | ||
{ | ||
/// <summary> | ||
/// Gets the subnet mask associated with the IP address. | ||
/// </summary> | ||
/// <param name="address"> | ||
/// An IP Addresses. | ||
/// </param> | ||
/// <returns> | ||
/// The subnet mask; ror example "127.0.0.1" returns "255.0.0.0". | ||
/// Or <b>null</b> When <paramref name="address"/> does not belong to | ||
/// the localhost. | ||
/// s</returns> | ||
public static IPAddress GetSubnetMask(this IPAddress address) | ||
{ | ||
return NetworkInterface.GetAllNetworkInterfaces() | ||
.SelectMany(nic => nic.GetIPProperties().UnicastAddresses) | ||
.Where(a => a.Address.Equals(address)) | ||
.Select(a => a.IPv4Mask) | ||
.FirstOrDefault(); | ||
} | ||
|
||
/// <summary> | ||
/// Determines if the local IP address can be used by the | ||
/// remote address. | ||
/// </summary> | ||
/// <param name="local"></param> | ||
/// <param name="remote"></param> | ||
/// <returns> | ||
/// <b>true</b> if <paramref name="local"/> can be used by <paramref name="remote"/>; | ||
/// otherwise, <b>false</b>. | ||
/// </returns> | ||
public static bool IsReachable(this IPAddress local, IPAddress remote) | ||
{ | ||
// Loopback addresses are only reachable when the remote is | ||
// the same host. | ||
if (local.Equals(IPAddress.Loopback) || local.Equals(IPAddress.IPv6Loopback)) | ||
{ | ||
return MulticastService.GetIPAddresses().Contains(remote); | ||
} | ||
|
||
// IPv4 addresses are reachable when on the same subnet. | ||
if (local.AddressFamily == AddressFamily.InterNetwork && remote.AddressFamily == AddressFamily.InterNetwork) | ||
{ | ||
var mask = local.GetSubnetMask(); | ||
if (mask != null) | ||
{ | ||
var network = IPNetwork.Parse(local, mask); | ||
return network.Contains(remote); | ||
} | ||
} | ||
|
||
// IPv6 link local addresses are reachabe when using the same scope id. | ||
if (local.AddressFamily == AddressFamily.InterNetworkV6 && remote.AddressFamily == AddressFamily.InterNetworkV6) | ||
{ | ||
if (local.IsIPv6LinkLocal || remote.IsIPv6LinkLocal) | ||
{ | ||
return local.Equals(remote); | ||
} | ||
} | ||
|
||
// Can not determine reachability, assume that network routing can do it. | ||
return true; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
using Makaretu.Dns; | ||
using Microsoft.VisualStudio.TestTools.UnitTesting; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Net; | ||
using System.Net.NetworkInformation; | ||
using System.Net.Sockets; | ||
|
||
namespace Makaretu.Dns | ||
{ | ||
|
||
[TestClass] | ||
public class IPAddressExtensionsTest | ||
{ | ||
[TestMethod] | ||
public void SubnetMask_Ipv4Loopback() | ||
{ | ||
var mask = IPAddress.Loopback.GetSubnetMask(); | ||
Assert.AreEqual(IPAddress.Parse("255.0.0.0"), mask); | ||
var network = IPNetwork.Parse(IPAddress.Loopback, mask); | ||
Console.Write(network.ToString()); | ||
} | ||
|
||
[TestMethod] | ||
public void SubnetMask_Ipv6Loopback() | ||
{ | ||
var mask = IPAddress.IPv6Loopback.GetSubnetMask(); | ||
Assert.AreEqual(IPAddress.Parse("0.0.0.0"), mask); | ||
} | ||
|
||
[TestMethod] | ||
public void SubmetMask_NotLocalhost() | ||
{ | ||
var mask = IPAddress.Parse("1.1.1.1").GetSubnetMask(); | ||
Assert.IsNull(mask); | ||
} | ||
|
||
[TestMethod] | ||
public void SubnetMask_All() | ||
{ | ||
foreach (var a in MulticastService.GetIPAddresses()) | ||
{ | ||
var network = IPNetwork.Parse(a, a.GetSubnetMask()); | ||
|
||
Console.WriteLine($"{a} mask {a.GetSubnetMask()} {network}"); | ||
|
||
Assert.IsTrue(network.Contains(a), $"{a} is not reachable"); | ||
} | ||
} | ||
|
||
[TestMethod] | ||
public void LinkLocal() | ||
{ | ||
foreach (var a in MulticastService.GetIPAddresses()) | ||
{ | ||
Console.WriteLine($"{a} ll={a.IsIPv6LinkLocal} ss={a.IsIPv6SiteLocal}"); | ||
} | ||
} | ||
|
||
[TestMethod] | ||
public void Reachable_Loopback_From_Localhost() | ||
{ | ||
var me = IPAddress.Loopback; | ||
foreach (var a in MulticastService.GetIPAddresses()) | ||
{ | ||
Assert.IsTrue(me.IsReachable(a), $"{a}"); | ||
} | ||
Assert.IsFalse(me.IsReachable(IPAddress.Parse("1.1.1.1"))); | ||
Assert.IsFalse(me.IsReachable(IPAddress.Parse("2606:4700:4700::1111"))); | ||
|
||
me = IPAddress.IPv6Loopback; | ||
foreach (var a in MulticastService.GetIPAddresses()) | ||
{ | ||
Assert.IsTrue(me.IsReachable(a), $"{a}"); | ||
} | ||
Assert.IsFalse(me.IsReachable(IPAddress.Parse("1.1.1.1"))); | ||
Assert.IsFalse(me.IsReachable(IPAddress.Parse("2606:4700:4700::1111"))); | ||
} | ||
|
||
[TestMethod] | ||
public void Reachable_Ipv4() | ||
{ | ||
var me = MulticastService.GetIPAddresses() | ||
.First(a => a.AddressFamily == AddressFamily.InterNetwork && !IPAddress.IsLoopback(a)); | ||
Assert.IsTrue(me.IsReachable(me)); | ||
Assert.IsFalse(me.IsReachable(IPAddress.Parse("1.1.1.1"))); | ||
|
||
var nat = IPAddress.Parse("165.84.19.151"); // NAT PCP assigned address | ||
Assert.IsTrue(nat.IsReachable(IPAddress.Parse("1.1.1.1"))); | ||
} | ||
|
||
[TestMethod] | ||
public void Reachable_Ipv6_LinkLocal() | ||
{ | ||
var me1 = IPAddress.Parse("fe80::1:2:3:4%1"); | ||
var me2 = IPAddress.Parse("fe80::1:2:3:4%2"); | ||
var me5 = IPAddress.Parse("fe80::1:2:3:5%1"); | ||
Assert.IsTrue(me1.IsReachable(me1)); | ||
Assert.IsTrue(me2.IsReachable(me2)); | ||
Assert.IsFalse(me1.IsReachable(me2)); | ||
Assert.IsFalse(me1.IsReachable(me5)); | ||
} | ||
} | ||
} |