Skip to content

Commit 63060ae

Browse files
committed
Add STARTTLS for SmtpRelayClient, rather than only SMTPS. Especially useful for server-to-server connections, when acting as an MTA.
1 parent f567c0e commit 63060ae

2 files changed

Lines changed: 43 additions & 2 deletions

File tree

src/Zetian.Relay/Client/SmtpRelayClient.cs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using Zetian.Protocol;
1717
using Zetian.Relay.Abstractions;
1818
using Zetian.Relay.Models;
19+
using Zetian.Relay.Services;
1920

2021
namespace Zetian.Relay.Client
2122
{
@@ -62,13 +63,27 @@ public async Task ConnectAsync(CancellationToken cancellationToken = default)
6263
using CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
6364
cts.CancelAfter(Timeout);
6465

65-
await _tcpClient.ConnectAsync(Host, Port).ConfigureAwait(false);
66+
await _tcpClient.ConnectAsync(Host, Port, cts.Token).ConfigureAwait(false);
6667

6768
_stream = _tcpClient.GetStream();
6869

6970
if (EnableSsl)
7071
{
71-
await UpgradeToSslAsync(cts.Token).ConfigureAwait(false);
72+
try
73+
{
74+
await UpgradeToSslAsync(cts.Token).ConfigureAwait(false);
75+
}
76+
catch (Exception)
77+
{
78+
_logger.LogInformation("Failed to connect as SMTPS on {Host}:{Port}", Host, Port);
79+
80+
_stream.Close();
81+
await _stream.DisposeAsync();
82+
83+
_tcpClient = new TcpClient();
84+
await _tcpClient.ConnectAsync(Host, Port, cts.Token).ConfigureAwait(false);
85+
_stream = _tcpClient.GetStream();
86+
}
7287
}
7388

7489
_reader = new StreamReader(_stream, Encoding.ASCII);
@@ -84,6 +99,15 @@ public async Task ConnectAsync(CancellationToken cancellationToken = default)
8499
// Send EHLO
85100
await SendEhloAsync(cts.Token).ConfigureAwait(false);
86101

102+
// Upgrade the connection to STARTTLS if allowed
103+
if (EnableSsl && _stream is not SslStream && _serverCapabilities?.ContainsKey("STARTTLS") == true)
104+
{
105+
await UpgradeToStartTlsAsync(cts.Token).ConfigureAwait(false);
106+
107+
_reader = new StreamReader(_stream, Encoding.ASCII);
108+
_writer = new StreamWriter(_stream, Encoding.ASCII) { AutoFlush = true };
109+
}
110+
87111
_logger.LogInformation("Connected to {Host}:{Port}", Host, Port);
88112
}
89113
catch (Exception ex)
@@ -398,6 +422,18 @@ await sslStream.AuthenticateAsClientAsync(
398422
_logger.LogDebug("SSL/TLS connection established");
399423
}
400424

425+
private async Task UpgradeToStartTlsAsync(CancellationToken cancellationToken)
426+
{
427+
await SendCommandAsync("STARTTLS", cancellationToken).ConfigureAwait(false);
428+
429+
SmtpResponse response = await ReadResponseAsync(cancellationToken).ConfigureAwait(false);
430+
431+
if (response.IsSuccess)
432+
{
433+
await UpgradeToSslAsync(cancellationToken);
434+
}
435+
}
436+
401437
private async Task AuthPlainAsync(CancellationToken cancellationToken)
402438
{
403439
if (Credentials == null)

src/Zetian.Relay/Services/RelayService.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,11 @@ private async Task<bool> CanRelayAsync(ISmtpSession session, ISmtpMessage messag
426426
return config;
427427
}
428428

429+
if (Configuration.DefaultSmartHost?.Host == host && Configuration.DefaultSmartHost?.Port == port)
430+
{
431+
return Configuration.DefaultSmartHost;
432+
}
433+
429434
// Create default configuration
430435
return new SmartHostConfiguration
431436
{

0 commit comments

Comments
 (0)