public static ColumnDefinitionPayload Create(PayloadData payload)
        {
            var reader = new ByteArrayReader(payload.ArraySegment);
            var catalog = reader.ReadLengthEncodedByteString();
            var schema = reader.ReadLengthEncodedByteString();
            var table = reader.ReadLengthEncodedByteString();
            var physicalTable = reader.ReadLengthEncodedByteString();
            var name = Encoding.UTF8.GetString(reader.ReadLengthEncodedByteString());
            var physicalName = reader.ReadLengthEncodedByteString();
            reader.ReadByte(0x0C); // length of fixed-length fields, always 0x0C
            var characterSet = (CharacterSet) reader.ReadUInt16();
            var columnLength = (int) reader.ReadUInt32();
            var columnType = (ColumnType) reader.ReadByte();
            var columnFlags = (ColumnFlags) reader.ReadUInt16();
            var decimals = reader.ReadByte(); // 0x00 for integers and static strings, 0x1f for dynamic strings, double, float, 0x00 to 0x51 for decimals
            reader.ReadByte(0);
            if (reader.BytesRemaining > 0)
            {
                int defaultValuesCount = checked((int) reader.ReadLengthEncodedInteger());
                for (int i = 0; i < defaultValuesCount; i++)
                    reader.ReadLengthEncodedByteString();
            }

            if (reader.BytesRemaining != 0)
                throw new FormatException("Extra bytes at end of payload.");
            return new ColumnDefinitionPayload(name, characterSet, columnLength, columnType, columnFlags);
        }
		private async Task DoSendAsync(PayloadData payload, CancellationToken cancellationToken)
		{
			var bytesSent = 0;
			var data = payload.ArraySegment;
			const int maxBytesToSend = 16777215;
			int bytesToSend;
			do
			{
				// break payload into packets of at most (2^24)-1 bytes
				bytesToSend = Math.Min(data.Count - bytesSent, maxBytesToSend);

				// write four-byte packet header; https://dev.mysql.com/doc/internals/en/mysql-packet.html
				SerializationUtility.WriteUInt32((uint) bytesToSend, m_buffer, 0, 3);
				m_buffer[3] = (byte) m_sequenceId;
				m_sequenceId++;

				if (bytesToSend <= m_buffer.Length - 4)
				{
					Buffer.BlockCopy(data.Array, data.Offset, m_buffer, 4, bytesToSend);
					m_socketAwaitable.EventArgs.SetBuffer(0, bytesToSend + 4);
					await m_socket.SendAsync(m_socketAwaitable);
				}
				else
				{
					m_socketAwaitable.EventArgs.SetBuffer(null, 0, 0);
					m_socketAwaitable.EventArgs.BufferList = new[] { new ArraySegment<byte>(m_buffer, 0, 4), data };
					await m_socket.SendAsync(m_socketAwaitable);
					m_socketAwaitable.EventArgs.BufferList = null;
					m_socketAwaitable.EventArgs.SetBuffer(m_buffer, 0, 0);
				}

				bytesSent += bytesToSend;
			} while (bytesToSend == maxBytesToSend);
		}
 public static AuthenticationMethodSwitchRequestPayload Create(PayloadData payload)
 {
     var reader = new ByteArrayReader(payload.ArraySegment);
     reader.ReadByte(Signature);
     var name = Encoding.UTF8.GetString(reader.ReadNullTerminatedByteString());
     var data = reader.ReadByteString(reader.BytesRemaining);
     return new AuthenticationMethodSwitchRequestPayload(name, data);
 }
示例#4
0
        public static ErrorPayload Create(PayloadData payload)
        {
            var reader = new ByteArrayReader(payload.ArraySegment);
            reader.ReadByte(Signature);

            var errorCode = reader.ReadUInt16();
            reader.ReadByte(0x23);
            var state = Encoding.ASCII.GetString(reader.ReadByteString(5));
            var message = Encoding.UTF8.GetString(reader.ReadByteString(payload.ArraySegment.Count - 9));
            return new ErrorPayload(errorCode, state, message);
        }
示例#5
0
        public static EofPayload Create(PayloadData payload)
        {
            var reader = new ByteArrayReader(payload.ArraySegment);
            reader.ReadByte(Signature);
            if (payload.ArraySegment.Count > 5)
                throw new FormatException("Not an EOF packet");
            int warningCount = reader.ReadUInt16();
            ServerStatus serverStatus = (ServerStatus) reader.ReadUInt16();

            if (reader.BytesRemaining != 0)
                throw new FormatException("Extra bytes at end of payload.");
            return new EofPayload(warningCount, serverStatus);
        }
        public static PrepareStatementOkPayload Create(PayloadData payload)
        {
            var reader = new ByteArrayReader(payload.ArraySegment);
            reader.ReadByte(0);
            var statementId = reader.ReadUInt32();
            int columnCount = reader.ReadUInt16();
            int parameterCount = reader.ReadUInt16();
            reader.ReadByte(0);
            int warningCount = reader.ReadUInt16();

            if (reader.BytesRemaining != 0)
                throw new FormatException("Extra bytes at end of payload.");
            return new PrepareStatementOkPayload(statementId, columnCount, parameterCount, warningCount);
        }
