diff --git a/src/Middleware/HttpOverrides/src/ForwardedHeadersOptions.cs b/src/Middleware/HttpOverrides/src/ForwardedHeadersOptions.cs
index ceb8ab32b997..6715668688d4 100644
--- a/src/Middleware/HttpOverrides/src/ForwardedHeadersOptions.cs
+++ b/src/Middleware/HttpOverrides/src/ForwardedHeadersOptions.cs
@@ -3,6 +3,7 @@
using Microsoft.AspNetCore.HttpOverrides;
using IPAddress = System.Net.IPAddress;
+using IPNetwork = System.Net.IPNetwork;
namespace Microsoft.AspNetCore.Builder;
@@ -83,7 +84,10 @@ public class ForwardedHeadersOptions
///
/// Address ranges of known proxies to accept forwarded headers from.
///
- public IList KnownNetworks { get; } = new List() { new IPNetwork(IPAddress.Loopback, 8) };
+ public IList KnownNetworks { get; } = new List()
+ {
+ IPNetwork.Parse("127.0.0.0/8")
+ };
///
/// The allowed values from x-forwarded-host. If the list is empty then all hosts are allowed.
diff --git a/src/Middleware/HttpOverrides/src/IPNetwork.cs b/src/Middleware/HttpOverrides/src/IPNetwork.cs
index 9888de2d1535..e28862bf229e 100644
--- a/src/Middleware/HttpOverrides/src/IPNetwork.cs
+++ b/src/Middleware/HttpOverrides/src/IPNetwork.cs
@@ -3,204 +3,65 @@
using System.Diagnostics.CodeAnalysis;
using System.Net;
-using System.Net.Sockets;
namespace Microsoft.AspNetCore.HttpOverrides;
///
/// A representation of an IP network based on CIDR notation.
///
+[System.Obsolete("Please use System.Net.IPNetwork instead")]
public class IPNetwork
{
+ private readonly System.Net.IPNetwork _network;
+
///
/// Create a new with the specified and prefix length.
///
/// The .
/// The prefix length.
/// is out of range.
- public IPNetwork(IPAddress prefix, int prefixLength) : this(prefix, prefixLength, true)
+ public IPNetwork(IPAddress prefix, int prefixLength)
{
+ _network = new(prefix, prefixLength);
}
- private IPNetwork(IPAddress prefix, int prefixLength, bool checkPrefixLengthRange)
- {
- if (checkPrefixLengthRange &&
- !IsValidPrefixLengthRange(prefix, prefixLength))
- {
- throw new ArgumentOutOfRangeException(nameof(prefixLength), "The prefix length was out of range.");
- }
-
- Prefix = prefix;
- PrefixLength = prefixLength;
- PrefixBytes = Prefix.GetAddressBytes();
- Mask = CreateMask();
- }
+ private IPNetwork(System.Net.IPNetwork network) => _network = network;
///
/// Get the that represents the prefix for the network.
///
- public IPAddress Prefix { get; }
-
- private byte[] PrefixBytes { get; }
+ public IPAddress Prefix => _network.BaseAddress;
///
/// The CIDR notation of the subnet mask
///
- public int PrefixLength { get; }
-
- private byte[] Mask { get; }
+ public int PrefixLength => _network.PrefixLength;
///
/// Determine whether a given The is part of the IP network.
///
/// The .
/// if the is part of the IP network. Otherwise, .
- public bool Contains(IPAddress address)
- {
- if (Prefix.AddressFamily != address.AddressFamily)
- {
- return false;
- }
-
- var addressBytes = address.GetAddressBytes();
- for (int i = 0; i < PrefixBytes.Length && Mask[i] != 0; i++)
- {
- if ((PrefixBytes[i] & Mask[i]) != (addressBytes[i] & Mask[i]))
- {
- return false;
- }
- }
+ public bool Contains(IPAddress address) => _network.Contains(address);
- return true;
- }
+ ///
+ public static IPNetwork Parse(ReadOnlySpan networkSpan) => System.Net.IPNetwork.Parse(networkSpan);
- private byte[] CreateMask()
- {
- var mask = new byte[PrefixBytes.Length];
- int remainingBits = PrefixLength;
- int i = 0;
- while (remainingBits >= 8)
- {
- mask[i] = 0xFF;
- i++;
- remainingBits -= 8;
- }
- if (remainingBits > 0)
- {
- mask[i] = (byte)(0xFF << (8 - remainingBits));
- }
-
- return mask;
- }
-
- private static bool IsValidPrefixLengthRange(IPAddress prefix, int prefixLength)
- {
- if (prefixLength < 0)
- {
- return false;
- }
-
- return prefix.AddressFamily switch
- {
- AddressFamily.InterNetwork => prefixLength <= 32,
- AddressFamily.InterNetworkV6 => prefixLength <= 128,
- _ => true
- };
- }
-
- ///
- /// Converts the specified of representation of
- /// an IP address and a prefix length to its equivalent.
- ///
- /// The of to convert, in CIDR notation.
- ///
- ///The equivalent to the IP address and prefix length contained in .
- ///
- /// is not in the correct format.
- /// The prefix length contained in is out of range.
- ///
- public static IPNetwork Parse(ReadOnlySpan networkSpan)
+ ///
+ public static bool TryParse(ReadOnlySpan networkSpan, [NotNullWhen(true)] out IPNetwork? network)
{
- if (!TryParseComponents(networkSpan, out var prefix, out var prefixLength))
- {
- throw new FormatException("An invalid IP address or prefix length was specified.");
- }
-
- if (!IsValidPrefixLengthRange(prefix, prefixLength))
+ if (System.Net.IPNetwork.TryParse(networkSpan, out var ipNetwork))
{
- throw new ArgumentOutOfRangeException(nameof(networkSpan), "The prefix length was out of range.");
+ network = ipNetwork;
+ return true;
}
- return new IPNetwork(prefix, prefixLength, false);
+ network = null;
+ return false;
}
///
- /// Converts the specified of representation of
- /// an IP address and a prefix length to its equivalent, and returns a value
- /// that indicates whether the conversion succeeded.
+ /// Convert to implicitly
///
- /// The of to validate.
- ///
- /// When this method returns, contains the equivalent to the IP Address
- /// and prefix length contained in , if the conversion succeeded,
- /// or if the conversion failed. This parameter is passed uninitialized.
- ///
- ///
- /// if the parameter was
- /// converted successfully; otherwise .
- ///
- ///
- public static bool TryParse(ReadOnlySpan networkSpan, [NotNullWhen(true)] out IPNetwork? network)
- {
- network = null;
-
- if (!TryParseComponents(networkSpan, out var prefix, out var prefixLength))
- {
- return false;
- }
-
- if (!IsValidPrefixLengthRange(prefix, prefixLength))
- {
- return false;
- }
-
- network = new IPNetwork(prefix, prefixLength, false);
- return true;
- }
-
- ///
- ///
- /// The specified representation must be expressed using CIDR (Classless Inter-Domain Routing) notation, or 'slash notation',
- /// which contains an IPv4 or IPv6 address and the subnet mask prefix length, separated by a forward slash.
- ///
- ///
- /// e.g. "192.168.0.1/31" for IPv4, "2001:db8:3c4d::1/127" for IPv6
- ///
- ///
- private static bool TryParseComponents(
- ReadOnlySpan networkSpan,
- [NotNullWhen(true)] out IPAddress? prefix,
- out int prefixLength)
- {
- prefix = null;
- prefixLength = default;
-
- var forwardSlashIndex = networkSpan.IndexOf('/');
- if (forwardSlashIndex < 0)
- {
- return false;
- }
-
- if (!IPAddress.TryParse(networkSpan.Slice(0, forwardSlashIndex), out prefix))
- {
- return false;
- }
-
- if (!int.TryParse(networkSpan.Slice(forwardSlashIndex + 1), out prefixLength))
- {
- return false;
- }
-
- return true;
- }
+ public static implicit operator IPNetwork(System.Net.IPNetwork ipNetwork) => new IPNetwork(ipNetwork);
}
diff --git a/src/Middleware/HttpOverrides/src/PublicAPI.Unshipped.txt b/src/Middleware/HttpOverrides/src/PublicAPI.Unshipped.txt
index 7dc5c58110bf..81fc23c7a834 100644
--- a/src/Middleware/HttpOverrides/src/PublicAPI.Unshipped.txt
+++ b/src/Middleware/HttpOverrides/src/PublicAPI.Unshipped.txt
@@ -1 +1,4 @@
#nullable enable
+*REMOVED*Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.KnownNetworks.get -> System.Collections.Generic.IList!
+Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.KnownNetworks.get -> System.Collections.Generic.IList!
+static Microsoft.AspNetCore.HttpOverrides.IPNetwork.implicit operator Microsoft.AspNetCore.HttpOverrides.IPNetwork!(System.Net.IPNetwork ipNetwork) -> Microsoft.AspNetCore.HttpOverrides.IPNetwork!
diff --git a/src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs b/src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs
index 4fd1341acc45..96c1242cc5f3 100644
--- a/src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs
+++ b/src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs
@@ -1072,7 +1072,7 @@ public async Task PartiallyEnabledForwardsPartiallyChangesRequest()
[InlineData("22.33.44.55,::ffff:172.123.142.121", "172.123.142.121", "", "22.33.44.55")]
[InlineData("22.33.44.55,::ffff:172.123.142.121", "::ffff:172.123.142.121", "", "22.33.44.55")]
[InlineData("22.33.44.55,::ffff:172.123.142.121,172.32.24.23", "", "172.0.0.0/8", "22.33.44.55")]
- [InlineData("2a00:1450:4009:802::200e,2a02:26f0:2d:183::356e,::ffff:172.123.142.121,172.32.24.23", "", "172.0.0.0/8,2a02:26f0:2d:183::1/64", "2a00:1450:4009:802::200e")]
+ [InlineData("2a00:1450:4009:802::200e,2a02:26f0:2d:183::356e,::ffff:172.123.142.121,172.32.24.23", "", "172.0.0.0/8,2a02:26f0:2d:183::/64", "2a00:1450:4009:802::200e")]
[InlineData("22.33.44.55,2a02:26f0:2d:183::356e,::ffff:127.0.0.1", "2a02:26f0:2d:183::356e", "", "22.33.44.55")]
public async Task XForwardForIPv4ToIPv6Mapping(string forHeader, string knownProxies, string knownNetworks, string expectedRemoteIp)
{
@@ -1092,7 +1092,7 @@ public async Task XForwardForIPv4ToIPv6Mapping(string forHeader, string knownPro
var knownNetworkParts = knownNetwork.Split('/');
var networkIp = IPAddress.Parse(knownNetworkParts[0]);
var prefixLength = int.Parse(knownNetworkParts[1], CultureInfo.InvariantCulture);
- options.KnownNetworks.Add(new IPNetwork(networkIp, prefixLength));
+ options.KnownNetworks.Add(new System.Net.IPNetwork(networkIp, prefixLength));
}
using var host = new HostBuilder()
diff --git a/src/Middleware/HttpOverrides/test/IPNetworkTest.cs b/src/Middleware/HttpOverrides/test/IPNetworkTest.cs
deleted file mode 100644
index c8f33f7a333b..000000000000
--- a/src/Middleware/HttpOverrides/test/IPNetworkTest.cs
+++ /dev/null
@@ -1,197 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-using System.Net;
-
-namespace Microsoft.AspNetCore.HttpOverrides;
-
-public class IPNetworkTest
-{
- [Theory]
- [InlineData("10.1.1.0", 8, "10.1.1.10")]
- [InlineData("174.0.0.0", 7, "175.1.1.10")]
- [InlineData("10.174.0.0", 15, "10.175.1.10")]
- [InlineData("10.168.0.0", 14, "10.171.1.10")]
- [InlineData("192.168.0.1", 31, "192.168.0.0")]
- [InlineData("192.168.0.1", 31, "192.168.0.1")]
- [InlineData("192.168.0.1", 32, "192.168.0.1")]
- [InlineData("192.168.1.1", 0, "0.0.0.0")]
- [InlineData("192.168.1.1", 0, "255.255.255.255")]
- [InlineData("2001:db8:3c4d::", 127, "2001:db8:3c4d::1")]
- [InlineData("2001:db8:3c4d::1", 128, "2001:db8:3c4d::1")]
- [InlineData("2001:db8:3c4d::1", 0, "::")]
- [InlineData("2001:db8:3c4d::1", 0, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")]
- public void Contains_Positive(string prefixText, int length, string addressText)
- {
- var network = new IPNetwork(IPAddress.Parse(prefixText), length);
- Assert.True(network.Contains(IPAddress.Parse(addressText)));
- }
-
- [Theory]
- [InlineData("10.1.0.0", 16, "10.2.1.10")]
- [InlineData("174.0.0.0", 7, "173.1.1.10")]
- [InlineData("10.174.0.0", 15, "10.173.1.10")]
- [InlineData("10.168.0.0", 14, "10.172.1.10")]
- [InlineData("192.168.0.1", 31, "192.168.0.2")]
- [InlineData("192.168.0.1", 32, "192.168.0.0")]
- [InlineData("2001:db8:3c4d::", 127, "2001:db8:3c4d::2")]
- public void Contains_Negative(string prefixText, int length, string addressText)
- {
- var network = new IPNetwork(IPAddress.Parse(prefixText), length);
- Assert.False(network.Contains(IPAddress.Parse(addressText)));
- }
-
- [Theory]
- [InlineData("192.168.1.1", 0)]
- [InlineData("192.168.1.1", 32)]
- [InlineData("2001:db8:3c4d::1", 0)]
- [InlineData("2001:db8:3c4d::1", 128)]
- public void Ctor_WithValidFormat_IsSuccessfullyCreated(string prefixText, int prefixLength)
- {
- // Arrange
- var address = IPAddress.Parse(prefixText);
-
- // Act
- var network = new IPNetwork(address, prefixLength);
-
- // Assert
- Assert.Equal(prefixText, network.Prefix.ToString());
- Assert.Equal(prefixLength, network.PrefixLength);
- }
-
- [Theory]
- [InlineData("192.168.1.1", -1)]
- [InlineData("192.168.1.1", 33)]
- [InlineData("2001:db8:3c4d::1", -1)]
- [InlineData("2001:db8:3c4d::1", 129)]
- public void Ctor_WithPrefixLengthOutOfRange_ThrowsArgumentOutOfRangeException(string prefixText, int prefixLength)
- {
- // Arrange
- var address = IPAddress.Parse(prefixText);
-
- // Act
- var ex = Assert.Throws(() => new IPNetwork(address, prefixLength));
-
- // Assert
- Assert.StartsWith("The prefix length was out of range.", ex.Message);
- }
-
- [Theory]
- [MemberData(nameof(ValidPrefixWithPrefixLengthData))]
- public void Parse_WithValidFormat_ParsedCorrectly(string input, string expectedPrefix, int expectedPrefixLength)
- {
- // Act
- var network = IPNetwork.Parse(input);
-
- // Assert
- Assert.Equal(expectedPrefix, network.Prefix.ToString());
- Assert.Equal(expectedPrefixLength, network.PrefixLength);
- }
-
- [Theory]
- [InlineData(null)]
- [MemberData(nameof(InvalidPrefixOrPrefixLengthData))]
- public void Parse_WithInvalidFormat_ThrowsFormatException(string input)
- {
- // Arrange & Act & Assert
- var ex = Assert.Throws(() => IPNetwork.Parse(input));
- Assert.Equal("An invalid IP address or prefix length was specified.", ex.Message);
- }
-
- [Theory]
- [MemberData(nameof(PrefixLengthOutOfRangeData))]
- public void Parse_WithOutOfRangePrefixLength_ThrowsArgumentOutOfRangeException(string input)
- {
- // Arrange & Act & Assert
- var ex = Assert.Throws(() => IPNetwork.Parse(input));
- Assert.StartsWith("The prefix length was out of range.", ex.Message);
- }
-
- [Theory]
- [MemberData(nameof(ValidPrefixWithPrefixLengthData))]
- public void TryParse_WithValidFormat_ParsedCorrectly(string input, string expectedPrefix, int expectedPrefixLength)
- {
- // Act
- var result = IPNetwork.TryParse(input, out var network);
-
- // Assert
- Assert.True(result);
- Assert.NotNull(network);
- Assert.Equal(expectedPrefix, network.Prefix.ToString());
- Assert.Equal(expectedPrefixLength, network.PrefixLength);
- }
-
- [Theory]
- [InlineData(null)]
- [MemberData(nameof(InvalidPrefixOrPrefixLengthData))]
- [MemberData(nameof(PrefixLengthOutOfRangeData))]
- public void TryParse_WithInvalidFormat_ReturnsFalse(string input)
- {
- // Act
- var result = IPNetwork.TryParse(input, out var network);
-
- // Assert
- Assert.False(result);
- Assert.Null(network);
- }
-
- public static TheoryData ValidPrefixWithPrefixLengthData() => new()
- {
- // IPv4
- { "10.1.0.0/16", "10.1.0.0", 16 },
- { "10.1.1.0/8", "10.1.1.0", 8 },
- { "174.0.0.0/7", "174.0.0.0", 7 },
- { "10.174.0.0/15", "10.174.0.0", 15 },
- { "10.168.0.0/14", "10.168.0.0", 14 },
- { "192.168.0.1/31", "192.168.0.1", 31 },
- { "192.168.0.1/31", "192.168.0.1", 31 },
- { "192.168.0.1/32", "192.168.0.1", 32 },
- { "192.168.1.1/0", "192.168.1.1", 0 },
- { "192.168.1.1/0", "192.168.1.1", 0 },
-
- // IPv6
- { "2001:db8:3c4d::/127", "2001:db8:3c4d::", 127 },
- { "2001:db8:3c4d::1/128", "2001:db8:3c4d::1", 128 },
- { "2001:db8:3c4d::1/0", "2001:db8:3c4d::1", 0 },
- { "2001:db8:3c4d::1/0", "2001:db8:3c4d::1", 0 }
- };
-
- public static TheoryData InvalidPrefixOrPrefixLengthData() => new()
- {
- string.Empty,
- "abcdefg",
-
- // Missing forward slash
- "10.1.0.016",
- "2001:db8:3c4d::1127",
-
- // Invalid prefix
- "/16",
- "10.1./16",
- "10.1.0./16",
- "10.1.ABC.0/16",
- "200123:db8:3c4d::/127",
- ":db8:3c4d::/127",
- "2001:?:3c4d::1/0",
-
- // Invalid prefix length
- "10.1.0.0/",
- "10.1.0.0/16-",
- "10.1.0.0/ABC",
- "2001:db8:3c4d::/",
- "2001:db8:3c4d::1/128-",
- "2001:db8:3c4d::1/ABC"
- };
-
- public static TheoryData PrefixLengthOutOfRangeData() => new()
- {
- // Negative prefix length
- "10.1.0.0/-16",
- "2001:db8:3c4d::/-127",
-
- // Prefix length out of range (IPv4)
- "10.1.0.0/33",
-
- // Prefix length out of range (IPv6)
- "2001:db8:3c4d::/129"
- };
-}