in MySQL.Data/src/NativeDriver.cs [182:328]
public async Task OpenAsync(bool execAsync, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
// connect to one of our specified hosts
try
{
var result = await StreamCreator.GetStreamAsync(Settings, cancellationToken, execAsync).ConfigureAwait(false);
baseStream = result.Item1;
networkStream = result.Item2;
if (Settings.IncludeSecurityAsserts)
MySqlSecurityPermission.CreatePermissionSet(false).Assert();
}
catch (System.Security.SecurityException) { throw; }
catch (TimeoutException) { throw; }
catch (AggregateException ae)
{
ae.Handle(ex =>
{
if (ex is System.Net.Sockets.SocketException)
throw new MySqlException(Resources.UnableToConnectToHost, (int)MySqlErrorCode.UnableToConnectToHost, ex);
return ex is MySqlException;
});
}
catch (Exception ex)
{
throw new MySqlException(Resources.UnableToConnectToHost, (int)MySqlErrorCode.UnableToConnectToHost, ex);
}
if (baseStream == null)
throw new MySqlException(Resources.UnableToConnectToHost, (int)MySqlErrorCode.UnableToConnectToHost);
int maxSinglePacket = 255 * 255 * 255;
stream = new MySqlStream(baseStream, Encoding, false, networkStream?.Socket);
stream.ResetTimeout((int)Settings.ConnectionTimeout * 1000);
// read off the welcome packet and parse out it's values
packet = await stream.ReadPacketAsync(execAsync).ConfigureAwait(false);
int protocol = packet.ReadByte();
if (protocol != 10)
throw new MySqlException("Unsupported protocol version.");
string versionString = packet.ReadString();
version = DBVersion.Parse(versionString);
threadId = packet.ReadInteger(4);
byte[] seedPart1 = packet.ReadStringAsBytes();
maxSinglePacket = (256 * 256 * 256) - 1;
// read in Server capabilities if they are provided
ClientFlags serverCaps = 0;
if (packet.HasMoreData)
serverCaps = (ClientFlags)packet.ReadInteger(2);
/* New protocol with 16 bytes to describe server characteristics */
owner.ConnectionCharSetIndex = (int)packet.ReadByte();
serverStatus = (ServerStatusFlags)packet.ReadInteger(2);
// Since 5.5, high bits of server caps are stored after status.
// Previously, it was part of reserved always 0x00 13-byte filler.
uint serverCapsHigh = (uint)packet.ReadInteger(2);
serverCaps |= (ClientFlags)(serverCapsHigh << 16);
packet.Position += 11;
byte[] seedPart2 = packet.ReadStringAsBytes();
encryptionSeed = new byte[seedPart1.Length + seedPart2.Length];
seedPart1.CopyTo(encryptionSeed, 0);
seedPart2.CopyTo(encryptionSeed, seedPart1.Length);
string authenticationMethod = Settings.DefaultAuthenticationPlugin;
if (string.IsNullOrWhiteSpace(authenticationMethod))
{
if ((serverCaps & ClientFlags.PLUGIN_AUTH) != 0)
authenticationMethod = packet.ReadString();
else
// Some MySql versions like 5.1, don't give name of plugin, default to native password.
authenticationMethod = "mysql_native_password";
}
// based on our settings, set our connection flags
SetConnectionFlags(serverCaps);
packet.Clear();
await packet.WriteIntegerAsync((int)connectionFlags, 4, execAsync).ConfigureAwait(false);
await packet.WriteIntegerAsync(maxSinglePacket, 4, execAsync).ConfigureAwait(false);
packet.WriteByte(33); //character set utf-8
await packet.WriteAsync(new byte[23], execAsync).ConfigureAwait(false);
// Server doesn't support SSL connections
if ((serverCaps & ClientFlags.SSL) == 0)
{
if (Settings.SslMode != MySqlSslMode.Disabled && Settings.SslMode != MySqlSslMode.Prefered)
throw new MySqlException(string.Format(Resources.NoServerSSLSupport, Settings.Server));
}
// Current connection doesn't support SSL connections
else if ((connectionFlags & ClientFlags.SSL) == 0)
{
if (Settings.SslMode != MySqlSslMode.Disabled && Settings.SslMode != MySqlSslMode.Prefered)
throw new MySqlException(string.Format(Resources.SslNotAllowedForConnectionProtocol, Settings.ConnectionProtocol));
}
// Server and connection supports SSL connections and Client are requisting a secure connection
else
{
await stream.SendPacketAsync(packet, execAsync).ConfigureAwait(false);
var result = await new Ssl(Settings).StartSSLAsync(baseStream, Encoding, Settings.ToString(), cancellationToken, execAsync).ConfigureAwait(false);
stream = result.Item1;
baseStream = result.Item2;
packet.Clear();
await packet.WriteIntegerAsync((int)connectionFlags, 4, execAsync).ConfigureAwait(false);
await packet.WriteIntegerAsync(maxSinglePacket, 4, execAsync).ConfigureAwait(false);
packet.WriteByte(33); //character set utf-8
await packet.WriteAsync(new byte[23], execAsync).ConfigureAwait(false);
}
try
{
await AuthenticateAsync(authenticationMethod, false, execAsync).ConfigureAwait(false);
}
catch (Exception)
{
// If the authenticationMethod is kerberos and KerberosAuthMode is on AUTO, it will retry the connection using GSSAPI mode
if ((authenticationMethod == "authentication_kerberos_client" || authPlugin.SwitchedPlugin == "authentication_kerberos_client")
&& Settings.KerberosAuthMode == KerberosAuthMode.AUTO)
{
Settings.KerberosAuthMode = KerberosAuthMode.GSSAPI;
await OpenAsync(execAsync, cancellationToken).ConfigureAwait(false);
}
else
throw;
}
// if we are using compression, then we use our CompressedStream class
// to hide the ugliness of managing the compression
if ((connectionFlags & ClientFlags.COMPRESS) != 0)
stream = new MySqlStream(baseStream, Encoding, true, networkStream?.Socket);
// give our stream the server version we are connected to.
// We may have some fields that are read differently based
// on the version of the server we are connected to.
packet.Version = version;
stream.MaxBlockSize = maxSinglePacket;
}