| | | 1 | | using System; |
| | | 2 | | using System.Collections.Generic; |
| | | 3 | | using System.Globalization; |
| | | 4 | | |
| | | 5 | | using Renci.SshNet.Common; |
| | | 6 | | using Renci.SshNet.Messages; |
| | | 7 | | using Renci.SshNet.Messages.Authentication; |
| | | 8 | | using Renci.SshNet.Messages.Connection; |
| | | 9 | | using Renci.SshNet.Messages.Transport; |
| | | 10 | | |
| | | 11 | | namespace Renci.SshNet |
| | | 12 | | { |
| | | 13 | | internal sealed class SshMessageFactory |
| | | 14 | | { |
| | | 15 | | private readonly MessageMetadata[] _enabledMessagesByNumber; |
| | | 16 | | private readonly bool[] _activatedMessagesById; |
| | 1883 | 17 | | private readonly object _lock = new object(); |
| | | 18 | | |
| | 4 | 19 | | private static readonly MessageMetadata[] AllMessages = new MessageMetadata[] |
| | 4 | 20 | | { |
| | 4 | 21 | | new MessageMetadata<KeyExchangeInitMessage>(0, "SSH_MSG_KEXINIT", 20), |
| | 4 | 22 | | new MessageMetadata<NewKeysMessage>(1, "SSH_MSG_NEWKEYS", 21), |
| | 4 | 23 | | new MessageMetadata<RequestFailureMessage>(2, "SSH_MSG_REQUEST_FAILURE", 82), |
| | 4 | 24 | | new MessageMetadata<ChannelOpenFailureMessage>(3, "SSH_MSG_CHANNEL_OPEN_FAILURE", 92), |
| | 4 | 25 | | new MessageMetadata<ChannelFailureMessage>(4, "SSH_MSG_CHANNEL_FAILURE", 100), |
| | 4 | 26 | | new MessageMetadata<ChannelExtendedDataMessage>(5, "SSH_MSG_CHANNEL_EXTENDED_DATA", 95), |
| | 4 | 27 | | new MessageMetadata<ChannelDataMessage>(6, "SSH_MSG_CHANNEL_DATA", 94), |
| | 4 | 28 | | new MessageMetadata<ChannelRequestMessage>(7, "SSH_MSG_CHANNEL_REQUEST", 98), |
| | 4 | 29 | | new MessageMetadata<BannerMessage>(8, "SSH_MSG_USERAUTH_BANNER", 53), |
| | 4 | 30 | | new MessageMetadata<InformationResponseMessage>(9, "SSH_MSG_USERAUTH_INFO_RESPONSE", 61), |
| | 4 | 31 | | new MessageMetadata<FailureMessage>(10, "SSH_MSG_USERAUTH_FAILURE", 51), |
| | 4 | 32 | | new MessageMetadata<DebugMessage>(11, "SSH_MSG_DEBUG", 4), |
| | 4 | 33 | | new MessageMetadata<GlobalRequestMessage>(12, "SSH_MSG_GLOBAL_REQUEST", 80), |
| | 4 | 34 | | new MessageMetadata<ChannelOpenMessage>(13, "SSH_MSG_CHANNEL_OPEN", 90), |
| | 4 | 35 | | new MessageMetadata<ChannelOpenConfirmationMessage>(14, "SSH_MSG_CHANNEL_OPEN_CONFIRMATION", 91), |
| | 4 | 36 | | new MessageMetadata<InformationRequestMessage>(15, "SSH_MSG_USERAUTH_INFO_REQUEST", 60), |
| | 4 | 37 | | new MessageMetadata<UnimplementedMessage>(16, "SSH_MSG_UNIMPLEMENTED", 3), |
| | 4 | 38 | | new MessageMetadata<RequestSuccessMessage>(17, "SSH_MSG_REQUEST_SUCCESS", 81), |
| | 4 | 39 | | new MessageMetadata<ChannelSuccessMessage>(18, "SSH_MSG_CHANNEL_SUCCESS", 99), |
| | 4 | 40 | | new MessageMetadata<PasswordChangeRequiredMessage>(19, "SSH_MSG_USERAUTH_PASSWD_CHANGEREQ", 60), |
| | 4 | 41 | | new MessageMetadata<DisconnectMessage>(20, "SSH_MSG_DISCONNECT", 1), |
| | 4 | 42 | | new MessageMetadata<SuccessMessage>(21, "SSH_MSG_USERAUTH_SUCCESS", 52), |
| | 4 | 43 | | new MessageMetadata<PublicKeyMessage>(22, "SSH_MSG_USERAUTH_PK_OK", 60), |
| | 4 | 44 | | new MessageMetadata<IgnoreMessage>(23, "SSH_MSG_IGNORE", 2), |
| | 4 | 45 | | new MessageMetadata<ChannelWindowAdjustMessage>(24, "SSH_MSG_CHANNEL_WINDOW_ADJUST", 93), |
| | 4 | 46 | | new MessageMetadata<ChannelEofMessage>(25, "SSH_MSG_CHANNEL_EOF", 96), |
| | 4 | 47 | | new MessageMetadata<ChannelCloseMessage>(26, "SSH_MSG_CHANNEL_CLOSE", 97), |
| | 4 | 48 | | new MessageMetadata<ServiceAcceptMessage>(27, "SSH_MSG_SERVICE_ACCEPT", 6), |
| | 4 | 49 | | new MessageMetadata<KeyExchangeDhGroupExchangeGroup>(28, "SSH_MSG_KEX_DH_GEX_GROUP", 31), |
| | 4 | 50 | | new MessageMetadata<KeyExchangeDhReplyMessage>(29, "SSH_MSG_KEXDH_REPLY", 31), |
| | 4 | 51 | | new MessageMetadata<KeyExchangeDhGroupExchangeReply>(30, "SSH_MSG_KEX_DH_GEX_REPLY", 33), |
| | 4 | 52 | | new MessageMetadata<KeyExchangeEcdhReplyMessage>(31, "SSH_MSG_KEX_ECDH_REPLY", 31) |
| | 4 | 53 | | }; |
| | 4 | 54 | | 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> |
| | 1883 | 69 | | public SshMessageFactory() |
| | 1883 | 70 | | { |
| | 1883 | 71 | | _activatedMessagesById = new bool[TotalMessageCount]; |
| | 1883 | 72 | | _enabledMessagesByNumber = new MessageMetadata[HighestMessageNumber + 1]; |
| | 1883 | 73 | | } |
| | | 74 | | |
| | | 75 | | /// <summary> |
| | | 76 | | /// Disables and deactivate all messages. |
| | | 77 | | /// </summary> |
| | | 78 | | public void Reset() |
| | 0 | 79 | | { |
| | 0 | 80 | | Array.Clear(_activatedMessagesById, 0, _activatedMessagesById.Length); |
| | 0 | 81 | | Array.Clear(_enabledMessagesByNumber, 0, _enabledMessagesByNumber.Length); |
| | 0 | 82 | | } |
| | | 83 | | |
| | | 84 | | public Message Create(byte messageNumber) |
| | 58295 | 85 | | { |
| | 58295 | 86 | | if (messageNumber > HighestMessageNumber) |
| | 42 | 87 | | { |
| | 42 | 88 | | throw CreateMessageTypeNotSupportedException(messageNumber); |
| | | 89 | | } |
| | | 90 | | |
| | 58253 | 91 | | var enabledMessageMetadata = _enabledMessagesByNumber[messageNumber]; |
| | 58253 | 92 | | if (enabledMessageMetadata is null) |
| | 0 | 93 | | { |
| | 0 | 94 | | MessageMetadata definedMessageMetadata = null; |
| | | 95 | | |
| | | 96 | | // find first message with specified number |
| | 0 | 97 | | for (var i = 0; i < AllMessages.Length; i++) |
| | 0 | 98 | | { |
| | 0 | 99 | | var messageMetadata = AllMessages[i]; |
| | 0 | 100 | | if (messageMetadata.Number == messageNumber) |
| | 0 | 101 | | { |
| | 0 | 102 | | definedMessageMetadata = messageMetadata; |
| | 0 | 103 | | break; |
| | | 104 | | } |
| | 0 | 105 | | } |
| | | 106 | | |
| | 0 | 107 | | if (definedMessageMetadata is null) |
| | 0 | 108 | | { |
| | 0 | 109 | | throw CreateMessageTypeNotSupportedException(messageNumber); |
| | | 110 | | } |
| | | 111 | | |
| | 0 | 112 | | throw new SshException(string.Format(CultureInfo.InvariantCulture, "Message type {0} is not valid in the |
| | | 113 | | } |
| | | 114 | | |
| | 58253 | 115 | | return enabledMessageMetadata.Create(); |
| | 58253 | 116 | | } |
| | | 117 | | |
| | | 118 | | public void DisableNonKeyExchangeMessages() |
| | 1814 | 119 | | { |
| | 119724 | 120 | | for (var i = 0; i < AllMessages.Length; i++) |
| | 58048 | 121 | | { |
| | 58048 | 122 | | var messageMetadata = AllMessages[i]; |
| | | 123 | | |
| | 58048 | 124 | | var messageNumber = messageMetadata.Number; |
| | 58048 | 125 | | if (messageNumber is (> 2 and < 20) or > 30) |
| | 50792 | 126 | | { |
| | 50792 | 127 | | _enabledMessagesByNumber[messageNumber] = null; |
| | 50792 | 128 | | } |
| | 58048 | 129 | | } |
| | 1814 | 130 | | } |
| | | 131 | | |
| | | 132 | | public void EnableActivatedMessages() |
| | 1813 | 133 | | { |
| | 119658 | 134 | | for (var i = 0; i < AllMessages.Length; i++) |
| | 58016 | 135 | | { |
| | 58016 | 136 | | var messageMetadata = AllMessages[i]; |
| | | 137 | | |
| | 58016 | 138 | | if (!_activatedMessagesById[messageMetadata.Id]) |
| | 43512 | 139 | | { |
| | 43512 | 140 | | continue; |
| | | 141 | | } |
| | | 142 | | |
| | 14504 | 143 | | var enabledMessageMetadata = _enabledMessagesByNumber[messageMetadata.Number]; |
| | 14504 | 144 | | if (enabledMessageMetadata != null && enabledMessageMetadata != messageMetadata) |
| | 0 | 145 | | { |
| | 0 | 146 | | throw CreateMessageTypeAlreadyEnabledForOtherMessageException(messageMetadata.Number, |
| | 0 | 147 | | messageMetadata.Name, |
| | 0 | 148 | | enabledMessageMetadata.Name); |
| | | 149 | | } |
| | | 150 | | |
| | 14504 | 151 | | _enabledMessagesByNumber[messageMetadata.Number] = messageMetadata; |
| | 14504 | 152 | | } |
| | 1813 | 153 | | } |
| | | 154 | | |
| | | 155 | | public void EnableAndActivateMessage(string messageName) |
| | 44075 | 156 | | { |
| | 44075 | 157 | | if (messageName is null) |
| | 0 | 158 | | { |
| | 0 | 159 | | throw new ArgumentNullException(nameof(messageName)); |
| | | 160 | | } |
| | | 161 | | |
| | 44075 | 162 | | lock (_lock) |
| | 44075 | 163 | | { |
| | 44075 | 164 | | if (!MessagesByName.TryGetValue(messageName, out var messageMetadata)) |
| | 0 | 165 | | { |
| | 0 | 166 | | throw CreateMessageNotSupportedException(messageName); |
| | | 167 | | } |
| | | 168 | | |
| | 44075 | 169 | | var enabledMessageMetadata = _enabledMessagesByNumber[messageMetadata.Number]; |
| | 44075 | 170 | | if (enabledMessageMetadata != null && enabledMessageMetadata != messageMetadata) |
| | 0 | 171 | | { |
| | 0 | 172 | | throw CreateMessageTypeAlreadyEnabledForOtherMessageException(messageMetadata.Number, |
| | 0 | 173 | | messageMetadata.Name, |
| | 0 | 174 | | enabledMessageMetadata.Name); |
| | | 175 | | } |
| | | 176 | | |
| | 44075 | 177 | | _enabledMessagesByNumber[messageMetadata.Number] = messageMetadata; |
| | 44075 | 178 | | _activatedMessagesById[messageMetadata.Id] = true; |
| | 44075 | 179 | | } |
| | 44075 | 180 | | } |
| | | 181 | | |
| | | 182 | | public void DisableAndDeactivateMessage(string messageName) |
| | 6012 | 183 | | { |
| | 6012 | 184 | | if (messageName is null) |
| | 0 | 185 | | { |
| | 0 | 186 | | throw new ArgumentNullException(nameof(messageName)); |
| | | 187 | | } |
| | | 188 | | |
| | 6012 | 189 | | lock (_lock) |
| | 6012 | 190 | | { |
| | 6012 | 191 | | if (!MessagesByName.TryGetValue(messageName, out var messageMetadata)) |
| | 0 | 192 | | { |
| | 0 | 193 | | throw CreateMessageNotSupportedException(messageName); |
| | | 194 | | } |
| | | 195 | | |
| | 6012 | 196 | | var enabledMessageMetadata = _enabledMessagesByNumber[messageMetadata.Number]; |
| | 6012 | 197 | | if (enabledMessageMetadata != null && enabledMessageMetadata != messageMetadata) |
| | 0 | 198 | | { |
| | 0 | 199 | | throw CreateMessageTypeAlreadyEnabledForOtherMessageException(messageMetadata.Number, |
| | 0 | 200 | | messageMetadata.Name, |
| | 0 | 201 | | enabledMessageMetadata.Name); |
| | | 202 | | } |
| | | 203 | | |
| | 6012 | 204 | | _activatedMessagesById[messageMetadata.Id] = false; |
| | 6012 | 205 | | _enabledMessagesByNumber[messageMetadata.Number] = null; |
| | 6012 | 206 | | } |
| | 6012 | 207 | | } |
| | | 208 | | |
| | | 209 | | private static Dictionary<string, MessageMetadata> CreateMessagesByNameMapping() |
| | 4 | 210 | | { |
| | 4 | 211 | | var messagesByName = new Dictionary<string, MessageMetadata>(AllMessages.Length); |
| | | 212 | | |
| | 264 | 213 | | for (var i = 0; i < AllMessages.Length; i++) |
| | 128 | 214 | | { |
| | 128 | 215 | | var messageMetadata = AllMessages[i]; |
| | 128 | 216 | | messagesByName.Add(messageMetadata.Name, messageMetadata); |
| | 128 | 217 | | } |
| | | 218 | | |
| | 4 | 219 | | return messagesByName; |
| | 4 | 220 | | } |
| | | 221 | | |
| | | 222 | | private static SshException CreateMessageTypeNotSupportedException(byte messageNumber) |
| | 42 | 223 | | { |
| | 42 | 224 | | throw new SshException(string.Format(CultureInfo.InvariantCulture, "Message type {0} is not supported.", mes |
| | | 225 | | } |
| | | 226 | | |
| | | 227 | | private static SshException CreateMessageNotSupportedException(string messageName) |
| | 0 | 228 | | { |
| | 0 | 229 | | throw new SshException(string.Format(CultureInfo.InvariantCulture, "Message '{0}' is not supported.", messag |
| | | 230 | | } |
| | | 231 | | |
| | | 232 | | private static SshException CreateMessageTypeAlreadyEnabledForOtherMessageException(byte messageNumber, string m |
| | 0 | 233 | | { |
| | 0 | 234 | | throw new SshException(string.Format(CultureInfo.InvariantCulture, |
| | 0 | 235 | | "Cannot enable message '{0}'. Message type {1} is already enabled for ' |
| | 0 | 236 | | messageName, |
| | 0 | 237 | | messageNumber, |
| | 0 | 238 | | currentEnabledForMessageName)); |
| | | 239 | | } |
| | | 240 | | |
| | | 241 | | private abstract class MessageMetadata |
| | | 242 | | { |
| | 128 | 243 | | protected MessageMetadata(byte id, string name, byte number) |
| | 128 | 244 | | { |
| | 128 | 245 | | Id = id; |
| | 128 | 246 | | Name = name; |
| | 128 | 247 | | Number = number; |
| | 128 | 248 | | } |
| | | 249 | | |
| | 108103 | 250 | | public byte Id { get; } |
| | | 251 | | |
| | 128 | 252 | | public string Name { get; } |
| | | 253 | | |
| | 187230 | 254 | | 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) |
| | 128 | 263 | | : base(id, name, number) |
| | 128 | 264 | | { |
| | 128 | 265 | | } |
| | | 266 | | |
| | | 267 | | public override Message Create() |
| | 58253 | 268 | | { |
| | 58253 | 269 | | return new T(); |
| | 58253 | 270 | | } |
| | | 271 | | } |
| | | 272 | | } |
| | | 273 | | } |