Skip to content

Commit dcf6856

Browse files
committed
Remove TLS version fallback code. Fixes #1409
1 parent c9d2217 commit dcf6856

File tree

4 files changed

+84
-146
lines changed

4 files changed

+84
-146
lines changed

src/MySqlConnector/Core/ConnectionPool.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ internal sealed class ConnectionPool : IDisposable
1818

1919
public ConnectionSettings ConnectionSettings { get; }
2020

21-
public SslProtocols SslProtocols { get; set; }
22-
2321
public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection, long startingTimestamp, int timeoutMilliseconds, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
2422
{
2523
cancellationToken.ThrowIfCancellationRequested();
@@ -600,7 +598,6 @@ private ConnectionPool(MySqlConnectorLoggingConfiguration loggingConfiguration,
600598
m_connectionLogger = loggingConfiguration.ConnectionLogger;
601599
ConnectionSettings = cs;
602600
Name = cs.ApplicationName;
603-
SslProtocols = cs.TlsVersions;
604601
m_generation = 0;
605602
m_cleanSemaphore = new(1);
606603
m_sessionSemaphore = new(cs.MaximumPoolSize);

src/MySqlConnector/Core/ServerSession.cs

Lines changed: 84 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -422,157 +422,106 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
422422
}
423423
}
424424

