Skip to content

Commit 72692ad

Browse files
authored
[release/10.0] Implement KnownNetworks dual list (#63658)
* Refactor unit tests to handle the obsolete property * Implement KnownNetworks dual list Fixes #63627
1 parent 285efc1 commit 72692ad

File tree

6 files changed

+579
-23
lines changed

6 files changed

+579
-23
lines changed

src/DefaultBuilder/src/ForwardedHeadersOptionsSetup.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,6 @@ public void Configure(ForwardedHeadersOptions options)
2727
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
2828
// Only loopback proxies are allowed by default. Clear that restriction because forwarders are
2929
// being enabled by explicit configuration.
30-
#pragma warning disable ASPDEPR005 // KnownNetworks is obsolete
31-
options.KnownNetworks.Clear();
32-
#pragma warning restore ASPDEPR005 // KnownNetworks is obsolete
3330
options.KnownIPNetworks.Clear();
3431
options.KnownProxies.Clear();
3532
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
#pragma warning disable ASPDEPR005 // Type or member is obsolete
5+
6+
using AspNetIPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork;
7+
using IPAddress = System.Net.IPAddress;
8+
using IPNetwork = System.Net.IPNetwork;
9+
10+
namespace Microsoft.AspNetCore.Builder;
11+
12+
/// <summary>
13+
/// Internal list implementation that keeps <see cref="System.Net.IPNetwork"/> and the obsolete
14+
/// <see cref="Microsoft.AspNetCore.HttpOverrides.IPNetwork"/> collections in sync. Modifications
15+
/// through either interface are reflected in the other.
16+
/// </summary>
17+
internal sealed class DualIPNetworkList : IList<IPNetwork>, IList<AspNetIPNetwork>
18+
{
19+
// Two independent underlying lists so each side behaves exactly like a List<T> with respect to
20+
// enumeration versioning, capacity growth, etc. They are kept strictly in sync by all mutating operations.
21+
private readonly List<IPNetwork> _system = new();
22+
private readonly List<AspNetIPNetwork> _aspnet = new();
23+
24+
public DualIPNetworkList()
25+
{
26+
// Default entry (loopback) added to both representations.
27+
var loopback = new IPNetwork(IPAddress.Loopback, 8);
28+
_system.Add(loopback);
29+
_aspnet.Add(new AspNetIPNetwork(loopback.BaseAddress, loopback.PrefixLength));
30+
}
31+
32+
int ICollection<IPNetwork>.Count => _system.Count;
33+
int ICollection<AspNetIPNetwork>.Count => _aspnet.Count;
34+
35+
bool ICollection<IPNetwork>.IsReadOnly => false;
36+
bool ICollection<AspNetIPNetwork>.IsReadOnly => false;
37+
38+
IPNetwork IList<IPNetwork>.this[int index]
39+
{
40+
get => _system[index];
41+
set
42+
{
43+
_system[index] = value;
44+
_aspnet[index] = new AspNetIPNetwork(value.BaseAddress, value.PrefixLength);
45+
}
46+
}
47+
48+
AspNetIPNetwork IList<AspNetIPNetwork>.this[int index]
49+
{
50+
get => _aspnet[index];
51+
set
52+
{
53+
_aspnet[index] = value;
54+
_system[index] = new IPNetwork(value.Prefix, value.PrefixLength);
55+
}
56+
}
57+
58+
void ICollection<IPNetwork>.Add(IPNetwork item)
59+
{
60+
_system.Add(item);
61+
_aspnet.Add(new AspNetIPNetwork(item.BaseAddress, item.PrefixLength));
62+
}
63+
64+
void ICollection<AspNetIPNetwork>.Add(AspNetIPNetwork item)
65+
{
66+
_aspnet.Add(item);
67+
_system.Add(new IPNetwork(item.Prefix, item.PrefixLength));
68+
}
69+
70+
public void Clear()
71+
{
72+
_system.Clear();
73+
_aspnet.Clear();
74+
}
75+
76+
void ICollection<IPNetwork>.Clear() => Clear();
77+
void ICollection<AspNetIPNetwork>.Clear() => Clear();
78+
79+
bool ICollection<IPNetwork>.Contains(IPNetwork item) => _system.Contains(item);
80+
bool ICollection<AspNetIPNetwork>.Contains(AspNetIPNetwork item) => _aspnet.Contains(item);
81+
82+
public void CopyTo(IPNetwork[] array, int arrayIndex) => _system.CopyTo(array, arrayIndex);
83+
public void CopyTo(AspNetIPNetwork[] array, int arrayIndex) => _aspnet.CopyTo(array, arrayIndex);
84+
85+
void ICollection<IPNetwork>.CopyTo(IPNetwork[] array, int arrayIndex) => CopyTo(array, arrayIndex);
86+
void ICollection<AspNetIPNetwork>.CopyTo(AspNetIPNetwork[] array, int arrayIndex) => CopyTo(array, arrayIndex);
87+
88+
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => _system.GetEnumerator();
89+
90+
IEnumerator<IPNetwork> IEnumerable<IPNetwork>.GetEnumerator() => _system.GetEnumerator();
91+
IEnumerator<AspNetIPNetwork> IEnumerable<AspNetIPNetwork>.GetEnumerator() => _aspnet.GetEnumerator();
92+
93+
int IList<IPNetwork>.IndexOf(IPNetwork item) => _system.IndexOf(item);
94+
int IList<AspNetIPNetwork>.IndexOf(AspNetIPNetwork item) => _aspnet.IndexOf(item);
95+
96+
void IList<IPNetwork>.Insert(int index, IPNetwork item)
97+
{
98+
_system.Insert(index, item);
99+
_aspnet.Insert(index, new AspNetIPNetwork(item.BaseAddress, item.PrefixLength));
100+
}
101+
102+
void IList<AspNetIPNetwork>.Insert(int index, AspNetIPNetwork item)
103+
{
104+
_aspnet.Insert(index, item);
105+
_system.Insert(index, new IPNetwork(item.Prefix, item.PrefixLength));
106+
}
107+
108+
bool ICollection<IPNetwork>.Remove(IPNetwork item)
109+
{
110+
var idx = _system.IndexOf(item);
111+
if (idx >= 0)
112+
{
113+
RemoveAt(idx);
114+
return true;
115+
}
116+
return false;
117+
}
118+
119+
bool ICollection<AspNetIPNetwork>.Remove(AspNetIPNetwork item)
120+
{
121+
var idx = _aspnet.IndexOf(item);
122+
if (idx >= 0)
123+
{
124+
RemoveAt(idx);
125+
return true;
126+
}
127+
return false;
128+
}
129+
130+
public void RemoveAt(int index)
131+
{
132+
_system.RemoveAt(index);
133+
_aspnet.RemoveAt(index);
134+
}
135+
136+
void IList<IPNetwork>.RemoveAt(int index) => RemoveAt(index);
137+
void IList<AspNetIPNetwork>.RemoveAt(int index) => RemoveAt(index);
138+
}
139+
140+
#pragma warning restore ASPDEPR005 // Type or member is obsolete

src/Middleware/HttpOverrides/src/ForwardedHeadersMiddleware.cs

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -213,11 +213,7 @@ public void ApplyForwarders(HttpContext context)
213213
// Host and Scheme initial values are never inspected, no need to set them here.
214214
};
215215

