Updated `DistributedRoutingTable` and supporting classes

pull/350/head
David Hall 2022-10-11 11:54:05 -06:00
parent 66c6e8324e
commit 23f455efaa
3 changed files with 115 additions and 44 deletions

View File

@ -197,13 +197,16 @@ public class CustomDnsBootstapper : DrtCustomBootstrapProvider
}
/// <summary>Represents a distributed routing table from Win32.</summary>
public class DistributedRoutingTable
public class DistributedRoutingTable : IDisposable
{
private static readonly SafeWSA ws = SafeWSA.Initialize();
private readonly SafeRegisteredWaitHandle? drtWaitEvent;
private readonly SafeEventHandle? evt;
private readonly SafeHDRT? hDrt;
private readonly SafeHDRT_TRANSPORT hTransport;
private readonly IntPtr selfPin;
private readonly SafeWSA ws = SafeWSA.Initialize();
private bool disposedValue;
private DRT_SETTINGS pSettings;
/// <summary>Initializes a new instance of the <see cref="DistributedRoutingTable"/> class.</summary>
@ -228,8 +231,9 @@ public class DistributedRoutingTable
pSecurityProvider = pSecProv,
pBootstrapProvider = pBootProv,
};
DrtCreateIpv6UdpTransport(DRT_SCOPE.DRT_GLOBAL_SCOPE, 0, 300, ref port, out pSettings.hTransport).ThrowIfFailed();
DrtCreateIpv6UdpTransport(DRT_SCOPE.DRT_GLOBAL_SCOPE, 0, 300, ref port, out hTransport).ThrowIfFailed();
pSettings.hTransport = hTransport;
evt = CreateEvent(null, false, false);
DrtOpen(pSettings, evt, default, out HDRT h).ThrowIfFailed();
hDrt = new((IntPtr)h);
@ -237,6 +241,13 @@ public class DistributedRoutingTable
Win32Error.ThrowLastErrorIfFalse(RegisterWaitForSingleObject(out drtWaitEvent, evt, DrtEventCallback, selfPin, INFINITE, WT.WT_EXECUTEDEFAULT));
}
/// <summary>Finalizes an instance of the <see cref="DistributedRoutingTable"/> class.</summary>
~DistributedRoutingTable()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: false);
}
/// <summary>Occurs when the leaf set key changes.</summary>
public event EventHandler<DrtLeafSetKeyChangeEventArgs>? LeafSetKeyChange;
@ -245,6 +256,38 @@ public class DistributedRoutingTable
/// <summary>Occurs when the status changes.</summary>
public event EventHandler<DrtStatusChangeEventArgs>? StatusChange;
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
/// <summary>Releases unmanaged and - optionally - managed resources.</summary>
/// <param name="disposing">
/// <see langword="true"/> to release both managed and unmanaged resources; <see langword="false"/> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
if (selfPin != IntPtr.Zero)
GCHandle.FromIntPtr(selfPin).Free();
}
drtWaitEvent?.Dispose();
evt?.Dispose();
hDrt?.Dispose();
hTransport?.Dispose();
disposedValue = true;
}
}
private static void DrtEventCallback(IntPtr Param, bool TimedOut)
{
var Drt = (DistributedRoutingTable)GCHandle.FromIntPtr(Param).Target;
@ -304,7 +347,22 @@ public class DrtBootstrapProvider : IDisposable
}
/// <summary>Initializes a new instance of the <see cref="DrtBootstrapProvider"/> class.</summary>
private DrtBootstrapProvider() { }
protected DrtBootstrapProvider() { }
/// <summary>
/// Creates a bootstrap resolver that will use the GetAddrInfo system function to resolve the hostname of a will known node already
/// present in the DRT mesh.
/// </summary>
/// <param name="hostname">Specifies the hostname of the well known node.</param>
/// <param name="port">Specifies the port to which the DRT protocol is bound on the well known node.</param>
/// <returns>A DNS <see cref="DrtBootstrapProvider"/> instance.</returns>
public DrtBootstrapProvider(string? hostname, ushort port)
{
hostname ??= LocalDnsHost;
DrtCreateDnsBootstrapResolver(port, hostname, out IntPtr pbp).ThrowIfFailed();
pProv = pbp;
pProvType = 'd';
}
/// <summary>Initializes a new instance of the <see cref="DrtBootstrapProvider"/> class.</summary>
/// <param name="ptr">The PTR.</param>
@ -325,20 +383,6 @@ public class DrtBootstrapProvider : IDisposable
}
}
/// <summary>
/// Creates a bootstrap resolver that will use the GetAddrInfo system function to resolve the hostname of a will known node already
/// present in the DRT mesh.
/// </summary>
/// <param name="hostname">Specifies the hostname of the well known node.</param>
/// <param name="port">Specifies the port to which the DRT protocol is bound on the well known node.</param>
/// <returns>A DNS <see cref="DrtBootstrapProvider"/> instance.</returns>
public static DrtBootstrapProvider CreateDnsBootstrapResolver(string? hostname, ushort port)
{
hostname ??= LocalDnsHost;
DrtCreateDnsBootstrapResolver(port, hostname, out IntPtr pbp).ThrowIfFailed();
return new(pbp, 'd');
}
/// <summary>Creates a bootstrap resolver based on the Peer Name Resolution Protocol (PNRP).</summary>
/// <param name="peerName">The name of the peer to search for in the PNRP cloud. This string has a maximum limit of 137 unicode characters</param>
/// <param name="cloudName">
@ -546,8 +590,8 @@ public class DrtLeafSetKeyChangeEventArgs : DrtEventArgs
internal DrtLeafSetKeyChangeEventArgs(in DRT_EVENT_DATA data) : base(data)
{
Type = data.union.leafsetKeyChange.change;
LocalKey = data.union.leafsetKeyChange.localKey.GetArray();
RemoteKey = data.union.leafsetKeyChange.remoteKey.GetArray();
LocalKey = (byte[])data.union.leafsetKeyChange.localKey;
RemoteKey = (byte[])data.union.leafsetKeyChange.remoteKey;
}
/// <summary>Specifies the local key associated with the leaf set that has changed.</summary>
@ -567,7 +611,7 @@ public class DrtRegistrationStateChangeEventArgs : DrtEventArgs
internal DrtRegistrationStateChangeEventArgs(in DRT_EVENT_DATA data) : base(data)
{
State = data.union.registrationStateChange.state;
LocalKey = data.union.registrationStateChange.localKey.GetArray();
LocalKey = (byte[])data.union.registrationStateChange.localKey;
}
/// <summary>Specifies the local key associated with the registration that has changed.</summary>
@ -660,7 +704,7 @@ public class DrtStatusChangeEventArgs : DrtEventArgs
internal DrtStatusChangeEventArgs(in DRT_EVENT_DATA data) : base(data)
{
Status = data.union.statusChange.status;
BootstrapAddresses = Array.ConvertAll(data.union.statusChange.bootstrapAddresses.Addresses, Cvt);
BootstrapAddresses = data.union.statusChange.bootstrapAddresses.Addresses?.Select(Cvt).ToArray();
static IPEndPoint Cvt(SOCKADDR_STORAGE input)
{
@ -672,7 +716,7 @@ public class DrtStatusChangeEventArgs : DrtEventArgs
}
/// <summary>Contains an array of <see cref="IPEndPoint"/> returned by the bootstrap provider.</summary>
public IPEndPoint[] BootstrapAddresses { get; }
public IPEndPoint[]? BootstrapAddresses { get; }
/// <summary>Contains the current DRT_STATUS of the local DRT instance.</summary>
public DRT_STATUS Status { get; }
@ -975,7 +1019,7 @@ public abstract class DrtCustomSecurityProvider : DrtSecurityProvider
}
private HRESULT InternalDecryptData(IntPtr pvContext, in DRT_DATA pKeyToken, IntPtr pvKeyContext, uint dwBuffers, DRT_DATA[] pData) =>
DecryptData(pKeyToken.GetArray(), pvKeyContext, Array.ConvertAll(pData, p => p.GetArray()));
DecryptData((byte[])pKeyToken, pvKeyContext, Array.ConvertAll(pData, p => (byte[])p));
private void InternalDetach(IntPtr pvContext)
{
@ -985,7 +1029,7 @@ public abstract class DrtCustomSecurityProvider : DrtSecurityProvider
private HRESULT InternalEncryptData(IntPtr pvContext, in DRT_DATA pRemoteCredential, uint dwBuffers, DRT_DATA[] pDataBuffers, DRT_DATA[] pEncryptedBuffers, out DRT_DATA pKeyToken)
{
var hr = EncryptData(pRemoteCredential.GetArray(), Array.ConvertAll(pDataBuffers, p => p.GetArray()), Array.ConvertAll(pEncryptedBuffers, p => p.GetArray()), out byte[]? pkt);
var hr = EncryptData((byte[])pRemoteCredential, Array.ConvertAll(pDataBuffers, p => (byte[])p), Array.ConvertAll(pEncryptedBuffers, p => (byte[])p), out byte[]? pkt);
pKeyToken = ToData(pkt);
return hr;
}
@ -1005,8 +1049,8 @@ public abstract class DrtCustomSecurityProvider : DrtSecurityProvider
uint dwFlags, in DRT_DATA pKey, DRT_DATA* pPayload, IntPtr pAddressList, in DRT_DATA pNonce, out DRT_DATA pSecuredAddressPayload,
DRT_DATA* pClassifier, DRT_DATA* pSecuredPayload, DRT_DATA* pCertChain)
{
HRESULT hr = SecureAndPackPayload(pvContext, bProtocolMajor, bProtocolMinor, dwFlags, pKey.GetArray(), pPayload is null ? null : (*pPayload).GetArray(),
ToEndPoints(pAddressList.ToNullableStructure<SOCKET_ADDRESS_LIST>()), pNonce.GetArray(), out byte[]? sap, out byte[]? cl, out byte[]? sp, out byte[]? cc);
HRESULT hr = SecureAndPackPayload(pvContext, bProtocolMajor, bProtocolMinor, dwFlags, (byte[])pKey, pPayload is null ? null : (byte[])(*pPayload),
ToEndPoints(pAddressList.ToNullableStructure<SOCKET_ADDRESS_LIST>()), (byte[])pNonce, out byte[]? sap, out byte[]? cl, out byte[]? sp, out byte[]? cc);
pSecuredAddressPayload = ToData(sap);
if (cl is not null)
*pClassifier = ToData(cl);
@ -1019,22 +1063,22 @@ public abstract class DrtCustomSecurityProvider : DrtSecurityProvider
private HRESULT InternalSignData(IntPtr pvContext, uint dwBuffers, DRT_DATA[] pDataBuffers, out DRT_DATA pKeyIdentifier, out DRT_DATA pSignature)
{
HRESULT hr = SignData(Array.ConvertAll(pDataBuffers, b => b.GetArray()), out byte[]? id, out byte[]? sig);
HRESULT hr = SignData(Array.ConvertAll(pDataBuffers, b => (byte[])b), out byte[]? id, out byte[]? sig);
pKeyIdentifier = ToData(id);
pSignature = ToData(sig);
return hr;
}
private HRESULT InternalUnregisterKey(IntPtr pvContext, in DRT_DATA pKey, IntPtr pvKeyContext) =>
UnregisterKey(pKey.GetArray());
UnregisterKey((byte[])pKey);
private unsafe HRESULT InternalValidateAndUnpackPayload(IntPtr pvContext, in DRT_DATA pSecuredAddressPayload, DRT_DATA* pCertChain,
DRT_DATA* pClassifier, DRT_DATA* pNonce, DRT_DATA* pSecuredPayload, byte* pbProtocolMajor, byte* pbProtocolMinor,
out DRT_DATA pKey, DRT_DATA* pPayload, CERT_PUBLIC_KEY_INFO** ppPublicKey, void** ppAddressList, out uint pdwFlags)
{
HRESULT hr = ValidateAndUnpackPayload(pSecuredAddressPayload, pCertChain is null ? null : (*pCertChain).GetArray(),
pClassifier is null ? null : (*pClassifier).GetArray(), pNonce is null ? null : (*pNonce).GetArray(),
pSecuredPayload is null ? null : (*pSecuredPayload).GetArray(), out byte maj, out byte min, out byte[]? k,
HRESULT hr = ValidateAndUnpackPayload(pSecuredAddressPayload, pCertChain is null ? null : (byte[])(*pCertChain),
pClassifier is null ? null : (byte[])(*pClassifier), pNonce is null ? null : (byte[])(*pNonce),
pSecuredPayload is null ? null : (byte[])(*pSecuredPayload), out byte maj, out byte min, out byte[]? k,
out byte[]? pl, out SafeCoTaskMemStruct<CERT_PUBLIC_KEY_INFO>? pk, out IPEndPoint[]? al, out pdwFlags);
*pbProtocolMajor = maj;
*pbProtocolMinor = min;
@ -1047,10 +1091,10 @@ public abstract class DrtCustomSecurityProvider : DrtSecurityProvider
}
private HRESULT InternalValidateRemoteCredential(IntPtr pvContext, in DRT_DATA pRemoteCredential) =>
ValidateRemoteCredential(pRemoteCredential.GetArray());
ValidateRemoteCredential((byte[])pRemoteCredential);
private HRESULT InternalVerifyData(IntPtr pvContext, uint dwBuffers, DRT_DATA[] pDataBuffers, in DRT_DATA pRemoteCredentials, in DRT_DATA pKeyIdentifier, in DRT_DATA pSignature) =>
VerifyData(Array.ConvertAll(pDataBuffers, b => b.GetArray()), pRemoteCredentials.GetArray(), pKeyIdentifier.GetArray(), pSignature.GetArray());
VerifyData(Array.ConvertAll(pDataBuffers, b => (byte[])b), (byte[])pRemoteCredentials, (byte[])pKeyIdentifier, (byte[])pSignature);
}
/*

View File

@ -491,22 +491,22 @@ namespace Vanara.PInvoke
/// The local node is connected to the DRT mesh and participating in the DRT system. This is also an indication that remote
/// nodes exist and are present in the cache of the local node.
/// </summary>
DRT_ACTIVE,
DRT_ACTIVE = 0,
/// <summary>
/// The local node is participating in the DRT system, but is waiting for remote nodes to join the DRT mesh. This is an
/// indication that remote nodes do not exist, or are not yet present in the cache of the local node.
/// </summary>
DRT_ALONE,
DRT_ALONE = 1,
/// <summary>The local node does not have network connectivity.</summary>
DRT_NO_NETWORK,
DRT_NO_NETWORK = 10,
/// <summary>
/// A critical error has occurred in the local DRT instance. The DrtClose function must be called, after which an attempt to
/// re-open the DRT can be made.
/// </summary>
DRT_FAULTED,
DRT_FAULTED = 20,
}
/// <summary>The <c>DrtClose</c> function closes the local instance of the DRT.</summary>
@ -728,7 +728,7 @@ namespace Vanara.PInvoke
// DRT_SCOPE scope, ULONG dwScopeId, ULONG dwLocalityThreshold, USHORT *pwPort, HDRT_TRANSPORT *phTransport );
[DllImport(Lib_DrtTrans, SetLastError = false, ExactSpelling = true)]
[PInvokeData("drt.h", MSDNShortId = "NF:drt.DrtCreateIpv6UdpTransport")]
public static extern HRESULT DrtCreateIpv6UdpTransport(DRT_SCOPE scope, uint dwScopeId, uint dwLocalityThreshold, ref ushort pwPort, out HDRT_TRANSPORT phTransport);
public static extern HRESULT DrtCreateIpv6UdpTransport(DRT_SCOPE scope, uint dwScopeId, uint dwLocalityThreshold, ref ushort pwPort, out SafeHDRT_TRANSPORT phTransport);
/// <summary>
/// The <c>DrtCreateNullSecurityProvider</c> function creates a null security provider. This security provider does not require
@ -1792,7 +1792,7 @@ namespace Vanara.PInvoke
/// <summary>Performs an implicit conversion from <see cref="DRT_DATA"/> to <see cref="System.Byte"/>[].</summary>
/// <param name="d">The <see cref="DRT_DATA"/> instance.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator byte[](DRT_DATA d) => d.GetArray();
public static implicit operator byte[](DRT_DATA d) => d.pb == IntPtr.Zero ? null : d.GetArray();
}
/// <summary>
@ -2486,5 +2486,27 @@ namespace Vanara.PInvoke
/// <inheritdoc/>
protected override bool InternalReleaseHandle() { DrtClose(handle); return true; }
}
/// <summary>Provides a <see cref="SafeHandle"/> for <see cref="HDRT_TRANSPORT"/> that is disposed using <see cref="DrtDeleteIpv6UdpTransport"/>.</summary>
public class SafeHDRT_TRANSPORT : SafeHANDLE
{
/// <summary>Initializes a new instance of the <see cref="SafeHDRT_TRANSPORT"/> class and assigns an existing handle.</summary>
/// <param name="preexistingHandle">An <see cref="IntPtr"/> object that represents the pre-existing handle to use.</param>
/// <param name="ownsHandle">
/// <see langword="true"/> to reliably release the handle during the finalization phase; otherwise, <see langword="false"/> (not recommended).
/// </param>
public SafeHDRT_TRANSPORT(IntPtr preexistingHandle, bool ownsHandle = true) : base(preexistingHandle, ownsHandle) { }
/// <summary>Initializes a new instance of the <see cref="SafeHDRT_TRANSPORT"/> class.</summary>
private SafeHDRT_TRANSPORT() : base() { }
/// <summary>Performs an implicit conversion from <see cref="SafeHDRT_TRANSPORT"/> to <see cref="HDRT_TRANSPORT"/>.</summary>
/// <param name="h">The safe handle instance.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator HDRT_TRANSPORT(SafeHDRT_TRANSPORT h) => h.handle;
/// <inheritdoc/>
protected override bool InternalReleaseHandle() => DrtDeleteIpv6UdpTransport(handle).Succeeded;
}
}
}

View File

@ -79,12 +79,17 @@ public class NetTests
[Test]
public void TestDrt()
{
using var fw = new Firewall();
fw.AddApp(Process.GetCurrentProcess().ProcessName, Process.GetCurrentProcess().MainModule.FileName);
using Firewall fw = new();
if (TestHelper.IsElevated)
fw.AddApp(Process.GetCurrentProcess().ProcessName, Process.GetCurrentProcess().MainModule.FileName);
try
{
DistributedRoutingTable drt = new(null, DrtBootstrapProvider.CreateDnsBootstrapResolver(null, 0));
using DistributedRoutingTable drt = new(null, new(null, 0));
drt.StatusChange += (s, e) => e.WriteValues();
const int msinc = 250;
const float totms = 5000;
for (int ms = 0; ms < totms; ms += msinc)
Thread.Sleep(msinc);
}
finally
{