425-
// TLS negotiation should automatically fall back to the best version supported by client and server. However,
426-
// Windows Schannel clients will fail to connect to a yaSSL-based MySQL Server if TLS 1.2 is requested and
427-
// have to use only TLS 1.1: https://github.com/mysql-net/MySqlConnector/pull/101
428-
// In order to use the best protocol possible (i.e., not always default to TLS 1.1), we try the OS-default protocol
429-
// (which is SslProtocols.None; see https://docs.microsoft.com/en-us/dotnet/framework/network-programming/tls),
430-
// then fall back to SslProtocols.Tls11 if that fails and it's possible that the cause is a yaSSL server.
431-
bool shouldRetrySsl;
432-
var shouldUpdatePoolSslProtocols = false;
433-
var sslProtocols = Pool?.SslProtocols ?? cs.TlsVersions;
434-
PayloadData payload;
435-
InitialHandshakePayload initialHandshake;
436-
do
425+
var connected = false;
426+
if (cs.ConnectionProtocol == MySqlConnectionProtocol.Sockets)
427+
connected = await OpenTcpSocketAsync(cs, loadBalancer ?? throw new ArgumentNullException(nameof(loadBalancer)), activity, ioBehavior, cancellationToken).ConfigureAwait(false);
428+
else if (cs.ConnectionProtocol == MySqlConnectionProtocol.UnixSocket)
429+
connected = await OpenUnixSocketAsync(cs, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
430+
else if (cs.ConnectionProtocol == MySqlConnectionProtocol.NamedPipe)
431+
connected = await OpenNamedPipeAsync(cs, startingTimestamp, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
432+
if (!connected)
437433
{
438-
var isTls11or10Supported = (sslProtocols & (SslProtocols.Tls | SslProtocols.Tls11)) != SslProtocols.None;
439-
var isTls12Supported = (sslProtocols & SslProtocols.Tls12) == SslProtocols.Tls12;
440-
shouldRetrySsl = (sslProtocols == SslProtocols.None || (isTls12Supported && isTls11or10Supported)) && Utility.IsWindows();
441-
442-
var connected = false;
443-
if (cs.ConnectionProtocol == MySqlConnectionProtocol.Sockets)
444-
connected = await OpenTcpSocketAsync(cs, loadBalancer ?? throw new ArgumentNullException(nameof(loadBalancer)), activity, ioBehavior, cancellationToken).ConfigureAwait(false);
445-
else if (cs.ConnectionProtocol == MySqlConnectionProtocol.UnixSocket)
446-
connected = await OpenUnixSocketAsync(cs, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
447-
else if (cs.ConnectionProtocol == MySqlConnectionProtocol.NamedPipe)
448-
connected = await OpenNamedPipeAsync(cs, startingTimestamp, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
449-
if (!connected)
450-
{
451-
lock (m_lock)
452-
m_state = State.Failed;
453-
Log.ConnectingFailed(m_logger, Id);
454-
throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Unable to connect to any of the specified MySQL hosts.");
455-
}
434+
lock (m_lock)
435+
m_state = State.Failed;
436+
Log.ConnectingFailed(m_logger, Id);
437+
throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Unable to connect to any of the specified MySQL hosts.");
438+
}
456439

457-
var byteHandler = m_socket is null ? new StreamByteHandler(m_stream!) : (IByteHandler) new SocketByteHandler(m_socket);
458-
if (cs.ConnectionTimeout != 0)
459-
byteHandler.RemainingTimeout = Math.Max(1, cs.ConnectionTimeoutMilliseconds - Utility.GetElapsedMilliseconds(startingTimestamp));
460-
m_payloadHandler = new StandardPayloadHandler(byteHandler);
440+
var byteHandler = m_socket is null ? new StreamByteHandler(m_stream!) : (IByteHandler) new SocketByteHandler(m_socket);
441+
if (cs.ConnectionTimeout != 0)
442+
byteHandler.RemainingTimeout = Math.Max(1, cs.ConnectionTimeoutMilliseconds - Utility.GetElapsedMilliseconds(startingTimestamp));
443+
m_payloadHandler = new StandardPayloadHandler(byteHandler);
461444

462-
payload = await ReceiveAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
463-
initialHandshake = InitialHandshakePayload.Create(payload.Span);
445+
var payload = await ReceiveAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
446+
var initialHandshake = InitialHandshakePayload.Create(payload.Span);
464447

465-
// if PluginAuth is supported, then use the specified auth plugin; else, fall back to protocol capabilities to determine the auth type to use
466-
string authPluginName;
467-
if ((initialHandshake.ProtocolCapabilities & ProtocolCapabilities.PluginAuth) != 0)
468-
authPluginName = initialHandshake.AuthPluginName!;
469-
else
470-
authPluginName = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.SecureConnection) == 0 ? "mysql_old_password" : "mysql_native_password";
471-
Log.ServerSentAuthPluginName(m_logger, Id, authPluginName);
472-
if (authPluginName != "mysql_native_password" && authPluginName != "sha256_password" && authPluginName != "caching_sha2_password")
473-
{
474-
Log.UnsupportedAuthenticationMethod(m_logger, Id, authPluginName);
475-
throw new NotSupportedException($"Authentication method '{initialHandshake.AuthPluginName}' is not supported.");
476-
}
448+
// if PluginAuth is supported, then use the specified auth plugin; else, fall back to protocol capabilities to determine the auth type to use
449+
string authPluginName;
450+
if ((initialHandshake.ProtocolCapabilities & ProtocolCapabilities.PluginAuth) != 0)
451+
authPluginName = initialHandshake.AuthPluginName!;
452+
else
453+
authPluginName = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.SecureConnection) == 0 ? "mysql_old_password" : "mysql_native_password";
454+
Log.ServerSentAuthPluginName(m_logger, Id, authPluginName);
455+
if (authPluginName != "mysql_native_password" && authPluginName != "sha256_password" && authPluginName != "caching_sha2_password")
456+
{
457+
Log.UnsupportedAuthenticationMethod(m_logger, Id, authPluginName);
458+
throw new NotSupportedException($"Authentication method '{initialHandshake.AuthPluginName}' is not supported.");
459+
}
477460

478-
ServerVersion = new(initialHandshake.ServerVersion);
479-
ConnectionId = initialHandshake.ConnectionId;
480-
AuthPluginData = initialHandshake.AuthPluginData;
481-
m_useCompression = cs.UseCompression && (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Compress) != 0;
482-
CancellationTimeout = cs.CancellationTimeout;
483-
UserID = cs.UserID;
461+
ServerVersion = new(initialHandshake.ServerVersion);
462+
ConnectionId = initialHandshake.ConnectionId;
463+
AuthPluginData = initialHandshake.AuthPluginData;
464+
m_useCompression = cs.UseCompression && (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Compress) != 0;
465+
CancellationTimeout = cs.CancellationTimeout;
466+
UserID = cs.UserID;
484467

485-
// set activity tags
486-
{
487-
var connectionId = ConnectionId.ToString(CultureInfo.InvariantCulture);
488-
m_activityTags[ActivitySourceHelper.DatabaseConnectionIdTagName] = connectionId;
489-
if (activity is { IsAllDataRequested: true })
490-
activity.SetTag(ActivitySourceHelper.DatabaseConnectionIdTagName, connectionId);
491-
}
468+
// set activity tags
469+
{
470+
var connectionId = ConnectionId.ToString(CultureInfo.InvariantCulture);
471+
m_activityTags[ActivitySourceHelper.DatabaseConnectionIdTagName] = connectionId;
472+
if (activity is { IsAllDataRequested: true })
473+
activity.SetTag(ActivitySourceHelper.DatabaseConnectionIdTagName, connectionId);
474+
}
492475

493-
m_supportsConnectionAttributes = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.ConnectionAttributes) != 0;
494-
m_supportsDeprecateEof = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.DeprecateEof) != 0;
495-
SupportsCachedPreparedMetadata = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.MariaDbCacheMetadata) != 0;
496-
SupportsQueryAttributes = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.QueryAttributes) != 0;
497-
m_supportsSessionTrack = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.SessionTrack) != 0;
498-
var serverSupportsSsl = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Ssl) != 0;
499-
m_characterSet = ServerVersion.Version >= ServerVersions.SupportsUtf8Mb4 ? CharacterSet.Utf8Mb4GeneralCaseInsensitive : CharacterSet.Utf8Mb3GeneralCaseInsensitive;
500-
m_setNamesPayload = ServerVersion.Version >= ServerVersions.SupportsUtf8Mb4 ?
501-
(SupportsQueryAttributes ? s_setNamesUtf8mb4WithAttributesPayload : s_setNamesUtf8mb4NoAttributesPayload) :
502-
(SupportsQueryAttributes ? s_setNamesUtf8WithAttributesPayload : s_setNamesUtf8NoAttributesPayload);
503-
504-
// disable pipelining for RDS MySQL 5.7 (assuming Aurora); otherwise take it from the connection string or default to true
505-
if (!cs.Pipelining.HasValue && ServerVersion.Version.Major == 5 && ServerVersion.Version.Minor == 7 && HostName.EndsWith(".rds.amazonaws.com", StringComparison.OrdinalIgnoreCase))
506-
{
507-
Log.AutoDetectedAurora57(m_logger, Id, HostName);
508-
m_supportsPipelining = false;
509-
}
510-
else
511-
{
512-
// pipelining is not currently compatible with compression
513-
m_supportsPipelining = !cs.UseCompression && cs.Pipelining is not false;
476+
m_supportsConnectionAttributes = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.ConnectionAttributes) != 0;
477+
m_supportsDeprecateEof = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.DeprecateEof) != 0;
478+
SupportsCachedPreparedMetadata = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.MariaDbCacheMetadata) != 0;
479+
SupportsQueryAttributes = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.QueryAttributes) != 0;
480+
m_supportsSessionTrack = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.SessionTrack) != 0;
481+
var serverSupportsSsl = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Ssl) != 0;
482+
m_characterSet = ServerVersion.Version >= ServerVersions.SupportsUtf8Mb4 ? CharacterSet.Utf8Mb4GeneralCaseInsensitive : CharacterSet.Utf8Mb3GeneralCaseInsensitive;
483+
m_setNamesPayload = ServerVersion.Version >= ServerVersions.SupportsUtf8Mb4 ?
484+
(SupportsQueryAttributes ? s_setNamesUtf8mb4WithAttributesPayload : s_setNamesUtf8mb4NoAttributesPayload) :
485+
(SupportsQueryAttributes ? s_setNamesUtf8WithAttributesPayload : s_setNamesUtf8NoAttributesPayload);
486+
487+
// disable pipelining for RDS MySQL 5.7 (assuming Aurora); otherwise take it from the connection string or default to true
488+
if (!cs.Pipelining.HasValue && ServerVersion.Version.Major == 5 && ServerVersion.Version.Minor == 7 && HostName.EndsWith(".rds.amazonaws.com", StringComparison.OrdinalIgnoreCase))
489+
{
490+
Log.AutoDetectedAurora57(m_logger, Id, HostName);
491+
m_supportsPipelining = false;
492+
}
493+
else
494+
{
495+
// pipelining is not currently compatible with compression
496+
m_supportsPipelining = !cs.UseCompression && cs.Pipelining is not false;
514497

515-
// for pipelining, concatenate reset connection and SET NAMES query into one buffer
516-
if (m_supportsPipelining)
517-
{
518-
m_pipelinedResetConnectionBytes = new byte[m_setNamesPayload.Span.Length + 9];
498+
// for pipelining, concatenate reset connection and SET NAMES query into one buffer
499+
if (m_supportsPipelining)
500+
{
501+
m_pipelinedResetConnectionBytes = new byte[m_setNamesPayload.Span.Length + 9];
519502

520-
// first packet: reset connection
521-
m_pipelinedResetConnectionBytes[0] = 1;
522-
m_pipelinedResetConnectionBytes[4] = (byte) CommandKind.ResetConnection;
503+
// first packet: reset connection
504+
m_pipelinedResetConnectionBytes[0] = 1;
505+
m_pipelinedResetConnectionBytes[4] = (byte) CommandKind.ResetConnection;
523506

524-
// second packet: SET NAMES query
525-
m_pipelinedResetConnectionBytes[5] = (byte) m_setNamesPayload.Span.Length;
526-
m_setNamesPayload.Span.CopyTo(m_pipelinedResetConnectionBytes.AsSpan()[9..]);
527-
}
507+
// second packet: SET NAMES query
508+
m_pipelinedResetConnectionBytes[5] = (byte) m_setNamesPayload.Span.Length;
509+
m_setNamesPayload.Span.CopyTo(m_pipelinedResetConnectionBytes.AsSpan()[9..]);
528510
}
511+
}
529512

