protected void ProcessMsgUserInfoResponse(SshStreamReader msgReader)
        {
            if (_isDisposed)
            {
                throw new ObjectDisposedException(this.GetType().FullName);
            }

            // Read response info.
            int numResponses = msgReader.ReadInt32();

            string[] responses = new string[numResponses];

            for (int i = 0; i < numResponses; i++)
            {
                responses[i] = Encoding.UTF8.GetString(msgReader.ReadByteString());
            }

            // Raise event to get result of auth attempt.
            var authUserEventArgs = new AuthUserKeyboardInteractiveEventArgs(_lastUserName,
                                                                             responses);

            if (AuthenticateUserKeyboardInteractive != null)
            {
                AuthenticateUserKeyboardInteractive(this,
                                                    authUserEventArgs);
            }

            // Check result of auth attempt.
            switch (authUserEventArgs.Result)
            {
            case AuthenticationResult.Success:
                // Auth has succeeded.
                AuthenticateUser(_lastServiceName);

                break;

            case AuthenticationResult.FurtherAuthRequired:
                // Auth has succeeded, but further auth is required.
                SendMsgUserAuthFailure(true);

                break;

            case AuthenticationResult.Failure:
                // Auth has failed.
                SendMsgUserAuthFailure(false);

                break;

            case AuthenticationResult.RequestMoreInfo:
                // Request more prompt info from client.
                RequestPromptInfo(null);

                break;
            }
        }
        protected void ProcessMsgUserAuthRequest(SshStreamReader msgReader)
        {
            if (_isDisposed)
            {
                throw new ObjectDisposedException(this.GetType().FullName);
            }

            // Read auth information.
            string userName    = Encoding.UTF8.GetString(msgReader.ReadByteString());
            string serviceName = msgReader.ReadString();
            string methodName  = msgReader.ReadString();

            // Store user name and service name used for this auth.
            _lastUserName    = userName;
            _lastServiceName = serviceName;

            // Check if service with specified name exists.
            if (_client.Services.Count(item => item.Name == serviceName) == 0)
            {
                // Service was not found.
                _client.Disconnect(SshDisconnectReason.ServiceNotAvailable, string.Format(
                                       "The service with name {0} is not supported by this server."));
                throw new DisconnectedException();
            }

            // Check method of authentication.
            switch (methodName)
            {
            case "none":
                ProcessMsgUserAuthRequestNone(msgReader);
                break;

            case "publickey":
                ProcessMsgUserAuthRequestPublicKey(msgReader);
                break;

            case "password":
                ProcessMsgUserAuthRequestPassword(msgReader);
                break;

            case "hostbased":
                ProcessMsgUserAuthRequestHostBased(msgReader);
                break;

            case "keyboard-interactive":
                ProcessMsgUserAuthRequestKeyboardInteractive(msgReader);
                break;

            default:
                // Invalid auth method.
                _client.Disconnect(false);
                break;
            }
        }