示例#7
0
        private async Task <PayloadData> SendEncryptedPasswordAsync(
            AuthenticationMethodSwitchRequestPayload switchRequest,
            string rsaPublicKey,
            ConnectionSettings cs,
            IOBehavior ioBehavior,
            CancellationToken cancellationToken)
        {
            // load the RSA public key
            RSA rsa;

            try
            {
                rsa = Utility.DecodeX509PublicKey(rsaPublicKey);
            }
            catch (Exception ex)
            {
                throw new MySqlException("Couldn't load server's RSA public key; try using a secure connection instead.", ex);
            }

            // add NUL terminator to password
            var passwordBytes = Encoding.UTF8.GetBytes(cs.Password);

            Array.Resize(ref passwordBytes, passwordBytes.Length + 1);

            using (rsa)
            {
                // XOR the password bytes with the challenge
                AuthPluginData = Utility.TrimZeroByte(switchRequest.Data);
                for (var i = 0; i < passwordBytes.Length; i++)
                {
                    passwordBytes[i] ^= AuthPluginData[i % AuthPluginData.Length];
                }

                // encrypt with RSA public key
                var padding           = switchRequest.Name == "caching_sha2_password" ? RSAEncryptionPadding.Pkcs1 : RSAEncryptionPadding.OaepSHA1;
                var encryptedPassword = rsa.Encrypt(passwordBytes, padding);
                var payload           = new PayloadData(new ArraySegment <byte>(encryptedPassword));
                await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);

                return(await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false));
            }
        }
示例#8
0
        private ValueTask <PayloadData> TryAsync(Func <ProtocolErrorBehavior, IOBehavior, ValueTask <ArraySegment <byte> > > func, IOBehavior ioBehavior, CancellationToken cancellationToken)
        {
            VerifyConnected();
            var task = func(ProtocolErrorBehavior.Throw, ioBehavior);

            if (task.IsCompletedSuccessfully)
            {
                var payload = new PayloadData(task.Result);
                if (payload.HeaderByte != ErrorPayload.Signature)
                {
                    return(new ValueTask <PayloadData>(payload));
                }

                var exception = ErrorPayload.Create(payload).ToException();
                return(ValueTaskExtensions.FromException <PayloadData>(exception));
            }

            return(new ValueTask <PayloadData>(task.AsTask()
                                               .ContinueWith(TryAsyncContinuation, cancellationToken, TaskContinuationOptions.LazyCancellation | TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default)));
        }
示例#9
0
        // Continues a conversation with the server by sending a reply to a packet received with 'Receive' or 'ReceiveReply'.
        public ValueTask <int> SendReplyAsync(PayloadData payload, IOBehavior ioBehavior, CancellationToken cancellationToken)
        {
            ValueTask <int> task;

            try
            {
                VerifyConnected();
                task = m_payloadHandler.WritePayloadAsync(payload.ArraySegment, ioBehavior);
            }
            catch (Exception ex)
            {
                task = ValueTaskExtensions.FromException <int>(ex);
            }

            if (task.IsCompletedSuccessfully)
            {
                return(task);
            }

            return(new ValueTask <int>(task.AsTask().ContinueWith(TryAsyncContinuation, cancellationToken, TaskContinuationOptions.LazyCancellation | TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default)));
        }
示例#10
0
        private PayloadData TryAsyncContinuation(Task <ArraySegment <byte> > task)
        {
            if (task.IsFaulted)
            {
                SetFailed();
            }
            ArraySegment <byte> bytes;

            try
            {
                bytes = task.GetAwaiter().GetResult();
            }
            catch (MySqlException ex) when(ex.Number == (int)MySqlErrorCode.CommandTimeoutExpired)
            {
                HandleTimeout();
                throw;
            }
            var payload = new PayloadData(bytes);

            payload.ThrowIfError();
            return(payload);
        }
示例#11
0
        public static ErrorPayload Create(PayloadData payload)
        {
            var reader = new ByteArrayReader(payload.ArraySegment);

            reader.ReadByte(Signature);

            var    errorCode = reader.ReadUInt16();
            var    stateMarker = Encoding.ASCII.GetString(reader.ReadByteString(1));
            string state, message;

            if (stateMarker == "#")
            {
                state   = Encoding.ASCII.GetString(reader.ReadByteString(5));
                message = Encoding.UTF8.GetString(reader.ReadByteString(payload.ArraySegment.Count - 9));
            }
            else
            {
                state   = "HY000";
                message = stateMarker + Encoding.UTF8.GetString(reader.ReadByteString(payload.ArraySegment.Count - 4));
            }
            return(new ErrorPayload(errorCode, state, message));
        }
        public static AuthenticationMethodSwitchRequestPayload Create(PayloadData payload)
        {
            var reader = new ByteArrayReader(payload.ArraySegment);

            reader.ReadByte(Signature);
            string name;

            byte[] data;
            if (payload.ArraySegment.Count == 1)
            {
                // if the packet is just the header byte (0xFE), it's an "Old Authentication Method Switch Request Packet"
                // (possibly sent by a server that doesn't support CLIENT_PLUGIN_AUTH)
                name = "mysql_old_password";
                data = new byte[0];
            }
            else
            {
                name = Encoding.UTF8.GetString(reader.ReadNullTerminatedByteString());
                data = reader.ReadByteString(reader.BytesRemaining);
            }
            return(new AuthenticationMethodSwitchRequestPayload(name, data));
        }
