SecuCore/TLS/TLSNetworkController.cs

250 lines
7.3 KiB
C#
Raw Permalink Normal View History

2026-03-03 23:53:04 -05:00
/*
* Secucore
*
* Copyright (C) 2023 Trevor Hall
* All rights reserved.
*
* This software may be modified and distributed under the terms
* of the MIT license.
*
*/
using Microsoft.VisualStudio.Threading;
using SecuCore.Shared;
using SecuCore.Sockets;
using SecuCore.TLS.Exceptions;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace SecuCore.TLS
{
class TLSNetworkController : IDataController
{
TCPSocket _socket;
public bool isFaulted = false;
public string lastError = "";
public TLSNetworkController(TCPSocket tcps)
{
_socket = tcps;
_ = Start();
}
private bool shutdownRequested = false;
private AsyncAutoResetEvent requestReset = new AsyncAutoResetEvent();
private AsyncAutoResetEvent flushReset = new AsyncAutoResetEvent();
private AsyncAutoResetEvent nextEmpty = new AsyncAutoResetEvent();
private async Task Start()
{
_ = WriteLoop();
_ = ReadLoop();
}
private bool GetNextDataDispatch(out DataDispatch next)
{
if (outgoing == null || shutdownRequested)
{
next = null;
return false;
}
return outgoing.TryDequeue(out next);
}
private bool GetNextDataRequest(out DataRequest next)
{
if (requests == null || shutdownRequested)
{
next = null;
return false;
}
return requests.TryDequeue(out next);
}
private async Task WriteLoop()
{
while (!shutdownRequested)
{
await flushReset.WaitAsync().ConfigureAwait(false);
while (GetNextDataDispatch(out DataDispatch next))
{
if (!await DispatchAsync(next).ConfigureAwait(false))
{
Shutdown(true);
return;
}
}
}
}
private async Task ReadLoop()
{
while (!shutdownRequested)
{
await requestReset.WaitAsync().ConfigureAwait(false);
try
{
while (GetNextDataRequest(out DataRequest next))
{
if (!await HandleRequestAsync(next).ConfigureAwait(false))
{
Shutdown(true);
return;
}
}
}catch (Exception) {}
}
}
bool shutdownCalled = false;
private void Shutdown(bool faulted)
{
if (shutdownCalled)
return;
shutdownCalled = true;
shutdownRequested = true;
if (faulted)
{
this.isFaulted = true;
// clear buffers and inform failure
while (outgoing.TryDequeue(out DataDispatch next))
{
if (next.tcs != null)
next.tcs.TrySetException(new Exception("Network controller failure: " + this.lastError));
}
while (requests.TryDequeue(out DataRequest next))
{
if (next.tcs != null)
next.tcs.TrySetException(new Exception("Network controller failure: " + this.lastError));
}
}
else
{
while (outgoing.TryDequeue(out DataDispatch next))
{
if (next.tcs != null)
next.tcs.TrySetCanceled();
}
while (requests.TryDequeue(out DataRequest next))
{
if (next.tcs != null)
next.tcs.TrySetCanceled();
}
}
flushReset.Set();
nextEmpty.Set();
requestReset.Set();
_socket = null;
outgoing = null;
requests = null;
}
private async ValueTask<bool> DispatchAsync(DataDispatch data)
{
if (data == null)
return false;
bool sent = await WriteNSAsync(data.data).ConfigureAwait(false);
if (data.tcs != null)
data.tcs.TrySetResult();
return sent;
}
private async ValueTask<bool> HandleRequestAsync(DataRequest request)
{
byte[] buffer = new byte[request.length];
int pos = 0;
int remain = request.length;
while (remain > 0)
{
int len = await ReadNSAsync(buffer, pos, remain).ConfigureAwait(false);
if (len > 0)
{
pos += len;
remain -= len;
}
else
{
request.tcs.TrySetException(new Exception("Network failure or timeout"));
return false;
}
}
// success
if (!request.tcs.TrySetResult(buffer))
{
lastError = "failed to set result to datarequest";
return false;
}
return true;
}
private async ValueTask<int> ReadNSAsync(byte[] buf, int offset, int length)
{
try
{
int rlen = await _socket.RecieveAsync(buf, offset, length).ConfigureAwait(false);
if (rlen > 0)
{
return rlen;
}
}
catch (Exception ex)
{
this.lastError = ex.Message;
}
return -1;
}
private async ValueTask<bool> WriteNSAsync(byte[] dat)
{
try
{
await _socket.SendAsync(dat).ConfigureAwait(false);
return true;
}
catch (Exception ex)
{
this.lastError = ex.Message;
}
return false;
}
public Queue<DataDispatch> outgoing = new Queue<DataDispatch>();
public Queue<DataRequest> requests = new Queue<DataRequest>();
public void RequestData(DataRequest request)
{
if (shutdownRequested)
throw new TLSNetworkException("Shutdown has been requested");
requests.Enqueue(request);
requestReset.Set();
}
public void QueueData(DataDispatch dispatch)
{
if (shutdownRequested)
throw new TLSNetworkException("Shutdown has been requested");
outgoing.Enqueue(dispatch);
}
public void FlushData()
{
if (shutdownRequested)
throw new TLSNetworkException("Shutdown has been requested");
flushReset.Set();
}
public async Task FlushDataFully()
{
if (shutdownRequested)
throw new TLSNetworkException("Shutdown has been requested");
flushReset.Set();
await nextEmpty.WaitAsync().ConfigureAwait(false);
}
public void Dispose() { Shutdown(false); }
}
}