< Summary

Information
Class: Renci.SshNet.SshMessageFactory
Assembly: Renci.SshNet
File(s): \home\appveyor\projects\ssh-net\src\Renci.SshNet\SshMessageFactory.cs
Line coverage
72%
Covered lines: 121
Uncovered lines: 46
Coverable lines: 167
Total lines: 273
Line coverage: 72.4%
Branch coverage
67%
Covered branches: 31
Total branches: 46
Branch coverage: 67.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

\home\appveyor\projects\ssh-net\src\Renci.SshNet\SshMessageFactory.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Globalization;
 4
 5using Renci.SshNet.Common;
 6using Renci.SshNet.Messages;
 7using Renci.SshNet.Messages.Authentication;
 8using Renci.SshNet.Messages.Connection;
 9using Renci.SshNet.Messages.Transport;
 10
 11namespace Renci.SshNet
 12{
 13    internal sealed class SshMessageFactory
 14    {
 15        private readonly MessageMetadata[] _enabledMessagesByNumber;
 16        private readonly bool[] _activatedMessagesById;
 188317        private readonly object _lock = new object();
 18
 419        private static readonly MessageMetadata[] AllMessages = new MessageMetadata[]
 420            {
 421                new MessageMetadata<KeyExchangeInitMessage>(0, "SSH_MSG_KEXINIT", 20),
 422                new MessageMetadata<NewKeysMessage>(1, "SSH_MSG_NEWKEYS", 21),
 423                new MessageMetadata<RequestFailureMessage>(2, "SSH_MSG_REQUEST_FAILURE", 82),
 424                new MessageMetadata<ChannelOpenFailureMessage>(3, "SSH_MSG_CHANNEL_OPEN_FAILURE", 92),
 425                new MessageMetadata<ChannelFailureMessage>(4, "SSH_MSG_CHANNEL_FAILURE", 100),
 426                new MessageMetadata<ChannelExtendedDataMessage>(5, "SSH_MSG_CHANNEL_EXTENDED_DATA", 95),
 427                new MessageMetadata<ChannelDataMessage>(6, "SSH_MSG_CHANNEL_DATA", 94),
 428                new MessageMetadata<ChannelRequestMessage>(7, "SSH_MSG_CHANNEL_REQUEST", 98),
 429                new MessageMetadata<BannerMessage>(8, "SSH_MSG_USERAUTH_BANNER", 53),
 430                new MessageMetadata<InformationResponseMessage>(9, "SSH_MSG_USERAUTH_INFO_RESPONSE", 61),
 431                new MessageMetadata<FailureMessage>(10, "SSH_MSG_USERAUTH_FAILURE", 51),
 432                new MessageMetadata<DebugMessage>(11, "SSH_MSG_DEBUG", 4),
 433                new MessageMetadata<GlobalRequestMessage>(12, "SSH_MSG_GLOBAL_REQUEST", 80),
 434                new MessageMetadata<ChannelOpenMessage>(13, "SSH_MSG_CHANNEL_OPEN", 90),
 435                new MessageMetadata<ChannelOpenConfirmationMessage>(14, "SSH_MSG_CHANNEL_OPEN_CONFIRMATION", 91),
 436                new MessageMetadata<InformationRequestMessage>(15, "SSH_MSG_USERAUTH_INFO_REQUEST", 60),
 437                new MessageMetadata<UnimplementedMessage>(16, "SSH_MSG_UNIMPLEMENTED", 3),
 438                new MessageMetadata<RequestSuccessMessage>(17, "SSH_MSG_REQUEST_SUCCESS", 81),
 439                new MessageMetadata<ChannelSuccessMessage>(18, "SSH_MSG_CHANNEL_SUCCESS", 99),
 440                new MessageMetadata<PasswordChangeRequiredMessage>(19, "SSH_MSG_USERAUTH_PASSWD_CHANGEREQ", 60),
 441                new MessageMetadata<DisconnectMessage>(20, "SSH_MSG_DISCONNECT", 1),
 442                new MessageMetadata<SuccessMessage>(21, "SSH_MSG_USERAUTH_SUCCESS", 52),
 443                new MessageMetadata<PublicKeyMessage>(22, "SSH_MSG_USERAUTH_PK_OK", 60),
 444                new MessageMetadata<IgnoreMessage>(23, "SSH_MSG_IGNORE", 2),
 445                new MessageMetadata<ChannelWindowAdjustMessage>(24, "SSH_MSG_CHANNEL_WINDOW_ADJUST", 93),
 446                new MessageMetadata<ChannelEofMessage>(25, "SSH_MSG_CHANNEL_EOF", 96),
 447                new MessageMetadata<ChannelCloseMessage>(26, "SSH_MSG_CHANNEL_CLOSE", 97),
 448                new MessageMetadata<ServiceAcceptMessage>(27, "SSH_MSG_SERVICE_ACCEPT", 6),
 449                new MessageMetadata<KeyExchangeDhGroupExchangeGroup>(28, "SSH_MSG_KEX_DH_GEX_GROUP", 31),
 450                new MessageMetadata<KeyExchangeDhReplyMessage>(29, "SSH_MSG_KEXDH_REPLY", 31),
 451                new MessageMetadata<KeyExchangeDhGroupExchangeReply>(30, "SSH_MSG_KEX_DH_GEX_REPLY", 33),
 452                new MessageMetadata<KeyExchangeEcdhReplyMessage>(31, "SSH_MSG_KEX_ECDH_REPLY", 31)
 453            };
 454        private static readonly Dictionary<string, MessageMetadata> MessagesByName = CreateMessagesByNameMapping();
 55
 56        /// <summary>
 57        /// Defines the highest message number that is currently supported.
 58        /// </summary>
 59        internal const byte HighestMessageNumber = 100;
 60
 61        /// <summary>
 62        /// Defines the total number of supported messages.
 63        /// </summary>
 64        internal const int TotalMessageCount = 32;
 65
 66        /// <summary>
 67        /// Initializes a new instance of the <see cref="SshMessageFactory"/> class.
 68        /// </summary>
 188369        public SshMessageFactory()
 188370        {
 188371            _activatedMessagesById = new bool[TotalMessageCount];
 188372            _enabledMessagesByNumber = new MessageMetadata[HighestMessageNumber + 1];
 188373        }
 74
 75        /// <summary>
 76        /// Disables and deactivate all messages.
 77        /// </summary>
 78        public void Reset()
 079        {
 080            Array.Clear(_activatedMessagesById, 0, _activatedMessagesById.Length);
 081            Array.Clear(_enabledMessagesByNumber, 0, _enabledMessagesByNumber.Length);
 082        }
 83
 84        public Message Create(byte messageNumber)
 5829585        {
 5829586            if (messageNumber > HighestMessageNumber)
 4287            {
 4288                throw CreateMessageTypeNotSupportedException(messageNumber);
 89            }
 90
 5825391            var enabledMessageMetadata = _enabledMessagesByNumber[messageNumber];
 5825392            if (enabledMessageMetadata is null)
 093            {
 094                MessageMetadata definedMessageMetadata = null;
 95
 96                // find first message with specified number
 097                for (var i = 0; i < AllMessages.Length; i++)
 098                {
 099                    var messageMetadata = AllMessages[i];
 0100                    if (messageMetadata.Number == messageNumber)
 0101                    {
 0102                        definedMessageMetadata = messageMetadata;
 0103                        break;
 104                    }
 0105                }
 106
 0107                if (definedMessageMetadata is null)
 0108                {
 0109                    throw CreateMessageTypeNotSupportedException(messageNumber);
 110                }
 111
 0112                throw new SshException(string.Format(CultureInfo.InvariantCulture, "Message type {0} is not valid in the
 113            }
 114
 58253115            return enabledMessageMetadata.Create();
 58253116        }
 117
 118        public void DisableNonKeyExchangeMessages()
 1814119        {
 119724120            for (var i = 0; i < AllMessages.Length; i++)
 58048121            {
 58048122                var messageMetadata = AllMessages[i];
 123
 58048124                var messageNumber = messageMetadata.Number;
 58048125                if (messageNumber is (> 2 and < 20) or > 30)
 50792126                {
 50792127                    _enabledMessagesByNumber[messageNumber] = null;
 50792128                }
 58048129            }
 1814130        }
 131
 132        public void EnableActivatedMessages()
 1813133        {
 119658134            for (var i = 0; i < AllMessages.Length; i++)
 58016135            {
 58016136                var messageMetadata = AllMessages[i];
 137
 58016138                if (!_activatedMessagesById[messageMetadata.Id])
 43512139                {
 43512140                    continue;
 141                }
 142
 14504143                var enabledMessageMetadata = _enabledMessagesByNumber[messageMetadata.Number];
 14504144                if (enabledMessageMetadata != null && enabledMessageMetadata != messageMetadata)
 0145                {
 0146                    throw CreateMessageTypeAlreadyEnabledForOtherMessageException(messageMetadata.Number,
 0147                                                                                  messageMetadata.Name,
 0148                                                                                  enabledMessageMetadata.Name);
 149                }
 150
 14504151                _enabledMessagesByNumber[messageMetadata.Number] = messageMetadata;
 14504152            }
 1813153        }
 154
 155        public void EnableAndActivateMessage(string messageName)
 44075156        {
 44075157            if (messageName is null)
 0158            {
 0159                throw new ArgumentNullException(nameof(messageName));
 160            }
 161
 44075162            lock (_lock)
 44075163            {
 44075164                if (!MessagesByName.TryGetValue(messageName, out var messageMetadata))
 0165                {
 0166                    throw CreateMessageNotSupportedException(messageName);
 167                }
 168
 44075169                var enabledMessageMetadata = _enabledMessagesByNumber[messageMetadata.Number];
 44075170                if (enabledMessageMetadata != null && enabledMessageMetadata != messageMetadata)
 0171                {
 0172                    throw CreateMessageTypeAlreadyEnabledForOtherMessageException(messageMetadata.Number,
 0173                        messageMetadata.Name,
 0174                        enabledMessageMetadata.Name);
 175                }
 176
 44075177                _enabledMessagesByNumber[messageMetadata.Number] = messageMetadata;
 44075178                _activatedMessagesById[messageMetadata.Id] = true;
 44075179            }
 44075180        }
 181
 182        public void DisableAndDeactivateMessage(string messageName)
 6012183        {
 6012184            if (messageName is null)
 0185            {
 0186                throw new ArgumentNullException(nameof(messageName));
 187            }
 188
 6012189            lock (_lock)
 6012190            {
 6012191                if (!MessagesByName.TryGetValue(messageName, out var messageMetadata))
 0192                {
 0193                    throw CreateMessageNotSupportedException(messageName);
 194                }
 195
 6012196                var enabledMessageMetadata = _enabledMessagesByNumber[messageMetadata.Number];
 6012197                if (enabledMessageMetadata != null && enabledMessageMetadata != messageMetadata)
 0198                {
 0199                    throw CreateMessageTypeAlreadyEnabledForOtherMessageException(messageMetadata.Number,
 0200                                                                                  messageMetadata.Name,
 0201                                                                                  enabledMessageMetadata.Name);
 202                }
 203
 6012204                _activatedMessagesById[messageMetadata.Id] = false;
 6012205                _enabledMessagesByNumber[messageMetadata.Number] = null;
 6012206            }
 6012207        }
 208
 209        private static Dictionary<string, MessageMetadata> CreateMessagesByNameMapping()
 4210        {
 4211            var messagesByName = new Dictionary<string, MessageMetadata>(AllMessages.Length);
 212
 264213            for (var i = 0; i < AllMessages.Length; i++)
 128214            {
 128215                var messageMetadata = AllMessages[i];
 128216                messagesByName.Add(messageMetadata.Name, messageMetadata);
 128217            }
 218
 4219            return messagesByName;
 4220        }
 221
 222        private static SshException CreateMessageTypeNotSupportedException(byte messageNumber)
 42223        {
 42224            throw new SshException(string.Format(CultureInfo.InvariantCulture, "Message type {0} is not supported.", mes
 225        }
 226
 227        private static SshException CreateMessageNotSupportedException(string messageName)
 0228        {
 0229            throw new SshException(string.Format(CultureInfo.InvariantCulture, "Message '{0}' is not supported.", messag
 230        }
 231
 232        private static SshException CreateMessageTypeAlreadyEnabledForOtherMessageException(byte messageNumber, string m
 0233        {
 0234            throw new SshException(string.Format(CultureInfo.InvariantCulture,
 0235                                                 "Cannot enable message '{0}'. Message type {1} is already enabled for '
 0236                                                 messageName,
 0237                                                 messageNumber,
 0238                                                 currentEnabledForMessageName));
 239        }
 240
 241        private abstract class MessageMetadata
 242        {
 128243            protected MessageMetadata(byte id, string name, byte number)
 128244            {
 128245                Id = id;
 128246                Name = name;
 128247                Number = number;
 128248            }
 249
 108103250            public byte Id { get; }
 251
 128252            public string Name { get; }
 253
 187230254            public byte Number { get; }
 255
 256            public abstract Message Create();
 257        }
 258
 259        private sealed class MessageMetadata<T> : MessageMetadata
 260            where T : Message, new()
 261        {
 262            public MessageMetadata(byte id, string name, byte number)
 128263                : base(id, name, number)
 128264            {
 128265            }
 266
 267            public override Message Create()
 58253268            {
 58253269                return new T();
 58253270            }
 271        }
 272    }
 273}