示例#13
0
		private bool ReadAsyncRemainder(PayloadData payload)
		{
			if (payload.HeaderByte == EofPayload.Signature)
			{
				var eof = EofPayload.Create(payload);
				m_state = eof.ServerStatus.HasFlag(ServerStatus.MoreResultsExist) ? State.HasMoreData : State.NoMoreData;
				return false;
			}

			var reader = new ByteArrayReader(payload.ArraySegment);
			for (var column = 0; column < m_dataOffsets.Length; column++)
			{
				var length = checked((int) ReadFieldLength(reader));
				m_dataLengths[column] = length == -1 ? 0 : length;
				m_dataOffsets[column] = length == -1 ? -1 : reader.Offset;
				reader.Offset += m_dataLengths[column];
			}

			m_currentRow = payload.ArraySegment.Array;

			m_state = State.ReadingRows;
			return true;
		}
示例#14
0
        public async Task ResetConnectionAsync(ConnectionSettings cs, IOBehavior ioBehavior, CancellationToken cancellationToken)
        {
            VerifyState(State.Connected);
            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 = new PayloadData(new ArraySegment <byte>(PayloadUtilities.CreateEofStringPayload(CommandKind.Query, "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)
                {
                    await SwitchAuthenticationAsync(cs, payload, ioBehavior, cancellationToken).ConfigureAwait(false);

                    payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
                }
                OkPayload.Create(payload);
            }
        }
示例#15
0
        public void FinishQuerying()
        {
            bool clearConnection = false;

            lock (m_lock)
            {
                if (m_state == State.CancelingQuery)
                {
                    m_state         = State.ClearingPendingCancellation;
                    clearConnection = true;
                }
            }

            if (clearConnection)
            {
                // KILL QUERY will kill a subsequent query if the command it was intended to cancel has already completed.
                // In order to handle this case, we issue a dummy query that will consume the pending cancellation.
                // See https://bugs.mysql.com/bug.php?id=45679
                var payload = new PayloadData(new ArraySegment <byte>(PayloadUtilities.CreateEofStringPayload(CommandKind.Query, "DO SLEEP(0);")));
                SendAsync(payload, IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
                payload = ReceiveReplyAsync(IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
                OkPayload.Create(payload);
            }

            lock (m_lock)
            {
                if (m_state == State.Querying || m_state == State.ClearingPendingCancellation)
                {
                    m_state = State.Connected;
                }
                else
                {
                    VerifyState(State.Failed);
                }
                m_activeCommandId = 0;
            }
        }
示例#16
0
        private async Task DoSendAsync(PayloadData payload, CancellationToken cancellationToken)
        {
            var       bytesSent      = 0;
            var       data           = payload.ArraySegment;
            const int maxBytesToSend = 16777215;
            int       bytesToSend;

            do
            {
                // break payload into packets of at most (2^24)-1 bytes
                bytesToSend = Math.Min(data.Count - bytesSent, maxBytesToSend);

                // write four-byte packet header; https://dev.mysql.com/doc/internals/en/mysql-packet.html
                SerializationUtility.WriteUInt32((uint)bytesToSend, m_buffer, 0, 3);
                m_buffer[3] = (byte)m_sequenceId;
                m_sequenceId++;

                if (bytesToSend <= m_buffer.Length - 4)
                {
                    Buffer.BlockCopy(data.Array, data.Offset, m_buffer, 4, bytesToSend);
                    m_socketAwaitable.EventArgs.SetBuffer(0, bytesToSend + 4);
                    await m_socket.SendAsync(m_socketAwaitable);
                }
                else
                {
                    m_socketAwaitable.EventArgs.SetBuffer(null, 0, 0);
                    m_socketAwaitable.EventArgs.BufferList = new[] { new ArraySegment <byte>(m_buffer, 0, 4), data };
                    await m_socket.SendAsync(m_socketAwaitable);

                    m_socketAwaitable.EventArgs.BufferList = null;
                    m_socketAwaitable.EventArgs.SetBuffer(m_buffer, 0, 0);
                }

                bytesSent += bytesToSend;
            } while (bytesToSend == maxBytesToSend);
        }
示例#17
0
 // Starts a new conversation with the server by sending the first packet.
 public ValueTask <int> SendAsync(PayloadData payload, IOBehavior ioBehavior, CancellationToken cancellationToken)
 {
     m_payloadHandler.StartNewConversation();
     return(SendReplyAsync(payload, ioBehavior, cancellationToken));
 }
示例#18
0
        private async Task <PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs, PayloadData payload, IOBehavior ioBehavior, CancellationToken cancellationToken)
        {
            // if the server didn't support the hashed password; rehash with the new challenge
            var switchRequest = AuthenticationMethodSwitchRequestPayload.Create(payload);

            switch (switchRequest.Name)
            {
            case "mysql_native_password":
                AuthPluginData = switchRequest.Data;
                var hashedPassword = AuthenticationUtility.CreateAuthenticationResponse(AuthPluginData, 0, cs.Password);
                payload = new PayloadData(new ArraySegment <byte>(hashedPassword));
                await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);

                return(await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false));

            case "mysql_clear_password":
                if (!m_isSecureConnection)
                {
                    throw new MySqlException("Authentication method '{0}' requires a secure connection.".FormatInvariant(switchRequest.Name));
                }
                payload = new PayloadData(new ArraySegment <byte>(Encoding.UTF8.GetBytes(cs.Password)));
                await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);

                return(await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false));

            case "caching_sha2_password":
                var scrambleBytes = AuthenticationUtility.CreateScrambleResponse(Utility.TrimZeroByte(switchRequest.Data), cs.Password);
                payload = new PayloadData(new ArraySegment <byte>(scrambleBytes));
                await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);

                payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);

                var cachingSha2ServerResponsePayload = CachingSha2ServerResponsePayload.Create(payload);
                if (cachingSha2ServerResponsePayload.Succeeded)
                {
                    return(await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false));
                }

                goto case "sha256_password";

            case "sha256_password":
                if (!m_isSecureConnection && cs.Password.Length > 1)
                {
#if NET45
                    throw new MySqlException("Authentication method '{0}' requires a secure connection (prior to .NET 4.6).".FormatInvariant(switchRequest.Name));
#else
                    var publicKey = await GetRsaPublicKeyAsync(switchRequest.Name, cs, ioBehavior, cancellationToken).ConfigureAwait(false);

                    return(await SendEncryptedPasswordAsync(switchRequest, publicKey, cs, ioBehavior, cancellationToken).ConfigureAwait(false));
#endif
                }
                else
                {
                    return(await SendClearPasswordAsync(cs, ioBehavior, cancellationToken).ConfigureAwait(false));
                }

            case "mysql_old_password":
                throw new NotSupportedException("'MySQL Server is requesting the insecure pre-4.1 auth mechanism (mysql_old_password). The user password must be upgraded; see https://dev.mysql.com/doc/refman/5.7/en/account-upgrades.html.");

            default:
                throw new NotSupportedException("Authentication method '{0}' is not supported.".FormatInvariant(switchRequest.Name));
            }
        }