示例#3
0
        public byte[] GetSignature(byte[] signatureData)
        {
            using (var dataStream = new MemoryStream(signatureData))
            {
                using (var dataReader = new SshStreamReader(dataStream))
                {
                    // Read signature from stream.
                    if (dataReader.ReadString() != this.Name)
                    {
                        throw new CryptographicException(
                                  "Signature was not created with this algorithm.");
                    }
                    var signature = dataReader.ReadByteString();

                    return(signature);
                }
            }
        }
        protected void ProcessMsgChannelData(SshStreamReader msgReader)
        {
            if (_isDisposed)
            {
                throw new ObjectDisposedException(this.GetType().FullName);
            }

            // Read channel number and get channel object.
            uint       channelNum = msgReader.ReadUInt32();
            SshChannel channel;

            try { channel = _channels.SingleOrDefault(item => item.ServerChannel == channelNum); }
            catch (InvalidOperationException) { return; }

            // Let channel read data.
            var data = msgReader.ReadByteString();

            channel.ProcessData(data);
        }
        protected void ProcessMsgUserAuthRequestKeyboardInteractive(SshStreamReader msgReader)
        {
            if (_isDisposed)
            {
                throw new ObjectDisposedException(this.GetType().FullName);
            }

            // Raise event to specify requested auth method.
            if (AuthenticationMethodRequested != null)
            {
                AuthenticationMethodRequested(this,
                                              new AuthMethodRequestedEventArgs(AuthenticationMethod.KeyboardInteractive));
            }

            // Read request information.
            string language = msgReader.ReadString();

            string[] subMethods = Encoding.UTF8.GetString(msgReader.ReadByteString()).Split(
                new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);

            // Request prompt info from client.
            RequestPromptInfo(subMethods);
        }
        protected void ProcessMsgUserInfoResponse(SshStreamReader msgReader)
        {
            if (_isDisposed) throw new ObjectDisposedException(this.GetType().FullName);

            // Read response info.
            int numResponses = msgReader.ReadInt32();
            string[] responses = new string[numResponses];

            for (int i = 0; i < numResponses; i++)
                responses[i] = Encoding.UTF8.GetString(msgReader.ReadByteString());

            // Raise event to get result of auth attempt.
            var authUserEventArgs = new AuthUserKeyboardInteractiveEventArgs(_lastUserName,
                responses);

            if (AuthenticateUserKeyboardInteractive != null) AuthenticateUserKeyboardInteractive(this,
                authUserEventArgs);

            // Check result of auth attempt.
            switch (authUserEventArgs.Result)
            {
                case AuthenticationResult.Success:
                    // Auth has succeeded.
                    AuthenticateUser(_lastServiceName);

                    break;
                case AuthenticationResult.FurtherAuthRequired:
                    // Auth has succeeded, but further auth is required.
                    SendMsgUserAuthFailure(true);

                    break;
                case AuthenticationResult.Failure:
                    // Auth has failed.
                    SendMsgUserAuthFailure(false);

                    break;
                case AuthenticationResult.RequestMoreInfo:
                    // Request more prompt info from client.
                    RequestPromptInfo(null);

                    break;
            }
        }
        protected void ProcessMsgUserAuthRequestPublicKey(SshStreamReader msgReader)
        {
            if (_isDisposed) throw new ObjectDisposedException(this.GetType().FullName);

            // Raise event to specify requested auth method.
            if (AuthenticationMethodRequested != null) AuthenticationMethodRequested(this,
                new AuthMethodRequestedEventArgs(AuthenticationMethod.PublicKey));

            // Read request information.
            bool isAuthRequest = msgReader.ReadBoolean();
            string keyAlgName = msgReader.ReadString();
            byte[] keyAndCertsData = msgReader.ReadByteString();

            // Try to find public key algorithm.
            PublicKeyAlgorithm keyAlg = null;

            try
            {
                keyAlg = (PublicKeyAlgorithm)_client.PublicKeyAlgorithms.Single(item =>
                    item.Name == keyAlgName).Clone();
            }
            catch (InvalidOperationException)
            {
                // Public key algorithm is not supported.
                SendMsgUserAuthFailure(false);
            }

            // Load key and certificats data for algorithm.
            keyAlg.LoadKeyAndCertificatesData(keyAndCertsData);

            // Check if request is actual auth request or query of whether specified public key is
            // acceptable.
            if (isAuthRequest)
            {
                // Read client signature.
                var signatureData = msgReader.ReadByteString();
                var signature = keyAlg.GetSignature(signatureData);

                // Verify signature.
                var payloadData = ((MemoryStream)msgReader.BaseStream).ToArray();

                if (VerifyPublicKeySignature(keyAlg, payloadData, 0, payloadData.Length -
                   signatureData.Length - 4, signature))
                {
                    // Raise event to get result of auth attempt.
                    var authUserEventArgs = new AuthUserPublicKeyEventArgs(_lastUserName,
                        keyAlg.ExportPublicKey());

                    AuthenticateUserPublicKey(this, authUserEventArgs);

                    // Check result of auth attempt.
                    switch (authUserEventArgs.Result)
                    {
                        case AuthenticationResult.Success:
                            // Auth has succeeded.
                            AuthenticateUser(_lastServiceName);

                            break;
                        case AuthenticationResult.FurtherAuthRequired:
                            // Auth has succeeded, but further auth is required.
                            SendMsgUserAuthFailure(true);

                            break;
                        case AuthenticationResult.Failure:
                            // Auth has failed.
                            SendMsgUserAuthFailure(false);

                            break;
                    }
                }
                else
                {
                    // Signature is invalid.
                    SendMsgUserAuthFailure(false);
                }
            }
            else
            {
                // Public key is acceptable.
                SendMsgUserAuthPkOk(keyAlgName, keyAndCertsData);
            }
        }
        protected void ProcessMsgUserAuthRequestPassword(SshStreamReader msgReader)
        {
            if (_isDisposed) throw new ObjectDisposedException(this.GetType().FullName);

            // Raise event to specify requested auth method.
            if (AuthenticationMethodRequested != null) AuthenticationMethodRequested(this,
                new AuthMethodRequestedEventArgs(AuthenticationMethod.Password));

            // Check whether client is changing password.
            bool changingPassword = msgReader.ReadBoolean();

            if (changingPassword)
            {
                // Read old and new passwords (in plaintext).
                string oldPassword = Encoding.UTF8.GetString(msgReader.ReadByteString());
                string newPassword = Encoding.UTF8.GetString(msgReader.ReadByteString());

                // Raise event to get result of password change request.
                var changePasswordEventArgs = new ChangePasswordEventArgs(oldPassword, newPassword);

                if (ChangePassword != null) ChangePassword(this, changePasswordEventArgs);

                // Check result of password change request.
                switch (changePasswordEventArgs.Result)
                {
                    case PasswordChangeResult.Success:
                        // Password change and auth have succeeded.
                        AuthenticateUser(_lastServiceName);

                        break;
                    case PasswordChangeResult.FurtherAuthRequired:
                        // Password change has succeeded, but further auth is required.
                        SendMsgUserAuthFailure(true);

                        break;
                    case PasswordChangeResult.Failure:
                        // Password change has failed.
                        SendMsgUserAuthFailure(false);

                        break;
                    case PasswordChangeResult.NewPasswordUnacceptable:
                        // Password was not changed.
                        SendMsgUserAuthPasswdChangeReq(changePasswordEventArgs.ReplyPrompt, "");

                        break;
                }
            }
            else
            {
                // Read password (in plaintext).
                string password = Encoding.UTF8.GetString(msgReader.ReadByteString());

                // Raise event to get result of auth attempt.
                var authUserEventArgs = new AuthUserPasswordEventArgs(_lastUserName, password);

                if (AuthenticateUserPassword != null) AuthenticateUserPassword(this, authUserEventArgs);

                // Check result of auth attempt.
                switch (authUserEventArgs.Result)
                {
                    case AuthenticationResult.Success:
                        // Auth has succeeded.
                        AuthenticateUser(_lastServiceName);

                        break;
                    case AuthenticationResult.FurtherAuthRequired:
                        // Auth has succeeded, but further auth is required.
                        SendMsgUserAuthFailure(true);

                        break;
                    case AuthenticationResult.Failure:
                        // Increment number of failed auth attempts.
                        _failedAuthAttempts++;

                        if (_failedAuthAttempts < this.MaximumAuthAttempts)
                        {
                            // Auth has failed, but allow client to reattempt auth.
                            SendMsgUserAuthFailure(false);
                        }
                        else
                        {
                            // Auth has failed too many times, disconnect.
                            _client.Disconnect(false);
                            throw new DisconnectedException();
                        }

                        break;
                    case AuthenticationResult.PasswordExpired:
                        // Password change is required.
                        SendMsgUserAuthPasswdChangeReq("The specified password has expired.", "");

                        break;
                }
            }
        }
        protected void ProcessMsgUserAuthRequestKeyboardInteractive(SshStreamReader msgReader)
        {
            if (_isDisposed) throw new ObjectDisposedException(this.GetType().FullName);

            // Raise event to specify requested auth method.
            if (AuthenticationMethodRequested != null) AuthenticationMethodRequested(this,
                new AuthMethodRequestedEventArgs(AuthenticationMethod.KeyboardInteractive));

            // Read request information.
            string language = msgReader.ReadString();
            string[] subMethods = Encoding.UTF8.GetString(msgReader.ReadByteString()).Split(
                new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);

            // Request prompt info from client.
            RequestPromptInfo(subMethods);
        }
        protected void ProcessMsgUserAuthRequest(SshStreamReader msgReader)
        {
            if (_isDisposed) throw new ObjectDisposedException(this.GetType().FullName);

            // Read auth information.
            string userName = Encoding.UTF8.GetString(msgReader.ReadByteString());
            string serviceName = msgReader.ReadString();
            string methodName = msgReader.ReadString();

            // Store user name and service name used for this auth.
            _lastUserName = userName;
            _lastServiceName = serviceName;

            // Check if service with specified name exists.
            if (_client.Services.Count(item => item.Name == serviceName) == 0)
            {
                // Service was not found.
                _client.Disconnect(SshDisconnectReason.ServiceNotAvailable, string.Format(
                    "The service with name {0} is not supported by this server."));
                throw new DisconnectedException();
            }

            // Check method of authentication.
            switch (methodName)
            {
                case "none":
                    ProcessMsgUserAuthRequestNone(msgReader);
                    break;
                case "publickey":
                    ProcessMsgUserAuthRequestPublicKey(msgReader);
                    break;
                case "password":
                    ProcessMsgUserAuthRequestPassword(msgReader);
                    break;
                case "hostbased":
                    ProcessMsgUserAuthRequestHostBased(msgReader);
                    break;
                case "keyboard-interactive":
                    ProcessMsgUserAuthRequestKeyboardInteractive(msgReader);
                    break;
                default:
                    // Invalid auth method.
                    _client.Disconnect(false);
                    break;
            }
        }
        public byte[] GetSignature(byte[] signatureData)
        {
            using (var dataStream = new MemoryStream(signatureData))
            {
                using (var dataReader = new SshStreamReader(dataStream))
                {
                    // Read signature from stream.
                    if (dataReader.ReadString() != this.Name) throw new CryptographicException(
                       "Signature was not created with this algorithm.");
                    var signature = dataReader.ReadByteString();

                    return signature;
                }
            }
        }
        protected void ProcessMsgChannelExtendedData(SshStreamReader msgReader)
        {
            if (_isDisposed) throw new ObjectDisposedException(this.GetType().FullName);

            // Read channel number and get channel object.
            uint channelNum = msgReader.ReadUInt32();
            SshChannel channel;

            try { channel = _channels.SingleOrDefault(item => item.ServerChannel == channelNum); }
            catch (InvalidOperationException) { return; }

            // Let channel read extended data.
            var dataType = (SshExtendedDataType)msgReader.ReadUInt32();
            var data = msgReader.ReadByteString();

            channel.ProcessExtendedData(dataType, data);
        }
        protected internal override void ProcessRequest(string requestType, bool wantReply,
                                                        SshStreamReader msgReader)
        {
            if (_isDisposed)
            {
                throw new ObjectDisposedException(this.GetType().FullName);
            }

            switch (requestType)
            {
            case "pty-req":
                // Read information about pseudo-terminal.
                var termEnvVar       = msgReader.ReadString();
                var termCharsWidth   = msgReader.ReadUInt32();
                var termCharsHeight  = msgReader.ReadUInt32();
                var termPixelsWidth  = msgReader.ReadUInt32();
                var termPixelsHeight = msgReader.ReadUInt32();
                var termModes        = ReadTerminalModes(msgReader.ReadByteString());

                // Raise event to request pseudo terminal.
                var pseudoTerminalRequestedEventArgs = new PseudoTerminalRequestedEventArgs(termEnvVar);

                OnPseudoTerminalRequested(pseudoTerminalRequestedEventArgs);

                // Check if request to allocate pseudo terminal failed.
                if (!pseudoTerminalRequestedEventArgs.Success)
                {
                    break;
                }

                _termEnvVar       = termEnvVar;
                _termCharsWidth   = termCharsWidth;
                _termCharsHeight  = termCharsHeight;
                _termPixelsWidth  = termPixelsWidth;
                _termPixelsHeight = termPixelsHeight;
                _termModes        = termModes;

                // Add TERM to list of environment variables.
                _envVars.Add("TERM", _termEnvVar);

                // Raise event, pseudo terminal has been allocated.
                OnPseudoTerminalAllocated(new EventArgs());

                if (wantReply)
                {
                    _connService.SendMsgChannelSuccess(this);
                }
                return;

            case "env":
                // Read name and value of environment variable.
                var varName  = msgReader.ReadString();
                var varValue = msgReader.ReadString();

                // Add variable to list.
                _envVars.Add(varName, varValue);

                if (wantReply)
                {
                    _connService.SendMsgChannelSuccess(this);
                }
                return;

            case "shell":
                // Start default shell.
                StartShell();

                if (wantReply)
                {
                    _connService.SendMsgChannelSuccess(this);
                }
                return;

            case "exec":
                // not implemented

                break;

            default:
                base.ProcessRequest(requestType, wantReply, msgReader);
                return;
            }

            // Request has failed.
            if (wantReply)
            {
                _connService.SendMsgChannelFailure(this);
            }
        }
        protected void ProcessMsgUserAuthRequestHostBased(SshStreamReader msgReader)
        {
            if (_isDisposed)
            {
                throw new ObjectDisposedException(this.GetType().FullName);
            }

            // Raise event to specify requested auth method.
            if (AuthenticationMethodRequested != null)
            {
                AuthenticationMethodRequested(this,
                                              new AuthMethodRequestedEventArgs(AuthenticationMethod.HostBased));
            }

            // Read request information.
            string keyAlgName = msgReader.ReadString();

            byte[] keyAndCertsData = msgReader.ReadByteString();
            string clientHostName  = msgReader.ReadString();
            string clientUserName  = msgReader.ReadString();

            // Try to find public key algorithm.
            PublicKeyAlgorithm keyAlg = null;

            try
            {
                keyAlg = (PublicKeyAlgorithm)_client.PublicKeyAlgorithms.Single(item =>
                                                                                item.Name == keyAlgName).Clone();
            }
            catch (InvalidOperationException)
            {
                // Public key algorithm is not supported.
                SendMsgUserAuthFailure(false);
            }

            // Load key and certificats data for algorithm.
            keyAlg.LoadKeyAndCertificatesData(keyAndCertsData);

            // Read client signature.
            var signatureData = msgReader.ReadByteString();
            var signature     = keyAlg.GetSignature(signatureData);

            // Verify signature.
            var payloadData = ((MemoryStream)msgReader.BaseStream).ToArray();

            if (VerifyPublicKeySignature(keyAlg, payloadData, 0, payloadData.Length -
                                         signatureData.Length - 4, signature))
            {
                // Raise event to get result of auth attempt.
                var authUserEventArgs = new AuthUserHostBasedEventArgs(_lastUserName, clientHostName,
                                                                       clientUserName, keyAlg.ExportPublicKey());

                if (AuthenticateUserHostBased != null)
                {
                    AuthenticateUserHostBased(this, authUserEventArgs);
                }

                // Check result of auth attempt.
                switch (authUserEventArgs.Result)
                {
                case AuthenticationResult.Success:
                    // Auth has succeeded.
                    AuthenticateUser(_lastServiceName);

                    break;

                case AuthenticationResult.FurtherAuthRequired:
                    // Auth has succeeded, but further auth is required.
                    SendMsgUserAuthFailure(true);

                    break;

                case AuthenticationResult.Failure:
                    // Auth has failed.
                    SendMsgUserAuthFailure(false);

                    break;
                }
            }
            else
            {
                // Signature is invalid.
                SendMsgUserAuthFailure(false);
            }
        }
        protected void ProcessMsgUserAuthRequestPassword(SshStreamReader msgReader)
        {
            if (_isDisposed)
            {
                throw new ObjectDisposedException(this.GetType().FullName);
            }

            // Raise event to specify requested auth method.
            if (AuthenticationMethodRequested != null)
            {
                AuthenticationMethodRequested(this,
                                              new AuthMethodRequestedEventArgs(AuthenticationMethod.Password));
            }

            // Check whether client is changing password.
            bool changingPassword = msgReader.ReadBoolean();

            if (changingPassword)
            {
                // Read old and new passwords (in plaintext).
                string oldPassword = Encoding.UTF8.GetString(msgReader.ReadByteString());
                string newPassword = Encoding.UTF8.GetString(msgReader.ReadByteString());

                // Raise event to get result of password change request.
                var changePasswordEventArgs = new ChangePasswordEventArgs(oldPassword, newPassword);

                if (ChangePassword != null)
                {
                    ChangePassword(this, changePasswordEventArgs);
                }

                // Check result of password change request.
                switch (changePasswordEventArgs.Result)
                {
                case PasswordChangeResult.Success:
                    // Password change and auth have succeeded.
                    AuthenticateUser(_lastServiceName);

                    break;

                case PasswordChangeResult.FurtherAuthRequired:
                    // Password change has succeeded, but further auth is required.
                    SendMsgUserAuthFailure(true);

                    break;

                case PasswordChangeResult.Failure:
                    // Password change has failed.
                    SendMsgUserAuthFailure(false);

                    break;

                case PasswordChangeResult.NewPasswordUnacceptable:
                    // Password was not changed.
                    SendMsgUserAuthPasswdChangeReq(changePasswordEventArgs.ReplyPrompt, "");

                    break;
                }
            }
            else
            {
                // Read password (in plaintext).
                string password = Encoding.UTF8.GetString(msgReader.ReadByteString());

                // Raise event to get result of auth attempt.
                var authUserEventArgs = new AuthUserPasswordEventArgs(_lastUserName, password);

                if (AuthenticateUserPassword != null)
                {
                    AuthenticateUserPassword(this, authUserEventArgs);
                }

                // Check result of auth attempt.
                switch (authUserEventArgs.Result)
                {
                case AuthenticationResult.Success:
                    // Auth has succeeded.
                    AuthenticateUser(_lastServiceName);

                    break;

                case AuthenticationResult.FurtherAuthRequired:
                    // Auth has succeeded, but further auth is required.
                    SendMsgUserAuthFailure(true);

                    break;

                case AuthenticationResult.Failure:
                    // Increment number of failed auth attempts.
                    _failedAuthAttempts++;

                    if (_failedAuthAttempts < this.MaximumAuthAttempts)
                    {
                        // Auth has failed, but allow client to reattempt auth.
                        SendMsgUserAuthFailure(false);
                    }
                    else
                    {
                        // Auth has failed too many times, disconnect.
                        _client.Disconnect(false);
                        throw new DisconnectedException();
                    }

                    break;

                case AuthenticationResult.PasswordExpired:
                    // Password change is required.
                    SendMsgUserAuthPasswdChangeReq("The specified password has expired.", "");

                    break;
                }
            }
        }
        protected internal override void ProcessRequest(string requestType, bool wantReply,
            SshStreamReader msgReader)
        {
            if (_isDisposed) throw new ObjectDisposedException(this.GetType().FullName);

            switch (requestType)
            {
                case "pty-req":
                    // Read information about pseudo-terminal.
                    var termEnvVar = msgReader.ReadString();
                    var termCharsWidth = msgReader.ReadUInt32();
                    var termCharsHeight = msgReader.ReadUInt32();
                    var termPixelsWidth = msgReader.ReadUInt32();
                    var termPixelsHeight = msgReader.ReadUInt32();
                    var termModes = ReadTerminalModes(msgReader.ReadByteString());

                    // Raise event to request pseudo terminal.
                    var pseudoTerminalRequestedEventArgs = new PseudoTerminalRequestedEventArgs(termEnvVar);

                    OnPseudoTerminalRequested(pseudoTerminalRequestedEventArgs);

                    // Check if request to allocate pseudo terminal failed.
                    if (!pseudoTerminalRequestedEventArgs.Success) break;

                    _termEnvVar = termEnvVar;
                    _termCharsWidth = termCharsWidth;
                    _termCharsHeight = termCharsHeight;
                    _termPixelsWidth = termPixelsWidth;
                    _termPixelsHeight = termPixelsHeight;
                    _termModes = termModes;

                    // Add TERM to list of environment variables.
                    _envVars.Add("TERM", _termEnvVar);

                    // Raise event, pseudo terminal has been allocated.
                    OnPseudoTerminalAllocated(new EventArgs());

                    if (wantReply) _connService.SendMsgChannelSuccess(this);
                    return;

                case "env":
                    // Read name and value of environment variable.
                    var varName = msgReader.ReadString();
                    var varValue = msgReader.ReadString();

                    // Add variable to list.
                    _envVars.Add(varName, varValue);

                    if (wantReply) _connService.SendMsgChannelSuccess(this);
                    return;
                case "shell":
                    // Start default shell.
                    StartShell();

                    if (wantReply) _connService.SendMsgChannelSuccess(this);
                    return;
                case "exec":
                    // not implemented

                    break;
                default:
                    base.ProcessRequest(requestType, wantReply, msgReader);
                    return;
            }

            // Request has failed.
            if (wantReply) _connService.SendMsgChannelFailure(this);
        }