From 8258d9012bebe0795c95f8f295b234a20afa4459 Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Fri, 27 Jun 2025 10:25:21 +0800 Subject: [PATCH 01/26] Update IMicrosoft.AspNetCore.HttpOverrides.PNetwork implementation --- src/Middleware/HttpOverrides/src/IPNetwork.cs | 145 ++---------------- 1 file changed, 17 insertions(+), 128 deletions(-) diff --git a/src/Middleware/HttpOverrides/src/IPNetwork.cs b/src/Middleware/HttpOverrides/src/IPNetwork.cs index 9888de2d1535..4defb9b7c269 100644 --- a/src/Middleware/HttpOverrides/src/IPNetwork.cs +++ b/src/Middleware/HttpOverrides/src/IPNetwork.cs @@ -12,101 +12,35 @@ namespace Microsoft.AspNetCore.HttpOverrides; /// 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) { - } - - 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(); + _network = new(prefix, prefixLength); } /// /// 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; - } - } - - return true; - } - - 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 - }; - } + public bool Contains(IPAddress address) => _network.Contains(address); /// /// Converts the specified of representation of @@ -121,17 +55,7 @@ private static bool IsValidPrefixLengthRange(IPAddress prefix, int prefixLength) /// public static IPNetwork Parse(ReadOnlySpan networkSpan) { - 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)) - { - throw new ArgumentOutOfRangeException(nameof(networkSpan), "The prefix length was out of range."); - } - - return new IPNetwork(prefix, prefixLength, false); + return System.Net.IPNetwork.Parse(networkSpan); } /// @@ -152,55 +76,20 @@ public static IPNetwork Parse(ReadOnlySpan networkSpan) /// 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)) + if (System.Net.IPNetwork.TryParse(networkSpan, out var ipNetwork)) { - return false; + network = ipNetwork; + return true; } - network = new IPNetwork(prefix, prefixLength, false); - return true; + return false; } - /// - /// - /// 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) + /// + /// Convert to implicitly + /// + public static implicit operator IPNetwork(System.Net.IPNetwork ipNetwork) { - 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; + return new IPNetwork(ipNetwork.BaseAddress, ipNetwork.PrefixLength); } } From 8bbc434bab8e8899ce79e57829f11ab31693affc Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Fri, 27 Jun 2025 10:28:02 +0800 Subject: [PATCH 02/26] Update IPNetwork constructor for implicit convert --- src/Middleware/HttpOverrides/src/IPNetwork.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Middleware/HttpOverrides/src/IPNetwork.cs b/src/Middleware/HttpOverrides/src/IPNetwork.cs index 4defb9b7c269..97062fa6d30a 100644 --- a/src/Middleware/HttpOverrides/src/IPNetwork.cs +++ b/src/Middleware/HttpOverrides/src/IPNetwork.cs @@ -25,6 +25,8 @@ public IPNetwork(IPAddress prefix, int prefixLength) _network = new(prefix, prefixLength); } + private IPNetwork(System.Net.IPNetwork network) => _network = network; + /// /// Get the that represents the prefix for the network. /// @@ -90,6 +92,6 @@ public static bool TryParse(ReadOnlySpan networkSpan, [NotNullWhen(true)] /// public static implicit operator IPNetwork(System.Net.IPNetwork ipNetwork) { - return new IPNetwork(ipNetwork.BaseAddress, ipNetwork.PrefixLength); + return new IPNetwork(ipNetwork); } } From 7372e0b1aab72c43e7e86ee88de3ed60288db275 Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Fri, 27 Jun 2025 10:38:20 +0800 Subject: [PATCH 03/26] Obsolete IPNetwork.cs --- src/Middleware/HttpOverrides/src/IPNetwork.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Middleware/HttpOverrides/src/IPNetwork.cs b/src/Middleware/HttpOverrides/src/IPNetwork.cs index 97062fa6d30a..67ebe00af373 100644 --- a/src/Middleware/HttpOverrides/src/IPNetwork.cs +++ b/src/Middleware/HttpOverrides/src/IPNetwork.cs @@ -10,6 +10,7 @@ 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; From 1e44ff76284480fd251620e5a144a34a7aec1544 Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Fri, 27 Jun 2025 11:05:50 +0800 Subject: [PATCH 04/26] Update invalid comment tag --- src/Middleware/HttpOverrides/src/IPNetwork.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Middleware/HttpOverrides/src/IPNetwork.cs b/src/Middleware/HttpOverrides/src/IPNetwork.cs index 67ebe00af373..af25b660565f 100644 --- a/src/Middleware/HttpOverrides/src/IPNetwork.cs +++ b/src/Middleware/HttpOverrides/src/IPNetwork.cs @@ -90,7 +90,7 @@ public static bool TryParse(ReadOnlySpan networkSpan, [NotNullWhen(true)] /// /// Convert to implicitly - /// + /// public static implicit operator IPNetwork(System.Net.IPNetwork ipNetwork) { return new IPNetwork(ipNetwork); From 7e5db2a43be581e0ca4c874ca2a0b148ea0ad84c Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Fri, 27 Jun 2025 12:47:17 +0800 Subject: [PATCH 05/26] fix IPNetwork.cs error --- src/Middleware/HttpOverrides/src/IPNetwork.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/Middleware/HttpOverrides/src/IPNetwork.cs b/src/Middleware/HttpOverrides/src/IPNetwork.cs index af25b660565f..3ee201b7cb92 100644 --- a/src/Middleware/HttpOverrides/src/IPNetwork.cs +++ b/src/Middleware/HttpOverrides/src/IPNetwork.cs @@ -3,7 +3,6 @@ using System.Diagnostics.CodeAnalysis; using System.Net; -using System.Net.Sockets; namespace Microsoft.AspNetCore.HttpOverrides; @@ -56,10 +55,7 @@ public IPNetwork(IPAddress prefix, int prefixLength) /// is not in the correct format. /// The prefix length contained in is out of range. /// - public static IPNetwork Parse(ReadOnlySpan networkSpan) - { - return System.Net.IPNetwork.Parse(networkSpan); - } + public static IPNetwork Parse(ReadOnlySpan networkSpan) => System.Net.IPNetwork.Parse(networkSpan); /// /// Converts the specified of representation of @@ -81,18 +77,16 @@ public static bool TryParse(ReadOnlySpan networkSpan, [NotNullWhen(true)] { if (System.Net.IPNetwork.TryParse(networkSpan, out var ipNetwork)) { - network = ipNetwork; + network = new(ipNetwork); return true; } + network = null; return false; } /// /// Convert to implicitly /// - public static implicit operator IPNetwork(System.Net.IPNetwork ipNetwork) - { - return new IPNetwork(ipNetwork); - } + public static implicit operator IPNetwork(System.Net.IPNetwork ipNetwork) => new IPNetwork(ipNetwork); } From f71426d22a0d82c6c392f1e3eb0a6f4cbbefa3eb Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Fri, 27 Jun 2025 13:02:33 +0800 Subject: [PATCH 06/26] use System.Net.IPNetwork for ForwardedHeadersOptions --- src/Middleware/HttpOverrides/src/ForwardedHeadersOptions.cs | 1 + .../HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Middleware/HttpOverrides/src/ForwardedHeadersOptions.cs b/src/Middleware/HttpOverrides/src/ForwardedHeadersOptions.cs index ceb8ab32b997..cd49867fec1e 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; diff --git a/src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs b/src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs index 4fd1341acc45..69dc2e6dd9aa 100644 --- a/src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs +++ b/src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs @@ -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() From 7993c1f1735d8e737929c1209c2c357b635ff133 Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Fri, 27 Jun 2025 13:06:12 +0800 Subject: [PATCH 07/26] update publicapi --- src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt b/src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt index f650613bac7b..a25bda4c3144 100644 --- a/src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt +++ b/src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt @@ -39,7 +39,7 @@ Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.ForwardedProtoHeaderName.ge Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.ForwardedProtoHeaderName.set -> void Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.ForwardLimit.get -> int? Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.ForwardLimit.set -> void -Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.KnownNetworks.get -> System.Collections.Generic.IList! +Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.KnownNetworks.get -> System.Collections.Generic.IList! Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.KnownProxies.get -> System.Collections.Generic.IList! Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.OriginalForHeaderName.get -> string! Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.OriginalForHeaderName.set -> void From 996592b34d5ed038b1f78461435f4ae1ac125335 Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Fri, 27 Jun 2025 13:22:24 +0800 Subject: [PATCH 08/26] Update IPNetwork.cs comment inherit error --- src/Middleware/HttpOverrides/src/IPNetwork.cs | 29 ++----------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/src/Middleware/HttpOverrides/src/IPNetwork.cs b/src/Middleware/HttpOverrides/src/IPNetwork.cs index 3ee201b7cb92..83db33e2c10d 100644 --- a/src/Middleware/HttpOverrides/src/IPNetwork.cs +++ b/src/Middleware/HttpOverrides/src/IPNetwork.cs @@ -44,35 +44,10 @@ public IPNetwork(IPAddress prefix, int prefixLength) /// if the is part of the IP network. Otherwise, . public bool Contains(IPAddress address) => _network.Contains(address); - /// - /// 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) => System.Net.IPNetwork.Parse(networkSpan); - /// - /// 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. - /// - /// 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) { if (System.Net.IPNetwork.TryParse(networkSpan, out var ipNetwork)) From 2d015aac96bdd75d084e0328bbd9842868682515 Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Fri, 27 Jun 2025 13:33:09 +0800 Subject: [PATCH 09/26] include implicit convert into public api --- src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt b/src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt index a25bda4c3144..478d787edf7f 100644 --- a/src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt +++ b/src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt @@ -100,4 +100,5 @@ static Microsoft.AspNetCore.HttpOverrides.ForwardedHeadersDefaults.XOriginalPref static Microsoft.AspNetCore.HttpOverrides.ForwardedHeadersDefaults.XOriginalProtoHeaderName.get -> string! static Microsoft.AspNetCore.HttpOverrides.IPNetwork.Parse(System.ReadOnlySpan networkSpan) -> Microsoft.AspNetCore.HttpOverrides.IPNetwork! static Microsoft.AspNetCore.HttpOverrides.IPNetwork.TryParse(System.ReadOnlySpan networkSpan, out Microsoft.AspNetCore.HttpOverrides.IPNetwork? network) -> bool +static Microsoft.AspNetCore.HttpOverrides.IPNetwork.implicit operator Microsoft.AspNetCore.HttpOverrides.IPNetwork(System.Net.IPNetwork ipNetwork) -> Microsoft.AspNetCore.HttpOverrides.IPNetwork static Microsoft.Extensions.DependencyInjection.CertificateForwardingServiceExtensions.AddCertificateForwarding(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action! configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! From 634651987735e5895deb0710771f9e8c7a10b6e7 Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Fri, 27 Jun 2025 14:00:48 +0800 Subject: [PATCH 10/26] fix IPNetwork.TryParse comment --- src/Middleware/HttpOverrides/src/IPNetwork.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Middleware/HttpOverrides/src/IPNetwork.cs b/src/Middleware/HttpOverrides/src/IPNetwork.cs index 83db33e2c10d..32ee60db8419 100644 --- a/src/Middleware/HttpOverrides/src/IPNetwork.cs +++ b/src/Middleware/HttpOverrides/src/IPNetwork.cs @@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.HttpOverrides; public class IPNetwork { private readonly System.Net.IPNetwork _network; - + /// /// Create a new with the specified and prefix length. /// @@ -47,7 +47,7 @@ public IPNetwork(IPAddress prefix, int prefixLength) /// public static IPNetwork Parse(ReadOnlySpan networkSpan) => System.Net.IPNetwork.Parse(networkSpan); - /// + /// public static bool TryParse(ReadOnlySpan networkSpan, [NotNullWhen(true)] out IPNetwork? network) { if (System.Net.IPNetwork.TryParse(networkSpan, out var ipNetwork)) From 74be29fd5f7ccb437138e3f651550b8f556efe81 Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Sat, 28 Jun 2025 11:51:40 +0800 Subject: [PATCH 11/26] fix comment and publish api error --- src/Middleware/HttpOverrides/src/IPNetwork.cs | 4 ++-- src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Middleware/HttpOverrides/src/IPNetwork.cs b/src/Middleware/HttpOverrides/src/IPNetwork.cs index 32ee60db8419..e28862bf229e 100644 --- a/src/Middleware/HttpOverrides/src/IPNetwork.cs +++ b/src/Middleware/HttpOverrides/src/IPNetwork.cs @@ -47,12 +47,12 @@ public IPNetwork(IPAddress prefix, int prefixLength) /// public static IPNetwork Parse(ReadOnlySpan networkSpan) => System.Net.IPNetwork.Parse(networkSpan); - /// + /// public static bool TryParse(ReadOnlySpan networkSpan, [NotNullWhen(true)] out IPNetwork? network) { if (System.Net.IPNetwork.TryParse(networkSpan, out var ipNetwork)) { - network = new(ipNetwork); + network = ipNetwork; return true; } diff --git a/src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt b/src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt index 478d787edf7f..c35396b86a80 100644 --- a/src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt +++ b/src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt @@ -100,5 +100,5 @@ static Microsoft.AspNetCore.HttpOverrides.ForwardedHeadersDefaults.XOriginalPref static Microsoft.AspNetCore.HttpOverrides.ForwardedHeadersDefaults.XOriginalProtoHeaderName.get -> string! static Microsoft.AspNetCore.HttpOverrides.IPNetwork.Parse(System.ReadOnlySpan networkSpan) -> Microsoft.AspNetCore.HttpOverrides.IPNetwork! static Microsoft.AspNetCore.HttpOverrides.IPNetwork.TryParse(System.ReadOnlySpan networkSpan, out Microsoft.AspNetCore.HttpOverrides.IPNetwork? network) -> bool -static Microsoft.AspNetCore.HttpOverrides.IPNetwork.implicit operator Microsoft.AspNetCore.HttpOverrides.IPNetwork(System.Net.IPNetwork ipNetwork) -> Microsoft.AspNetCore.HttpOverrides.IPNetwork +static Microsoft.AspNetCore.HttpOverrides.IPNetwork.implicit operator Microsoft.AspNetCore.HttpOverrides.IPNetwork!(System.Net.IPNetwork ipNetwork) -> Microsoft.AspNetCore.HttpOverrides.IPNetwork! static Microsoft.Extensions.DependencyInjection.CertificateForwardingServiceExtensions.AddCertificateForwarding(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action! configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! From 61c9246cd344cfe4c950f576dc8827c7400ca9f6 Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Sat, 28 Jun 2025 12:29:07 +0800 Subject: [PATCH 12/26] fix KnownNetwork PublicAPI --- src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt b/src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt index c35396b86a80..0b4e0ada3f77 100644 --- a/src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt +++ b/src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt @@ -39,7 +39,7 @@ Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.ForwardedProtoHeaderName.ge Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.ForwardedProtoHeaderName.set -> void Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.ForwardLimit.get -> int? Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.ForwardLimit.set -> void -Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.KnownNetworks.get -> System.Collections.Generic.IList! +Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.KnownNetworks.get -> System.Collections.Generic.IList! Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.KnownProxies.get -> System.Collections.Generic.IList! Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.OriginalForHeaderName.get -> string! Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.OriginalForHeaderName.set -> void From dfc068ca55f01090dec0c6027751f613902a2c10 Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Sat, 28 Jun 2025 13:32:28 +0800 Subject: [PATCH 13/26] update PublicAPI --- src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt | 2 +- src/Middleware/HttpOverrides/src/PublicAPI.Unshipped.txt | 3 +++ src/Middleware/HttpOverrides/test/IPNetworkTest.cs | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt b/src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt index 0b4e0ada3f77..837226d1305a 100644 --- a/src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt +++ b/src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt @@ -39,7 +39,7 @@ Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.ForwardedProtoHeaderName.ge Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.ForwardedProtoHeaderName.set -> void Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.ForwardLimit.get -> int? Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.ForwardLimit.set -> void -Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.KnownNetworks.get -> System.Collections.Generic.IList! +Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.KnownNetworks.get -> System.Collections.Generic.IList! Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.KnownProxies.get -> System.Collections.Generic.IList! Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.OriginalForHeaderName.get -> string! Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.OriginalForHeaderName.set -> void 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/IPNetworkTest.cs b/src/Middleware/HttpOverrides/test/IPNetworkTest.cs index c8f33f7a333b..2257139b32c9 100644 --- a/src/Middleware/HttpOverrides/test/IPNetworkTest.cs +++ b/src/Middleware/HttpOverrides/test/IPNetworkTest.cs @@ -4,6 +4,7 @@ namespace Microsoft.AspNetCore.HttpOverrides; +[Obsolete("Keep it only for compatibility verify")] public class IPNetworkTest { [Theory] From a0f13a02b2cd89671f963e04921aa5fdac186184 Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Sat, 28 Jun 2025 13:34:13 +0800 Subject: [PATCH 14/26] remove IPNetwork implicit from PublicAPI.Shipped.txt --- src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt b/src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt index 837226d1305a..f650613bac7b 100644 --- a/src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt +++ b/src/Middleware/HttpOverrides/src/PublicAPI.Shipped.txt @@ -100,5 +100,4 @@ static Microsoft.AspNetCore.HttpOverrides.ForwardedHeadersDefaults.XOriginalPref static Microsoft.AspNetCore.HttpOverrides.ForwardedHeadersDefaults.XOriginalProtoHeaderName.get -> string! static Microsoft.AspNetCore.HttpOverrides.IPNetwork.Parse(System.ReadOnlySpan networkSpan) -> Microsoft.AspNetCore.HttpOverrides.IPNetwork! static Microsoft.AspNetCore.HttpOverrides.IPNetwork.TryParse(System.ReadOnlySpan networkSpan, out Microsoft.AspNetCore.HttpOverrides.IPNetwork? network) -> bool -static Microsoft.AspNetCore.HttpOverrides.IPNetwork.implicit operator Microsoft.AspNetCore.HttpOverrides.IPNetwork!(System.Net.IPNetwork ipNetwork) -> Microsoft.AspNetCore.HttpOverrides.IPNetwork! static Microsoft.Extensions.DependencyInjection.CertificateForwardingServiceExtensions.AddCertificateForwarding(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action! configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! From 4f63a58f5b48da82185d93d9b97bc6b98cf860a1 Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Sat, 28 Jun 2025 19:43:42 +0800 Subject: [PATCH 15/26] test: fix test cases --- .../src/ForwardedHeadersOptions.cs | 5 +- .../test/ForwardedHeadersMiddlewareTest.cs | 2 +- .../HttpOverrides/test/IPNetworkTest.cs | 198 ------------------ 3 files changed, 5 insertions(+), 200 deletions(-) delete mode 100644 src/Middleware/HttpOverrides/test/IPNetworkTest.cs diff --git a/src/Middleware/HttpOverrides/src/ForwardedHeadersOptions.cs b/src/Middleware/HttpOverrides/src/ForwardedHeadersOptions.cs index cd49867fec1e..6715668688d4 100644 --- a/src/Middleware/HttpOverrides/src/ForwardedHeadersOptions.cs +++ b/src/Middleware/HttpOverrides/src/ForwardedHeadersOptions.cs @@ -84,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/test/ForwardedHeadersMiddlewareTest.cs b/src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs index 69dc2e6dd9aa..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) { diff --git a/src/Middleware/HttpOverrides/test/IPNetworkTest.cs b/src/Middleware/HttpOverrides/test/IPNetworkTest.cs deleted file mode 100644 index 2257139b32c9..000000000000 --- a/src/Middleware/HttpOverrides/test/IPNetworkTest.cs +++ /dev/null @@ -1,198 +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; - -[Obsolete("Keep it only for compatibility verify")] -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" - }; -} From 1c2b21f8229d6b25a9c16ba6766d87127b5b8401 Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Fri, 11 Jul 2025 23:43:53 +0800 Subject: [PATCH 16/26] refactor: remove the implicit convert for obsolete IPNetwork --- src/Middleware/HttpOverrides/src/IPNetwork.cs | 7 +------ src/Middleware/HttpOverrides/src/PublicAPI.Unshipped.txt | 1 - 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/Middleware/HttpOverrides/src/IPNetwork.cs b/src/Middleware/HttpOverrides/src/IPNetwork.cs index e28862bf229e..80c7b8640997 100644 --- a/src/Middleware/HttpOverrides/src/IPNetwork.cs +++ b/src/Middleware/HttpOverrides/src/IPNetwork.cs @@ -52,16 +52,11 @@ public static bool TryParse(ReadOnlySpan networkSpan, [NotNullWhen(true)] { if (System.Net.IPNetwork.TryParse(networkSpan, out var ipNetwork)) { - network = ipNetwork; + network = new(ipNetwork); return true; } network = null; return false; } - - /// - /// Convert to implicitly - /// - 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 81fc23c7a834..a8a1a579461f 100644 --- a/src/Middleware/HttpOverrides/src/PublicAPI.Unshipped.txt +++ b/src/Middleware/HttpOverrides/src/PublicAPI.Unshipped.txt @@ -1,4 +1,3 @@ #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! From b74969e19c19ad0e78f2e896dd40e7e985b72825 Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Sat, 12 Jul 2025 00:04:00 +0800 Subject: [PATCH 17/26] remove missed implicit convert --- src/Middleware/HttpOverrides/src/IPNetwork.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Middleware/HttpOverrides/src/IPNetwork.cs b/src/Middleware/HttpOverrides/src/IPNetwork.cs index 80c7b8640997..7e0da19c040c 100644 --- a/src/Middleware/HttpOverrides/src/IPNetwork.cs +++ b/src/Middleware/HttpOverrides/src/IPNetwork.cs @@ -45,7 +45,7 @@ public IPNetwork(IPAddress prefix, int prefixLength) public bool Contains(IPAddress address) => _network.Contains(address); /// - public static IPNetwork Parse(ReadOnlySpan networkSpan) => System.Net.IPNetwork.Parse(networkSpan); + public static IPNetwork Parse(ReadOnlySpan networkSpan) => new(System.Net.IPNetwork.Parse(networkSpan)); /// public static bool TryParse(ReadOnlySpan networkSpan, [NotNullWhen(true)] out IPNetwork? network) From f637aec26964993654d5ccd6f7cd884a8ad02954 Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Sat, 12 Jul 2025 07:08:44 +0800 Subject: [PATCH 18/26] revert unnecessay changes --- .../src/ForwardedHeadersOptions.cs | 5 +- .../HttpOverrides/test/IPNetworkTest.cs | 197 ++++++++++++++++++ 2 files changed, 198 insertions(+), 4 deletions(-) create mode 100644 src/Middleware/HttpOverrides/test/IPNetworkTest.cs diff --git a/src/Middleware/HttpOverrides/src/ForwardedHeadersOptions.cs b/src/Middleware/HttpOverrides/src/ForwardedHeadersOptions.cs index 6715668688d4..cd49867fec1e 100644 --- a/src/Middleware/HttpOverrides/src/ForwardedHeadersOptions.cs +++ b/src/Middleware/HttpOverrides/src/ForwardedHeadersOptions.cs @@ -84,10 +84,7 @@ public class ForwardedHeadersOptions /// /// Address ranges of known proxies to accept forwarded headers from. /// - public IList KnownNetworks { get; } = new List() - { - IPNetwork.Parse("127.0.0.0/8") - }; + public IList KnownNetworks { get; } = new List() { new IPNetwork(IPAddress.Loopback, 8) }; /// /// The allowed values from x-forwarded-host. If the list is empty then all hosts are allowed. diff --git a/src/Middleware/HttpOverrides/test/IPNetworkTest.cs b/src/Middleware/HttpOverrides/test/IPNetworkTest.cs new file mode 100644 index 000000000000..c8f33f7a333b --- /dev/null +++ b/src/Middleware/HttpOverrides/test/IPNetworkTest.cs @@ -0,0 +1,197 @@ +// 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" + }; +} From d8c7cb3956489f425bc19b678853799a83d8811d Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Mon, 14 Jul 2025 23:30:24 +0800 Subject: [PATCH 19/26] test: update test cases --- .../HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs | 2 +- src/Middleware/HttpOverrides/test/IPNetworkTest.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs b/src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs index 96c1242cc5f3..69dc2e6dd9aa 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::/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::1/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) { diff --git a/src/Middleware/HttpOverrides/test/IPNetworkTest.cs b/src/Middleware/HttpOverrides/test/IPNetworkTest.cs index c8f33f7a333b..9538f121c550 100644 --- a/src/Middleware/HttpOverrides/test/IPNetworkTest.cs +++ b/src/Middleware/HttpOverrides/test/IPNetworkTest.cs @@ -4,6 +4,7 @@ namespace Microsoft.AspNetCore.HttpOverrides; +[System.Obsolete] public class IPNetworkTest { [Theory] From 8795915d58eba2b9d5d39e620475066b8cdb644c Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Fri, 18 Jul 2025 08:44:06 +0800 Subject: [PATCH 20/26] feat: add KnownIPNetworks --- .../HttpOverrides/src/ForwardedHeadersMiddleware.cs | 13 ++++++++++++- .../HttpOverrides/src/ForwardedHeadersOptions.cs | 9 ++++++++- .../test/ForwardedHeadersMiddlewareTest.cs | 12 ++++++------ 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/Middleware/HttpOverrides/src/ForwardedHeadersMiddleware.cs b/src/Middleware/HttpOverrides/src/ForwardedHeadersMiddleware.cs index d00ccfa0a13d..b1775d8c2e17 100644 --- a/src/Middleware/HttpOverrides/src/ForwardedHeadersMiddleware.cs +++ b/src/Middleware/HttpOverrides/src/ForwardedHeadersMiddleware.cs @@ -213,7 +213,9 @@ public void ApplyForwarders(HttpContext context) // Host and Scheme initial values are never inspected, no need to set them here. }; - var checkKnownIps = _options.KnownNetworks.Count > 0 || _options.KnownProxies.Count > 0; +#pragma warning disable CS0618 // Type or member is obsolete + var checkKnownIps = _options.KnownIPNetworks.Count > 0 || _options.KnownNetworks.Count > 0 || _options.KnownProxies.Count > 0; +#pragma warning restore CS0618 // Type or member is obsolete bool applyChanges = false; int entriesConsumed = 0; @@ -399,6 +401,14 @@ private bool CheckKnownAddress(IPAddress address) { return true; } + foreach (var network in _options.KnownIPNetworks) + { + if (network.Contains(address)) + { + return true; + } + } +#pragma warning disable CS0618 // Type or member is obsolete foreach (var network in _options.KnownNetworks) { if (network.Contains(address)) @@ -406,6 +416,7 @@ private bool CheckKnownAddress(IPAddress address) return true; } } +#pragma warning restore CS0618 // Type or member is obsolete return false; } diff --git a/src/Middleware/HttpOverrides/src/ForwardedHeadersOptions.cs b/src/Middleware/HttpOverrides/src/ForwardedHeadersOptions.cs index cd49867fec1e..af03bb3b7252 100644 --- a/src/Middleware/HttpOverrides/src/ForwardedHeadersOptions.cs +++ b/src/Middleware/HttpOverrides/src/ForwardedHeadersOptions.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.AspNetCore.HttpOverrides; +using AspNetIPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork; using IPAddress = System.Net.IPAddress; using IPNetwork = System.Net.IPNetwork; @@ -84,7 +85,13 @@ public class ForwardedHeadersOptions /// /// Address ranges of known proxies to accept forwarded headers from. /// - public IList KnownNetworks { get; } = new List() { new IPNetwork(IPAddress.Loopback, 8) }; + [Obsolete("Please use KnownIPNetworks instead")] + public IList KnownNetworks { get; } = new List() { new AspNetIPNetwork(IPAddress.Loopback, 8) }; + + /// + /// Address ranges of known proxies to accept forwarded headers from. + /// + public IList KnownIPNetworks { get; } = new List() { new IPNetwork(IPAddress.Loopback, 8) }; /// /// The allowed values from x-forwarded-host. If the list is empty then all hosts are allowed. diff --git a/src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs b/src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs index 69dc2e6dd9aa..f27f6a566815 100644 --- a/src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs +++ b/src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs @@ -120,7 +120,7 @@ public async Task XForwardedForForwardLimit(int limit, string header, string exp ForwardLimit = limit, }; options.KnownProxies.Clear(); - options.KnownNetworks.Clear(); + options.KnownIPNetworks.Clear(); app.UseForwardedHeaders(options); }); }).Build(); @@ -861,7 +861,7 @@ public async Task XForwardedProtoOverrideLimitedByLoopback(string protoHeader, s }; if (!loopback) { - options.KnownNetworks.Clear(); + options.KnownIPNetworks.Clear(); options.KnownProxies.Clear(); } app.UseForwardedHeaders(options); @@ -888,7 +888,7 @@ public void AllForwardsDisabledByDefault() var options = new ForwardedHeadersOptions(); Assert.True(options.ForwardedHeaders == ForwardedHeaders.None); Assert.Equal(1, options.ForwardLimit); - Assert.Single(options.KnownNetworks); + Assert.Single(options.KnownIPNetworks); Assert.Single(options.KnownProxies); } @@ -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 System.Net.IPNetwork(networkIp, prefixLength)); + options.KnownIPNetworks.Add(new System.Net.IPNetwork(networkIp, prefixLength)); } using var host = new HostBuilder() @@ -1134,7 +1134,7 @@ public async Task ForwardersWithDIOptionsRunsOnce(int limit, string header, stri { options.ForwardedHeaders = ForwardedHeaders.XForwardedProto; options.KnownProxies.Clear(); - options.KnownNetworks.Clear(); + options.KnownIPNetworks.Clear(); options.ForwardLimit = limit; }); }) @@ -1176,7 +1176,7 @@ public async Task ForwardersWithDirectOptionsRunsTwice(int limit, string header, ForwardLimit = limit, }; options.KnownProxies.Clear(); - options.KnownNetworks.Clear(); + options.KnownIPNetworks.Clear(); app.UseForwardedHeaders(options); app.UseForwardedHeaders(options); }); From 22748dbb85b63b412790db0983da2671a10d6627 Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Fri, 18 Jul 2025 08:46:35 +0800 Subject: [PATCH 21/26] feat: update PublicAPI.Unshipped.txt --- src/Middleware/HttpOverrides/src/PublicAPI.Unshipped.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Middleware/HttpOverrides/src/PublicAPI.Unshipped.txt b/src/Middleware/HttpOverrides/src/PublicAPI.Unshipped.txt index a8a1a579461f..c08a53cc6255 100644 --- a/src/Middleware/HttpOverrides/src/PublicAPI.Unshipped.txt +++ b/src/Middleware/HttpOverrides/src/PublicAPI.Unshipped.txt @@ -1,3 +1,2 @@ #nullable enable -*REMOVED*Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.KnownNetworks.get -> System.Collections.Generic.IList! -Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.KnownNetworks.get -> System.Collections.Generic.IList! +Microsoft.AspNetCore.Builder.ForwardedHeadersOptions.KnownIPNetworks.get -> System.Collections.Generic.IList! From 5f59285adb4929459078538d32fabcaed89374b9 Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Fri, 18 Jul 2025 08:56:37 +0800 Subject: [PATCH 22/26] feat: add back Clear for legacy IPNetwork --- .../src/ForwardedHeadersOptionsSetup.cs | 3 +++ .../HttpOverrides/src/ForwardedHeadersOptions.cs | 5 +++-- .../test/ForwardedHeadersMiddlewareTest.cs | 12 ++++++++++++ src/Middleware/HttpOverrides/test/IPNetworkTest.cs | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/DefaultBuilder/src/ForwardedHeadersOptionsSetup.cs b/src/DefaultBuilder/src/ForwardedHeadersOptionsSetup.cs index 98835ff05482..b88fcf8638cc 100644 --- a/src/DefaultBuilder/src/ForwardedHeadersOptionsSetup.cs +++ b/src/DefaultBuilder/src/ForwardedHeadersOptionsSetup.cs @@ -27,7 +27,10 @@ public void Configure(ForwardedHeadersOptions options) options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; // Only loopback proxies are allowed by default. Clear that restriction because forwarders are // being enabled by explicit configuration. +#pragma warning disable CS0618 // Type or member is obsolete options.KnownNetworks.Clear(); +#pragma warning restore CS0618 // Type or member is obsolete + options.KnownIPNetworks.Clear(); options.KnownProxies.Clear(); } } diff --git a/src/Middleware/HttpOverrides/src/ForwardedHeadersOptions.cs b/src/Middleware/HttpOverrides/src/ForwardedHeadersOptions.cs index af03bb3b7252..ada03773fdf1 100644 --- a/src/Middleware/HttpOverrides/src/ForwardedHeadersOptions.cs +++ b/src/Middleware/HttpOverrides/src/ForwardedHeadersOptions.cs @@ -84,14 +84,15 @@ public class ForwardedHeadersOptions /// /// Address ranges of known proxies to accept forwarded headers from. + /// Obsolete, please use instead /// [Obsolete("Please use KnownIPNetworks instead")] - public IList KnownNetworks { get; } = new List() { new AspNetIPNetwork(IPAddress.Loopback, 8) }; + public IList KnownNetworks { get; } = new List() { new(IPAddress.Loopback, 8) }; /// /// Address ranges of known proxies to accept forwarded headers from. /// - public IList KnownIPNetworks { get; } = new List() { new IPNetwork(IPAddress.Loopback, 8) }; + public IList KnownIPNetworks { get; } = new List() { new(IPAddress.Loopback, 8) }; /// /// The allowed values from x-forwarded-host. If the list is empty then all hosts are allowed. diff --git a/src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs b/src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs index f27f6a566815..a34c0c7bff27 100644 --- a/src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs +++ b/src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs @@ -120,6 +120,9 @@ public async Task XForwardedForForwardLimit(int limit, string header, string exp ForwardLimit = limit, }; options.KnownProxies.Clear(); +#pragma warning disable CS0618 // Type or member is obsolete + options.KnownNetworks.Clear(); +#pragma warning restore CS0618 // Type or member is obsolete options.KnownIPNetworks.Clear(); app.UseForwardedHeaders(options); }); @@ -861,6 +864,9 @@ public async Task XForwardedProtoOverrideLimitedByLoopback(string protoHeader, s }; if (!loopback) { +#pragma warning disable CS0618 // Type or member is obsolete + options.KnownNetworks.Clear(); +#pragma warning restore CS0618 // Type or member is obsolete options.KnownIPNetworks.Clear(); options.KnownProxies.Clear(); } @@ -1134,6 +1140,9 @@ public async Task ForwardersWithDIOptionsRunsOnce(int limit, string header, stri { options.ForwardedHeaders = ForwardedHeaders.XForwardedProto; options.KnownProxies.Clear(); +#pragma warning disable CS0618 // Type or member is obsolete + options.KnownNetworks.Clear(); +#pragma warning restore CS0618 // Type or member is obsolete options.KnownIPNetworks.Clear(); options.ForwardLimit = limit; }); @@ -1176,6 +1185,9 @@ public async Task ForwardersWithDirectOptionsRunsTwice(int limit, string header, ForwardLimit = limit, }; options.KnownProxies.Clear(); +#pragma warning disable CS0618 // Type or member is obsolete + options.KnownNetworks.Clear(); +#pragma warning restore CS0618 // Type or member is obsolete options.KnownIPNetworks.Clear(); app.UseForwardedHeaders(options); app.UseForwardedHeaders(options); diff --git a/src/Middleware/HttpOverrides/test/IPNetworkTest.cs b/src/Middleware/HttpOverrides/test/IPNetworkTest.cs index 9538f121c550..a3e83a0ec5e2 100644 --- a/src/Middleware/HttpOverrides/test/IPNetworkTest.cs +++ b/src/Middleware/HttpOverrides/test/IPNetworkTest.cs @@ -4,7 +4,7 @@ namespace Microsoft.AspNetCore.HttpOverrides; -[System.Obsolete] +[Obsolete("IPNetwork is obsolete")] public class IPNetworkTest { [Theory] From 720d723502708f695d2623ee0faa219e4e08598c Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Fri, 18 Jul 2025 11:13:05 +0800 Subject: [PATCH 23/26] feat: revert Microsoft.AspNetCore.HttpOverrides.IPNetwork --- src/Middleware/HttpOverrides/src/IPNetwork.cs | 178 ++++++++++++++++-- .../HttpOverrides/test/IPNetworkTest.cs | 1 - 2 files changed, 161 insertions(+), 18 deletions(-) diff --git a/src/Middleware/HttpOverrides/src/IPNetwork.cs b/src/Middleware/HttpOverrides/src/IPNetwork.cs index 7e0da19c040c..9888de2d1535 100644 --- a/src/Middleware/HttpOverrides/src/IPNetwork.cs +++ b/src/Middleware/HttpOverrides/src/IPNetwork.cs @@ -3,60 +3,204 @@ 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) + public IPNetwork(IPAddress prefix, int prefixLength) : this(prefix, prefixLength, true) { - _network = new(prefix, prefixLength); } - private IPNetwork(System.Net.IPNetwork network) => _network = network; + 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(); + } /// /// Get the that represents the prefix for the network. /// - public IPAddress Prefix => _network.BaseAddress; + public IPAddress Prefix { get; } + + private byte[] PrefixBytes { get; } /// /// The CIDR notation of the subnet mask /// - public int PrefixLength => _network.PrefixLength; + public int PrefixLength { get; } + + private byte[] Mask { get; } /// /// 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) => _network.Contains(address); + 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 static IPNetwork Parse(ReadOnlySpan networkSpan) => new(System.Net.IPNetwork.Parse(networkSpan)); + return true; + } - /// - public static bool TryParse(ReadOnlySpan networkSpan, [NotNullWhen(true)] out IPNetwork? network) + 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) { - if (System.Net.IPNetwork.TryParse(networkSpan, out var ipNetwork)) + 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)) { - network = new(ipNetwork); - return true; + throw new ArgumentOutOfRangeException(nameof(networkSpan), "The prefix length was out of range."); } + return new IPNetwork(prefix, prefixLength, 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. + /// + /// 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; - return false; + + 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; } } diff --git a/src/Middleware/HttpOverrides/test/IPNetworkTest.cs b/src/Middleware/HttpOverrides/test/IPNetworkTest.cs index a3e83a0ec5e2..c8f33f7a333b 100644 --- a/src/Middleware/HttpOverrides/test/IPNetworkTest.cs +++ b/src/Middleware/HttpOverrides/test/IPNetworkTest.cs @@ -4,7 +4,6 @@ namespace Microsoft.AspNetCore.HttpOverrides; -[Obsolete("IPNetwork is obsolete")] public class IPNetworkTest { [Theory] From f3a8519d733156e4fcf5bba76d337a3d74596f07 Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Fri, 18 Jul 2025 17:10:27 +0800 Subject: [PATCH 24/26] mark Microsoft.AspNetCore.HttpOverrides.IPNetwork obsolete --- src/Middleware/HttpOverrides/src/IPNetwork.cs | 2 ++ src/Middleware/HttpOverrides/test/IPNetworkTest.cs | 1 + 2 files changed, 3 insertions(+) diff --git a/src/Middleware/HttpOverrides/src/IPNetwork.cs b/src/Middleware/HttpOverrides/src/IPNetwork.cs index 9888de2d1535..71f063245c56 100644 --- a/src/Middleware/HttpOverrides/src/IPNetwork.cs +++ b/src/Middleware/HttpOverrides/src/IPNetwork.cs @@ -9,7 +9,9 @@ namespace Microsoft.AspNetCore.HttpOverrides; /// /// A representation of an IP network based on CIDR notation. +/// Please use instead /// +[Obsolete("Please use System.Net.IPNetwork instead")] public class IPNetwork { /// diff --git a/src/Middleware/HttpOverrides/test/IPNetworkTest.cs b/src/Middleware/HttpOverrides/test/IPNetworkTest.cs index c8f33f7a333b..365dd9857a56 100644 --- a/src/Middleware/HttpOverrides/test/IPNetworkTest.cs +++ b/src/Middleware/HttpOverrides/test/IPNetworkTest.cs @@ -4,6 +4,7 @@ namespace Microsoft.AspNetCore.HttpOverrides; +[Obsolete("Microsoft.AspNetCore.HttpOverrides.IPNetwork is obsolete")] public class IPNetworkTest { [Theory] From 3d4f91aa7602b6bedb15378a0856adbf595629c5 Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Sat, 19 Jul 2025 08:31:39 +0800 Subject: [PATCH 25/26] update obsolete message and diagnosticId --- .../src/ForwardedHeadersOptionsSetup.cs | 4 ++-- .../src/ForwardedHeadersMiddleware.cs | 12 +++++++----- .../HttpOverrides/src/ForwardedHeadersOptions.cs | 2 +- .../test/ForwardedHeadersMiddlewareTest.cs | 16 ++++++++-------- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/DefaultBuilder/src/ForwardedHeadersOptionsSetup.cs b/src/DefaultBuilder/src/ForwardedHeadersOptionsSetup.cs index b88fcf8638cc..8109ca39b323 100644 --- a/src/DefaultBuilder/src/ForwardedHeadersOptionsSetup.cs +++ b/src/DefaultBuilder/src/ForwardedHeadersOptionsSetup.cs @@ -27,9 +27,9 @@ public void Configure(ForwardedHeadersOptions options) options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; // Only loopback proxies are allowed by default. Clear that restriction because forwarders are // being enabled by explicit configuration. -#pragma warning disable CS0618 // Type or member is obsolete +#pragma warning disable ASPDEPR005 // KnownNetworks is obsolete options.KnownNetworks.Clear(); -#pragma warning restore CS0618 // Type or member is obsolete +#pragma warning restore ASPDEPR005 // KnownNetworks is obsolete options.KnownIPNetworks.Clear(); options.KnownProxies.Clear(); } diff --git a/src/Middleware/HttpOverrides/src/ForwardedHeadersMiddleware.cs b/src/Middleware/HttpOverrides/src/ForwardedHeadersMiddleware.cs index b1775d8c2e17..fb1e757ff2e2 100644 --- a/src/Middleware/HttpOverrides/src/ForwardedHeadersMiddleware.cs +++ b/src/Middleware/HttpOverrides/src/ForwardedHeadersMiddleware.cs @@ -213,9 +213,11 @@ public void ApplyForwarders(HttpContext context) // Host and Scheme initial values are never inspected, no need to set them here. }; -#pragma warning disable CS0618 // Type or member is obsolete - var checkKnownIps = _options.KnownIPNetworks.Count > 0 || _options.KnownNetworks.Count > 0 || _options.KnownProxies.Count > 0; -#pragma warning restore CS0618 // Type or member is obsolete + var checkKnownIps = _options.KnownIPNetworks.Count > 0 +#pragma warning disable ASPDEPR005 // KnownNetworks is obsolete + || _options.KnownNetworks.Count > 0 +#pragma warning restore ASPDEPR005 // KnownNetworks is obsolete + || _options.KnownProxies.Count > 0; bool applyChanges = false; int entriesConsumed = 0; @@ -408,7 +410,7 @@ private bool CheckKnownAddress(IPAddress address) return true; } } -#pragma warning disable CS0618 // Type or member is obsolete +#pragma warning disable ASPDEPR005 // KnownNetworks is obsolete foreach (var network in _options.KnownNetworks) { if (network.Contains(address)) @@ -416,7 +418,7 @@ private bool CheckKnownAddress(IPAddress address) return true; } } -#pragma warning restore CS0618 // Type or member is obsolete +#pragma warning restore ASPDEPR005 // KnownNetworks is obsolete return false; } diff --git a/src/Middleware/HttpOverrides/src/ForwardedHeadersOptions.cs b/src/Middleware/HttpOverrides/src/ForwardedHeadersOptions.cs index ada03773fdf1..e0ed1820c001 100644 --- a/src/Middleware/HttpOverrides/src/ForwardedHeadersOptions.cs +++ b/src/Middleware/HttpOverrides/src/ForwardedHeadersOptions.cs @@ -86,7 +86,7 @@ public class ForwardedHeadersOptions /// Address ranges of known proxies to accept forwarded headers from. /// Obsolete, please use instead /// - [Obsolete("Please use KnownIPNetworks instead")] + [Obsolete("Please use KnownIPNetworks instead. For more information, visit https://aka.ms/aspnet/deprecate/005.", DiagnosticId = "ASPDEPR005")] public IList KnownNetworks { get; } = new List() { new(IPAddress.Loopback, 8) }; /// diff --git a/src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs b/src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs index a34c0c7bff27..317d2853d023 100644 --- a/src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs +++ b/src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs @@ -120,9 +120,9 @@ public async Task XForwardedForForwardLimit(int limit, string header, string exp ForwardLimit = limit, }; options.KnownProxies.Clear(); -#pragma warning disable CS0618 // Type or member is obsolete +#pragma warning disable ASPDEPR005 // KnownNetworks is obsolete options.KnownNetworks.Clear(); -#pragma warning restore CS0618 // Type or member is obsolete +#pragma warning restore ASPDEPR005 // KnownNetworks is obsolete options.KnownIPNetworks.Clear(); app.UseForwardedHeaders(options); }); @@ -864,9 +864,9 @@ public async Task XForwardedProtoOverrideLimitedByLoopback(string protoHeader, s }; if (!loopback) { -#pragma warning disable CS0618 // Type or member is obsolete +#pragma warning disable ASPDEPR005 // KnownNetworks is obsolete options.KnownNetworks.Clear(); -#pragma warning restore CS0618 // Type or member is obsolete +#pragma warning restore ASPDEPR005 // KnownNetworks is obsolete options.KnownIPNetworks.Clear(); options.KnownProxies.Clear(); } @@ -1140,9 +1140,9 @@ public async Task ForwardersWithDIOptionsRunsOnce(int limit, string header, stri { options.ForwardedHeaders = ForwardedHeaders.XForwardedProto; options.KnownProxies.Clear(); -#pragma warning disable CS0618 // Type or member is obsolete +#pragma warning disable ASPDEPR005 // KnownNetworks is obsolete options.KnownNetworks.Clear(); -#pragma warning restore CS0618 // Type or member is obsolete +#pragma warning restore ASPDEPR005 // KnownNetworks is obsolete options.KnownIPNetworks.Clear(); options.ForwardLimit = limit; }); @@ -1185,9 +1185,9 @@ public async Task ForwardersWithDirectOptionsRunsTwice(int limit, string header, ForwardLimit = limit, }; options.KnownProxies.Clear(); -#pragma warning disable CS0618 // Type or member is obsolete +#pragma warning disable ASPDEPR005 // KnownNetworks is obsolete options.KnownNetworks.Clear(); -#pragma warning restore CS0618 // Type or member is obsolete +#pragma warning restore ASPDEPR005 // KnownNetworks is obsolete options.KnownIPNetworks.Clear(); app.UseForwardedHeaders(options); app.UseForwardedHeaders(options); From 90840a1c9bb483e5aeae1bc04f77a9af284ef484 Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Sat, 19 Jul 2025 08:36:29 +0800 Subject: [PATCH 26/26] update obsolete message for HttpOverride.IPNetwork --- src/Middleware/HttpOverrides/src/IPNetwork.cs | 2 +- src/Middleware/HttpOverrides/test/IPNetworkTest.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Middleware/HttpOverrides/src/IPNetwork.cs b/src/Middleware/HttpOverrides/src/IPNetwork.cs index 71f063245c56..945d3e8eacb7 100644 --- a/src/Middleware/HttpOverrides/src/IPNetwork.cs +++ b/src/Middleware/HttpOverrides/src/IPNetwork.cs @@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.HttpOverrides; /// A representation of an IP network based on CIDR notation. /// Please use instead /// -[Obsolete("Please use System.Net.IPNetwork instead")] +[Obsolete("Please use System.Net.IPNetwork instead. For more information, visit https://aka.ms/aspnet/deprecate/005.", DiagnosticId = "ASPDEPR005")] public class IPNetwork { /// diff --git a/src/Middleware/HttpOverrides/test/IPNetworkTest.cs b/src/Middleware/HttpOverrides/test/IPNetworkTest.cs index 365dd9857a56..5ad9acfd925e 100644 --- a/src/Middleware/HttpOverrides/test/IPNetworkTest.cs +++ b/src/Middleware/HttpOverrides/test/IPNetworkTest.cs @@ -4,7 +4,7 @@ namespace Microsoft.AspNetCore.HttpOverrides; -[Obsolete("Microsoft.AspNetCore.HttpOverrides.IPNetwork is obsolete")] +[Obsolete("Microsoft.AspNetCore.HttpOverrides.IPNetwork is obsolete. For more information, visit https://aka.ms/aspnet/deprecate/005.", DiagnosticId = "ASPDEPR005")] public class IPNetworkTest { [Theory]