示例#19
0
 // Continues a conversation with the server by sending a reply to a packet received with 'Receive' or 'ReceiveReply'.
 public Task SendReplyAsync(PayloadData payload, IOBehavior ioBehavior, CancellationToken cancellationToken)
 => DoSendAsync(payload, ioBehavior, cancellationToken);
示例#20
0
 /* See
  * http://web.archive.org/web/20160604101747/http://dev.mysql.com/doc/internals/en/packet-OK_Packet.html
  * https://mariadb.com/kb/en/the-mariadb-library/resultset/
  * https://github.com/MariaDB/mariadb-connector-j/blob/5fa814ac6e1b4c9cb6d141bd221cbd5fc45c8a78/src/main/java/org/mariadb/jdbc/internal/com/read/resultset/SelectResultSet.java#L443-L444
  */
 public static bool IsOk(PayloadData payload, bool deprecateEof) =>
 payload.ArraySegment.Array != null && payload.ArraySegment.Count > 0 &&
 ((payload.ArraySegment.Count > 6 && payload.ArraySegment.Array[payload.ArraySegment.Offset] == Signature) ||
  (deprecateEof && payload.ArraySegment.Count < 0xFF_FFFF && payload.ArraySegment.Array[payload.ArraySegment.Offset] == EofPayload.Signature));
示例#21
0
        public async Task ConnectAsync(ConnectionSettings cs, IOBehavior ioBehavior, CancellationToken cancellationToken)
        {
            lock (m_lock)
            {
                VerifyState(State.Created);
                m_state = State.Connecting;
            }
            var connected = false;

            if (cs.ConnectionType == ConnectionType.Tcp)
            {
                connected = await OpenTcpSocketAsync(cs, ioBehavior, cancellationToken).ConfigureAwait(false);
            }
            else if (cs.ConnectionType == ConnectionType.Unix)
            {
                connected = await OpenUnixSocketAsync(cs, ioBehavior, cancellationToken).ConfigureAwait(false);
            }
            if (!connected)
            {
                lock (m_lock)
                    m_state = State.Failed;
                throw new MySqlException("Unable to connect to any of the specified MySQL hosts.");
            }

            var byteHandler = new SocketByteHandler(m_socket);

            m_payloadHandler = new StandardPayloadHandler(byteHandler);

            var payload = await ReceiveAsync(ioBehavior, cancellationToken).ConfigureAwait(false);

            var reader           = new ByteArrayReader(payload.ArraySegment.Array, payload.ArraySegment.Offset, payload.ArraySegment.Count);
            var initialHandshake = new InitialHandshakePacket(reader);

            // if PluginAuth is supported, then use the specified auth plugin; else, fall back to protocol capabilities to determine the auth type to use
            string authPluginName;

            if ((initialHandshake.ProtocolCapabilities & ProtocolCapabilities.PluginAuth) != 0)
            {
                authPluginName = initialHandshake.AuthPluginName;
            }
            else
            {
                authPluginName = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.SecureConnection) == 0 ? "mysql_old_password" : "mysql_native_password";
            }
            if (authPluginName != "mysql_native_password" && authPluginName != "sha256_password" && authPluginName != "caching_sha2_password")
            {
                throw new NotSupportedException("Authentication method '{0}' is not supported.".FormatInvariant(initialHandshake.AuthPluginName));
            }

            ServerVersion    = new ServerVersion(Encoding.ASCII.GetString(initialHandshake.ServerVersion));
            ConnectionId     = initialHandshake.ConnectionId;
            AuthPluginData   = initialHandshake.AuthPluginData;
            m_useCompression = cs.UseCompression && (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Compress) != 0;

            var serverSupportsSsl = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Ssl) != 0;

            if (cs.SslMode != MySqlSslMode.None && (cs.SslMode != MySqlSslMode.Preferred || serverSupportsSsl))
            {
                if (!serverSupportsSsl)
                {
                    throw new MySqlException("Server does not support SSL");
                }
                await InitSslAsync(initialHandshake.ProtocolCapabilities, cs, ioBehavior, cancellationToken).ConfigureAwait(false);
            }

            m_supportsConnectionAttributes = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.ConnectionAttributes) != 0;
            if (m_supportsConnectionAttributes && s_connectionAttributes == null)
            {
                s_connectionAttributes = CreateConnectionAttributes();
            }

            m_supportsDeprecateEof = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.DeprecateEof) != 0;

            var response = HandshakeResponse41Packet.Create(initialHandshake, cs, m_useCompression, m_supportsConnectionAttributes ? s_connectionAttributes : null);

            payload = new PayloadData(new ArraySegment <byte>(response));
            await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);

            payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);

            // if server doesn't support the authentication fast path, it will send a new challenge
            if (payload.HeaderByte == AuthenticationMethodSwitchRequestPayload.Signature)
            {
                payload = await SwitchAuthenticationAsync(cs, payload, ioBehavior, cancellationToken).ConfigureAwait(false);
            }

            OkPayload.Create(payload);

            if (m_useCompression)
            {
                m_payloadHandler = new CompressedPayloadHandler(m_payloadHandler.ByteHandler);
            }
        }