216-
var checkKnownIps = _options.KnownIPNetworks.Count > 0
217-
#pragma warning disable ASPDEPR005 // KnownNetworks is obsolete
218-
|| _options.KnownNetworks.Count > 0
219-
#pragma warning restore ASPDEPR005 // KnownNetworks is obsolete
220-
|| _options.KnownProxies.Count > 0;
216+
var checkKnownIps = _options.KnownIPNetworks.Count > 0 || _options.KnownProxies.Count > 0;
221217
bool applyChanges = false;
222218
int entriesConsumed = 0;
223219

@@ -410,15 +406,6 @@ private bool CheckKnownAddress(IPAddress address)
410406
return true;
411407
}
412408
}
413-
#pragma warning disable ASPDEPR005 // KnownNetworks is obsolete
414-
foreach (var network in _options.KnownNetworks)
415-
{
416-
if (network.Contains(address))
417-
{
418-
return true;
419-
}
420-
}
421-
#pragma warning restore ASPDEPR005 // KnownNetworks is obsolete
422409
return false;
423410
}
424411

src/Middleware/HttpOverrides/src/ForwardedHeadersOptions.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ namespace Microsoft.AspNetCore.Builder;
1313
/// </summary>
1414
public class ForwardedHeadersOptions
1515
{
16+
// Backing dual list that keeps the obsolete and new types in sync.
17+
// Once the obsolete IList<IPNetwork> property is removed this can be changed to a simple List<IPNetwork>.
18+
private readonly DualIPNetworkList _knownNetworks = new();
19+
1620
/// <summary>
1721
/// Gets or sets the header used to retrieve the originating client IP. Defaults to the value specified by
1822
/// <see cref="ForwardedHeadersDefaults.XForwardedForHeaderName"/>.
@@ -87,12 +91,12 @@ public class ForwardedHeadersOptions
8791
/// Obsolete, please use <see cref="KnownIPNetworks"/> instead
8892
/// </summary>
8993
[Obsolete("Please use KnownIPNetworks instead. For more information, visit https://aka.ms/aspnet/deprecate/005.", DiagnosticId = "ASPDEPR005")]
90-
public IList<AspNetIPNetwork> KnownNetworks { get; } = new List<AspNetIPNetwork>() { new(IPAddress.Loopback, 8) };
94+
public IList<AspNetIPNetwork> KnownNetworks => _knownNetworks;
9195

9296
/// <summary>
9397
/// Address ranges of known proxies to accept forwarded headers from.
9498
/// </summary>
95-
public IList<IPNetwork> KnownIPNetworks { get; } = new List<IPNetwork>() { new(IPAddress.Loopback, 8) };
99+
public IList<IPNetwork> KnownIPNetworks => _knownNetworks;
96100

97101
/// <summary>
98102
/// The allowed values from x-forwarded-host. If the list is empty then all hosts are allowed.

0 commit comments

Comments
 (0)