Add encrypted managed UDP relay and native voice conformance

This commit is contained in:
2026-09-15 22:53:54 +02:00
parent 4067bab7c2
commit 05eacb3092
19 changed files with 1068 additions and 46 deletions
@@ -21,6 +21,8 @@ internal sealed class TlsControlConnection : IAsyncDisposable
private int prefixBytes;
private byte[]? payload;
private int payloadBytes;
private readonly TaskCompletionSource mediaReady = new(TaskCreationOptions.RunContinuationsAsynchronously);
private MediaSessionCrypto? mediaCrypto;
public Task Completion { get; }
public CancellationToken CancellationToken => lifetime.Token;
@@ -48,6 +50,12 @@ internal sealed class TlsControlConnection : IAsyncDisposable
public void CompleteWrites() => outgoing.Writer.TryComplete();
internal async Task<MediaSessionCrypto> TakeMediaCryptoAsync(CancellationToken cancellationToken)
{
await mediaReady.Task.WaitAsync(cancellationToken).ConfigureAwait(false);
return Interlocked.Exchange(ref mediaCrypto, null) ?? throw new InvalidOperationException("Media crypto already has an owner.");
}
private async Task RunAsync()
{
byte[] ciphertext = new byte[16384];
@@ -80,6 +88,13 @@ internal sealed class TlsControlConnection : IAsyncDisposable
break;
}
tls.ReceiveCiphertext(ciphertext.AsSpan(0, count));
if (tls.IsReady && !mediaReady.Task.IsCompleted)
{
var encryptor = tls.CreateMediaEncryptor();
try { mediaCrypto = new(encryptor, tls.CreateMediaDecryptor()); }
catch { encryptor.Dispose(); throw; }
mediaReady.SetResult();
}
if (tls.IsReady) lifetime.CancelAfter(TimeSpan.FromSeconds(60));
while ((count = tls.ReadPlaintext(plaintext)) > 0) Parse(plaintext.AsSpan(0, count));
await FlushAsync(sendBuffer, cancellationToken).ConfigureAwait(false);
@@ -104,6 +119,7 @@ internal sealed class TlsControlConnection : IAsyncDisposable
}
finally
{
mediaReady.TrySetCanceled();
lifetime.Cancel();
socket.Dispose();
if (receive is not null)
@@ -163,7 +179,7 @@ internal sealed class TlsControlConnection : IAsyncDisposable
public async ValueTask DisposeAsync()
{
lifetime.Cancel();
await Completion.ConfigureAwait(false);
lifetime.Dispose();
try { await Completion.ConfigureAwait(false); }
finally { Interlocked.Exchange(ref mediaCrypto, null)?.Dispose(); lifetime.Dispose(); }
}
}