示例#22
0
        private async Task SwitchAuthenticationAsync(ConnectionSettings cs, PayloadData payload, IOBehavior ioBehavior, CancellationToken cancellationToken)
        {
            // if the server didn't support the hashed password; rehash with the new challenge
            var switchRequest = AuthenticationMethodSwitchRequestPayload.Create(payload);

            switch (switchRequest.Name)
            {
            case "mysql_native_password":
                AuthPluginData = switchRequest.Data;
                var hashedPassword = AuthenticationUtility.CreateAuthenticationResponse(AuthPluginData, 0, cs.Password);
                payload = new PayloadData(new ArraySegment <byte>(hashedPassword));
                await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);

                break;

            case "mysql_clear_password":
                if (!m_isSecureConnection)
                {
                    throw new MySqlException("Authentication method '{0}' requires a secure connection.".FormatInvariant(switchRequest.Name));
                }
                payload = new PayloadData(new ArraySegment <byte>(Encoding.UTF8.GetBytes(cs.Password)));
                await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);

                break;

            case "sha256_password":
                // add NUL terminator to password
                var passwordBytes = Encoding.UTF8.GetBytes(cs.Password);
                Array.Resize(ref passwordBytes, passwordBytes.Length + 1);

                if (!m_isSecureConnection && passwordBytes.Length > 1)
                {
#if NET45
                    throw new MySqlException("Authentication method '{0}' requires a secure connection (prior to .NET 4.6).".FormatInvariant(switchRequest.Name));
#else
                    string publicKey;
                    if (!string.IsNullOrEmpty(cs.ServerRsaPublicKeyFile))
                    {
                        try
                        {
                            publicKey = File.ReadAllText(cs.ServerRsaPublicKeyFile);
                        }
                        catch (IOException ex)
                        {
                            throw new MySqlException("Couldn't load server's RSA public key from '{0}'".FormatInvariant(cs.ServerRsaPublicKeyFile), ex);
                        }
                    }
                    else if (cs.AllowPublicKeyRetrieval)
                    {
                        // request the RSA public key
                        await SendReplyAsync(new PayloadData(new ArraySegment <byte>(new byte[] { 0x01 }, 0, 1)), ioBehavior, cancellationToken).ConfigureAwait(false);

                        payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);

                        var publicKeyPayload = AuthenticationMoreDataPayload.Create(payload);
                        publicKey = Encoding.ASCII.GetString(publicKeyPayload.Data);
                    }
                    else
                    {
                        throw new MySqlException("Authentication method '{0}' failed. Either use a secure connection, specify the server's RSA public key with ServerRSAPublicKeyFile, or set AllowPublicKeyRetrieval=True.".FormatInvariant(switchRequest.Name));
                    }

                    // load the RSA public key
                    RSA rsa;
                    try
                    {
                        rsa = Utility.DecodeX509PublicKey(publicKey);
                    }
                    catch (Exception ex)
                    {
                        throw new MySqlException("Couldn't load server's RSA public key; try using a secure connection instead.", ex);
                    }

                    using (rsa)
                    {
                        // XOR the password bytes with the challenge
                        AuthPluginData = Utility.TrimZeroByte(switchRequest.Data);
                        for (int i = 0; i < passwordBytes.Length; i++)
                        {
                            passwordBytes[i] ^= AuthPluginData[i % AuthPluginData.Length];
                        }

                        // encrypt with RSA public key
                        var encryptedPassword = rsa.Encrypt(passwordBytes, RSAEncryptionPadding.OaepSHA1);
                        payload = new PayloadData(new ArraySegment <byte>(encryptedPassword));
                        await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
                    }
#endif
                }
                else
                {
                    // send plaintext password
                    payload = new PayloadData(new ArraySegment <byte>(passwordBytes));
                    await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
                }
                break;

            default:
                throw new NotSupportedException("Authentication method '{0}' is not supported.".FormatInvariant(switchRequest.Name));
            }
        }
