Code
·
141 lines
·
4916 bytes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141using MailSharp.Common;
using MailSharp.SMTP.Metrics;
using MailSharp.SMTP.Services;
using MailSharp.SMTP.Session;
using System.Net;
using System.Net.Sockets;
namespace MailSharp.SMTP.Server;
public class SmtpServer
{
private readonly IConfiguration configuration;
private readonly ILogger<SmtpServer> logger;
private readonly ILogger<SmtpSession> sessionLogger;
private readonly DkimSigner dkimSigner;
private readonly SpfChecker spfChecker;
private readonly DkimVerifier dkimVerifier;
private readonly DmarcChecker dmarcChecker;
private readonly List<ServerContext> listeners = [];
private readonly SmtpMetrics metrics;
private CancellationTokenSource? cts;
public SmtpServer(
IConfiguration configuration,
ILogger<SmtpServer> logger,
ILogger<SmtpSession> sessionLogger,
DkimSigner dkimSigner,
SpfChecker spfChecker,
DkimVerifier dkimVerifier,
DmarcChecker dmarcChecker,
SmtpMetrics metrics)
{
this.configuration = configuration;
this.logger = logger;
this.sessionLogger = sessionLogger;
this.dkimSigner = dkimSigner;
this.spfChecker = spfChecker;
this.dkimVerifier = dkimVerifier;
this.dmarcChecker = dmarcChecker;
this.metrics = metrics;
var ports = configuration.GetSection("SmtpSettings:Ports").Get<List<PortConfig>>()
?? throw new InvalidOperationException("Ports not configured");
foreach (var port in ports)
{
listeners.Add(new(new TcpListener(IPAddress.Parse(port.Host), port.Port), port.Security));
}
}
public async Task StartAsync()
{
cts = new CancellationTokenSource();
var active = new List<ServerContext>();
foreach (var context in listeners)
{
try
{
context.Listener.Start();
var eventIdConfig = configuration.GetSection("SmtpEventIds:ServerStarted").Get<EventIdConfig>()
?? throw new InvalidOperationException("Missing SmtpEventIds:ServerStarted");
logger.LogInformation(
new EventId(eventIdConfig.Id, eventIdConfig.Name),
configuration["SmtpLogMessages:ServerStarted"],
context.Listener.LocalEndpoint, context.Security);
active.Add(context);
}
catch (SocketException ex)
{
var eventIdConfig = configuration.GetSection("SmtpEventIds:ServerStartFailed").Get<EventIdConfig>()
?? throw new InvalidOperationException("Missing SmtpEventIds:ServerStartFailed");
logger.LogError(
new EventId(eventIdConfig.Id, eventIdConfig.Name),
ex,
configuration["SmtpLogMessages:ServerStartFailed"],
((IPEndPoint)context.Listener.LocalEndpoint).Port);
}
}
if (active.Count > 0)
await Task.WhenAll(active.Select(context =>
Task.Run(() => AcceptClientsAsync(context, cts.Token), cts.Token)));
await StopAsync();
}
private async Task AcceptClientsAsync(ServerContext context, CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
try
{
var client = await context.Listener.AcceptTcpClientAsync(cancellationToken);
var eventIdConfig = configuration.GetSection("SmtpEventIds:ClientAccepted").Get<EventIdConfig>()
?? throw new InvalidOperationException("Missing SmtpEventIds:ClientAccepted");
logger.LogInformation(
new EventId(eventIdConfig.Id, eventIdConfig.Name),
configuration["SmtpLogMessages:ClientAccepted"],
client.Client.RemoteEndPoint);
var session = new SmtpSession(client, configuration, context.Security, dkimSigner, spfChecker, dkimVerifier, dmarcChecker, metrics, sessionLogger);
_ = session.ProcessAsync(cancellationToken);
}
catch (OperationCanceledException)
{
var eventIdConfig = configuration.GetSection("SmtpEventIds:ListenerStopped").Get<EventIdConfig>()
?? throw new InvalidOperationException("Missing SmtpEventIds:ListenerStopped");
logger.LogInformation(
new EventId(eventIdConfig.Id, eventIdConfig.Name),
configuration["SmtpLogMessages:ListenerStopped"],
context.Listener.LocalEndpoint);
break;
}
catch (Exception ex)
{
var eventIdConfig = configuration.GetSection("SmtpEventIds:ClientAcceptError").Get<EventIdConfig>()
?? throw new InvalidOperationException("Missing SmtpEventIds:ClientAcceptError");
logger.LogError(
new EventId(eventIdConfig.Id, eventIdConfig.Name),
ex,
configuration["SmtpLogMessages:ClientAcceptError"],
context.Listener.LocalEndpoint);
}
}
}
public async Task StopAsync()
{
if (cts != null)
{
await cts.CancelAsync();
}
foreach (var context in listeners)
{
context.Listener.Stop();
var eventIdConfig = configuration.GetSection("SmtpEventIds:ListenerStopped").Get<EventIdConfig>()
?? throw new InvalidOperationException("Missing SmtpEventIds:ListenerStopped");
logger.LogInformation(
new EventId(eventIdConfig.Id, eventIdConfig.Name),
configuration["SmtpLogMessages:ListenerStopped"],
context.Listener.LocalEndpoint);
context.Listener.Dispose();
}
listeners.Clear();
}
}