Skip to content

Commit 996b613

Browse files
committed
IPAddress and NetworkTypes changes
- IPAddressExtensions - EnumExtensions - fixed IPv6 Remote addresses for NetworkFilterMiddleware
1 parent 6a8defc commit 996b613

File tree

11 files changed

+174
-82
lines changed

11 files changed

+174
-82
lines changed

.editorconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,12 @@ csharp_preserve_single_line_blocks = true
136136
csharp_style_namespace_declarations = file_scoped:silent
137137

138138
# ReSharper properties
139+
resharper_csharp_wrap_after_declaration_lpar = true
139140
resharper_csharp_wrap_after_invocation_lpar = true
140141
resharper_csharp_wrap_arguments_style = chop_if_long
142+
resharper_csharp_wrap_before_declaration_rpar = true
141143
resharper_csharp_wrap_before_invocation_rpar = true
144+
resharper_csharp_wrap_parameters_style = chop_if_long
142145
resharper_keep_existing_invocation_parens_arrangement = false
143146
resharper_keep_existing_property_patterns_arrangement = false
144147
resharper_max_invocation_arguments_on_line = 3
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace Intersect.Framework;
2+
3+
public static class EnumExtensions
4+
{
5+
public static TEnum[] GetFlags<TEnum>(this TEnum @enum) where TEnum : struct, Enum =>
6+
Enum.GetValues<TEnum>().Where(flag => !flag.Equals(default(TEnum)) && @enum.HasFlag(flag)).ToArray();
7+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
2+
using System.Net;
3+
4+
namespace Intersect.Framework.Net;
5+
6+
public static class IPAddressExtensions
7+
{
8+
private static readonly Dictionary<NetworkTypes, IPRange[]> IPRangesForNetworkType = new()
9+
{
10+
{
11+
NetworkTypes.Loopback, [
12+
new IPRange(IPAddress.Parse("::1")),
13+
new IPRange(IPAddress.Parse("0.0.0.0"), IPAddress.Parse("0.255.255.255")),
14+
new IPRange(IPAddress.Parse("127.0.0.0"), IPAddress.Parse("127.255.255.255")),
15+
]
16+
},
17+
{
18+
NetworkTypes.Subnet, [
19+
new IPRange(IPAddress.Parse("fe80::"), IPAddress.Parse("fe80::ffff:ffff:ffff:ffff")),
20+
new IPRange(IPAddress.Parse("169.254.0.0"), IPAddress.Parse("169.254.255.255")),
21+
new IPRange(IPAddress.Parse("255.255.255.255")),
22+
]
23+
},
24+
{
25+
NetworkTypes.PrivateNetwork, [
26+
new IPRange(IPAddress.Parse("fc00::"), IPAddress.Parse("fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")),
27+
new IPRange(IPAddress.Parse("::ffff:10.0.0.0"), IPAddress.Parse("::ffff:10.255.255.255")),
28+
// new IpRange(IPAddress.Parse("100.64.0.0"), IPAddress.Parse("100.127.255.255")),
29+
// new IpRange(IPAddress.Parse("172.16.0.0"), IPAddress.Parse("172.31.255.255")),
30+
// new IpRange(IPAddress.Parse("192.0.0.0"), IPAddress.Parse("192.0.0.255")),
31+
// new IpRange(IPAddress.Parse("192.168.0.0"), IPAddress.Parse("192.168.255.255")),
32+
// new IpRange(IPAddress.Parse("192.18.0.0"), IPAddress.Parse("192.19.255.255")),
33+
new IPRange(IPAddress.Parse("10.0.0.0"), IPAddress.Parse("10.255.255.255")),
34+
new IPRange(IPAddress.Parse("100.64.0.0"), IPAddress.Parse("100.127.255.255")),
35+
new IPRange(IPAddress.Parse("172.16.0.0"), IPAddress.Parse("172.31.255.255")),
36+
new IPRange(IPAddress.Parse("192.0.0.0"), IPAddress.Parse("192.0.0.255")),
37+
new IPRange(IPAddress.Parse("192.168.0.0"), IPAddress.Parse("192.168.255.255")),
38+
new IPRange(IPAddress.Parse("192.18.0.0"), IPAddress.Parse("192.19.255.255")),
39+
]
40+
},
41+
};
42+
43+
public static bool IsLoopback(this IPAddress address) => MatchesNetworkTypes(address, NetworkTypes.Loopback);
44+
45+
public static bool IsPrivate(this IPAddress address) => MatchesNetworkTypes(address, NetworkTypes.PrivateNetwork);
46+
47+
public static bool IsPublic(this IPAddress address) =>
48+
IPRangesForNetworkType.Values.All(ipRanges => !ipRanges.Any(range => range.Contains(address)));
49+
50+
public static bool IsSubnet(this IPAddress address) => MatchesNetworkTypes(address, NetworkTypes.Subnet);
51+
52+
public static bool MatchesNetworkTypes(this IPAddress address, NetworkTypes networkTypes)
53+
{
54+
if (address.IsIPv4MappedToIPv6)
55+
{
56+
return MatchesNetworkTypes(address.MapToIPv4(), networkTypes);
57+
}
58+
59+
if (networkTypes == NetworkTypes.Public)
60+
{
61+
return IsPublic(address);
62+
}
63+
64+
var individualFlags = networkTypes.GetFlags();
65+
return individualFlags.Any(
66+
flag => IPRangesForNetworkType.TryGetValue(flag, out var ranges) &&
67+
ranges.Any(range => range.Contains(address))
68+
);
69+
}
70+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using System.Net;
2+
using System.Net.Sockets;
3+
4+
namespace Intersect.Framework.Net;
5+
6+
public readonly record struct IPRange
7+
{
8+
public AddressFamily AddressFamily { get; }
9+
public byte[] Start { get; }
10+
public byte[] End { get; }
11+
12+
public IPRange(IPAddress address)
13+
{
14+
ArgumentNullException.ThrowIfNull(address, nameof(address));
15+
16+
AddressFamily = address.AddressFamily;
17+
Start = address.GetAddressBytes();
18+
End = address.GetAddressBytes();
19+
}
20+
21+
public IPRange(IPAddress start, IPAddress end)
22+
{
23+
ArgumentNullException.ThrowIfNull(start, nameof(start));
24+
ArgumentNullException.ThrowIfNull(end, nameof(end));
25+
26+
if (start.AddressFamily != end.AddressFamily)
27+
{
28+
throw new ArgumentException("AddressFamily mismatch");
29+
}
30+
31+
AddressFamily = start.AddressFamily;
32+
Start = start.GetAddressBytes();
33+
End = end.GetAddressBytes();
34+
}
35+
36+
public bool Contains(IPAddress address)
37+
{
38+
ArgumentNullException.ThrowIfNull(address, nameof(address));
39+
40+
if (address.AddressFamily != AddressFamily)
41+
{
42+
return false;
43+
}
44+
45+
var octets = address.GetAddressBytes();
46+
if (Start.Length != End.Length || End.Length != octets.Length)
47+
{
48+
return false;
49+
}
50+
51+
for (var index = 0; index < octets.Length; index++)
52+
{
53+
var start = Start[index];
54+
var end = End[index];
55+
var octet = octets[index];
56+
57+
if (octet < start || end < octet)
58+
{
59+
return false;
60+
}
61+
}
62+
63+
return true;
64+
}
65+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace Intersect.Framework.Net;
2+
3+
[Flags]
4+
public enum NetworkTypes
5+
{
6+
Public = 0,
7+
Loopback = 1,
8+
Subnet = 2,
9+
PrivateNetwork = 4,
10+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace Intersect.Framework.Net;
2+
3+
public static class NetworkTypesExtensions
4+
{
5+
public static readonly NetworkTypes[] Flags = Enum.GetValues<NetworkTypes>();
6+
}

Intersect.Server/Web/Net7/Configuration/ApiConfiguration.cs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
11
using System.Collections.Immutable;
22
using System.ComponentModel;
3-
using System.Net;
4-
using System.Reflection;
5-
using System.Text.Json;
6-
using System.Text.Json.Serialization;
7-
using Intersect.Reflection;
8-
using Intersect.Server.Web.RestApi.Configuration;
9-
using Microsoft.AspNetCore.Authentication.JwtBearer;
3+
using Intersect.Framework.Net;
104
using Microsoft.AspNetCore.Cors.Infrastructure;
115
using Newtonsoft.Json;
126
using Newtonsoft.Json.Converters;
13-
using JsonConverter = System.Text.Json.Serialization.JsonConverter;
147
using LogLevel = Intersect.Logging.LogLevel;
158

169
namespace Intersect.Server.Web.Configuration;
@@ -31,7 +24,7 @@ public sealed partial class ApiConfiguration
3124
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
3225
public bool Enabled { get; set; }
3326

34-
[Newtonsoft.Json.JsonIgnore] private ImmutableDictionary<string, CorsPolicy>? _corsPolicies;
27+
[JsonIgnore] private ImmutableDictionary<string, CorsPolicy>? _corsPolicies;
3528

3629
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
3730
public ImmutableDictionary<string, CorsPolicy>? Cors

Intersect.Server/Web/Net7/IApiService.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using Intersect.Core;
22
using Intersect.Server.Web.Configuration;
3-
using Intersect.Server.Web.RestApi.Configuration;
43

54
namespace Intersect.Server.Web;
65

Lines changed: 10 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
using System.Net;
2-
using Intersect.Server.Web.RestApi.Configuration;
3-
using Intersect.Server.Web.RestApi.Types;
2+
using Intersect.Framework.Net;
43
using Microsoft.AspNetCore.Builder;
54
using Microsoft.AspNetCore.Http;
65

76
namespace Intersect.Server.Web.Middleware;
87

98
internal static class NetworkFilterMiddleware
109
{
11-
private const string SCHEME_HTTPS = "https";
10+
private const string SchemeHttps = "https";
1211

1312
public static void UseNetworkFilterMiddleware(this WebApplication app, NetworkTypes allowedNetworkTypes)
1413
{
@@ -17,83 +16,32 @@ public static void UseNetworkFilterMiddleware(this WebApplication app, NetworkTy
1716
{
1817
var request = context.Request;
1918

20-
if (SCHEME_HTTPS.Equals(request.Scheme, StringComparison.OrdinalIgnoreCase))
19+
if (SchemeHttps.Equals(request.Scheme, StringComparison.OrdinalIgnoreCase))
2120
{
2221
await next.Invoke(context);
2322
return;
2423
}
2524

26-
if (!IPAddress.TryParse(context.Connection.RemoteIpAddress?.ToString() ?? string.Empty, out var remoteIpAddress))
25+
if (!IPAddress.TryParse(
26+
context.Connection.RemoteIpAddress?.ToString() ?? string.Empty,
27+
out var remoteIpAddress
28+
))
2729
{
2830
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
2931
return;
3032
}
3133

32-
foreach (var networkTypeFlag in NetworkTypeFlags)
34+
if (remoteIpAddress.MatchesNetworkTypes(allowedNetworkTypes))
3335
{
34-
if (!allowedNetworkTypes.HasFlag(networkTypeFlag))
35-
{
36-
continue;
37-
}
38-
39-
var ipRanges = IpRangesForNetworkType[networkTypeFlag];
40-
if (!ipRanges.Any(range => range.IsInRange(remoteIpAddress)))
41-
{
42-
continue;
43-
}
44-
4536
await next.Invoke(context);
4637
return;
4738
}
4839

4940
context.Response.StatusCode = (int)HttpStatusCode.UpgradeRequired;
50-
context.Response.Headers["Upgrade"] = "TLS/1.2";
51-
context.Response.Headers["Upgrade-Insecure-Requests"] = "1";
41+
context.Response.Headers.Upgrade = "TLS/1.2";
42+
context.Response.Headers.UpgradeInsecureRequests = "1";
5243
await context.Response.WriteAsync("HTTPS Required");
5344
}
5445
);
5546
}
56-
57-
private static readonly IDictionary<NetworkTypes, IpRange[]> IpRangesForNetworkType = new Dictionary<NetworkTypes, IpRange[]>
58-
{
59-
{
60-
NetworkTypes.Loopback,
61-
new[]
62-
{
63-
new IpRange(IPAddress.Parse("::1")),
64-
new IpRange(IPAddress.Parse("0.0.0.0"), IPAddress.Parse("0.255.255.255")),
65-
new IpRange(IPAddress.Parse("127.0.0.0"), IPAddress.Parse("127.255.255.255"))
66-
}
67-
},
68-
{
69-
NetworkTypes.Subnet,
70-
new[]
71-
{
72-
new IpRange(IPAddress.Parse("fe80::"), IPAddress.Parse("fe80::ffff:ffff:ffff:ffff")),
73-
new IpRange(IPAddress.Parse("169.254.0.0"), IPAddress.Parse("169.254.255.255")),
74-
new IpRange(IPAddress.Parse("255.255.255.255")),
75-
}
76-
},
77-
{
78-
NetworkTypes.PrivateNetwork,
79-
new[]
80-
{
81-
new IpRange(IPAddress.Parse("fc00::"), IPAddress.Parse("fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")),
82-
new IpRange(IPAddress.Parse("::ffff:10.0.0.0"), IPAddress.Parse("::ffff:10.255.255.255")),
83-
// new IpRange(IPAddress.Parse("100.64.0.0"), IPAddress.Parse("100.127.255.255")),
84-
// new IpRange(IPAddress.Parse("172.16.0.0"), IPAddress.Parse("172.31.255.255")),
85-
// new IpRange(IPAddress.Parse("192.0.0.0"), IPAddress.Parse("192.0.0.255")),
86-
// new IpRange(IPAddress.Parse("192.168.0.0"), IPAddress.Parse("192.168.255.255")),
87-
// new IpRange(IPAddress.Parse("192.18.0.0"), IPAddress.Parse("192.19.255.255")),
88-
new IpRange(IPAddress.Parse("10.0.0.0"), IPAddress.Parse("10.255.255.255")),
89-
new IpRange(IPAddress.Parse("100.64.0.0"), IPAddress.Parse("100.127.255.255")),
90-
new IpRange(IPAddress.Parse("172.16.0.0"), IPAddress.Parse("172.31.255.255")),
91-
new IpRange(IPAddress.Parse("192.0.0.0"), IPAddress.Parse("192.0.0.255")),
92-
new IpRange(IPAddress.Parse("192.168.0.0"), IPAddress.Parse("192.168.255.255")),
93-
new IpRange(IPAddress.Parse("192.18.0.0"), IPAddress.Parse("192.19.255.255")),
94-
}
95-
}
96-
};
97-
98-
private static readonly NetworkTypes[] NetworkTypeFlags = Enum.GetValues(typeof(NetworkTypes)).OfType<NetworkTypes>().ToArray();
9947
}

Intersect.Server/Web/RestApi/Configuration/NetworkTypes.cs

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)