diff --git a/src/libraries/System.Net.Mail/ref/System.Net.Mail.cs b/src/libraries/System.Net.Mail/ref/System.Net.Mail.cs
index 66a663d05940de..f531d503da81b3 100644
--- a/src/libraries/System.Net.Mail/ref/System.Net.Mail.cs
+++ b/src/libraries/System.Net.Mail/ref/System.Net.Mail.cs
@@ -181,6 +181,7 @@ public SmtpClient(string? host, int port) { }
public string? PickupDirectoryLocation { get { throw null; } set { } }
public int Port { get { throw null; } set { } }
public System.Net.ServicePoint ServicePoint { get { throw null; } }
+ public System.Net.Security.SslClientAuthenticationOptions SslOptions { get { throw null; } set { } }
public string? TargetName { get { throw null; } set { } }
public int Timeout { get { throw null; } set { } }
public bool UseDefaultCredentials { get { throw null; } set { } }
diff --git a/src/libraries/System.Net.Mail/ref/System.Net.Mail.csproj b/src/libraries/System.Net.Mail/ref/System.Net.Mail.csproj
index 26e17b8b347788..2d68e1d7bf609c 100644
--- a/src/libraries/System.Net.Mail/ref/System.Net.Mail.csproj
+++ b/src/libraries/System.Net.Mail/ref/System.Net.Mail.csproj
@@ -12,6 +12,7 @@
+
diff --git a/src/libraries/System.Net.Mail/src/System.Net.Mail.csproj b/src/libraries/System.Net.Mail/src/System.Net.Mail.csproj
index 46fc41705f60cb..7e835fc2971978 100644
--- a/src/libraries/System.Net.Mail/src/System.Net.Mail.csproj
+++ b/src/libraries/System.Net.Mail/src/System.Net.Mail.csproj
@@ -106,6 +106,8 @@
+
diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpClient.Wasm.cs b/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpClient.Wasm.cs
index b8b9c08b4cd65f..d6851d215ed39c 100644
--- a/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpClient.Wasm.cs
+++ b/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpClient.Wasm.cs
@@ -4,6 +4,7 @@
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
+using System.Net.Security;
using System.Runtime.Versioning;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
@@ -116,6 +117,13 @@ public bool EnableSsl
set => throw new PlatformNotSupportedException();
}
+ /// Gets or sets the options used to establish a TLS connection.
+ public SslClientAuthenticationOptions SslOptions
+ {
+ get => throw new PlatformNotSupportedException();
+ set => throw new PlatformNotSupportedException();
+ }
+
///
/// Certificates used by the client for establishing an SSL connection with the server.
///
diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpClient.cs b/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpClient.cs
index 372fd2b06ce932..4818f4b5e8eb1a 100644
--- a/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpClient.cs
+++ b/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpClient.cs
@@ -7,6 +7,7 @@
using System.Globalization;
using System.IO;
using System.Net.NetworkInformation;
+using System.Net.Security;
using System.Runtime.ExceptionServices;
using System.Runtime.Versioning;
using System.Security;
@@ -341,9 +342,41 @@ public bool EnableSsl
}
}
- ///
- /// Certificates used by the client for establishing an SSL connection with the server.
- ///
+ /// Gets or sets the options used to establish a TLS connection.
+ /// The TLS client authentication options. The default is a new instance.
+ ///
+ /// These options are used only when is .
+ /// When is ,
+ /// the current is used without modifying the options.
+ /// Changing this object in place, including its nested objects such as
+ /// , does not invalidate an existing connection.
+ /// To ensure changes are used for the next send, assign this property again, even to the same instance.
+ /// Assignment invalidates the cached connection so that the next send establishes a new connection.
+ /// Do not modify the options or their nested objects while a send is in progress.
+ ///
+ /// The value is .
+ /// A send operation is in progress.
+ public SslClientAuthenticationOptions SslOptions
+ {
+ get => _transport.SslOptions;
+ set
+ {
+ ArgumentNullException.ThrowIfNull(value);
+
+ if (_inCall)
+ {
+ throw new InvalidOperationException(SR.SmtpInvalidOperationDuringSend);
+ }
+
+ _transport.SslOptions = value;
+ }
+ }
+
+ /// Gets the certificates used by the client to establish a TLS connection with the server.
+ ///
+ /// Returns from ,
+ /// initializing an empty collection if necessary. Modifying the collection does not invalidate an existing connection.
+ ///
public X509CertificateCollection ClientCertificates
{
get
diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpConnection.cs b/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpConnection.cs
index 5744d9faceea55..f2bee927815dd8 100644
--- a/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpConnection.cs
+++ b/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpConnection.cs
@@ -9,9 +9,7 @@
using System.Net.Security;
using System.Net.Sockets;
using System.Runtime.ExceptionServices;
-using System.Security.Authentication;
using System.Security.Authentication.ExtendedProtection;
-using System.Security.Cryptography.X509Certificates;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
@@ -33,8 +31,6 @@ internal sealed partial class SmtpConnection
private readonly ICredentialsByHost? _credentials;
private string[]? _extensions;
- private bool _enableSsl;
- private X509CertificateCollection? _clientCertificates;
internal SmtpConnection(SmtpTransport parent, SmtpClient client, ICredentialsByHost? credentials, ISmtpAuthenticationModule[] authenticationModules)
{
@@ -54,29 +50,7 @@ internal SmtpConnection(SmtpTransport parent, SmtpClient client, ICredentialsByH
internal SmtpReplyReaderFactory? Reader => _responseReader;
- internal bool EnableSsl
- {
- get
- {
- return _enableSsl;
- }
- set
- {
- _enableSsl = value;
- }
- }
-
- internal X509CertificateCollection? ClientCertificates
- {
- get
- {
- return _clientCertificates;
- }
- set
- {
- _clientCertificates = value;
- }
- }
+ internal SslClientAuthenticationOptions? SslOptions { get; set; }
internal void InitializeConnection(string host, int port)
{
@@ -144,7 +118,7 @@ internal async Task GetConnectionAsync(string host, int port, Cancel
}
// Handle SSL/TLS
- if (_enableSsl)
+ if (SslOptions is SslClientAuthenticationOptions sslOptions)
{
if (!_serverSupportsStartTls)
{
@@ -157,30 +131,19 @@ internal async Task GetConnectionAsync(string host, int port, Cancel
await StartTlsCommand.SendAsync(this, cancellationToken).ConfigureAwait(false);
-#pragma warning disable SYSLIB0014 // ServicePointManager is obsolete
- SslStream sslStream = new SslStream(_stream!, false, ServicePointManager.ServerCertificateValidationCallback);
+ SslStream sslStream = new SslStream(_stream!);
+ _stream = sslStream;
if (isAsync)
{
// If we are using async, we need to use the async version of AuthenticateAsClientAsync
- await sslStream.AuthenticateAsClientAsync(
- new SslClientAuthenticationOptions
- {
- TargetHost = host,
- ClientCertificates = _clientCertificates,
- EnabledSslProtocols = (SslProtocols)ServicePointManager.SecurityProtocol, // enums use same values
- CertificateRevocationCheckMode = ServicePointManager.CheckCertificateRevocationList ?
- X509RevocationMode.Online : X509RevocationMode.NoCheck,
- },
- cancellationToken).ConfigureAwait(false);
+ await sslStream.AuthenticateAsClientAsync(sslOptions, cancellationToken).ConfigureAwait(false);
}
else
{
// Synchronous version
- sslStream.AuthenticateAsClient(host, _clientCertificates, (SslProtocols)ServicePointManager.SecurityProtocol, ServicePointManager.CheckCertificateRevocationList);
+ sslStream.AuthenticateAsClient(sslOptions);
}
-#pragma warning restore SYSLIB0014 // ServicePointManager is obsolete
- _stream = sslStream;
_responseReader = new SmtpReplyReaderFactory(_stream);
// According to RFC 3207: The client SHOULD send an EHLO command
diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpTransport.cs b/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpTransport.cs
index 19d01f8af9f838..d01c7a18e2c229 100644
--- a/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpTransport.cs
+++ b/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpTransport.cs
@@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.IO;
using System.Net.Mime;
+using System.Net.Security;
using System.Runtime.ExceptionServices;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
@@ -79,7 +80,17 @@ internal bool EnableSsl
}
}
- internal X509CertificateCollection ClientCertificates => field ??= new X509CertificateCollection();
+ internal SslClientAuthenticationOptions SslOptions
+ {
+ get => field ??= new SslClientAuthenticationOptions();
+ set
+ {
+ field = value;
+ InvalidateCachedConnection();
+ }
+ }
+
+ internal X509CertificateCollection ClientCertificates => SslOptions.ClientCertificates ??= new X509CertificateCollection();
internal bool ServerSupportsEai
{
@@ -122,8 +133,9 @@ internal Task GetConnectionAsync(string host, int port, Cancellation
if (EnableSsl)
{
- _connection.EnableSsl = true;
- _connection.ClientCertificates = ClientCertificates;
+ SslClientAuthenticationOptions sslOptions = SslOptions.ShallowClone();
+ sslOptions.TargetHost ??= host;
+ _connection.SslOptions = sslOptions;
}
return _connection.GetConnectionAsync(host, port, cancellationToken);
diff --git a/src/libraries/System.Net.Mail/tests/Functional/LoopbackSmtpServer.cs b/src/libraries/System.Net.Mail/tests/Functional/LoopbackSmtpServer.cs
index 39b73ff3eea558..2a138b2161ebeb 100644
--- a/src/libraries/System.Net.Mail/tests/Functional/LoopbackSmtpServer.cs
+++ b/src/libraries/System.Net.Mail/tests/Functional/LoopbackSmtpServer.cs
@@ -55,6 +55,8 @@ public class LoopbackSmtpServer : IDisposable
public ParsedMailMessage Message { get; private set; }
public bool IsEncrypted { get; private set; }
public string TlsHostName { get; private set; }
+ public System.Security.Authentication.SslProtocols TlsProtocol { get; private set; }
+ public SslApplicationProtocol ApplicationProtocol { get; private set; }
public int ConnectionCount { get; private set; }
public int MessagesReceived { get; private set; }
@@ -263,6 +265,8 @@ await SendMessageAsync(
await sslStream.AuthenticateAsServerAsync(SslOptions);
IsEncrypted = true;
TlsHostName = sslStream.TargetHostName;
+ TlsProtocol = sslStream.SslProtocol;
+ ApplicationProtocol = sslStream.NegotiatedApplicationProtocol;
stream = sslStream;
break;
diff --git a/src/libraries/System.Net.Mail/tests/Functional/SmtpClientTest.cs b/src/libraries/System.Net.Mail/tests/Functional/SmtpClientTest.cs
index 5cd51cd41c8920..7a934cef72a9e2 100644
--- a/src/libraries/System.Net.Mail/tests/Functional/SmtpClientTest.cs
+++ b/src/libraries/System.Net.Mail/tests/Functional/SmtpClientTest.cs
@@ -14,8 +14,11 @@
using System.Globalization;
using System.IO;
using System.Net.NetworkInformation;
+using System.Net.Security;
using System.Net.Sockets;
using System.Reflection;
+using System.Security.Authentication;
+using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.DotNet.RemoteExecutor;
@@ -80,6 +83,113 @@ public void EnableSslTest(bool value)
Assert.Equal(value, Smtp.EnableSsl);
}
+ [Fact]
+ public void SslOptions_DefaultsAndIdentity()
+ {
+ SslClientAuthenticationOptions options = Smtp.SslOptions;
+ Assert.Same(options, Smtp.SslOptions);
+ Assert.Null(options.TargetHost);
+ Assert.Null(options.ClientCertificates);
+ Assert.Null(options.RemoteCertificateValidationCallback);
+ Assert.Equal(SslProtocols.None, options.EnabledSslProtocols);
+ Assert.Equal(X509RevocationMode.NoCheck, options.CertificateRevocationCheckMode);
+
+ using var other = new SmtpClient();
+ Assert.NotSame(options, other.SslOptions);
+
+ var replacement = new SslClientAuthenticationOptions();
+ Smtp.SslOptions = replacement;
+ Assert.Same(replacement, Smtp.SslOptions);
+ AssertExtensions.Throws("value", () => Smtp.SslOptions = null!);
+ Assert.Same(replacement, Smtp.SslOptions);
+ }
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public void ClientCertificates_ForwardsToSslOptions(bool getCertificatesFirst)
+ {
+ if (getCertificatesFirst)
+ {
+ Assert.Empty(Smtp.ClientCertificates);
+ }
+
+ var certificates = new X509CertificateCollection();
+ var options = new SslClientAuthenticationOptions { ClientCertificates = certificates };
+ Smtp.SslOptions = options;
+ Assert.Same(certificates, Smtp.ClientCertificates);
+
+ var replacement = new X509CertificateCollection();
+ options.ClientCertificates = replacement;
+ Assert.Same(replacement, Smtp.ClientCertificates);
+
+ options.ClientCertificates = null;
+ X509CertificateCollection initialized = Smtp.ClientCertificates;
+ Assert.Empty(initialized);
+ Assert.Same(initialized, options.ClientCertificates);
+ Assert.Same(initialized, Smtp.ClientCertificates);
+
+ Smtp.SslOptions = new SslClientAuthenticationOptions();
+ Assert.NotSame(initialized, Smtp.ClientCertificates);
+ Assert.Same(Smtp.ClientCertificates, Smtp.SslOptions.ClientCertificates);
+ }
+
+ [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ [InlineData(false)]
+ [InlineData(true)]
+ public async Task SslOptions_ServicePointManagerIgnored(bool customValidation)
+ {
+ await RemoteExecutor.Invoke(async useCustomValidation =>
+ {
+ int globalCallbackCalls = 0;
+#pragma warning disable SYSLIB0014 // Verify that SMTP no longer uses these global settings.
+ ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls13;
+ ServicePointManager.CheckCertificateRevocationList = true;
+ ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, errors) =>
+ {
+ globalCallbackCalls++;
+ return !bool.Parse(useCustomValidation);
+ };
+#pragma warning restore SYSLIB0014
+
+ using var certificates = new CertificateSetup();
+ using var server = new LoopbackSmtpServer();
+ server.SslOptions = new SslServerAuthenticationOptions
+ {
+ ServerCertificateContext = certificates.CreateSslStreamCertificateContext(),
+ EnabledSslProtocols = SslProtocols.Tls12,
+ };
+ using SmtpClient client = server.CreateClient();
+ client.EnableSsl = true;
+ Assert.Equal(SslProtocols.None, client.SslOptions.EnabledSslProtocols);
+ Assert.Equal(X509RevocationMode.NoCheck, client.SslOptions.CertificateRevocationCheckMode);
+ Assert.Null(client.SslOptions.RemoteCertificateValidationCallback);
+
+ if (bool.Parse(useCustomValidation))
+ {
+ int clientCallbackCalls = 0;
+ client.SslOptions.RemoteCertificateValidationCallback = (sender, certificate, chain, errors) =>
+ {
+ clientCallbackCalls++;
+ Assert.Equal(X509RevocationMode.NoCheck, chain.ChainPolicy.RevocationMode);
+ return true;
+ };
+
+ await client.SendMailAsync("from@example.com", "to@example.com", "subject", "body");
+ Assert.Equal(1, clientCallbackCalls);
+ Assert.True(server.IsEncrypted);
+ }
+ else
+ {
+ SmtpException exception = await Assert.ThrowsAsync(() =>
+ client.SendMailAsync("from@example.com", "to@example.com", "subject", "body"));
+ Assert.IsType(exception.InnerException);
+ }
+
+ Assert.Equal(0, globalCallbackCalls);
+ }, customValidation.ToString()).DisposeAsync();
+ }
+
[Theory]
[InlineData("127.0.0.1")]
[InlineData("smtp.ximian.com")]
diff --git a/src/libraries/System.Net.Mail/tests/Functional/SmtpClientTlsTest.cs b/src/libraries/System.Net.Mail/tests/Functional/SmtpClientTlsTest.cs
index 262a01e06f7919..39cf7f23e3a929 100644
--- a/src/libraries/System.Net.Mail/tests/Functional/SmtpClientTlsTest.cs
+++ b/src/libraries/System.Net.Mail/tests/Functional/SmtpClientTlsTest.cs
@@ -52,9 +52,7 @@ public SmtpClientTlsTest(ITestOutputHelper output, CertificateSetup certificateS
ClientCertificateRequired = false,
};
-#pragma warning disable SYSLIB0014 // ServicePointManager is obsolete
- ServicePointManager.ServerCertificateValidationCallback = ServerCertValidationCallback;
-#pragma warning restore SYSLIB0014 // ServicePointManager is obsolete
+ Smtp.SslOptions.RemoteCertificateValidationCallback = ServerCertValidationCallback;
}
[ActiveIssue("https://github.com/dotnet/runtime/issues/120959", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot), nameof(PlatformDetection.IsAndroid))]
@@ -157,8 +155,10 @@ public async Task AuthenticationException_Propagates()
}
[ActiveIssue("https://github.com/dotnet/runtime/issues/120959", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot), nameof(PlatformDetection.IsAndroid))]
- [Fact]
- public async Task ClientCertificateRequired_Sent()
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public async Task ClientCertificateRequired_Sent(bool useSslOptions)
{
Server.SslOptions.ClientCertificateRequired = true;
X509Certificate2 clientCert = _certificateSetup.ServerCert; // use the server cert as a client cert for testing
@@ -176,7 +176,14 @@ public async Task ClientCertificateRequired_Sent()
Smtp.Credentials = new NetworkCredential("foo", "bar");
Smtp.EnableSsl = true;
- Smtp.ClientCertificates.Add(clientCert);
+ if (useSslOptions)
+ {
+ Smtp.SslOptions.ClientCertificates = new X509CertificateCollection { clientCert };
+ }
+ else
+ {
+ Smtp.ClientCertificates.Add(clientCert);
+ }
MailMessage msg = new MailMessage("foo@example.com", "bar@example.com", "hello", "howdydoo");
@@ -206,6 +213,163 @@ public async Task EnableSsl_ChangedAfterConnect_EstablishesNewEncryptedConnectio
Assert.True(Server.IsEncrypted, "Second connection should be encrypted after enabling SSL.");
}
+ [Theory]
+ [InlineData(null)]
+ [InlineData("")]
+ [InlineData("smtp.example.com")]
+ public async Task SslOptions_TargetHost(string? targetHost)
+ {
+ Smtp.EnableSsl = true;
+ Smtp.SslOptions.TargetHost = targetHost;
+ _serverCertValidationCallback = (cert, chain, errors) => true;
+ using var message = new MailMessage("from@example.com", "to@example.com", "subject", "body");
+
+ await SendMail(message);
+
+ Assert.Equal(targetHost ?? Smtp.Host, Server.TlsHostName);
+ Assert.Equal(targetHost, Smtp.SslOptions.TargetHost);
+ }
+
+ [Fact]
+ public async Task SslOptions_DefaultTargetHostFollowsHostChange()
+ {
+ Server.ReceiveMultipleConnections = true;
+ Smtp.EnableSsl = true;
+ string? validatedHost = null;
+ Smtp.SslOptions.RemoteCertificateValidationCallback = (sender, cert, chain, errors) =>
+ {
+ validatedHost = Assert.IsType(sender).TargetHostName;
+ return true;
+ };
+ using var message = new MailMessage("from@example.com", "to@example.com", "subject", "body");
+
+ await SendMail(message);
+ Assert.Equal("localhost", validatedHost);
+
+ Smtp.Host = "127.0.0.1";
+ await SendMail(message);
+ Assert.Equal("127.0.0.1", validatedHost);
+ Assert.Equal(2, Server.ConnectionCount);
+ Assert.Null(Smtp.SslOptions.TargetHost);
+ }
+
+ [Fact]
+ public async Task SslOptions_ProtocolSelection()
+ {
+ Smtp.EnableSsl = true;
+ Smtp.SslOptions.EnabledSslProtocols = SslProtocols.Tls12;
+ _serverCertValidationCallback = (cert, chain, errors) => true;
+ using var message = new MailMessage("from@example.com", "to@example.com", "subject", "body");
+
+ await SendMail(message);
+
+ Assert.Equal(SslProtocols.Tls12, Server.TlsProtocol);
+ }
+
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.SupportsAlpn))]
+ public async Task SslOptions_ApplicationProtocols()
+ {
+ var protocol = new SslApplicationProtocol("smtp-test");
+ Server.SslOptions.ApplicationProtocols = new() { protocol };
+ Smtp.EnableSsl = true;
+ Smtp.SslOptions.ApplicationProtocols = new() { protocol };
+ _serverCertValidationCallback = (cert, chain, errors) => true;
+ using var message = new MailMessage("from@example.com", "to@example.com", "subject", "body");
+
+ await SendMail(message);
+
+ Assert.Equal(protocol, Server.ApplicationProtocol);
+ }
+
+ [Fact]
+ public async Task SslOptions_DisabledSslDoesNotUseOptions()
+ {
+ bool callbackCalled = false;
+ Smtp.SslOptions.RemoteCertificateValidationCallback = (sender, cert, chain, errors) =>
+ {
+ callbackCalled = true;
+ return false;
+ };
+ using var message = new MailMessage("from@example.com", "to@example.com", "subject", "body");
+
+ await SendMail(message);
+
+ Assert.False(Server.IsEncrypted);
+ Assert.False(callbackCalled);
+ }
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public async Task SslOptions_AssignmentInvalidatesConnection(bool sameInstance)
+ {
+ Server.ReceiveMultipleConnections = true;
+ Smtp.EnableSsl = true;
+ X509CertificateCollection certificates = Smtp.ClientCertificates;
+ _serverCertValidationCallback = (cert, chain, errors) => true;
+ using var message = new MailMessage("from@example.com", "to@example.com", "subject", "body");
+ await SendMail(message);
+ await SendMail(message);
+ Assert.Equal(1, Server.ConnectionCount);
+
+ SslClientAuthenticationOptions options = sameInstance ? Smtp.SslOptions : new SslClientAuthenticationOptions
+ {
+ RemoteCertificateValidationCallback = ServerCertValidationCallback,
+ };
+ options.TargetHost = "smtp.example.com";
+ if (sameInstance)
+ {
+ certificates.Add(_certificateSetup.ServerCert);
+ Assert.Same(certificates, options.ClientCertificates);
+ await SendMail(message);
+ Assert.Equal(1, Server.ConnectionCount);
+ Assert.Equal("localhost", Server.TlsHostName);
+ }
+
+ Smtp.SslOptions = options;
+ await SendMail(message);
+ Assert.Equal(2, Server.ConnectionCount);
+ Assert.Equal("smtp.example.com", Server.TlsHostName);
+ }
+
+ [Fact]
+ public async Task SslOptions_InPlaceChangeAppliesAfterReconnect()
+ {
+ Server.ReceiveMultipleConnections = true;
+ Smtp.EnableSsl = true;
+ _serverCertValidationCallback = (cert, chain, errors) => true;
+ using var message = new MailMessage("from@example.com", "to@example.com", "subject", "body");
+ await SendMail(message);
+
+ Smtp.SslOptions.TargetHost = "smtp.example.com";
+ Smtp.TargetName = "SMTPSVC/another-name";
+ await SendMail(message);
+
+ Assert.Equal(2, Server.ConnectionCount);
+ Assert.Equal("smtp.example.com", Server.TlsHostName);
+ }
+
+ [Fact]
+ public async Task SslOptions_AssignmentDuringSendThrows()
+ {
+ Smtp.EnableSsl = true;
+ SslClientAuthenticationOptions original = Smtp.SslOptions;
+ bool callbackCalled = false;
+ _serverCertValidationCallback = (cert, chain, errors) =>
+ {
+ callbackCalled = true;
+ Assert.Throws(() => Smtp.SslOptions = new SslClientAuthenticationOptions());
+ Assert.Throws(() => Smtp.SslOptions = original);
+ Assert.Same(original, Smtp.SslOptions);
+ return true;
+ };
+ using var message = new MailMessage("from@example.com", "to@example.com", "subject", "body");
+
+ await SendMail(message);
+
+ Assert.True(callbackCalled);
+ }
+
private bool ServerCertValidationCallback(object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors)
{
if (_serverCertValidationCallback != null)
@@ -218,21 +382,16 @@ private bool ServerCertValidationCallback(object sender, X509Certificate? certif
}
}
- // since the tests change global state (ServicePointManager.ServerCertificateValidationCallback), we need to run them in isolation
-
- [Collection(nameof(DisableParallelization))]
public class SmtpClientTlsTest_Send : SmtpClientTlsTest, IClassFixture
{
public SmtpClientTlsTest_Send(ITestOutputHelper output, CertificateSetup certificateSetup) : base(output, certificateSetup) { }
}
- [Collection(nameof(DisableParallelization))]
public class SmtpClientTlsTest_SendAsync : SmtpClientTlsTest, IClassFixture
{
public SmtpClientTlsTest_SendAsync(ITestOutputHelper output, CertificateSetup certificateSetup) : base(output, certificateSetup) { }
}
- [Collection(nameof(DisableParallelization))]
public class SmtpClientTlsTest_SendMailAsync : SmtpClientTlsTest, IClassFixture
{
public SmtpClientTlsTest_SendMailAsync(ITestOutputHelper output, CertificateSetup certificateSetup) : base(output, certificateSetup) { }