refactor
This commit is contained in:
parent
7b147569e0
commit
d32207fbf7
6 changed files with 30 additions and 51 deletions
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
},
|
},
|
||||||
"Whitelist": {
|
"Whitelist": {
|
||||||
"Senders": [
|
"Senders": [
|
||||||
"me2@example.local"
|
"me@example.local"
|
||||||
],
|
],
|
||||||
"Receivers": [
|
"Receivers": [
|
||||||
"noreply@example.local",
|
"noreply@example.local",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue