This commit is contained in:
Kevin Kai Berthold 2025-12-03 18:19:06 +01:00
parent 7b147569e0
commit d32207fbf7
6 changed files with 30 additions and 51 deletions

View file

@ -32,6 +32,7 @@ public class GraphSender(GraphConfig config, ILogger<GraphSender> logger)
if (_cachedAccessToken == null || InvalidToken(_cachedAccessToken.Value)) if (_cachedAccessToken == null || InvalidToken(_cachedAccessToken.Value))
{ {
_cachedAccessToken = _cachedCredentials.GetToken(_tokenRequestContext, cancellationToken); _cachedAccessToken = _cachedCredentials.GetToken(_tokenRequestContext, cancellationToken);
_logger.LogInformation("Requested new Azure Access Token"); _logger.LogInformation("Requested new Azure Access Token");
// convert token => jwt // convert token => jwt
@ -69,7 +70,8 @@ public class GraphSender(GraphConfig config, ILogger<GraphSender> logger)
var sentFile = new FileInfo(Path.Combine(Common.SentDir.FullName, spoolFile.Name)); var sentFile = new FileInfo(Path.Combine(Common.SentDir.FullName, spoolFile.Name));
spoolFile.MoveTo(sentFile.FullName); spoolFile.MoveTo(sentFile.FullName);
_logger.LogInformation("Sent: {Path}", sentFile.FullName); if (_logger.IsEnabled(LogLevel.Information))
_logger.LogInformation("Sent: {Path}", sentFile.FullName);
return resp; return resp;
} }

View file

@ -24,7 +24,8 @@ public partial class GraphSmtpSession(GraphSender graphSender, ILogger<GraphSmtp
await File.WriteAllBytesAsync(spoolFile.FullName, data, cancellationToken); await File.WriteAllBytesAsync(spoolFile.FullName, data, cancellationToken);
_logger.LogInformation("Spooled: {Path}", spoolFile.FullName); if (_logger.IsEnabled(LogLevel.Information))
_logger.LogInformation("Spooled: {Path}", spoolFile.FullName);
return spoolFile; return spoolFile;
} }

View file

@ -129,7 +129,7 @@ internal partial class Program
if (string.IsNullOrWhiteSpace(graphConfig.SenderUpn)) if (string.IsNullOrWhiteSpace(graphConfig.SenderUpn))
throw new ArgumentNullException(graphConfig.SenderUpn, "{Graph:SenderUpn} cannot be null"); throw new ArgumentNullException(graphConfig.SenderUpn, "{Graph:SenderUpn} cannot be null");
if (!GraphHelper.ValidateEmailRegex().Match(graphConfig.SenderUpn).Success) if (!GraphHelper.ValidateEmailRegex().IsMatch(graphConfig.SenderUpn))
throw new ArgumentNullException(graphConfig.SenderUpn, "{Graph:SenderUpn} invalid format"); throw new ArgumentNullException(graphConfig.SenderUpn, "{Graph:SenderUpn} invalid format");
} }

View file

@ -30,7 +30,8 @@ public class SmtpServer<TSession>(IServiceScopeFactory scopeFactory, ILogger<Smt
IsRunning = true; IsRunning = true;
// log // log
_logger.LogInformation("SMTP listening on {HOST}:{PORT}", config.Host, config.Port); if (_logger.IsEnabled(LogLevel.Information))
_logger.LogInformation("SMTP listening on {HOST}:{PORT}", config.Host, config.Port);
// handler loop // handler loop
while (!cancellationToken.IsCancellationRequested) while (!cancellationToken.IsCancellationRequested)
@ -46,7 +47,8 @@ public class SmtpServer<TSession>(IServiceScopeFactory scopeFactory, ILogger<Smt
catch (OperationCanceledException) { } catch (OperationCanceledException) { }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex.ToString()); if (_logger.IsEnabled(LogLevel.Error))
_logger.LogError("ERROR: [{ERR}]", ex.ToString());
} }
} }

View file

