public async Task <bool> TryResetConnectionAsync(ConnectionSettings cs, IOBehavior ioBehavior, CancellationToken cancellationToken) { VerifyState(State.Connected); try { if (ServerVersion.Version.CompareTo(ServerVersions.SupportsResetConnection) >= 0) { await SendAsync(ResetConnectionPayload.Create(), ioBehavior, cancellationToken).ConfigureAwait(false); var payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false); OkPayload.Create(payload); // the "reset connection" packet also resets the connection charset, so we need to change that back to our default payload = QueryPayload.Create("SET NAMES utf8mb4 COLLATE utf8mb4_bin;"); await SendAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false); payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false); OkPayload.Create(payload); } else { // optimistically hash the password with the challenge from the initial handshake (supported by MariaDB; doesn't appear to be supported by MySQL) var hashedPassword = AuthenticationUtility.CreateAuthenticationResponse(AuthPluginData, 0, cs.Password); var payload = ChangeUserPayload.Create(cs.UserID, hashedPassword, cs.Database, m_supportsConnectionAttributes ? s_connectionAttributes : null); await SendAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false); payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false); if (payload.HeaderByte == AuthenticationMethodSwitchRequestPayload.Signature) { payload = await SwitchAuthenticationAsync(cs, payload, ioBehavior, cancellationToken).ConfigureAwait(false); } OkPayload.Create(payload); } return(true); } catch (IOException) { } catch (SocketException) { } return(false); }
private async Task ResetConnectionAsync(CancellationToken cancellationToken) { if (m_session.ServerVersion.Version.CompareTo(ServerVersions.SupportsResetConnection) >= 0) { await m_session.SendAsync(ResetConnectionPayload.Create(), cancellationToken).ConfigureAwait(false); var payload = await m_session.ReceiveReplyAsync(cancellationToken).ConfigureAwait(false); OkPayload.Create(payload); } else { // optimistically hash the password with the challenge from the initial handshake (supported by MariaDB; doesn't appear to be supported by MySQL) var hashedPassword = AuthenticationUtility.HashPassword(m_session.AuthPluginData, 0, m_connectionStringBuilder.Password); var payload = ChangeUserPayload.Create(m_connectionStringBuilder.UserID, hashedPassword, m_database); await m_session.SendAsync(payload, cancellationToken).ConfigureAwait(false); payload = await m_session.ReceiveReplyAsync(cancellationToken).ConfigureAwait(false); if (payload.HeaderByte == AuthenticationMethodSwitchRequestPayload.Signature) { // if the server didn't support the hashed password; rehash with the new challenge var switchRequest = AuthenticationMethodSwitchRequestPayload.Create(payload); if (switchRequest.Name != "mysql_native_password") { throw new NotSupportedException("Only 'mysql_native_password' authentication method is supported."); } hashedPassword = AuthenticationUtility.HashPassword(switchRequest.Data, 0, m_connectionStringBuilder.Password); payload = new PayloadData(new ArraySegment <byte>(hashedPassword)); await m_session.SendReplyAsync(payload, cancellationToken).ConfigureAwait(false); payload = await m_session.ReceiveReplyAsync(cancellationToken).ConfigureAwait(false); } OkPayload.Create(payload); } }
public override async Task OpenAsync(CancellationToken cancellationToken) { VerifyNotDisposed(); if (State != ConnectionState.Closed) { throw new InvalidOperationException("Cannot Open when State is {0}.".FormatInvariant(State)); } #if !NETSTANDARD1_3 if (System.Transactions.Transaction.Current != null) { throw new NotSupportedException("Ambient transactions are not supported. Use BeginTransaction instead."); } #endif if (m_connectionStringBuilder.UseCompression) { throw new NotSupportedException("Compression not supported."); } SetState(ConnectionState.Connecting); bool success = false; try { var pool = ConnectionPool.GetPool(m_connectionStringBuilder); m_session = pool?.TryGetSession(); if (m_session == null) { m_session = new MySqlSession(pool); var connected = await m_session.ConnectAsync(m_connectionStringBuilder.Server.Split(','), (int)m_connectionStringBuilder.Port).ConfigureAwait(false); if (!connected) { SetState(ConnectionState.Closed); throw new MySqlException("Unable to connect to any of the specified MySQL hosts."); } var payload = await m_session.ReceiveAsync(cancellationToken).ConfigureAwait(false); var reader = new ByteArrayReader(payload.ArraySegment.Array, payload.ArraySegment.Offset, payload.ArraySegment.Count); var initialHandshake = new InitialHandshakePacket(reader); if (initialHandshake.AuthPluginName != "mysql_native_password") { throw new NotSupportedException("Only 'mysql_native_password' authentication method is supported."); } m_session.ServerVersion = new ServerVersion(Encoding.ASCII.GetString(initialHandshake.ServerVersion)); var response = HandshakeResponse41Packet.Create(initialHandshake, m_connectionStringBuilder.UserID, m_connectionStringBuilder.Password, m_database); payload = new PayloadData(new ArraySegment <byte>(response)); await m_session.SendReplyAsync(payload, cancellationToken).ConfigureAwait(false); await m_session.ReceiveReplyAsync(cancellationToken).ConfigureAwait(false); // TODO: Check success } else if (m_connectionStringBuilder.ConnectionReset) { if (m_session.ServerVersion.Version.CompareTo(ServerVersions.SupportsResetConnection) >= 0) { await m_session.SendAsync(ResetConnectionPayload.Create(), cancellationToken).ConfigureAwait(false); var payload = await m_session.ReceiveReplyAsync(cancellationToken); OkPayload.Create(payload); } else { // MySQL doesn't appear to accept a replayed hashed password (using the challenge from the initial handshake), so just send zeroes // and expect to get a new challenge var payload = ChangeUserPayload.Create(m_connectionStringBuilder.UserID, new byte[20], m_database); await m_session.SendAsync(payload, cancellationToken).ConfigureAwait(false); payload = await m_session.ReceiveReplyAsync(cancellationToken).ConfigureAwait(false); var switchRequest = AuthenticationMethodSwitchRequestPayload.Create(payload); if (switchRequest.Name != "mysql_native_password") { throw new NotSupportedException("Only 'mysql_native_password' authentication method is supported."); } var hashedPassword = AuthenticationUtility.HashPassword(switchRequest.Data, 0, m_connectionStringBuilder.Password); payload = new PayloadData(new ArraySegment <byte>(hashedPassword)); await m_session.SendReplyAsync(payload, cancellationToken).ConfigureAwait(false); payload = await m_session.ReceiveReplyAsync(cancellationToken).ConfigureAwait(false); OkPayload.Create(payload); } } m_hasBeenOpened = true; SetState(ConnectionState.Open); success = true; } catch (MySqlException) { SetState(ConnectionState.Closed); throw; } catch (SocketException ex) { SetState(ConnectionState.Closed); throw new MySqlException("Unable to connect to any of the specified MySQL hosts.", ex); } finally { if (!success) { Utility.Dispose(ref m_session); } } }