Skip to content

Improve verification of IPv4-mapped IPv6 addresses in IAccessRule #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions src/Smdn.Net.MuninNode/Smdn.Net.MuninNode/AddressListAccessRule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,15 @@ namespace Smdn.Net.MuninNode;

internal sealed class AddressListAccessRule : IAccessRule {
private readonly IReadOnlyList<IPAddress> addressListAllowFrom;
private readonly bool shouldConsiderIPv4MappedIPv6Address;

public AddressListAccessRule(IReadOnlyList<IPAddress> addressListAllowFrom)
public AddressListAccessRule(
IReadOnlyList<IPAddress> addressListAllowFrom,
bool shouldConsiderIPv4MappedIPv6Address
)
{
this.addressListAllowFrom = addressListAllowFrom ?? throw new ArgumentNullException(nameof(addressListAllowFrom));
this.shouldConsiderIPv4MappedIPv6Address = shouldConsiderIPv4MappedIPv6Address;
}

public bool IsAcceptable(IPEndPoint remoteEndPoint)
Expand All @@ -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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,29 @@ public static class IAccessRuleServiceCollectionExtensions {
public static IServiceCollection AddMuninNodeAccessRule(
this IServiceCollection services,
IReadOnlyList<IPAddress> addressListAllowFrom
)
=> AddMuninNodeAccessRule(
services: services,
addressListAllowFrom: addressListAllowFrom ?? throw new ArgumentNullException(nameof(addressListAllowFrom)),
shouldConsiderIPv4MappedIPv6Address: true
);

/// <param name="services">The <see cref="IServiceCollection"/> to add services to.</param>
/// <param name="addressListAllowFrom">The <see cref="IReadOnlyList{IPAddress}"/> indicates the read-only list of addresses allowed to access <see cref="NodeBase"/>.</param>
/// <param name="shouldConsiderIPv4MappedIPv6Address">
/// 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.
/// </param>
public static IServiceCollection AddMuninNodeAccessRule(
this IServiceCollection services,
IReadOnlyList<IPAddress> 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
)
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<IPAddress> addressListAllowFrom,
bool shouldConsiderIPv4MappedIPv6Address,
IPAddress remoteAddress,
bool expected
)
{
var services = new ServiceCollection();

services.AddMuninNodeAccessRule(
addressListAllowFrom: addressListAllowFrom,
shouldConsiderIPv4MappedIPv6Address: shouldConsiderIPv4MappedIPv6Address
);

var accessRule = services.BuildServiceProvider().GetRequiredService<IAccessRule>();

Assert.That(
accessRule.IsAcceptable(new(remoteAddress, port: 0)),
Is.EqualTo(expected)
);
}
}