示例#23
0
 /// <summary>
 /// Returns <c>true</c> if <paramref name="payload"/> contains an <a href="https://dev.mysql.com/doc/internals/en/packet-EOF_Packet.html">EOF packet</a>.
 /// Note that EOF packets can appear in places where a length-encoded integer (which starts with the same signature byte) may appear, so the length
 /// has to be checked to verify that it is an EOF packet.
 /// </summary>
 /// <param name="payload">The payload to examine.</param>
 /// <returns><c>true</c> if this is an EOF packet; otherwise, <c>false</c>.</returns>
 public static bool IsEof(PayloadData payload) =>
 payload.ArraySegment.Count > 0 && payload.ArraySegment.Count < 9 && payload.ArraySegment.Array[payload.ArraySegment.Offset] == Signature;
 private LocalInfilePayload(PayloadData payload)
 {
     FileName = Utility.GetString(Encoding.UTF8, Utility.Slice(payload.ArraySegment, 1));
 }
示例#25
0
		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));
					m_session.AuthPluginData = initialHandshake.AuthPluginData;

					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).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);
					}
				}

				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);
			}
		}
示例#26
0
 // Starts a new conversation with the server by sending the first packet.
 public Task SendAsync(PayloadData payload, CancellationToken cancellationToken)
 => TryAsync(m_transmitter.SendAsync, payload, cancellationToken);
 public static LocalInfilePayload Create(PayloadData payload) => new LocalInfilePayload(payload);
示例#28
0
        private async Task InitSslAsync(ConnectionSettings cs, IOBehavior ioBehavior, CancellationToken cancellationToken)
        {
            X509Certificate2 certificate;

            try
            {
                certificate = new X509Certificate2(cs.CertificateFile, cs.CertificatePassword);
            }
            catch (CryptographicException ex)
            {
                if (!File.Exists(cs.CertificateFile))
                {
                    throw new MySqlException("Cannot find SSL Certificate File", ex);
                }
                throw new MySqlException("Either the SSL Certificate Password is incorrect or the SSL Certificate File is invalid", ex);
            }

            Func <object, string, X509CertificateCollection, X509Certificate, string[], X509Certificate> localCertificateCb =
                (lcbSender, lcbTargetHost, lcbLocalCertificates, lcbRemoteCertificate, lcbAcceptableIssuers) => lcbLocalCertificates[0];

            Func <object, X509Certificate, X509Chain, SslPolicyErrors, bool> remoteCertificateCb =
                (rcbSender, rcbCertificate, rcbChain, rcbPolicyErrors) =>
            {
                switch (rcbPolicyErrors)
                {
                case SslPolicyErrors.None:
                    return(true);

                case SslPolicyErrors.RemoteCertificateNameMismatch:
                    return(cs.SslMode != MySqlSslMode.VerifyFull);

                default:
                    return(cs.SslMode == MySqlSslMode.Required);
                }
            };

            var sslStream = new SslStream(m_networkStream, false,
                                          new RemoteCertificateValidationCallback(remoteCertificateCb),
                                          new LocalCertificateSelectionCallback(localCertificateCb));
            var clientCertificates = new X509CertificateCollection {
                certificate
            };

            // SslProtocols.Tls1.2 throws an exception in Windows, see https://github.com/mysql-net/MySqlConnector/pull/101
            var sslProtocols = SslProtocols.Tls | SslProtocols.Tls11;

            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                sslProtocols |= SslProtocols.Tls12;
            }

            var checkCertificateRevocation = cs.SslMode == MySqlSslMode.VerifyFull;

            var initSsl = new PayloadData(new ArraySegment <byte>(HandshakeResponse41Packet.InitSsl(cs)));

            await SendReplyAsync(initSsl, ioBehavior, cancellationToken).ConfigureAwait(false);

            try
            {
                if (ioBehavior == IOBehavior.Asynchronous)
                {
                    await sslStream.AuthenticateAsClientAsync(m_hostname, clientCertificates, sslProtocols, checkCertificateRevocation).ConfigureAwait(false);
                }
                else
                {
#if NETSTANDARD1_3
                    await sslStream.AuthenticateAsClientAsync(m_hostname, clientCertificates, sslProtocols, checkCertificateRevocation).ConfigureAwait(false);
#else
                    sslStream.AuthenticateAsClient(m_hostname, clientCertificates, sslProtocols, checkCertificateRevocation);
#endif
                }
                var sslByteHandler = new StreamByteHandler(sslStream);
                m_payloadHandler.ByteHandler = sslByteHandler;
            }
            catch (AuthenticationException ex)
            {
                ShutdownSocket();
                m_hostname = "";
                m_state    = State.Failed;
                throw new MySqlException("SSL Authentication Error", ex);
            }
        }