530-
Log.SessionMadeConnection(m_logger, Id, ServerVersion.OriginalString, ConnectionId, m_useCompression, m_supportsConnectionAttributes, m_supportsDeprecateEof, SupportsCachedPreparedMetadata, serverSupportsSsl, m_supportsSessionTrack, m_supportsPipelining, SupportsQueryAttributes);
531-
532-
if (cs.SslMode != MySqlSslMode.None && (cs.SslMode != MySqlSslMode.Preferred || serverSupportsSsl))
533-
{
534-
if (!serverSupportsSsl)
535-
{
536-
Log.ServerDoesNotSupportSsl(m_logger, Id);
537-
throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Server does not support SSL");
538-
}
539-
540-
try
541-
{
542-
await InitSslAsync(initialHandshake.ProtocolCapabilities, cs, connection, sslProtocols, ioBehavior, cancellationToken).ConfigureAwait(false);
543-
shouldRetrySsl = false;
544-
if (shouldUpdatePoolSslProtocols && Pool is not null)
545-
Pool.SslProtocols = sslProtocols;
546-
}
547-
catch (ArgumentException ex) when (ex.ParamName == "sslProtocolType" && sslProtocols == SslProtocols.None)
548-
{
549-
Log.SessionDoesNotSupportSslProtocolsNone(m_logger, ex, Id);
550-
sslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12;
551-
}
552-
catch (Exception ex) when (shouldRetrySsl && IsRetryableException(ex))
553-
{
554-
// negotiating TLS 1.2 with a yaSSL-based server throws an exception on Windows, see comment at top of method
555-
Log.FailedNegotiatingTls(m_logger, ex, Id);
556-
sslProtocols = sslProtocols == SslProtocols.None ? SslProtocols.Tls | SslProtocols.Tls11 : (SslProtocols.Tls | SslProtocols.Tls11) & sslProtocols;
557-
shouldUpdatePoolSslProtocols = true;
558-
}
513+
Log.SessionMadeConnection(m_logger, Id, ServerVersion.OriginalString, ConnectionId, m_useCompression, m_supportsConnectionAttributes, m_supportsDeprecateEof, SupportsCachedPreparedMetadata, serverSupportsSsl, m_supportsSessionTrack, m_supportsPipelining, SupportsQueryAttributes);
559514

560-
static bool IsRetryableException(Exception? ex) => (ex is MySqlException && IsRetryableException(ex.InnerException)) ||
561-
(ex is AuthenticationException or (IOException and not FileNotFoundException and not DirectoryNotFoundException and not DriveNotFoundException and not PathTooLongException));
562-
}
563-
else
515+
if (cs.SslMode != MySqlSslMode.None && (cs.SslMode != MySqlSslMode.Preferred || serverSupportsSsl))
516+
{
517+
if (!serverSupportsSsl)
564518
{
565-
shouldRetrySsl = false;
519+
Log.ServerDoesNotSupportSsl(m_logger, Id);
520+
throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Server does not support SSL");
566521
}
567522

568-
if (shouldRetrySsl)
569-
{
570-
// avoid "The collection already contains item with same key 'net.transport'" exception when retrying SSL
571-
m_activityTags.Remove(ActivitySourceHelper.NetTransportTagName);
572-
m_activityTags.Remove(ActivitySourceHelper.NetPeerNameTagName);
573-
m_activityTags.Remove(ActivitySourceHelper.NetPeerPortTagName);
574-
}
575-
} while (shouldRetrySsl);
523+
await InitSslAsync(initialHandshake.ProtocolCapabilities, cs, connection, cs.TlsVersions, ioBehavior, cancellationToken).ConfigureAwait(false);
524+
}
576525