@ -1,5 +1,4 @@
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using SmtpRelay.Graph;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@ -33,7 +32,8 @@ public abstract partial class SmtpSessionBase(ILogger<SmtpSessionBase> logger)
// set tcp options // set tcp options
socket.NoDelay = true; socket.NoDelay = true;
_logger.LogInformation("[{REP}] Connected", socket.RemoteEndPoint); if (_logger.IsEnabled(LogLevel.Information))
_logger.LogInformation("[{REP}] Connected", socket.RemoteEndPoint);
try try
{ {
@ -49,7 +49,9 @@ public abstract partial class SmtpSessionBase(ILogger<SmtpSessionBase> logger)
break; break;
var line = Encoding.ASCII.GetString(rawLine); var line = Encoding.ASCII.GetString(rawLine);
_logger.LogInformation("[{REP}] > {LINE}", socket.RemoteEndPoint, line);
if (_logger.IsEnabled(LogLevel.Debug))
_logger.LogDebug("[{REP}] > {LINE}", socket.RemoteEndPoint, line);
if (string.IsNullOrWhiteSpace(line)) if (string.IsNullOrWhiteSpace(line))
{ {
@ -77,10 +79,12 @@ public abstract partial class SmtpSessionBase(ILogger<SmtpSessionBase> logger)
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("[{REP}] Error: {MSG}", socket.RemoteEndPoint, ex.ToString()); if (_logger.IsEnabled(LogLevel.Error))
_logger.LogError("[{REP}] Error: {MSG}", socket.RemoteEndPoint, ex.ToString());
} }
_logger.LogInformation("[{REP}] Disconnected", socket.RemoteEndPoint); if (_logger.IsEnabled(LogLevel.Information))
_logger.LogInformation("[{REP}] Disconnected", socket.RemoteEndPoint);
} }
protected abstract Task OnProcessDataAsync(ReadOnlyMemory<byte> dataBytes, CancellationToken cancellationToken); protected abstract Task OnProcessDataAsync(ReadOnlyMemory<byte> dataBytes, CancellationToken cancellationToken);
@ -354,17 +358,12 @@ public abstract partial class SmtpSessionBase(ILogger<SmtpSessionBase> logger)
var rawData = await ReadToSpanAsync(socket, DataDelimiterSpan, true, cancellationToken); var rawData = await ReadToSpanAsync(socket, DataDelimiterSpan, true, cancellationToken);
await OnProcessDataAsync(rawData, cancellationToken); await OnProcessDataAsync(rawData, cancellationToken);
// dot unstuff
//if (response.StartsWith(".."))
// response = response[1..];
//var response = Encoding.ASCII.GetString(rawData);
//Console.WriteLine(JsonSerializer.Serialize(response));
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex.ToString()); if (_logger.IsEnabled(LogLevel.Error))
_logger.LogError("[{REP}] Error: {MSG}", socket.RemoteEndPoint, ex.ToString());
throw new InvalidOperationException(ex.ToString()); throw new InvalidOperationException(ex.ToString());
} }
@ -379,8 +378,6 @@ public abstract partial class SmtpSessionBase(ILogger<SmtpSessionBase> logger)
private async Task HandleAuthPlainAsync(Socket socket, SmtpConfig config, string? raw, CancellationToken cancellationToken) private async Task HandleAuthPlainAsync(Socket socket, SmtpConfig config, string? raw, CancellationToken cancellationToken)
{ {
_logger.LogCritical("AUTH PLAIN");
// PLAIN == base64 // PLAIN == base64
string payloadB64 = raw ?? string.Empty; string payloadB64 = raw ?? string.Empty;
@ -393,7 +390,8 @@ public abstract partial class SmtpSessionBase(ILogger<SmtpSessionBase> logger)
var rawLine = await ReadToSpanAsync(socket, CrlnSpan, true, cancellationToken); var rawLine = await ReadToSpanAsync(socket, CrlnSpan, true, cancellationToken);
payloadB64 = Encoding.ASCII.GetString(rawLine ?? throw new InvalidDataException("null")).Trim() ?? ""; payloadB64 = Encoding.ASCII.GetString(rawLine ?? throw new InvalidDataException("null")).Trim() ?? "";
_logger.LogInformation("[{REP}] > {LINE}", socket.RemoteEndPoint, rawLine); if (_logger.IsEnabled(LogLevel.Information))
_logger.LogInformation("[{REP}] > {LINE}", socket.RemoteEndPoint, rawLine);
} }
if (!SmtpHelper.TryBase64(payloadB64, out var bytes)) if (!SmtpHelper.TryBase64(payloadB64, out var bytes))
@ -411,13 +409,10 @@ public abstract partial class SmtpSessionBase(ILogger<SmtpSessionBase> logger)
return; return;
} }
var authzid = parts[0]; //var authzid = parts[0];
var user = parts[1]; var user = parts[1];
var pass = parts[2]; var pass = parts[2];
_logger.LogCritical(user);
_logger.LogCritical(pass);
if (ValidateUser(config, user, pass)) if (ValidateUser(config, user, pass))
{ {
_authenticated = true; _authenticated = true;
@ -432,8 +427,6 @@ public abstract partial class SmtpSessionBase(ILogger<SmtpSessionBase> logger)
private async Task HandleAuthLoginAsync(Socket socket, SmtpConfig config, string? raw, CancellationToken cancellationToken) private async Task HandleAuthLoginAsync(Socket socket, SmtpConfig config, string? raw, CancellationToken cancellationToken)
{ {
_logger.LogCritical("AUTH LOGIN");
string? user; string? user;
string? pass; string? pass;
@ -454,7 +447,8 @@ public abstract partial class SmtpSessionBase(ILogger<SmtpSessionBase> logger)
var rawLine = await ReadToSpanAsync(socket, CrlnSpan, true, cancellationToken); var rawLine = await ReadToSpanAsync(socket, CrlnSpan, true, cancellationToken);
var response = Encoding.ASCII.GetString(rawLine ?? throw new InvalidDataException("null")).Trim() ?? ""; var response = Encoding.ASCII.GetString(rawLine ?? throw new InvalidDataException("null")).Trim() ?? "";
_logger.LogInformation("[{REP}] > {LINE}", socket.RemoteEndPoint, rawLine); if (_logger.IsEnabled(LogLevel.Information))
_logger.LogInformation("[{REP}] > {LINE}", socket.RemoteEndPoint, rawLine);
if (!SmtpHelper.TryBase64(response, out var ub)) if (!SmtpHelper.TryBase64(response, out var ub))
{ {
@ -509,13 +503,6 @@ public abstract partial class SmtpSessionBase(ILogger<SmtpSessionBase> logger)
// cut out unsed buffer // cut out unsed buffer
var unusedBuffer = bufferMemory.Slice(_dataOffset, _bufferOffset - _dataOffset); var unusedBuffer = bufferMemory.Slice(_dataOffset, _bufferOffset - _dataOffset);
// DEBUG ONLY (no aot)
//_logger.LogTrace(_dataOffset.ToString());
//_logger.LogTrace(_bufferOffset.ToString());
//var dbgLine = Encoding.ASCII.GetString(unusedBuffer.Span);
//_logger.LogTrace(JsonSerializer.Serialize(dbgLine));
// find escape sequence if theres more left, before fetching new data // find escape sequence if theres more left, before fetching new data
var escapeIndex = unusedBuffer.Span.IndexOf(until.Span); var escapeIndex = unusedBuffer.Span.IndexOf(until.Span);
if (escapeIndex == -1) if (escapeIndex == -1)
@ -595,7 +582,8 @@ public abstract partial class SmtpSessionBase(ILogger<SmtpSessionBase> logger)
{ {
_writeLock.Release(); _writeLock.Release();
_logger.LogInformation("[{REP}] < {LINE}", socket.RemoteEndPoint, line); if (_logger.IsEnabled(LogLevel.Information))
_logger.LogInformation("[{REP}] < {LINE}", socket.RemoteEndPoint, line);
} }
} }
@ -612,20 +600,6 @@ public abstract partial class SmtpSessionBase(ILogger<SmtpSessionBase> logger)
private static bool ValidateReceiver(SmtpConfig config, string receiver) => SmtpHelper.MatchesAny(receiver, config.AllowedReceivers); private static bool ValidateReceiver(SmtpConfig config, string receiver) => SmtpHelper.MatchesAny(receiver, config.AllowedReceivers);
private async Task<FileInfo> SpoolToFileAsync(ReadOnlyMemory<byte> data, CancellationToken cancellationToken)
{
Directory.CreateDirectory(Common.SpoolDir.FullName);
var path = Path.Combine(Common.SpoolDir.FullName, $"{Guid.NewGuid()}.eml");
var spoolFile = new FileInfo(path);
await File.WriteAllBytesAsync(spoolFile.FullName, data, cancellationToken);
_logger.LogInformation("Spooled: {Path}", spoolFile.FullName);
return spoolFile;
}
[GeneratedRegex(@"^MAIL\s+FROM:\s*<([^>]+)>(?:\s+(.*))?$", RegexOptions.IgnoreCase)] [GeneratedRegex(@"^MAIL\s+FROM:\s*<([^>]+)>(?:\s+(.*))?$", RegexOptions.IgnoreCase)]
public static partial Regex MailFromRegex(); public static partial Regex MailFromRegex();

View file

@ -12,7 +12,7 @@
}, },
"Whitelist": { "Whitelist": {
"Senders": [ "Senders": [
"me2@example.local" "me@example.local"
], ],
"Receivers": [ "Receivers": [
"noreply@example.local", "noreply@example.local",