using Akari.Prototype.Server.Options; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System; using System.Buffers.Text; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; namespace Akari.Prototype.Server.Services { public class TcpProviderService : BackgroundService { public const int BufferLength = 4096; public const int MaxTokenLength = 24; public const int ReceiveTimeout = 500_000; private readonly ILogger _logger; private readonly TcpProviderOptions _options; private readonly IHostApplicationLifetime _hostApplicationLifetime; private readonly IFingerprintManager _fingerprintManager; private Task _backgroundTask; private TcpListener _listener; public TcpProviderService(ILogger logger, IOptions options, IHostApplicationLifetime hostApplicationLifetime, IFingerprintManager fingerprintManager) { _logger = logger; _options = options.Value; _hostApplicationLifetime = hostApplicationLifetime; _fingerprintManager = fingerprintManager; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { await TcpLoop(stoppingToken); } private async Task TcpLoop(CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return; } try { _listener = new TcpListener(IPAddress.Parse(_options.ListeningAddress), _options.Port); _listener.Start(); _logger.LogInformation($"Now listening on: {_listener.LocalEndpoint}"); while (!cancellationToken.IsCancellationRequested) { _logger.LogDebug("Waiting for client..."); var task = await Task.WhenAny(_listener.AcceptTcpClientAsync(), Task.Delay(Timeout.Infinite, cancellationToken)); if (task is not Task clientTask) { return; } using var client = clientTask.Result; _logger.LogDebug($"Client connected from: {client.Client.RemoteEndPoint}"); try { await ReceiveAuth(client, cancellationToken); } catch (Exception e) { _logger.LogDebug($"Couldn't proccess client request: {e.Message}"); } } } catch (Exception e) { _logger.LogError(e.ToString()); _logger.LogError("An error occured in TcpProvider, exiting..."); _hostApplicationLifetime.StopApplication(); Environment.Exit(1); } finally { _listener?.Stop(); } } private async Task ReceiveAuth(TcpClient client, CancellationToken cancellationToken) { using var stream = client.GetStream(); var data = new Memory(new byte[MaxTokenLength]); var buffer = new Memory(new byte[BufferLength]); // Receive token int position = 0; int read = 0; var timeoutToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, new CancellationTokenSource(ReceiveTimeout).Token).Token; _logger.LogDebug("Waiting for token..."); while (position < MaxTokenLength && (read = await stream.ReadAsync(buffer, timeoutToken)) != 0) { if (position + read > data.Length) { // Invalid token return; } buffer[..read].CopyTo(data[position..]); position += read; } _logger.LogDebug($"Received token: {Convert.ToBase64String(data[..position].Span)}"); await _fingerprintManager.VerifyToken(data[..position].ToArray()); } } }