示例#29
0
		protected override async Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
		{
			VerifyValid();
			Connection.HasActiveReader = true;

			MySqlDataReader reader = null;
			try
			{
				LastInsertedId = -1;
				var connection = (MySqlConnection) DbConnection;
				var statementPreparerOptions = StatementPreparerOptions.None;
				if (connection.AllowUserVariables)
					statementPreparerOptions |= StatementPreparerOptions.AllowUserVariables;
				if (connection.OldGuids)
					statementPreparerOptions |= StatementPreparerOptions.OldGuids;
				var preparer = new MySqlStatementPreparer(CommandText, m_parameterCollection, statementPreparerOptions);
				preparer.BindParameters();
				var payload = new PayloadData(new ArraySegment<byte>(Payload.CreateEofStringPayload(CommandKind.Query, preparer.PreparedSql)));
				await Session.SendAsync(payload, cancellationToken).ConfigureAwait(false);
				reader = await MySqlDataReader.CreateAsync(this, behavior, cancellationToken).ConfigureAwait(false);
				return reader;
			}
			finally
			{
				if (reader == null)
				{
					// received an error from MySQL and never created an active reader
					Connection.HasActiveReader = false;
				}
			}
		}
		// Continues a conversation with the server by sending a reply to a packet received with 'Receive' or 'ReceiveReply'.
		public Task SendReplyAsync(PayloadData payload, CancellationToken cancellationToken)
			=> DoSendAsync(payload, cancellationToken);
示例#31
0
 // Starts a new conversation with the server by sending the first packet.
 public Task SendAsync(PayloadData payload, CancellationToken cancellationToken)
 {
     m_sequenceId = 0;
     return(DoSendAsync(payload, cancellationToken));
 }
示例#32
0
 // Continues a conversation with the server by sending a reply to a packet received with 'Receive' or 'ReceiveReply'.
 public Task SendReplyAsync(PayloadData payload, CancellationToken cancellationToken)
 => DoSendAsync(payload, cancellationToken);
示例#33
0
        private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, ConnectionSettings cs, IOBehavior ioBehavior, CancellationToken cancellationToken)
        {
            X509CertificateCollection clientCertificates = null;

            if (cs.CertificateFile != null)
            {
                try
                {
                    var certificate = new X509Certificate2(cs.CertificateFile, cs.CertificatePassword);
#if !NET45
                    m_clientCertificate = certificate;
#endif
                    clientCertificates = new X509CertificateCollection {
                        certificate
                    };
                }
                catch (CryptographicException ex)
                {
                    if (!File.Exists(cs.CertificateFile))
                    {
                        throw new MySqlException("Cannot find Certificate File", ex);
                    }
                    throw new MySqlException("Either the Certificate Password is incorrect or the Certificate File is invalid", ex);
                }
            }

            X509Chain caCertificateChain = null;
            if (cs.CACertificateFile != null)
            {
                try
                {
                    var caCertificate = new X509Certificate2(cs.CACertificateFile);
#if !NET45
                    m_serverCertificate = caCertificate;
#endif
                    caCertificateChain = new X509Chain
                    {
                        ChainPolicy =
                        {
                            RevocationMode    = X509RevocationMode.NoCheck,
                            VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority
                        }
                    };
                    caCertificateChain.ChainPolicy.ExtraStore.Add(caCertificate);
                }
                catch (CryptographicException ex)
                {
                    if (!File.Exists(cs.CACertificateFile))
                    {
                        throw new MySqlException("Cannot find CA Certificate File", ex);
                    }
                    throw new MySqlException("The CA Certificate File is invalid", ex);
                }
            }

            X509Certificate ValidateLocalCertificate(object lcbSender, string lcbTargetHost, X509CertificateCollection lcbLocalCertificates, X509Certificate lcbRemoteCertificate, string[] lcbAcceptableIssuers) => lcbLocalCertificates[0];

            bool ValidateRemoteCertificate(object rcbSender, X509Certificate rcbCertificate, X509Chain rcbChain, SslPolicyErrors rcbPolicyErrors)
            {
                if (cs.SslMode == MySqlSslMode.Preferred || cs.SslMode == MySqlSslMode.Required)
                {
                    return(true);
                }

                if ((rcbPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors) != 0 && caCertificateChain != null)
                {
                    if (caCertificateChain.Build((X509Certificate2)rcbCertificate))
                    {
                        var chainStatus = caCertificateChain.ChainStatus[0].Status & ~X509ChainStatusFlags.UntrustedRoot;
                        if (chainStatus == X509ChainStatusFlags.NoError)
                        {
                            rcbPolicyErrors &= ~SslPolicyErrors.RemoteCertificateChainErrors;
                        }
                    }
                }

                if (cs.SslMode == MySqlSslMode.VerifyCA)
                {
                    rcbPolicyErrors &= ~SslPolicyErrors.RemoteCertificateNameMismatch;
                }

                return(rcbPolicyErrors == SslPolicyErrors.None);
            }

            SslStream sslStream;
            if (clientCertificates == null)
            {
                sslStream = new SslStream(m_networkStream, false, ValidateRemoteCertificate);
            }
            else
            {
                sslStream = new SslStream(m_networkStream, false, ValidateRemoteCertificate, ValidateLocalCertificate);
            }

            // SslProtocols.Tls1.2 throws an exception in Windows, see https://github.com/mysql-net/MySqlConnector/pull/101
            var sslProtocols = SslProtocols.Tls | SslProtocols.Tls11;
            if (!Utility.IsWindows())
            {
                sslProtocols |= SslProtocols.Tls12;
            }

            var checkCertificateRevocation = cs.SslMode == MySqlSslMode.VerifyFull;

            var initSsl = new PayloadData(new ArraySegment <byte>(HandshakeResponse41Packet.InitSsl(serverCapabilities, cs, m_useCompression)));
            await SendReplyAsync(initSsl, ioBehavior, cancellationToken).ConfigureAwait(false);

            try
            {
                if (ioBehavior == IOBehavior.Asynchronous)
                {
                    await sslStream.AuthenticateAsClientAsync(m_hostname, clientCertificates, sslProtocols, checkCertificateRevocation).ConfigureAwait(false);
                }
                else
                {
#if NETSTANDARD1_3
                    await sslStream.AuthenticateAsClientAsync(m_hostname, clientCertificates, sslProtocols, checkCertificateRevocation).ConfigureAwait(false);
#else
                    sslStream.AuthenticateAsClient(m_hostname, clientCertificates, sslProtocols, checkCertificateRevocation);
#endif
                }
                var sslByteHandler = new StreamByteHandler(sslStream);
                m_payloadHandler.ByteHandler = sslByteHandler;
                m_isSecureConnection         = true;
            }
            catch (Exception ex)
            {
                sslStream.Dispose();
                ShutdownSocket();
                m_hostname = "";
                lock (m_lock)
                    m_state = State.Failed;
                if (ex is AuthenticationException)
                {
                    throw new MySqlException("SSL Authentication Error", ex);
                }
                if (ex is IOException && clientCertificates != null)
                {
                    throw new MySqlException("MySQL Server rejected client certificate", ex);
                }
                throw;
            }
        }