577526
if (m_supportsConnectionAttributes && cs.ConnectionAttributes is null)
578527
cs.ConnectionAttributes = CreateConnectionAttributes(cs.ApplicationName);

src/MySqlConnector/Logging/EventIds.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,6 @@ internal static class EventIds
3333
public const int AutoDetectedAurora57 = 2103;
3434
public const int SessionMadeConnection = 2104;
3535
public const int ServerDoesNotSupportSsl = 2105;
36-
public const int SessionDoesNotSupportSslProtocolsNone = 2106;
37-
public const int FailedNegotiatingTls = 2107;
3836
public const int CouldNotConnectToServer = 2108;
3937
public const int SendingPipelinedResetConnectionRequest = 2109;
4038
public const int SendingResetConnectionRequest = 2110;

src/MySqlConnector/Logging/Log.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,6 @@ internal static partial class Log
5252
[LoggerMessage(EventIds.ServerDoesNotSupportSsl, LogLevel.Error, "Session {SessionId} requires SSL but server doesn't support it")]
5353
public static partial void ServerDoesNotSupportSsl(ILogger logger, string sessionId);
5454

55-
[LoggerMessage(EventIds.SessionDoesNotSupportSslProtocolsNone, LogLevel.Debug, "Session {SessionId} doesn't support SslProtocols.None; falling back to explicitly specifying SslProtocols")]
56-
public static partial void SessionDoesNotSupportSslProtocolsNone(ILogger logger, Exception exception, string sessionId);
57-
58-
[LoggerMessage(EventIds.FailedNegotiatingTls, LogLevel.Warning, "Session {SessionId} failed negotiating TLS; falling back to TLS 1.1")]
59-
public static partial void FailedNegotiatingTls(ILogger logger, Exception exception, string sessionId);
60-
6155
[LoggerMessage(EventIds.CouldNotConnectToServer, LogLevel.Error, "Session {SessionId} couldn't connect to server")]
6256
public static partial void CouldNotConnectToServer(ILogger logger, Exception exception, string sessionId);
6357

0 commit comments

Comments
 (0)