diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml
index 938ac77..80bfbce 100644
--- a/.github/workflows/dotnet.yml
+++ b/.github/workflows/dotnet.yml
@@ -39,3 +39,22 @@ jobs:
with:
file: coverage.lcov
format: lcov
+
+ windows-net48:
+ name: Test net48 (Windows)
+ runs-on: windows-latest
+
+ steps:
+ - uses: actions/checkout@v4
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 10.0.x
+ - name: Restore
+ run: dotnet restore ./src --verbosity m
+ - name: Build TestProject (net48)
+ run: dotnet build ./src/TestProject/TestProject.csproj -c Debug -f net48 --no-restore
+ - name: Run net48 tests against netstandard2.0 build
+ run: |
+ & .\src\TestProject\bin\Debug\net48\TestProject.exe
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
diff --git a/src/System.Net.IPNetwork/CidrNetworkAware.cs b/src/System.Net.IPNetwork/CidrNetworkAware.cs
index 4b72711..3721044 100644
--- a/src/System.Net.IPNetwork/CidrNetworkAware.cs
+++ b/src/System.Net.IPNetwork/CidrNetworkAware.cs
@@ -69,13 +69,13 @@ public bool TryGuessCidr(string? ip, out byte cidr)
// Reject if user passed a slash - this API expects a plain address.
// (You can relax this if you want to honor an explicitly supplied prefix.)
- if (ip.Contains('/'))
+ if (ip!.IndexOf('/') >= 0)
return false;
if (!IPAddress.TryParse(ip.Trim(), out var ipAddress))
return false;
- switch (ipAddress.AddressFamily)
+ switch (ipAddress!.AddressFamily)
{
case AddressFamily.InterNetwork:
cidr = GuessIpv4(ipAddress);
diff --git a/src/System.Net.IPNetwork/IPNetwork2WideSubnet.cs b/src/System.Net.IPNetwork/IPNetwork2WideSubnet.cs
index 5dbbb61..b99c809 100644
--- a/src/System.Net.IPNetwork/IPNetwork2WideSubnet.cs
+++ b/src/System.Net.IPNetwork/IPNetwork2WideSubnet.cs
@@ -141,7 +141,7 @@ internal static bool InternalWideSubnet(bool tryWide, IPNetwork2[] ipnetworks, [
IPNetwork2 nnin0 = nnin[0];
BigInteger uintNnin0 = nnin0.ipaddress;
- IPNetwork2 nninX = nnin[^1];
+ IPNetwork2 nninX = nnin[nnin.Length - 1];
IPAddress ipaddressX = nninX.Last;
AddressFamily family = ipnetworks[0].family;
diff --git a/src/System.Net.IPNetwork/Polyfills.cs b/src/System.Net.IPNetwork/Polyfills.cs
new file mode 100644
index 0000000..47e5eb3
--- /dev/null
+++ b/src/System.Net.IPNetwork/Polyfills.cs
@@ -0,0 +1,15 @@
+//
+// Copyright (c) IPNetwork. All rights reserved.
+//
+
+#if NETSTANDARD2_0
+namespace System.Diagnostics.CodeAnalysis;
+
+[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
+internal sealed class NotNullWhenAttribute : Attribute
+{
+ public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
+
+ public bool ReturnValue { get; }
+}
+#endif
diff --git a/src/System.Net.IPNetwork/System.Net.IPNetwork.csproj b/src/System.Net.IPNetwork/System.Net.IPNetwork.csproj
index a4a22d3..d8a5c76 100644
--- a/src/System.Net.IPNetwork/System.Net.IPNetwork.csproj
+++ b/src/System.Net.IPNetwork/System.Net.IPNetwork.csproj
@@ -1,7 +1,7 @@
- net10.0;net9.0;net8.0;netstandard2.1
+ net10.0;net9.0;net8.0;netstandard2.1;netstandard2.0
true
IPNetwork2
4.0.0
diff --git a/src/System.Net.IPNetwork/UniqueLocalAddress.cs b/src/System.Net.IPNetwork/UniqueLocalAddress.cs
index 293fecb..daf8178 100644
--- a/src/System.Net.IPNetwork/UniqueLocalAddress.cs
+++ b/src/System.Net.IPNetwork/UniqueLocalAddress.cs
@@ -179,7 +179,7 @@ private static byte[] GenerateGlobalIdFromMac(byte[] macAddress)
byte[] timestamp = BitConverter.GetBytes(DateTimeOffset.UtcNow.ToUnixTimeSeconds());
Array.Copy(timestamp, 0, input, macAddress.Length, timestamp.Length);
-#if NETSTANDARD2_1
+#if NETSTANDARD
using var sha2 = SHA256.Create();
byte[] hash = sha2.ComputeHash(input);
#else
@@ -198,7 +198,7 @@ private static byte[] GenerateGlobalIdFromMac(byte[] macAddress)
private static byte[] GenerateGlobalIdFromSeed(string seed)
{
byte[] seedBytes = Encoding.UTF8.GetBytes(seed);
-#if NETSTANDARD2_1
+#if NETSTANDARD
using var sha2 = SHA256.Create();
byte[] hash = sha2.ComputeHash(seedBytes);
#else
diff --git a/src/System.Net.IPNetwork/packages.lock.json b/src/System.Net.IPNetwork/packages.lock.json
index ab2b1d3..4989b6e 100644
--- a/src/System.Net.IPNetwork/packages.lock.json
+++ b/src/System.Net.IPNetwork/packages.lock.json
@@ -1,6 +1,22 @@
{
"version": 1,
"dependencies": {
+ ".NETStandard,Version=v2.0": {
+ "NETStandard.Library": {
+ "type": "Direct",
+ "requested": "[2.0.3, )",
+ "resolved": "2.0.3",
+ "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0"
+ }
+ },
+ "Microsoft.NETCore.Platforms": {
+ "type": "Transitive",
+ "resolved": "1.1.0",
+ "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
+ }
+ },
".NETStandard,Version=v2.1": {},
"net10.0": {},
"net8.0": {},
diff --git a/src/TestProject/IPAddressExtensionTests.cs b/src/TestProject/IPAddressExtensionTests.cs
index 4970af2..5e45732 100644
--- a/src/TestProject/IPAddressExtensionTests.cs
+++ b/src/TestProject/IPAddressExtensionTests.cs
@@ -98,7 +98,14 @@ e XOR'd with the byte count so the two sequences will be different. */
foreach (int _ in Enumerable.Range(1, 1000))
{
/* Hash the current interation to get a new block of deterministic bytes. */
+#if NET5_0_OR_GREATER
hashInput = SHA256.HashData(hashInput);
+#else
+ using (var sha = SHA256.Create())
+ {
+ hashInput = sha.ComputeHash(hashInput);
+ }
+#endif
/* Convert the first n bytes for an address. 4 will have an IPv4. 16 will make an IPv6. */
yield return new IPAddress(hashInput.Take(byteCount).ToArray()).ToString();
diff --git a/src/TestProject/TestProject.csproj b/src/TestProject/TestProject.csproj
index 9c27055..948c3fd 100644
--- a/src/TestProject/TestProject.csproj
+++ b/src/TestProject/TestProject.csproj
@@ -1,7 +1,7 @@
- net10.0
+ net10.0;net48
false
True
true
@@ -16,6 +16,7 @@
5
True
true
+ Exe
@@ -49,9 +50,14 @@
-
+
+
+
+
+
+
diff --git a/src/TestProject/packages.lock.json b/src/TestProject/packages.lock.json
index 5a5f402..19f703b 100644
--- a/src/TestProject/packages.lock.json
+++ b/src/TestProject/packages.lock.json
@@ -1,6 +1,216 @@
{
"version": 1,
"dependencies": {
+ ".NETFramework,Version=v4.8": {
+ "coverlet.collector": {
+ "type": "Direct",
+ "requested": "[10.0.0, )",
+ "resolved": "10.0.0",
+ "contentHash": "WFejCcOUR6k8UYyDnnR6Gk+obFYMsWrZuNqPJnsVFGVhpPSN0y20D4qbdKJnXinYGx9PQ397Hf9TnU1NBST8vA=="
+ },
+ "coverlet.msbuild": {
+ "type": "Direct",
+ "requested": "[10.0.0, )",
+ "resolved": "10.0.0",
+ "contentHash": "pmUxktztlMnsIvgHjzxG3fHRAXpqmZSA+W8dIsbVMfwI01SnBndrBIOE3Stg6987+o7HjBa62C4q5um3Ia6d9g=="
+ },
+ "Microsoft.CodeCoverage": {
+ "type": "Direct",
+ "requested": "[18.5.1, )",
+ "resolved": "18.5.1",
+ "contentHash": "vMFDR1ZjqzzgKmM0zrPie7Gv9Y+ZppjODB5Quzu9Eq0TlIusUfUCYFPEawO91zQuqwzvdFbJSU7WHNtjStffJQ=="
+ },
+ "Microsoft.NET.Test.Sdk": {
+ "type": "Direct",
+ "requested": "[18.5.1, )",
+ "resolved": "18.5.1",
+ "contentHash": "SfqVaLiIqAbRWuPg5BP4QFwBIirQj/YIL8Dhxl6zntBKbXp0cQykoV480SmwG+yRMiWptxEI6NbHQuGSZ8b97w==",
+ "dependencies": {
+ "Microsoft.CodeCoverage": "18.5.1"
+ }
+ },
+ "Microsoft.NETFramework.ReferenceAssemblies": {
+ "type": "Direct",
+ "requested": "[1.0.3, )",
+ "resolved": "1.0.3",
+ "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
+ "dependencies": {
+ "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3"
+ }
+ },
+ "MSTest.TestAdapter": {
+ "type": "Direct",
+ "requested": "[4.2.3, )",
+ "resolved": "4.2.3",
+ "contentHash": "oVV/luk0bBghnVvLaw8MwFlD7It0Cx9P2nKobeqIafmTQqFFWY69Wo801dxjeNaLzO/o9WQ84WSK84gXJuubhg==",
+ "dependencies": {
+ "MSTest.TestFramework": "4.2.3",
+ "Microsoft.Testing.Extensions.VSTestBridge": "2.2.3",
+ "Microsoft.Testing.Platform.MSBuild": "2.2.3",
+ "System.Threading.Tasks.Extensions": "4.5.4"
+ }
+ },
+ "MSTest.TestFramework": {
+ "type": "Direct",
+ "requested": "[4.2.3, )",
+ "resolved": "4.2.3",
+ "contentHash": "9zzij59YLh+tf+FRLNqhzHjmdspR91bol+jdQxLlxxTGMAML6LDbvuyXKMGdcrE84+QpKkk6KjTVBC5PBGrDeA==",
+ "dependencies": {
+ "MSTest.Analyzers": "4.2.3"
+ }
+ },
+ "NetArchTest.eNhancedEdition": {
+ "type": "Direct",
+ "requested": "[1.4.5, )",
+ "resolved": "1.4.5",
+ "contentHash": "dfWIjcLS2MBGz403OoT+vxr/4Dz/f7MnQhEwmgaf7PXlWzG39LSiUPhW7OPx9Ebg/GNWvDAttlpq5FY0mG/qkg==",
+ "dependencies": {
+ "Mono.Cecil": "0.11.6"
+ }
+ },
+ "Newtonsoft.Json": {
+ "type": "Direct",
+ "requested": "[13.0.4, )",
+ "resolved": "13.0.4",
+ "contentHash": "pdgNNMai3zv51W5aq268sujXUyx7SNdE2bj1wZcWjAQrKMFZV260lbqYop1d2GM67JI1huLRwxo9ZqnfF/lC6A=="
+ },
+ "Microsoft.ApplicationInsights": {
+ "type": "Transitive",
+ "resolved": "2.23.0",
+ "contentHash": "nWArUZTdU7iqZLycLKWe0TDms48KKGE6pONH2terYNa8REXiqixrMOkf1sk5DHGMaUTqONU2YkS4SAXBhLStgw==",
+ "dependencies": {
+ "System.Diagnostics.DiagnosticSource": "5.0.0"
+ }
+ },
+ "Microsoft.NETFramework.ReferenceAssemblies.net48": {
+ "type": "Transitive",
+ "resolved": "1.0.3",
+ "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ=="
+ },
+ "Microsoft.Testing.Extensions.Telemetry": {
+ "type": "Transitive",
+ "resolved": "2.2.3",
+ "contentHash": "mLdW+JOR3kXYGTdgR/qc/UZBA0r+eCR2k6bUxTcuDj5w9WdIQ7Lol5MBUU7YOSGd9bs9bvhSYWAptgz0YtQqCA==",
+ "dependencies": {
+ "Microsoft.ApplicationInsights": "2.23.0",
+ "Microsoft.Testing.Platform": "2.2.3",
+ "System.Diagnostics.DiagnosticSource": "6.0.0"
+ }
+ },
+ "Microsoft.Testing.Extensions.TrxReport.Abstractions": {
+ "type": "Transitive",
+ "resolved": "2.2.3",
+ "contentHash": "hntvxJEkmUAx6C2xXc/PO38DqEQl4rimzOgSvTR1hAMruMid7R4RcXOrzzF33J66gKaN7jRaQ0TMW/nNfaV9jw==",
+ "dependencies": {
+ "Microsoft.Testing.Platform": "2.2.3"
+ }
+ },
+ "Microsoft.Testing.Extensions.VSTestBridge": {
+ "type": "Transitive",
+ "resolved": "2.2.3",
+ "contentHash": "7WlJISO8QKUK+d+WhgnANwy4ACwUrvICnviY/mthPwjZ2gVeDaSUAeBnMy2cxfzZgm8VATtGUDbYzUxsgV2CyQ==",
+ "dependencies": {
+ "Microsoft.TestPlatform.ObjectModel": "18.3.0",
+ "Microsoft.Testing.Extensions.Telemetry": "2.2.3",
+ "Microsoft.Testing.Extensions.TrxReport.Abstractions": "2.2.3",
+ "Microsoft.Testing.Platform": "2.2.3"
+ }
+ },
+ "Microsoft.Testing.Platform": {
+ "type": "Transitive",
+ "resolved": "2.2.3",
+ "contentHash": "LhM1/Qoi8Ams5QcD4r3f09CSOono9iQr3NEJQItFtyzWB55nWTgEOsVqXqMWWWIwk3nkPqc+XfnlJmp8xUI5fg=="
+ },
+ "Microsoft.Testing.Platform.MSBuild": {
+ "type": "Transitive",
+ "resolved": "2.2.3",
+ "contentHash": "Q22jJYJLx4srTinsAuoCskqmzjrBJC8YeGJMHHIcrf1dQeHoEZ7wsqDzTlENkMoke2qfufF7U+9u58nlZunH/Q==",
+ "dependencies": {
+ "Microsoft.Testing.Platform": "2.2.3"
+ }
+ },
+ "Microsoft.TestPlatform.ObjectModel": {
+ "type": "Transitive",
+ "resolved": "18.3.0",
+ "contentHash": "AEIEX2aWdPO9XbtR96eBaJxmXRD9vaI9uQ1T/JbPEKlTAZwYx0ZrMzKyULMdh/HH9Sg03kXCoN7LszQ90o6nPQ==",
+ "dependencies": {
+ "System.Reflection.Metadata": "8.0.0"
+ }
+ },
+ "Mono.Cecil": {
+ "type": "Transitive",
+ "resolved": "0.11.6",
+ "contentHash": "f33RkDtZO8VlGXCtmQIviOtxgnUdym9xx/b1p9h91CRGOsJFxCFOFK1FDbVt1OCf1aWwYejUFa2MOQyFWTFjbA=="
+ },
+ "MSTest.Analyzers": {
+ "type": "Transitive",
+ "resolved": "4.2.3",
+ "contentHash": "dxOZt8/LWuiox7rugInJoIa5Mmu3pBmXdfaoZOx/mxx8+sUFFpjBXPlWXQXGeWzpkVPNC3x1Jf7rt2h2Zjyvvg=="
+ },
+ "System.Buffers": {
+ "type": "Transitive",
+ "resolved": "4.5.1",
+ "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
+ },
+ "System.Collections.Immutable": {
+ "type": "Transitive",
+ "resolved": "8.0.0",
+ "contentHash": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg==",
+ "dependencies": {
+ "System.Memory": "4.5.5",
+ "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+ }
+ },
+ "System.Diagnostics.DiagnosticSource": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==",
+ "dependencies": {
+ "System.Memory": "4.5.4",
+ "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+ }
+ },
+ "System.Memory": {
+ "type": "Transitive",
+ "resolved": "4.5.5",
+ "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
+ "dependencies": {
+ "System.Buffers": "4.5.1",
+ "System.Numerics.Vectors": "4.5.0",
+ "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+ }
+ },
+ "System.Numerics.Vectors": {
+ "type": "Transitive",
+ "resolved": "4.5.0",
+ "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
+ },
+ "System.Reflection.Metadata": {
+ "type": "Transitive",
+ "resolved": "8.0.0",
+ "contentHash": "ptvgrFh7PvWI8bcVqG5rsA/weWM09EnthFHR5SCnS6IN+P4mj6rE1lBDC4U8HL9/57htKAqy4KQ3bBj84cfYyQ==",
+ "dependencies": {
+ "System.Collections.Immutable": "8.0.0",
+ "System.Memory": "4.5.5"
+ }
+ },
+ "System.Runtime.CompilerServices.Unsafe": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
+ },
+ "System.Threading.Tasks.Extensions": {
+ "type": "Transitive",
+ "resolved": "4.5.4",
+ "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
+ "dependencies": {
+ "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+ }
+ },
+ "IPNetwork2": {
+ "type": "Project"
+ }
+ },
"net10.0": {
"coverlet.collector": {
"type": "Direct",