diff --git a/src/Smdn.Net.MuninNode/Smdn.Net.MuninNode/AddressListAccessRule.cs b/src/Smdn.Net.MuninNode/Smdn.Net.MuninNode/AddressListAccessRule.cs index 9723a86..248fe2b 100644 --- a/src/Smdn.Net.MuninNode/Smdn.Net.MuninNode/AddressListAccessRule.cs +++ b/src/Smdn.Net.MuninNode/Smdn.Net.MuninNode/AddressListAccessRule.cs @@ -9,10 +9,15 @@ namespace Smdn.Net.MuninNode; internal sealed class AddressListAccessRule : IAccessRule { private readonly IReadOnlyList addressListAllowFrom; + private readonly bool shouldConsiderIPv4MappedIPv6Address; - public AddressListAccessRule(IReadOnlyList addressListAllowFrom) + public AddressListAccessRule( + IReadOnlyList addressListAllowFrom, + bool shouldConsiderIPv4MappedIPv6Address + ) { this.addressListAllowFrom = addressListAllowFrom ?? throw new ArgumentNullException(nameof(addressListAllowFrom)); + this.shouldConsiderIPv4MappedIPv6Address = shouldConsiderIPv4MappedIPv6Address; } public bool IsAcceptable(IPEndPoint remoteEndPoint) @@ -23,10 +28,22 @@ public bool IsAcceptable(IPEndPoint remoteEndPoint) var remoteAddress = remoteEndPoint.Address; foreach (var addressAllowFrom in addressListAllowFrom) { - if (addressAllowFrom.AddressFamily == AddressFamily.InterNetwork) { - // test for client acceptability by IPv4 address - if (remoteAddress.IsIPv4MappedToIPv6) + if (shouldConsiderIPv4MappedIPv6Address) { + if ( + remoteAddress.IsIPv4MappedToIPv6 && + addressAllowFrom.AddressFamily == AddressFamily.InterNetwork + ) { + // test for client acceptability by IPv4 address remoteAddress = remoteAddress.MapToIPv4(); + } + + if ( + remoteAddress.AddressFamily == AddressFamily.InterNetwork && + addressAllowFrom.AddressFamily == AddressFamily.InterNetworkV6 + ) { + // test for client acceptability by IPv6 address + remoteAddress = remoteAddress.MapToIPv6(); + } } if (addressAllowFrom.Equals(remoteAddress)) diff --git a/src/Smdn.Net.MuninNode/Smdn.Net.MuninNode/IAccessRuleServiceCollectionExtensions.cs b/src/Smdn.Net.MuninNode/Smdn.Net.MuninNode/IAccessRuleServiceCollectionExtensions.cs index 96cf384..463715a 100644 --- a/src/Smdn.Net.MuninNode/Smdn.Net.MuninNode/IAccessRuleServiceCollectionExtensions.cs +++ b/src/Smdn.Net.MuninNode/Smdn.Net.MuninNode/IAccessRuleServiceCollectionExtensions.cs @@ -15,11 +15,29 @@ public static class IAccessRuleServiceCollectionExtensions { public static IServiceCollection AddMuninNodeAccessRule( this IServiceCollection services, IReadOnlyList addressListAllowFrom + ) + => AddMuninNodeAccessRule( + services: services, + addressListAllowFrom: addressListAllowFrom ?? throw new ArgumentNullException(nameof(addressListAllowFrom)), + shouldConsiderIPv4MappedIPv6Address: true + ); + + /// The to add services to. + /// The indicates the read-only list of addresses allowed to access . + /// + /// The value indicating whether the address resolution to be aware or not to be aware that + /// the IP address is an IPv4-mapped IPv6 address when comparing IP address. + /// + public static IServiceCollection AddMuninNodeAccessRule( + this IServiceCollection services, + IReadOnlyList addressListAllowFrom, + bool shouldConsiderIPv4MappedIPv6Address ) => AddMuninNodeAccessRule( services: services ?? throw new ArgumentNullException(nameof(services)), accessRule: new AddressListAccessRule( - addressListAllowFrom: addressListAllowFrom ?? throw new ArgumentNullException(nameof(addressListAllowFrom)) + addressListAllowFrom: addressListAllowFrom ?? throw new ArgumentNullException(nameof(addressListAllowFrom)), + shouldConsiderIPv4MappedIPv6Address: shouldConsiderIPv4MappedIPv6Address ) ); diff --git a/src/Smdn.Net.MuninNode/Smdn.Net.MuninNode/LocalNode.Create.cs b/src/Smdn.Net.MuninNode/Smdn.Net.MuninNode/LocalNode.Create.cs index 28dc002..d473413 100644 --- a/src/Smdn.Net.MuninNode/Smdn.Net.MuninNode/LocalNode.Create.cs +++ b/src/Smdn.Net.MuninNode/Smdn.Net.MuninNode/LocalNode.Create.cs @@ -128,7 +128,7 @@ public static LocalNode Create( pluginProvider: pluginProvider ?? throw new ArgumentNullException(nameof(pluginProvider)), hostName: string.IsNullOrEmpty(hostName) ? DefaultHostName : hostName, port: port, - accessRule: addressListAllowFrom is null ? null : new AddressListAccessRule(addressListAllowFrom), + accessRule: addressListAllowFrom is null ? null : new AddressListAccessRule(addressListAllowFrom, shouldConsiderIPv4MappedIPv6Address: true), serviceProvider: serviceProvider ); } diff --git a/tests/Smdn.Net.MuninNode/Smdn.Net.MuninNode/IAccessRuleServiceCollectionExtensions.cs b/tests/Smdn.Net.MuninNode/Smdn.Net.MuninNode/IAccessRuleServiceCollectionExtensions.cs index fe255ca..f668f7b 100644 --- a/tests/Smdn.Net.MuninNode/Smdn.Net.MuninNode/IAccessRuleServiceCollectionExtensions.cs +++ b/tests/Smdn.Net.MuninNode/Smdn.Net.MuninNode/IAccessRuleServiceCollectionExtensions.cs @@ -49,4 +49,70 @@ public void AddMuninNodeAccessRule_IAccessRule_ArgumentNull() () => services.AddMuninNodeAccessRule((IAccessRule)null!) ); } + + private static System.Collections.IEnumerable YieldTestCases_AddMuninNodeAccessRule_IReadOnlyListOfIPAddress_ShouldConsiderIPv4MappedIPv6Address() + { + var addressIPv4 = IPAddress.Parse("192.0.2.255"); + var addressIPv6MappedIPv4 = IPAddress.Parse("::ffff:192.0.2.255"); + + foreach (var shouldConsiderIPv4MappedIPv6Address in new[] { true, false }) { + yield return new object[] { + new[] { addressIPv4 }, + shouldConsiderIPv4MappedIPv6Address, + addressIPv6MappedIPv4, + shouldConsiderIPv4MappedIPv6Address + }; + + yield return new object[] { + new[] { addressIPv6MappedIPv4 }, + shouldConsiderIPv4MappedIPv6Address, + addressIPv4, + shouldConsiderIPv4MappedIPv6Address + }; + + yield return new object[] { + new[] { addressIPv4, addressIPv6MappedIPv4 }, + shouldConsiderIPv4MappedIPv6Address, + addressIPv6MappedIPv4, + true + }; + + yield return new object[] { + new[] { addressIPv4, addressIPv6MappedIPv4 }, + shouldConsiderIPv4MappedIPv6Address, + addressIPv4, + true + }; + + yield return new object[] { + new[] { addressIPv4, addressIPv6MappedIPv4 }, + shouldConsiderIPv4MappedIPv6Address, + IPAddress.Loopback, + false + }; + } + } + + [TestCaseSource(nameof(YieldTestCases_AddMuninNodeAccessRule_IReadOnlyListOfIPAddress_ShouldConsiderIPv4MappedIPv6Address))] + public void AddMuninNodeAccessRule_IReadOnlyListOfIPAddress_ShouldConsiderIPv4MappedIPv6Address( + IReadOnlyList addressListAllowFrom, + bool shouldConsiderIPv4MappedIPv6Address, + IPAddress remoteAddress, + bool expected + ) + { + var services = new ServiceCollection(); + + services.AddMuninNodeAccessRule( + addressListAllowFrom: addressListAllowFrom, + shouldConsiderIPv4MappedIPv6Address: shouldConsiderIPv4MappedIPv6Address + ); + + var accessRule = services.BuildServiceProvider().GetRequiredService(); + + Assert.That( + accessRule.IsAcceptable(new(remoteAddress, port: 0)), + Is.EqualTo(expected) + ); + } }