[DllImport(Interop.Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_SslCreateContext")]
internal static extern System.Net.SafeSslHandle SslCreateContext(int isServer);
+ [DllImport(Interop.Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_SslSetConnection")]
+ internal static extern int SslSetConnection(
+ SafeSslHandle sslHandle,
+ IntPtr sslConnection);
+
[DllImport(Interop.Libraries.AppleCryptoNative)]
private static extern int AppleCryptoNative_SslSetMinProtocolVersion(
SafeSslHandle sslHandle,
private static extern int AppleCryptoNative_SslSetAcceptClientCert(SafeSslHandle sslHandle);
[DllImport(Interop.Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_SslSetIoCallbacks")]
- internal static extern int SslSetIoCallbacks(
+ internal static extern unsafe int SslSetIoCallbacks(
SafeSslHandle sslHandle,
- SSLReadFunc readCallback,
- SSLWriteFunc writeCallback);
+ delegate* unmanaged<IntPtr, byte*, void**, int> readCallback,
+ delegate* unmanaged<IntPtr, byte*, void**, int> writeCallback);
[DllImport(Interop.Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_SslWrite")]
internal static extern unsafe PAL_TlsIo SslWrite(SafeSslHandle sslHandle, byte* writeFrom, int count, out int bytesWritten);
using System.Diagnostics;
using System.Net.Http;
using System.Net.Security;
+using System.Runtime.InteropServices;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Win32.SafeHandles;
private const int OSStatus_errSSLWouldBlock = -9803;
private const int InitialBufferSize = 2048;
private SafeSslHandle _sslContext;
- private Interop.AppleCrypto.SSLReadFunc _readCallback;
- private Interop.AppleCrypto.SSLWriteFunc _writeCallback;
private ArrayBuffer _inputBuffer = new ArrayBuffer(InitialBufferSize);
private ArrayBuffer _outputBuffer = new ArrayBuffer(InitialBufferSize);
{
int osStatus;
+ _sslContext = CreateSslContext(credential, sslAuthenticationOptions.IsServer);
+
+ // Make sure the class instance is associated to the session and is provided
+ // in the Read/Write callback connection parameter
+ SslSetConnection(_sslContext);
+
unsafe
{
- _readCallback = ReadFromConnection;
- _writeCallback = WriteToConnection;
+ osStatus = Interop.AppleCrypto.SslSetIoCallbacks(
+ _sslContext,
+ &ReadFromConnection,
+ &WriteToConnection);
}
- _sslContext = CreateSslContext(credential, sslAuthenticationOptions.IsServer);
-
- osStatus = Interop.AppleCrypto.SslSetIoCallbacks(
- _sslContext,
- _readCallback,
- _writeCallback);
-
if (osStatus != 0)
{
throw Interop.AppleCrypto.CreateExceptionForOSStatus(osStatus);
return sslContext;
}
+ private void SslSetConnection(SafeSslHandle sslContext)
+ {
+ GCHandle handle = GCHandle.Alloc(this, GCHandleType.Weak);
+
+ Interop.AppleCrypto.SslSetConnection(sslContext, GCHandle.ToIntPtr(handle));
+ }
+
public override bool IsInvalid => _sslContext?.IsInvalid ?? true;
protected override void Dispose(bool disposing)
base.Dispose(disposing);
}
- private unsafe int WriteToConnection(void* connection, byte* data, void** dataLength)
+ [UnmanagedCallersOnly]
+ private static unsafe int WriteToConnection(IntPtr connection, byte* data, void** dataLength)
{
+ SafeDeleteSslContext? context = (SafeDeleteSslContext?)GCHandle.FromIntPtr(connection).Target;
+ Debug.Assert(context != null);
+
// We don't pool these buffers and we can't because there's a race between their us in the native
// read/write callbacks and being disposed when the SafeHandle is disposed. This race is benign currently,
// but if we were to pool the buffers we would have a potential use-after-free issue.
int toWrite = (int)length;
var inputBuffer = new ReadOnlySpan<byte>(data, toWrite);
- _outputBuffer.EnsureAvailableSpace(toWrite);
- inputBuffer.CopyTo(_outputBuffer.AvailableSpan);
- _outputBuffer.Commit(toWrite);
+ context._outputBuffer.EnsureAvailableSpace(toWrite);
+ inputBuffer.CopyTo(context._outputBuffer.AvailableSpan);
+ context._outputBuffer.Commit(toWrite);
// Since we can enqueue everything, no need to re-assign *dataLength.
return OSStatus_noErr;
catch (Exception e)
{
if (NetEventSource.Log.IsEnabled())
- NetEventSource.Error(this, $"WritingToConnection failed: {e.Message}");
+ NetEventSource.Error(context, $"WritingToConnection failed: {e.Message}");
return OSStatus_writErr;
}
}
- private unsafe int ReadFromConnection(void* connection, byte* data, void** dataLength)
+ [UnmanagedCallersOnly]
+ private static unsafe int ReadFromConnection(IntPtr connection, byte* data, void** dataLength)
{
+ SafeDeleteSslContext? context = (SafeDeleteSslContext?)GCHandle.FromIntPtr(connection).Target;
+ Debug.Assert(context != null);
+
try
{
ulong toRead = (ulong)*dataLength;
uint transferred = 0;
- if (_inputBuffer.ActiveLength == 0)
+ if (context._inputBuffer.ActiveLength == 0)
{
*dataLength = (void*)0;
return OSStatus_errSSLWouldBlock;
}
- int limit = Math.Min((int)toRead, _inputBuffer.ActiveLength);
+ int limit = Math.Min((int)toRead, context._inputBuffer.ActiveLength);
- _inputBuffer.ActiveSpan.Slice(0, limit).CopyTo(new Span<byte>(data, limit));
- _inputBuffer.Discard(limit);
+ context._inputBuffer.ActiveSpan.Slice(0, limit).CopyTo(new Span<byte>(data, limit));
+ context._inputBuffer.Discard(limit);
transferred = (uint)limit;
*dataLength = (void*)transferred;
catch (Exception e)
{
if (NetEventSource.Log.IsEnabled())
- NetEventSource.Error(this, $"ReadFromConnectionfailed: {e.Message}");
+ NetEventSource.Error(context, $"ReadFromConnectionfailed: {e.Message}");
return OSStatus_readErr;
}
}