using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using System.Net; using System.Net.Sockets; namespace SmtpRelay; public class SmtpServer(IServiceScopeFactory scopeFactory, ILogger> logger) where TSession : SmtpSessionBase { protected readonly IServiceScopeFactory _scopeFactory = scopeFactory; protected readonly ILogger> _logger = logger; public bool IsRunning { get; private set; } = false; public async Task RunAsync(SmtpConfig config, CancellationToken cancellationToken) { if (IsRunning) return; var ep = new IPEndPoint(IPAddress.Parse(config.Host), config.Port); // start listener using var listener = new TcpListener(ep); listener.Start(); cancellationToken.Register(listener.Stop); // set listener state IsRunning = true; // log if (_logger.IsEnabled(LogLevel.Information)) _logger.LogInformation("SMTP listening on {HOST}:{PORT}", config.Host, config.Port); // handler loop while (!cancellationToken.IsCancellationRequested) { try { // await new client var client = await listener.AcceptTcpClientAsync(cancellationToken); // handle client async _ = HandleClientAsync(client, config, cancellationToken); } catch (OperationCanceledException) { } catch (Exception ex) { if (_logger.IsEnabled(LogLevel.Error)) _logger.LogError("ERROR: [{ERR}]", ex.ToString()); } } // set listener state IsRunning = false; } private async Task HandleClientAsync(TcpClient client, SmtpConfig config, CancellationToken cancellationToken) { // create session using var scope = _scopeFactory.CreateScope(); var session = scope.ServiceProvider.GetRequiredService(); try { await session.InitAsync(client.Client, config, cancellationToken); } finally { client?.Dispose(); } } }