示例#34
0
        public async Task ConnectAsync(ConnectionSettings cs, IOBehavior ioBehavior, CancellationToken cancellationToken)
        {
            var connected = false;

            if (cs.ConnectionType == ConnectionType.Tcp)
            {
                connected = await OpenTcpSocketAsync(cs, ioBehavior, cancellationToken).ConfigureAwait(false);
            }
            else if (cs.ConnectionType == ConnectionType.Unix)
            {
                connected = await OpenUnixSocketAsync(cs, ioBehavior, cancellationToken).ConfigureAwait(false);
            }
            if (!connected)
            {
                throw new MySqlException("Unable to connect to any of the specified MySQL hosts.");
            }

            var socketByteHandler = new SocketByteHandler(m_socket);

            m_payloadHandler = new StandardPayloadHandler(socketByteHandler);

            var payload = await ReceiveAsync(ioBehavior, cancellationToken).ConfigureAwait(false);

            var reader           = new ByteArrayReader(payload.ArraySegment.Array, payload.ArraySegment.Offset, payload.ArraySegment.Count);
            var initialHandshake = new InitialHandshakePacket(reader);

            // if PluginAuth is supported, then use the specified auth plugin; else, fall back to protocol capabilities to determine the auth type to use
            string authPluginName;

            if ((initialHandshake.ProtocolCapabilities & ProtocolCapabilities.PluginAuth) != 0)
            {
                authPluginName = initialHandshake.AuthPluginName;
            }
            else
            {
                authPluginName = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.SecureConnection) == 0 ? "mysql_old_password" : "mysql_native_password";
            }
            if (authPluginName != "mysql_native_password")
            {
                throw new NotSupportedException("Authentication method '{0}' is not supported.".FormatInvariant(initialHandshake.AuthPluginName));
            }

            ServerVersion  = new ServerVersion(Encoding.ASCII.GetString(initialHandshake.ServerVersion));
            ConnectionId   = initialHandshake.ConnectionId;
            AuthPluginData = initialHandshake.AuthPluginData;
            if (cs.UseCompression && (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Compress) == 0)
            {
                cs = cs.WithUseCompression(false);
            }

            if (cs.SslMode != MySqlSslMode.None)
            {
                await InitSslAsync(initialHandshake.ProtocolCapabilities, cs, ioBehavior, cancellationToken).ConfigureAwait(false);
            }

            var response = HandshakeResponse41Packet.Create(initialHandshake, cs);

            payload = new PayloadData(new ArraySegment <byte>(response));
            await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);

            payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);

            OkPayload.Create(payload);

            if (cs.UseCompression)
            {
                m_payloadHandler = new CompressedPayloadHandler(m_payloadHandler.ByteHandler);
            }
        }
		// Starts a new conversation with the server by sending the first packet.
		public Task SendAsync(PayloadData payload, CancellationToken cancellationToken)
		{
			m_sequenceId = 0;
			return DoSendAsync(payload, cancellationToken);
		}
示例#36
0
 // Starts a new conversation with the server by sending the first packet.
 public ValueTask <int> SendAsync(PayloadData payload, IOBehavior ioBehavior, CancellationToken cancellationToken)
 {
     m_payloadHandler.StartNewConversation();
     return(TryAsync(m_payloadHandler.WritePayloadAsync, payload.ArraySegment, ioBehavior, cancellationToken));
 }
示例#37
0
 // Continues a conversation with the server by sending a reply to a packet received with 'Receive' or 'ReceiveReply'.
 public ValueTask <int> SendReplyAsync(PayloadData payload, IOBehavior ioBehavior, CancellationToken cancellationToken) =>
 TryAsync(m_payloadHandler.WritePayloadAsync, payload.ArraySegment, ioBehavior, cancellationToken);
示例#38
0
 // Continues a conversation with the server by sending a reply to a packet received with 'Receive' or 'ReceiveReply'.
 public Task SendReplyAsync(PayloadData payload, IOBehavior ioBehavior, CancellationToken cancellationToken)
 => TryAsync(m_transmitter.SendReplyAsync, payload, ioBehavior, cancellationToken);