mirror of
https://github.com/sshnet/SSH.NET.git
synced 2026-09-10 01:05:42 +00:00
Initial checkin
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
namespace Renci.SshClient.Algorithms
|
||||
{
|
||||
public abstract class Algorithm
|
||||
{
|
||||
public abstract string Name { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace Renci.SshClient.Algorithms
|
||||
{
|
||||
public abstract class Compression : Algorithm
|
||||
{
|
||||
private class CompressionNone : Compression
|
||||
{
|
||||
|
||||
public override string Name
|
||||
{
|
||||
get { return "none"; }
|
||||
}
|
||||
}
|
||||
|
||||
static Compression()
|
||||
{
|
||||
Compression.None = new CompressionNone
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
public static Compression None { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,456 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Security.Cryptography;
|
||||
using Renci.SshClient.Common;
|
||||
using Renci.SshClient.Messages;
|
||||
using Renci.SshClient.Messages.Transport;
|
||||
|
||||
namespace Renci.SshClient.Algorithms
|
||||
{
|
||||
internal abstract class KeyExchange : Algorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates the key exchange algorithm to be used for key exchange.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <returns></returns>
|
||||
internal static KeyExchange Create(KeyExchangeInitMessage message, SessionInfo sessionInfo)
|
||||
{
|
||||
|
||||
// TODO: Determine key exchange algorithm
|
||||
var keyExchangeAlgorithm = (from s in message.KeyExchangeAlgorithms
|
||||
from c in Settings.KeyExchangeAlgorithms.Keys
|
||||
where s == c
|
||||
select c).FirstOrDefault();
|
||||
|
||||
// TODO: If dont agree on algorithms then send disconnect message
|
||||
if (keyExchangeAlgorithm == null)
|
||||
{
|
||||
throw new InvalidDataException("Failed to negotiate key exchange algorithm.");
|
||||
}
|
||||
|
||||
return Settings.KeyExchangeAlgorithms[keyExchangeAlgorithm](sessionInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies negotiated algorithm to encrypt information when sent to the server
|
||||
/// </summary>
|
||||
private Func<SymmetricAlgorithm> _clientEncryptionAlgorithm;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies negotiated algorithm to decrypt information from the server
|
||||
/// </summary>
|
||||
private Func<SymmetricAlgorithm> _serverDecryptionAlgorithm;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies negotiated HMAC algorithm to use for client
|
||||
/// </summary>
|
||||
private Func<IEnumerable<byte>, HMAC> _clientHmacAlgorithm;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies negotiated HMAC algorithm to use for server
|
||||
/// </summary>
|
||||
private Func<IEnumerable<byte>, HMAC> _serverHmacAlgorithm;
|
||||
|
||||
private IEnumerable<byte> _exchangeHash;
|
||||
/// <summary>
|
||||
/// Gets hash value
|
||||
/// </summary>
|
||||
public IEnumerable<byte> ExchangeHash
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this._exchangeHash == null)
|
||||
{
|
||||
this._exchangeHash = this.CalculateHash();
|
||||
}
|
||||
return this._exchangeHash;
|
||||
}
|
||||
}
|
||||
|
||||
public HMAC ServerMac { get; set; }
|
||||
|
||||
public HMAC ClientMac { get; set; }
|
||||
|
||||
public ICryptoTransform Encryption { get; set; }
|
||||
|
||||
public ICryptoTransform Decryption { get; set; }
|
||||
|
||||
public Compression ServerDecompression { get; set; }
|
||||
|
||||
public Compression ClientCompression { get; set; }
|
||||
|
||||
public bool IsCompleted { get; protected set; }
|
||||
|
||||
public bool IsSuccessed { get; protected set; }
|
||||
|
||||
protected SessionInfo SessionInfo { get; private set; }
|
||||
|
||||
protected string ClientPayload { get; set; }
|
||||
|
||||
protected string ServerPayload { get; set; }
|
||||
|
||||
protected string HostKey { get; set; }
|
||||
|
||||
protected BigInteger ClientExchangeValue { get; set; }
|
||||
|
||||
protected BigInteger ServerExchangeValue { get; set; }
|
||||
|
||||
protected BigInteger SharedKey { get; set; }
|
||||
|
||||
protected string Signature { get; set; }
|
||||
|
||||
public event EventHandler<KeyExchangeCompletedEventArgs> Completed;
|
||||
|
||||
public event EventHandler<KeyExchangeFailedEventArgs> Failed;
|
||||
|
||||
public KeyExchange(SessionInfo sessionInfo)
|
||||
{
|
||||
this.SessionInfo = sessionInfo;
|
||||
this.ServerDecompression = Compression.None;
|
||||
this.ClientCompression = Compression.None;
|
||||
}
|
||||
|
||||
public virtual void Start()
|
||||
{
|
||||
// TODO: If key exchange initiated by the client no need to send client message again
|
||||
var clientMessage = new KeyExchangeInitMessage()
|
||||
{
|
||||
KeyExchangeAlgorithms = Settings.KeyExchangeAlgorithms.Keys,
|
||||
ServerHostKeyAlgorithms = Settings.HostKeyAlgorithms.Keys,
|
||||
EncryptionAlgorithmsClientToServer = Settings.Encryptions.Keys,
|
||||
EncryptionAlgorithmsServerToClient = Settings.Encryptions.Keys,
|
||||
MacAlgorithmsClientToSserver = Settings.HmacAlgorithms.Keys,
|
||||
MacAlgorithmsServerToClient = Settings.HmacAlgorithms.Keys,
|
||||
CompressionAlgorithmsClientToServer = new string[] { "none" },
|
||||
CompressionAlgorithmsServerToClient = new string[] { "none" },
|
||||
LanguagesClientToServer = new string[] { string.Empty },
|
||||
LanguagesServerToClient = new string[] { string.Empty },
|
||||
FirstKexPacketFollows = false,
|
||||
Reserved = 0,
|
||||
};
|
||||
|
||||
this.ClientPayload = clientMessage.GetBytes().GetSshString();
|
||||
|
||||
this.SendMessage(clientMessage);
|
||||
}
|
||||
|
||||
public virtual void Start(KeyExchangeInitMessage message)
|
||||
{
|
||||
this.Start();
|
||||
|
||||
// Determine encryption algorithm
|
||||
var clientEncryptionAlgorithmName = (from a in message.EncryptionAlgorithmsClientToServer
|
||||
from b in Settings.Encryptions.Keys
|
||||
where a == b
|
||||
select a).FirstOrDefault();
|
||||
if (string.IsNullOrEmpty(clientEncryptionAlgorithmName))
|
||||
{
|
||||
throw new InvalidOperationException("Client encryption algorithm not found");
|
||||
}
|
||||
this._clientEncryptionAlgorithm = Settings.Encryptions[clientEncryptionAlgorithmName];
|
||||
|
||||
// Determine encryption algorithm
|
||||
var serverDecryptionAlgorithmName = (from a in message.EncryptionAlgorithmsServerToClient
|
||||
from b in Settings.Encryptions.Keys
|
||||
where a == b
|
||||
select a).FirstOrDefault();
|
||||
if (string.IsNullOrEmpty(serverDecryptionAlgorithmName))
|
||||
{
|
||||
throw new InvalidOperationException("Server decryption algorithm not found");
|
||||
}
|
||||
this._serverDecryptionAlgorithm = Settings.Encryptions[clientEncryptionAlgorithmName];
|
||||
|
||||
// Determine client hmac algorithm
|
||||
var clientHmacAlgorithmName = (from a in message.MacAlgorithmsClientToSserver
|
||||
from b in Settings.HmacAlgorithms.Keys
|
||||
where a == b
|
||||
select a).FirstOrDefault();
|
||||
if (string.IsNullOrEmpty(clientHmacAlgorithmName))
|
||||
{
|
||||
throw new InvalidOperationException("Server HMAC algorithm not found");
|
||||
}
|
||||
this._clientHmacAlgorithm = Settings.HmacAlgorithms[clientHmacAlgorithmName];
|
||||
|
||||
// Determine server hmac algorithm
|
||||
var serverHmacAlgorithmName = (from a in message.MacAlgorithmsServerToClient
|
||||
from b in Settings.HmacAlgorithms.Keys
|
||||
where a == b
|
||||
select a).FirstOrDefault();
|
||||
if (string.IsNullOrEmpty(serverHmacAlgorithmName))
|
||||
{
|
||||
throw new InvalidOperationException("Server HMAC algorithm not found");
|
||||
}
|
||||
this._serverHmacAlgorithm = Settings.HmacAlgorithms[serverHmacAlgorithmName];
|
||||
|
||||
}
|
||||
|
||||
public virtual void Finish()
|
||||
{
|
||||
// TODO: Validate that all required properties are set
|
||||
if (this.SessionInfo.SessionId == null)
|
||||
{
|
||||
this.SessionInfo.SessionId = this.ExchangeHash;
|
||||
}
|
||||
|
||||
// Set encryption
|
||||
ICryptoTransform encryption;
|
||||
using (var clientAlgorithm = this._clientEncryptionAlgorithm())
|
||||
{
|
||||
// Calculate client to server initial IV
|
||||
var clientValue = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'A', this.SessionInfo.SessionId));
|
||||
|
||||
// Calculate client to server encryption
|
||||
var clientKey = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'C', this.SessionInfo.SessionId));
|
||||
|
||||
clientKey = this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, clientKey, clientAlgorithm.KeySize / 8);
|
||||
|
||||
clientAlgorithm.Mode = System.Security.Cryptography.CipherMode.CBC;
|
||||
clientAlgorithm.Padding = System.Security.Cryptography.PaddingMode.None;
|
||||
|
||||
encryption = clientAlgorithm.CreateEncryptor(clientKey.Take(clientAlgorithm.KeySize / 8).ToArray(), clientValue.Take(clientAlgorithm.BlockSize / 8).ToArray());
|
||||
}
|
||||
|
||||
// Set decryption
|
||||
ICryptoTransform decryption;
|
||||
using (var serverAlgorithm = this._serverDecryptionAlgorithm())
|
||||
{
|
||||
// Calculate server to client initial IV
|
||||
var serverValue = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'B', this.SessionInfo.SessionId));
|
||||
|
||||
// Calculate server to client encryption
|
||||
var serverKey = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'D', this.SessionInfo.SessionId));
|
||||
|
||||
serverKey = this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, serverKey, serverAlgorithm.KeySize / 8);
|
||||
|
||||
serverAlgorithm.Mode = System.Security.Cryptography.CipherMode.CBC;
|
||||
serverAlgorithm.Padding = System.Security.Cryptography.PaddingMode.None;
|
||||
|
||||
decryption = serverAlgorithm.CreateDecryptor(serverKey.Take(serverAlgorithm.KeySize / 8).ToArray(), serverValue.Take(serverAlgorithm.BlockSize / 8).ToArray());
|
||||
}
|
||||
|
||||
// Calculate client to server integrity
|
||||
var MACc2s = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'E', this.SessionInfo.SessionId));
|
||||
var clientMac = this._clientHmacAlgorithm(MACc2s);
|
||||
|
||||
// Calculate server to client integrity
|
||||
var MACs2c = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'F', this.SessionInfo.SessionId));
|
||||
var serverMac = this._serverHmacAlgorithm(MACs2c);
|
||||
|
||||
// TODO: Create compression and decompression objects if any
|
||||
|
||||
this.Decryption = decryption;
|
||||
this.Encryption = encryption;
|
||||
this.ServerDecompression = Compression.None;
|
||||
this.ClientCompression = Compression.None;
|
||||
this.ServerMac = serverMac;
|
||||
this.ClientMac = clientMac;
|
||||
|
||||
this.IsCompleted = true;
|
||||
this.RaiseCompleted();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the Completed event.
|
||||
/// </summary>
|
||||
/// <param name="sessionId">The session id.</param>
|
||||
/// <param name="decryption">The decryption to be used.</param>
|
||||
/// <param name="encryption">The encryption to be used.</param>
|
||||
/// <param name="serverDecompression">The server decompression.</param>
|
||||
/// <param name="clientCompression">The client compression.</param>
|
||||
/// <param name="serverMac">The server mac.</param>
|
||||
/// <param name="clientMac">The client mac.</param>
|
||||
protected void RaiseCompleted()
|
||||
{
|
||||
if (this.Completed != null)
|
||||
{
|
||||
this.Completed(this, new KeyExchangeCompletedEventArgs());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the Failed event.
|
||||
/// </summary>
|
||||
/// <param name="message">The fail reason message.</param>
|
||||
protected void RaiseFailed(string message)
|
||||
{
|
||||
if (this.Failed != null)
|
||||
{
|
||||
this.Failed(this, new KeyExchangeFailedEventArgs(message));
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual IEnumerable<byte> Hash(IEnumerable<byte> hashBytes)
|
||||
{
|
||||
using (var md = new System.Security.Cryptography.SHA1CryptoServiceProvider())
|
||||
{
|
||||
using (var cs = new System.Security.Cryptography.CryptoStream(System.IO.Stream.Null, md, System.Security.Cryptography.CryptoStreamMode.Write))
|
||||
{
|
||||
var hashData = hashBytes.ToArray();
|
||||
cs.Write(hashData, 0, hashData.Length);
|
||||
cs.Close();
|
||||
return md.Hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected bool ValidateExchangeHash()
|
||||
{
|
||||
var bytes = this.HostKey.GetSshBytes();
|
||||
|
||||
var length = BitConverter.ToUInt32(bytes.Take(4).Reverse().ToArray(), 0);
|
||||
|
||||
var algorithmName = bytes.Skip(4).Take((int)length).GetSshString();
|
||||
|
||||
var data = bytes.Skip(4 + algorithmName.Length);
|
||||
|
||||
var signature = Settings.HostKeyAlgorithms[algorithmName](data);
|
||||
|
||||
return signature.ValidateSignature(this.ExchangeHash, this.Signature.GetSshBytes());
|
||||
}
|
||||
|
||||
protected void SendMessage(Message message)
|
||||
{
|
||||
this.SessionInfo.SendMessage(message);
|
||||
}
|
||||
|
||||
private IEnumerable<byte> CalculateHash()
|
||||
{
|
||||
var hashData = new _ExchangeHashData
|
||||
{
|
||||
ClientVersion = this.SessionInfo.ClientVersion,
|
||||
ServerVersion = this.SessionInfo.ServerVersion,
|
||||
ClientPayload = this.ClientPayload,
|
||||
ServerPayload = this.ServerPayload,
|
||||
HostKey = this.HostKey,
|
||||
ClientExchangeValue = this.ClientExchangeValue,
|
||||
ServerExchangeValue = this.ServerExchangeValue,
|
||||
SharedKey = this.SharedKey,
|
||||
}.GetBytes();
|
||||
|
||||
return this.Hash(hashData);
|
||||
}
|
||||
|
||||
private IEnumerable<byte> GenerateSessionKey(BigInteger sharedKey, IEnumerable<byte> exchangeHash, IEnumerable<byte> key, int size)
|
||||
{
|
||||
var result = new List<byte>(key);
|
||||
while (size > result.Count)
|
||||
{
|
||||
result.AddRange(this.Hash(new _SessionKeyAdjustment
|
||||
{
|
||||
SharedKey = sharedKey,
|
||||
ExcahngeHash = exchangeHash,
|
||||
Key = key,
|
||||
}.GetBytes()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private IEnumerable<byte> GenerateSessionKey(BigInteger sharedKey, IEnumerable<byte> exchangeHash, char p, IEnumerable<byte> sessionId)
|
||||
{
|
||||
return new _SessionKeyGeneration
|
||||
{
|
||||
SharedKey = sharedKey,
|
||||
ExchangeHash = exchangeHash,
|
||||
Char = p,
|
||||
SessionId = sessionId,
|
||||
}.GetBytes();
|
||||
}
|
||||
|
||||
private class _ExchangeHashData : SshData
|
||||
{
|
||||
public string ServerVersion { get; set; }
|
||||
|
||||
public string ClientVersion { get; set; }
|
||||
|
||||
public string ClientPayload { get; set; }
|
||||
|
||||
public string ServerPayload { get; set; }
|
||||
|
||||
public string HostKey { get; set; }
|
||||
|
||||
public UInt32? MinimumGroupSize { get; set; }
|
||||
|
||||
public UInt32? PreferredGroupSize { get; set; }
|
||||
|
||||
public UInt32? MaximumGroupSize { get; set; }
|
||||
|
||||
public IEnumerable<byte> Prime { get; set; }
|
||||
|
||||
public BigInteger ClientExchangeValue { get; set; }
|
||||
|
||||
public BigInteger ServerExchangeValue { get; set; }
|
||||
|
||||
public BigInteger SharedKey { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
this.Write(this.ClientVersion);
|
||||
this.Write(this.ServerVersion);
|
||||
this.Write(this.ClientPayload);
|
||||
this.Write(this.ServerPayload);
|
||||
this.Write(this.HostKey);
|
||||
if (this.MinimumGroupSize.HasValue)
|
||||
this.Write(this.MinimumGroupSize.Value);
|
||||
if (this.PreferredGroupSize.HasValue)
|
||||
this.Write(this.PreferredGroupSize.Value);
|
||||
if (this.MaximumGroupSize.HasValue)
|
||||
this.Write(this.MaximumGroupSize.Value);
|
||||
if (this.Prime != null)
|
||||
this.Write(this.Prime);
|
||||
this.Write(this.ClientExchangeValue);
|
||||
this.Write(this.ServerExchangeValue);
|
||||
this.Write(this.SharedKey);
|
||||
}
|
||||
}
|
||||
|
||||
private class _SessionKeyGeneration : SshData
|
||||
{
|
||||
public BigInteger SharedKey { get; set; }
|
||||
public IEnumerable<byte> ExchangeHash { get; set; }
|
||||
public char Char { get; set; }
|
||||
public IEnumerable<byte> SessionId { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
this.Write(this.SharedKey);
|
||||
this.Write(this.ExchangeHash);
|
||||
this.Write((byte)this.Char);
|
||||
this.Write(this.SessionId);
|
||||
}
|
||||
}
|
||||
|
||||
private class _SessionKeyAdjustment : SshData
|
||||
{
|
||||
public BigInteger SharedKey { get; set; }
|
||||
public IEnumerable<byte> ExcahngeHash { get; set; }
|
||||
public IEnumerable<byte> Key { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
this.Write(this.SharedKey);
|
||||
this.Write(this.ExcahngeHash);
|
||||
this.Write(this.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using System;
|
||||
|
||||
namespace Renci.SshClient.Algorithms
|
||||
{
|
||||
internal class KeyExchangeCompletedEventArgs : EventArgs
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Security.Cryptography;
|
||||
using Renci.SshClient.Common;
|
||||
using Renci.SshClient.Messages;
|
||||
using Renci.SshClient.Messages.Transport;
|
||||
|
||||
namespace Renci.SshClient.Algorithms
|
||||
{
|
||||
internal class KeyExchangeDiffieHellman : KeyExchange
|
||||
{
|
||||
private static RNGCryptoServiceProvider _randomizer = new System.Security.Cryptography.RNGCryptoServiceProvider();
|
||||
|
||||
private static BigInteger _prime = new BigInteger(new byte[] { (byte)0x00,
|
||||
(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,
|
||||
(byte)0xC9,(byte)0x0F,(byte)0xDA,(byte)0xA2,(byte)0x21,(byte)0x68,(byte)0xC2,(byte)0x34,
|
||||
(byte)0xC4,(byte)0xC6,(byte)0x62,(byte)0x8B,(byte)0x80,(byte)0xDC,(byte)0x1C,(byte)0xD1,
|
||||
(byte)0x29,(byte)0x02,(byte)0x4E,(byte)0x08,(byte)0x8A,(byte)0x67,(byte)0xCC,(byte)0x74,
|
||||
(byte)0x02,(byte)0x0B,(byte)0xBE,(byte)0xA6,(byte)0x3B,(byte)0x13,(byte)0x9B,(byte)0x22,
|
||||
(byte)0x51,(byte)0x4A,(byte)0x08,(byte)0x79,(byte)0x8E,(byte)0x34,(byte)0x04,(byte)0xDD,
|
||||
(byte)0xEF,(byte)0x95,(byte)0x19,(byte)0xB3,(byte)0xCD,(byte)0x3A,(byte)0x43,(byte)0x1B,
|
||||
(byte)0x30,(byte)0x2B,(byte)0x0A,(byte)0x6D,(byte)0xF2,(byte)0x5F,(byte)0x14,(byte)0x37,
|
||||
(byte)0x4F,(byte)0xE1,(byte)0x35,(byte)0x6D,(byte)0x6D,(byte)0x51,(byte)0xC2,(byte)0x45,
|
||||
(byte)0xE4,(byte)0x85,(byte)0xB5,(byte)0x76,(byte)0x62,(byte)0x5E,(byte)0x7E,(byte)0xC6,
|
||||
(byte)0xF4,(byte)0x4C,(byte)0x42,(byte)0xE9,(byte)0xA6,(byte)0x37,(byte)0xED,(byte)0x6B,
|
||||
(byte)0x0B,(byte)0xFF,(byte)0x5C,(byte)0xB6,(byte)0xF4,(byte)0x06,(byte)0xB7,(byte)0xED,
|
||||
(byte)0xEE,(byte)0x38,(byte)0x6B,(byte)0xFB,(byte)0x5A,(byte)0x89,(byte)0x9F,(byte)0xA5,
|
||||
(byte)0xAE,(byte)0x9F,(byte)0x24,(byte)0x11,(byte)0x7C,(byte)0x4B,(byte)0x1F,(byte)0xE6,
|
||||
(byte)0x49,(byte)0x28,(byte)0x66,(byte)0x51,(byte)0xEC,(byte)0xE6,(byte)0x53,(byte)0x81,
|
||||
(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF}.Reverse().ToArray());
|
||||
|
||||
private static BigInteger _group = new BigInteger(new byte[] { 2 });
|
||||
|
||||
private BigInteger _randomValue;
|
||||
|
||||
public override string Name
|
||||
{
|
||||
get { return "diffie-hellman-group1-sha1"; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="KeyExchangeDiffieHellman"/> class.
|
||||
/// </summary>
|
||||
/// <param name="sessionInfo">The session information.</param>
|
||||
internal KeyExchangeDiffieHellman(SessionInfo sessionInfo)
|
||||
: base(sessionInfo)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Start(KeyExchangeInitMessage message)
|
||||
{
|
||||
base.Start(message);
|
||||
|
||||
// TODO: Calculate random value correctly, enforce limits
|
||||
var clientExchangeValue = BigInteger.Zero;
|
||||
while (clientExchangeValue < 1 || clientExchangeValue > ((KeyExchangeDiffieHellman._prime - 1) / 2))
|
||||
{
|
||||
this._randomValue = new BigInteger(new Random().NextDouble() * long.MaxValue);
|
||||
clientExchangeValue = System.Numerics.BigInteger.ModPow(KeyExchangeDiffieHellman._group, this._randomValue, KeyExchangeDiffieHellman._prime);
|
||||
}
|
||||
|
||||
this.ServerPayload = message.GetBytes().GetSshString();
|
||||
|
||||
this.ClientExchangeValue = clientExchangeValue;
|
||||
|
||||
// Register expected message replies
|
||||
Message.RegisterMessageType<KeyExchangeDhReplyMessage>(MessageTypes.KeyExchangeDhReply);
|
||||
|
||||
this.SendMessage(new KeyExchangeDhInitMessage
|
||||
{
|
||||
E = this.ClientExchangeValue,
|
||||
});
|
||||
|
||||
this.SessionInfo.MessageReceived += SessionInfo_MessageReceived;
|
||||
|
||||
}
|
||||
|
||||
public override void Finish()
|
||||
{
|
||||
base.Finish();
|
||||
|
||||
this.SessionInfo.MessageReceived -= SessionInfo_MessageReceived;
|
||||
}
|
||||
|
||||
private void SessionInfo_MessageReceived(object sender, MessageReceivedEventArgs e)
|
||||
{
|
||||
this.HandleMessage((dynamic)e.Message);
|
||||
}
|
||||
|
||||
private void HandleMessage<T>(T message) where T : Message, new()
|
||||
{
|
||||
// Do nothing, handle only known messages
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the KeyExchangeDhReplyMessage message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
private void HandleMessage(KeyExchangeDhReplyMessage message)
|
||||
{
|
||||
// Unregister message once received
|
||||
Message.UnRegisterMessageType(MessageTypes.KeyExchangeDhReply);
|
||||
|
||||
var sharedKey = System.Numerics.BigInteger.ModPow(message.F, this._randomValue, KeyExchangeDiffieHellman._prime);
|
||||
|
||||
this.ServerExchangeValue = message.F;
|
||||
this.HostKey = message.HostKey;
|
||||
this.SharedKey = sharedKey;
|
||||
this.Signature = message.Signature;
|
||||
|
||||
// Validate hash value
|
||||
if (this.ValidateExchangeHash())
|
||||
{
|
||||
this.IsSuccessed = true;
|
||||
this.SendMessage(new NewKeysMessage());
|
||||
}
|
||||
else
|
||||
{
|
||||
this.IsSuccessed = false;
|
||||
this.RaiseFailed("Key negotiationed failed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace Renci.SshClient.Algorithms
|
||||
{
|
||||
internal class KeyExchangeFailedEventArgs : EventArgs
|
||||
{
|
||||
public string Message { get; private set; }
|
||||
|
||||
public KeyExchangeFailedEventArgs(string message)
|
||||
{
|
||||
this.Message = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using Renci.SshClient.Messages;
|
||||
|
||||
namespace Renci.SshClient.Algorithms
|
||||
{
|
||||
internal class KeyExchangeSendMessageEventArgs : EventArgs
|
||||
{
|
||||
public KeyExchangeSendMessageEventArgs(Message message)
|
||||
{
|
||||
this.Message = message;
|
||||
}
|
||||
|
||||
public Message Message { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Collections.Generic;
|
||||
namespace Renci.SshClient.Algorithms
|
||||
{
|
||||
internal abstract class Signature : Algorithm
|
||||
{
|
||||
protected IEnumerable<byte> Data { get; private set; }
|
||||
|
||||
public Signature(IEnumerable<byte> data)
|
||||
{
|
||||
this.Data = data;
|
||||
}
|
||||
|
||||
public abstract bool ValidateSignature(IEnumerable<byte> hash, IEnumerable<byte> signature);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Renci.SshClient.Algorithms
|
||||
{
|
||||
internal class SignatureDss : Signature
|
||||
{
|
||||
public override string Name
|
||||
{
|
||||
get { return "ssh-dss"; }
|
||||
}
|
||||
|
||||
public SignatureDss(IEnumerable<byte> data)
|
||||
: base(data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override bool ValidateSignature(IEnumerable<byte> hash, IEnumerable<byte> signature)
|
||||
{
|
||||
var pLength = BitConverter.ToUInt32(this.Data.Take(4).Reverse().ToArray(), 0);
|
||||
|
||||
var pData = this.Data.Skip(4).Take((int)pLength).ToArray();
|
||||
|
||||
var qLength = BitConverter.ToUInt32(this.Data.Skip(4 + (int)pLength).Take(4).Reverse().ToArray(), 0);
|
||||
|
||||
var qData = this.Data.Skip(4 + (int)pLength + 4).Take((int)qLength).ToArray();
|
||||
|
||||
var gLength = BitConverter.ToUInt32(this.Data.Skip(4 + (int)pLength + 4 + (int)qLength).Take(4).Reverse().ToArray(), 0);
|
||||
|
||||
var gData = this.Data.Skip(4 + (int)pLength + 4 + (int)qLength + 4).Take((int)gLength).ToArray();
|
||||
|
||||
var xLength = BitConverter.ToUInt32(this.Data.Skip(4 + (int)pLength + 4 + (int)qLength + 4 + (int)gLength).Take(4).Reverse().ToArray(), 0);
|
||||
|
||||
var xData = this.Data.Skip(4 + (int)pLength + 4 + (int)qLength + 4 + (int)xLength + 4).Take((int)xLength).ToArray();
|
||||
|
||||
using (var sha1 = new SHA1CryptoServiceProvider())
|
||||
{
|
||||
using (var cs = new CryptoStream(System.IO.Stream.Null, sha1, CryptoStreamMode.Write))
|
||||
{
|
||||
var data = hash.ToArray();
|
||||
cs.Write(data, 0, data.Length);
|
||||
cs.Close();
|
||||
}
|
||||
|
||||
using (var dsa = new DSACryptoServiceProvider())
|
||||
{
|
||||
dsa.ImportParameters(new DSAParameters
|
||||
{
|
||||
X = xData.TrimLeadinZero().ToArray(),
|
||||
P = pData.TrimLeadinZero().ToArray(),
|
||||
Q = qData.TrimLeadinZero().ToArray(),
|
||||
G = gData.TrimLeadinZero().ToArray(),
|
||||
});
|
||||
var dsaDeformatter = new DSASignatureDeformatter(dsa);
|
||||
dsaDeformatter.SetHashAlgorithm("SHA1");
|
||||
|
||||
long i = 0;
|
||||
long j = 0;
|
||||
byte[] tmp;
|
||||
|
||||
var sig = signature.ToArray();
|
||||
if (sig[0] == 0 && sig[1] == 0 && sig[2] == 0)
|
||||
{
|
||||
long i1 = (sig[i++] << 24) & 0xff000000;
|
||||
long i2 = (sig[i++] << 16) & 0x00ff0000;
|
||||
long i3 = (sig[i++] << 8) & 0x0000ff00;
|
||||
long i4 = (sig[i++]) & 0x000000ff;
|
||||
j = i1 | i2 | i3 | i4;
|
||||
|
||||
i += j;
|
||||
|
||||
i1 = (sig[i++] << 24) & 0xff000000;
|
||||
i2 = (sig[i++] << 16) & 0x00ff0000;
|
||||
i3 = (sig[i++] << 8) & 0x0000ff00;
|
||||
i4 = (sig[i++]) & 0x000000ff;
|
||||
j = i1 | i2 | i3 | i4;
|
||||
|
||||
tmp = new byte[j];
|
||||
Array.Copy(sig, i, tmp, 0, j);
|
||||
sig = tmp;
|
||||
}
|
||||
|
||||
return dsaDeformatter.VerifySignature(sha1, sig);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Renci.SshClient.Algorithms
|
||||
{
|
||||
internal class SignatureRsa : Signature
|
||||
{
|
||||
public override string Name
|
||||
{
|
||||
get { return "ssh-rsa"; }
|
||||
}
|
||||
|
||||
public SignatureRsa(IEnumerable<byte> data)
|
||||
: base(data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override bool ValidateSignature(IEnumerable<byte> hash, IEnumerable<byte> signature)
|
||||
{
|
||||
var exponentLength = BitConverter.ToUInt32(this.Data.Take(4).Reverse().ToArray(), 0);
|
||||
|
||||
var exponentData = this.Data.Skip(4).Take((int)exponentLength).ToArray();
|
||||
|
||||
var modulusLength = BitConverter.ToUInt32(this.Data.Skip(4 + (int)exponentLength).Take(4).Reverse().ToArray(), 0);
|
||||
|
||||
var modulusData = this.Data.Skip(4 + (int)exponentLength + 4).Take((int)modulusLength).ToArray();
|
||||
|
||||
using (var sha1 = new SHA1CryptoServiceProvider())
|
||||
{
|
||||
using (var cs = new CryptoStream(System.IO.Stream.Null, sha1, CryptoStreamMode.Write))
|
||||
{
|
||||
var data = hash.ToArray();
|
||||
cs.Write(data, 0, data.Length);
|
||||
cs.Close();
|
||||
}
|
||||
|
||||
using (var rsa = new RSACryptoServiceProvider())
|
||||
{
|
||||
rsa.ImportParameters(new RSAParameters
|
||||
{
|
||||
Exponent = exponentData,
|
||||
Modulus = modulusData.TrimLeadinZero().ToArray(),
|
||||
});
|
||||
var rsaDeformatter = new RSAPKCS1SignatureDeformatter(rsa);
|
||||
rsaDeformatter.SetHashAlgorithm("SHA1");
|
||||
|
||||
long i = 0;
|
||||
long j = 0;
|
||||
byte[] tmp;
|
||||
|
||||
var sig = signature.ToArray();
|
||||
if (sig[0] == 0 && sig[1] == 0 && sig[2] == 0)
|
||||
{
|
||||
long i1 = (sig[i++] << 24) & 0xff000000;
|
||||
long i2 = (sig[i++] << 16) & 0x00ff0000;
|
||||
long i3 = (sig[i++] << 8) & 0x0000ff00;
|
||||
long i4 = (sig[i++]) & 0x000000ff;
|
||||
j = i1 | i2 | i3 | i4;
|
||||
|
||||
i += j;
|
||||
|
||||
i1 = (sig[i++] << 24) & 0xff000000;
|
||||
i2 = (sig[i++] << 16) & 0x00ff0000;
|
||||
i3 = (sig[i++] << 8) & 0x0000ff00;
|
||||
i4 = (sig[i++]) & 0x000000ff;
|
||||
j = i1 | i2 | i3 | i4;
|
||||
|
||||
tmp = new byte[j];
|
||||
Array.Copy(sig, i, tmp, 0, j);
|
||||
sig = tmp;
|
||||
}
|
||||
|
||||
return rsaDeformatter.VerifySignature(sha1, sig);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Renci.SshClient.Common;
|
||||
using Renci.SshClient.Messages;
|
||||
using Renci.SshClient.Messages.Connection;
|
||||
namespace Renci.SshClient.Channels
|
||||
{
|
||||
internal abstract class Channel
|
||||
{
|
||||
private static uint _channelCounter = 0;
|
||||
|
||||
private static object _lock = new object();
|
||||
|
||||
private EventWaitHandle _channelOpenWaitHandle = new AutoResetEvent(false);
|
||||
|
||||
private EventWaitHandle _channelClosedWaitHandle = new AutoResetEvent(false);
|
||||
|
||||
private uint _initialWindowSize = 0x100000;
|
||||
|
||||
//private uint _maximumPacketSize = 0x4000;
|
||||
private uint _maximumPacketSize = 1024;
|
||||
|
||||
protected StringBuilder ChannelData { get; private set; }
|
||||
|
||||
protected StringBuilder ChannelExtendedData { get; private set; }
|
||||
|
||||
public abstract ChannelTypes ChannelType { get; }
|
||||
|
||||
public uint ClientChannelNumber { get; set; }
|
||||
|
||||
public uint ServerChannelNumber { get; set; }
|
||||
|
||||
public uint WindowSize { get; set; }
|
||||
|
||||
public uint PacketSize { get; set; }
|
||||
|
||||
public bool IsOpen { get; protected set; }
|
||||
|
||||
protected SessionInfo SessionInfo { get; private set; }
|
||||
|
||||
public Channel(SessionInfo sessionInfo, uint windowSize, uint packetSize)
|
||||
{
|
||||
this._initialWindowSize = windowSize;
|
||||
this._maximumPacketSize = Math.Max(packetSize, 0x8000); // Ensure minimum maximum packet size of 0x8000 bytes
|
||||
|
||||
Message.RegisterMessageType<ChannelOpenConfirmationMessage>(MessageTypes.ChannelOpenConfirmation);
|
||||
Message.RegisterMessageType<ChannelOpenFailureMessage>(MessageTypes.ChannelOpenFailure);
|
||||
Message.RegisterMessageType<ChannelWindowAdjustMessage>(MessageTypes.ChannelWindowAdjust);
|
||||
Message.RegisterMessageType<ChannelExtendedDataMessage>(MessageTypes.ChannelExtendedData);
|
||||
Message.RegisterMessageType<ChannelRequestMessage>(MessageTypes.ChannelRequest);
|
||||
Message.RegisterMessageType<ChannelSuccessMessage>(MessageTypes.ChannelSuccess);
|
||||
Message.RegisterMessageType<ChannelDataMessage>(MessageTypes.ChannelData);
|
||||
Message.RegisterMessageType<ChannelEofMessage>(MessageTypes.ChannelEof);
|
||||
Message.RegisterMessageType<ChannelCloseMessage>(MessageTypes.ChannelClose);
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
// TODO: Refactor to make channel number to come from the session, to avoid situation where new session will be open and first channel number will not be 0
|
||||
this.ClientChannelNumber = _channelCounter++;
|
||||
}
|
||||
|
||||
this.SessionInfo = sessionInfo;
|
||||
this.ChannelData = new StringBuilder((int)this._initialWindowSize);
|
||||
this.ChannelExtendedData = new StringBuilder((int)this._initialWindowSize);
|
||||
this.WindowSize = this._initialWindowSize; // Initial window size
|
||||
this.PacketSize = this._maximumPacketSize; // Maximum packet size
|
||||
}
|
||||
|
||||
public Channel(SessionInfo sessionInfo)
|
||||
: this(sessionInfo, 0x100000, 0x8000)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void Open()
|
||||
{
|
||||
this.SessionInfo.MessageReceived += SessionInfo_MessageReceived;
|
||||
|
||||
// Open session channel
|
||||
if (!this.IsOpen)
|
||||
{
|
||||
this.SendMessage(new ChannelOpenMessage
|
||||
{
|
||||
ChannelName = "session",
|
||||
ChannelNumber = this.ClientChannelNumber,
|
||||
InitialWindowSize = this.WindowSize,
|
||||
MaximumPacketSize = this.PacketSize,
|
||||
});
|
||||
|
||||
this.SessionInfo.WaitHandle(this._channelOpenWaitHandle);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Close()
|
||||
{
|
||||
if (this.IsOpen)
|
||||
{
|
||||
this.SendMessage(new ChannelCloseMessage
|
||||
{
|
||||
ChannelNumber = this.ServerChannelNumber,
|
||||
});
|
||||
|
||||
// Wait for channel to be closed
|
||||
this.SessionInfo.WaitHandle(this._channelClosedWaitHandle);
|
||||
}
|
||||
|
||||
this.CloseCleanup();
|
||||
}
|
||||
|
||||
protected virtual void OnChannelData(string data)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnChannelExtendedData(string data, uint dataTypeCode)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnChannelSuccess()
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnChannelEof()
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnChannelClose()
|
||||
{
|
||||
}
|
||||
|
||||
protected void SendMessage(Message message)
|
||||
{
|
||||
this.SessionInfo.SendMessage(message);
|
||||
}
|
||||
|
||||
private void SessionInfo_MessageReceived(object sender, MessageReceivedEventArgs e)
|
||||
{
|
||||
ChannelMessage message = e.Message as ChannelMessage;
|
||||
|
||||
// Handle only messages belong to this channel or channel open confirmation
|
||||
if (message.ChannelNumber == this.ClientChannelNumber || e.Message is ChannelOpenConfirmationMessage)
|
||||
{
|
||||
this.HandleMessage((dynamic)e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
#region Message handlers
|
||||
|
||||
private void HandleMessage<T>(T message) where T : Message
|
||||
{
|
||||
throw new NotSupportedException(string.Format("Message type '{0}' is not supported.", message.MessageType));
|
||||
}
|
||||
|
||||
private void HandleMessage(ChannelOpenConfirmationMessage message)
|
||||
{
|
||||
// Make sure we open channel only for requested channel number
|
||||
if (this.ClientChannelNumber != message.ChannelNumber)
|
||||
return;
|
||||
|
||||
this.ServerChannelNumber = message.ServerChannelNumber;
|
||||
this.IsOpen = true;
|
||||
this.WindowSize = message.InitialWindowSize;
|
||||
this.PacketSize = message.MaximumPacketSize;
|
||||
this._channelOpenWaitHandle.Set();
|
||||
}
|
||||
|
||||
private void HandleMessage(ChannelOpenFailureMessage message)
|
||||
{
|
||||
this.IsOpen = false;
|
||||
this._channelOpenWaitHandle.Set();
|
||||
}
|
||||
|
||||
private void HandleMessage(ChannelWindowAdjustMessage message)
|
||||
{
|
||||
this.WindowSize += message.BytesToAdd;
|
||||
}
|
||||
|
||||
private void HandleMessage(ChannelDataMessage message)
|
||||
{
|
||||
this.AdjustDataWindow(message.Data);
|
||||
this.OnChannelData(message.Data);
|
||||
}
|
||||
|
||||
private void HandleMessage(ChannelExtendedDataMessage message)
|
||||
{
|
||||
this.AdjustDataWindow(message.Data);
|
||||
this.OnChannelExtendedData(message.Data, message.DataTypeCode);
|
||||
}
|
||||
|
||||
private void HandleMessage(ChannelRequestMessage message)
|
||||
{
|
||||
Message replyMessage = new ChannelFailureMessage()
|
||||
{
|
||||
ChannelNumber = message.ChannelNumber,
|
||||
};
|
||||
|
||||
if (message.RequestName == RequestNames.ExitStatus)
|
||||
{
|
||||
var exitStatus = message.ExitStatus;
|
||||
replyMessage = new ChannelSuccessMessage()
|
||||
{
|
||||
ChannelNumber = message.ChannelNumber,
|
||||
};
|
||||
}
|
||||
|
||||
if (message.WantReply)
|
||||
{
|
||||
this.SendMessage(replyMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleMessage(ChannelSuccessMessage message)
|
||||
{
|
||||
this.OnChannelSuccess();
|
||||
}
|
||||
|
||||
private void HandleMessage(ChannelEofMessage message)
|
||||
{
|
||||
this.OnChannelEof();
|
||||
}
|
||||
|
||||
private void HandleMessage(ChannelCloseMessage message)
|
||||
{
|
||||
// TODO: Handle this message
|
||||
this.CloseCleanup();
|
||||
|
||||
this._channelClosedWaitHandle.Set();
|
||||
}
|
||||
|
||||
private void AdjustDataWindow(string messageData)
|
||||
{
|
||||
this.WindowSize -= (uint)messageData.Length;
|
||||
|
||||
// Adjust window if window size is too low
|
||||
if (this.WindowSize < this._initialWindowSize / 2)
|
||||
{
|
||||
this.SendMessage(new ChannelWindowAdjustMessage
|
||||
{
|
||||
ChannelNumber = this.ServerChannelNumber,
|
||||
BytesToAdd = this._initialWindowSize - this.WindowSize,
|
||||
});
|
||||
this.WindowSize = this._initialWindowSize;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
private void CloseCleanup()
|
||||
{
|
||||
|
||||
this.IsOpen = false;
|
||||
|
||||
this.SessionInfo.MessageReceived -= SessionInfo_MessageReceived;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Renci.SshClient.Messages.Connection;
|
||||
|
||||
namespace Renci.SshClient.Channels
|
||||
{
|
||||
internal class ChannelSession : Channel
|
||||
{
|
||||
private EventWaitHandle _channelEofWaitHandle = new AutoResetEvent(false);
|
||||
private StringBuilder _response = new StringBuilder();
|
||||
|
||||
public override ChannelTypes ChannelType
|
||||
{
|
||||
get { return ChannelTypes.Session; }
|
||||
}
|
||||
|
||||
public ChannelSession(SessionInfo sessionInfo)
|
||||
: base(sessionInfo, 0x100000, 0x1000)
|
||||
{
|
||||
}
|
||||
|
||||
internal string Execute(string command)
|
||||
{
|
||||
this.Open();
|
||||
|
||||
// Send channel command request
|
||||
this.SendMessage(new ChannelRequestMessage
|
||||
{
|
||||
ChannelNumber = this.ServerChannelNumber,
|
||||
RequestName = RequestNames.Exec,
|
||||
WantReply = false,
|
||||
Command = command,
|
||||
});
|
||||
|
||||
|
||||
this.SessionInfo.WaitHandle(this._channelEofWaitHandle);
|
||||
|
||||
this.Close();
|
||||
|
||||
|
||||
return this._response.ToString();
|
||||
}
|
||||
|
||||
protected override void OnChannelEof()
|
||||
{
|
||||
base.OnChannelEof();
|
||||
|
||||
// TODO: All wait handles add timeout and then throw an exception or monitor connection closed event
|
||||
this._channelEofWaitHandle.Set();
|
||||
}
|
||||
|
||||
protected override void OnChannelData(string data)
|
||||
{
|
||||
base.OnChannelData(data);
|
||||
|
||||
this._response.Append(data);
|
||||
}
|
||||
|
||||
private void Init()
|
||||
{
|
||||
this.ChannelData.Length = 0;
|
||||
this.ChannelExtendedData.Length = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,547 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Renci.SshClient.Common;
|
||||
using Renci.SshClient.Messages.Connection;
|
||||
using Renci.SshClient.Messages.Sftp;
|
||||
|
||||
namespace Renci.SshClient.Channels
|
||||
{
|
||||
internal class ChannelSftp : Channel
|
||||
{
|
||||
private EventWaitHandle _channelRequestSuccessWaitHandle = new AutoResetEvent(false);
|
||||
|
||||
private EventWaitHandle _testWaitHandle = new AutoResetEvent(false);
|
||||
|
||||
private EventWaitHandle _responseMessageReceivedWaitHandle = new EventWaitHandle(false, EventResetMode.ManualReset);
|
||||
|
||||
private uint _requestId;
|
||||
|
||||
private SftpMessage _responseMessage;
|
||||
|
||||
private string _remoteCurrentDir;
|
||||
|
||||
private string _localCurentDir;
|
||||
|
||||
private StringBuilder _packetData;
|
||||
|
||||
public override ChannelTypes ChannelType
|
||||
{
|
||||
get { return ChannelTypes.Session; }
|
||||
}
|
||||
|
||||
public ChannelSftp(SessionInfo sessionInfo, uint windowSize, uint packetSize)
|
||||
: base(sessionInfo, windowSize, packetSize)
|
||||
{
|
||||
}
|
||||
|
||||
public ChannelSftp(SessionInfo sessionInfo)
|
||||
: base(sessionInfo, 0x100000, 0x4000)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
// Send channel command request
|
||||
this.SendMessage(new ChannelRequestMessage
|
||||
{
|
||||
ChannelNumber = this.ServerChannelNumber,
|
||||
RequestName = RequestNames.Subsystem,
|
||||
WantReply = true,
|
||||
SubsystemName = "sftp",
|
||||
});
|
||||
|
||||
this.SessionInfo.WaitHandle(this._channelRequestSuccessWaitHandle);
|
||||
|
||||
this.SendMessage(new InitMessage
|
||||
{
|
||||
Version = 6,
|
||||
});
|
||||
|
||||
var versionMessage = this.ReceiveMessage<VersionMessage>();
|
||||
|
||||
if (versionMessage == null)
|
||||
{
|
||||
throw new InvalidOperationException("Version message expected.");
|
||||
}
|
||||
|
||||
if (versionMessage.Version != 3)
|
||||
{
|
||||
throw new NotSupportedException(string.Format("Server SFTP version {0} is not supported.", versionMessage.Version));
|
||||
}
|
||||
|
||||
// Get default current directories
|
||||
var files = this.GetRealPath(".");
|
||||
|
||||
this._remoteCurrentDir = files.First().Name;
|
||||
this._localCurentDir = Directory.GetCurrentDirectory();
|
||||
}
|
||||
|
||||
public void UploadFile(Stream source, string destination)
|
||||
{
|
||||
this.Open();
|
||||
|
||||
string handle = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
handle = this.OpenRemoteFile(destination, Flags.Write | Flags.CreateNewOrOpen | Flags.Truncate);
|
||||
|
||||
var buffer = new byte[1024];
|
||||
ulong offset = 0;
|
||||
while (source.Read(buffer, 0, buffer.Length) > 0)
|
||||
{
|
||||
this.RemoteWrite(handle, offset, buffer.GetSshString());
|
||||
offset += (ulong)buffer.Length;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!string.IsNullOrEmpty(handle))
|
||||
this.CloseRemoteHandle(handle);
|
||||
}
|
||||
|
||||
this.Close();
|
||||
}
|
||||
|
||||
internal void DownloadFile(string fileName, Stream destination)
|
||||
{
|
||||
this.Open();
|
||||
|
||||
string handle = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
handle = this.OpenRemoteFile(fileName, Flags.Read);
|
||||
|
||||
ulong offset = 0;
|
||||
uint bufferSize = 1024;
|
||||
string data;
|
||||
|
||||
while ((data = this.RemoteRead(handle, offset, bufferSize)) != null)
|
||||
{
|
||||
var fileData = data.GetSshBytes().ToArray();
|
||||
destination.Write(fileData, 0, (int)bufferSize);
|
||||
destination.Flush();
|
||||
offset += (ulong)fileData.Length;
|
||||
}
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!string.IsNullOrEmpty(handle))
|
||||
this.CloseRemoteHandle(handle);
|
||||
}
|
||||
|
||||
this.Close();
|
||||
}
|
||||
|
||||
public void CreateDirectory(string directoryName)
|
||||
{
|
||||
this.Open();
|
||||
|
||||
this.CreateRemoteDirectory(directoryName);
|
||||
|
||||
this.Close();
|
||||
}
|
||||
|
||||
public void RemoveDirectory(string directoryName)
|
||||
{
|
||||
this.Open();
|
||||
|
||||
this.RemoveRemoteDirectory(directoryName);
|
||||
|
||||
this.Close();
|
||||
}
|
||||
|
||||
public void RemoveFile(string fileName)
|
||||
{
|
||||
this.Open();
|
||||
|
||||
this.RemoveRemoteFile(fileName);
|
||||
|
||||
this.Close();
|
||||
}
|
||||
|
||||
public void RenameFile(string oldFileName, string newFileName)
|
||||
{
|
||||
this.Open();
|
||||
|
||||
this.RenameRemoteFile(oldFileName, newFileName);
|
||||
|
||||
this.Close();
|
||||
}
|
||||
|
||||
public IEnumerable<FtpFileInfo> ListDirectory(string path)
|
||||
{
|
||||
// Open channel
|
||||
this.Open();
|
||||
|
||||
string handle = string.Empty;
|
||||
IEnumerable<FtpFileInfo> files = null;
|
||||
|
||||
try
|
||||
{
|
||||
// Open directory
|
||||
handle = this.OpenRemoteDirectory(path);
|
||||
|
||||
// Read directory data
|
||||
files = this.ReadRemoteDirectory(handle);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Close directory
|
||||
if (!string.IsNullOrEmpty(handle))
|
||||
this.CloseRemoteHandle(handle);
|
||||
}
|
||||
|
||||
// Read directory
|
||||
this.Close();
|
||||
|
||||
return files;
|
||||
|
||||
}
|
||||
|
||||
protected override void OnChannelSuccess()
|
||||
{
|
||||
base.OnChannelSuccess();
|
||||
|
||||
this._channelRequestSuccessWaitHandle.Set();
|
||||
}
|
||||
|
||||
protected override void OnChannelData(string data)
|
||||
{
|
||||
base.OnChannelData(data);
|
||||
|
||||
if (this._packetData == null)
|
||||
{
|
||||
var packetLength = BitConverter.ToUInt32(data.GetSshBytes().Take(4).Reverse().ToArray(), 0);
|
||||
this._packetData = new StringBuilder((int)packetLength, (int)packetLength);
|
||||
this._packetData.Append(data.GetSshBytes().Skip(4).GetSshString());
|
||||
}
|
||||
else
|
||||
{
|
||||
this._packetData.Append(data);
|
||||
}
|
||||
|
||||
|
||||
if (this._packetData.Length < this._packetData.MaxCapacity)
|
||||
{
|
||||
// Wait for more packet data
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
dynamic sftpMessage = SftpMessage.Load(this._packetData.ToString().GetSshBytes());
|
||||
|
||||
this._packetData = null;
|
||||
|
||||
// TODO: Handle SSH_FXP_STATUS here
|
||||
// TODO: Validate message request id is correct
|
||||
|
||||
this._responseMessage = sftpMessage;
|
||||
|
||||
this._responseMessageReceivedWaitHandle.Set();
|
||||
}
|
||||
|
||||
private T ReceiveMessage<T>() where T : SftpMessage
|
||||
{
|
||||
var message = this.ReceiveMessage() as T;
|
||||
|
||||
if (message == null)
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("Message of type '{0}' expected in this context.", typeof(T).Name));
|
||||
}
|
||||
return message;
|
||||
|
||||
}
|
||||
|
||||
private SftpMessage ReceiveMessage()
|
||||
{
|
||||
this.SessionInfo.WaitHandle(this._responseMessageReceivedWaitHandle);
|
||||
|
||||
var statusMessage = this._responseMessage as StatusMessage;
|
||||
|
||||
if (statusMessage != null)
|
||||
{
|
||||
// Handle error status messages
|
||||
switch (statusMessage.StatusCode)
|
||||
{
|
||||
case StatusCodes.Ok:
|
||||
break;
|
||||
case StatusCodes.Eof:
|
||||
break;
|
||||
case StatusCodes.NoSuchFile:
|
||||
throw new FileNotFoundException("File or directory not found on the remote server.");
|
||||
case StatusCodes.PermissionDenied:
|
||||
throw new NotImplementedException();
|
||||
case StatusCodes.Failure:
|
||||
throw new InvalidOperationException("Operation failed.");
|
||||
case StatusCodes.BadMessage:
|
||||
throw new NotImplementedException();
|
||||
case StatusCodes.NoConnection:
|
||||
throw new NotImplementedException();
|
||||
case StatusCodes.ConnectionLost:
|
||||
throw new NotImplementedException();
|
||||
case StatusCodes.OperationUnsupported:
|
||||
throw new NotSupportedException("Operation is not supported.");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return this._responseMessage;
|
||||
}
|
||||
|
||||
private void SendMessage(SftpMessage sftpMessage)
|
||||
{
|
||||
sftpMessage.RequestId = this._requestId++;
|
||||
var message = new SftpDataMessage
|
||||
{
|
||||
ChannelNumber = this.ServerChannelNumber,
|
||||
Data = sftpMessage,
|
||||
};
|
||||
|
||||
this.SendMessage(message);
|
||||
|
||||
this._responseMessageReceivedWaitHandle.Reset();
|
||||
}
|
||||
|
||||
private string OpenRemoteFile(string fileName, Flags flags)
|
||||
{
|
||||
this.SendMessage(new OpenMessage
|
||||
{
|
||||
Filename = fileName,
|
||||
Flags = flags,
|
||||
});
|
||||
|
||||
var handleMessage = this.ReceiveMessage<HandleMessage>();
|
||||
|
||||
return handleMessage.Handle;
|
||||
}
|
||||
|
||||
private string RemoteRead(string handle, ulong offset, uint length)
|
||||
{
|
||||
this.SendMessage(new ReadMessage
|
||||
{
|
||||
Handle = handle,
|
||||
Offset = offset,
|
||||
Length = length,
|
||||
});
|
||||
|
||||
var message = this.ReceiveMessage();
|
||||
|
||||
var statusMessage = message as StatusMessage;
|
||||
var dataMessage = message as DataMessage;
|
||||
|
||||
if (statusMessage != null)
|
||||
{
|
||||
if (statusMessage.StatusCode == StatusCodes.Eof)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Invalid status code.");
|
||||
}
|
||||
else if (dataMessage != null)
|
||||
{
|
||||
return dataMessage.Data;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("Message type '{0}' is not valid in this context.", message.SftpMessageType));
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoteWrite(string handle, ulong offset, string data)
|
||||
{
|
||||
this.SendMessage(new WriteMessage
|
||||
{
|
||||
Handle = handle,
|
||||
Offset = offset,
|
||||
Data = data,
|
||||
});
|
||||
|
||||
var message = this.ReceiveMessage<StatusMessage>();
|
||||
|
||||
this.EnsureStatusCode(message, StatusCodes.Ok);
|
||||
}
|
||||
|
||||
private void RemoveRemoteFile(string fileName)
|
||||
{
|
||||
this.SendMessage(new RemoveMessage
|
||||
{
|
||||
Filename = fileName,
|
||||
});
|
||||
|
||||
var message = this.ReceiveMessage<StatusMessage>();
|
||||
|
||||
this.EnsureStatusCode(message, StatusCodes.Ok);
|
||||
}
|
||||
|
||||
private void RenameRemoteFile(string oldFileName, string newFileName)
|
||||
{
|
||||
this.SendMessage(new RenameMessage
|
||||
{
|
||||
OldPath = oldFileName,
|
||||
NewPath = newFileName,
|
||||
});
|
||||
|
||||
var message = this.ReceiveMessage<StatusMessage>();
|
||||
|
||||
this.EnsureStatusCode(message, StatusCodes.Ok);
|
||||
}
|
||||
|
||||
private void CreateRemoteDirectory(string directoryName)
|
||||
{
|
||||
this.SendMessage(new MkDirMessage
|
||||
{
|
||||
Path = directoryName,
|
||||
});
|
||||
|
||||
var message = this.ReceiveMessage<StatusMessage>();
|
||||
|
||||
this.EnsureStatusCode(message, StatusCodes.Ok);
|
||||
}
|
||||
|
||||
private void RemoveRemoteDirectory(string directoryName)
|
||||
{
|
||||
this.SendMessage(new RmDirMessage
|
||||
{
|
||||
Path = directoryName,
|
||||
});
|
||||
|
||||
var message = this.ReceiveMessage<StatusMessage>();
|
||||
|
||||
this.EnsureStatusCode(message, StatusCodes.Ok);
|
||||
}
|
||||
|
||||
private string OpenRemoteDirectory(string path)
|
||||
{
|
||||
this.SendMessage(new OpenDirMessage
|
||||
{
|
||||
Path = path,
|
||||
});
|
||||
|
||||
var handleMessage = this.ReceiveMessage<HandleMessage>();
|
||||
|
||||
return handleMessage.Handle;
|
||||
}
|
||||
|
||||
private IEnumerable<FtpFileInfo> ReadRemoteDirectory(string handle)
|
||||
{
|
||||
this.SendMessage(new ReadDirMessage
|
||||
{
|
||||
Handle = handle,
|
||||
});
|
||||
|
||||
var message = this.ReceiveMessage<NameMessage>();
|
||||
|
||||
return message.Files;
|
||||
}
|
||||
|
||||
private void CloseRemoteHandle(string handle)
|
||||
{
|
||||
this.SendMessage(new CloseMessage
|
||||
{
|
||||
Handle = handle,
|
||||
});
|
||||
|
||||
var status = this.ReceiveMessage<StatusMessage>();
|
||||
// TODO: If close is fails wait a litle a try to close it again, in case server fluashed data into the file during close
|
||||
}
|
||||
|
||||
private Attributes GetRemoteFileAttributes(string filename)
|
||||
{
|
||||
this.SendMessage(new StatMessage
|
||||
{
|
||||
Path = filename,
|
||||
});
|
||||
|
||||
var message = this.ReceiveMessage<AttrsMessage>();
|
||||
|
||||
return message.Attributes;
|
||||
}
|
||||
|
||||
private Attributes GetRemoteLinkFileAttributes(string filename)
|
||||
{
|
||||
this.SendMessage(new LStatMessage
|
||||
{
|
||||
Path = filename,
|
||||
});
|
||||
|
||||
var message = this.ReceiveMessage<AttrsMessage>();
|
||||
|
||||
return message.Attributes;
|
||||
}
|
||||
|
||||
private Attributes GetRemoteOpenFileAttributes(string handle)
|
||||
{
|
||||
this.SendMessage(new FStatMessage
|
||||
{
|
||||
Handle = handle,
|
||||
});
|
||||
|
||||
var message = this.ReceiveMessage<AttrsMessage>();
|
||||
|
||||
return message.Attributes;
|
||||
}
|
||||
|
||||
private void SetRemoteFileAttributes(string filename, Attributes attributes)
|
||||
{
|
||||
this.SendMessage(new SetStatMessage
|
||||
{
|
||||
Path = filename,
|
||||
Attributes = attributes
|
||||
});
|
||||
|
||||
var message = this.ReceiveMessage<StatusMessage>();
|
||||
|
||||
this.EnsureStatusCode(message, StatusCodes.Ok);
|
||||
}
|
||||
|
||||
private void SetRemoteOpenFileAttributes(string handle, Attributes attributes)
|
||||
{
|
||||
this.SendMessage(new FSetStatMessage
|
||||
{
|
||||
Handle = handle,
|
||||
Attributes = attributes
|
||||
});
|
||||
|
||||
var message = this.ReceiveMessage<StatusMessage>();
|
||||
|
||||
this.EnsureStatusCode(message, StatusCodes.Ok);
|
||||
}
|
||||
|
||||
private IEnumerable<FtpFileInfo> GetRealPath(string path)
|
||||
{
|
||||
this.SendMessage(new RealPathMessage
|
||||
{
|
||||
Path = path,
|
||||
});
|
||||
|
||||
var message = this.ReceiveMessage<NameMessage>();
|
||||
|
||||
return message.Files;
|
||||
|
||||
}
|
||||
|
||||
private void EnsureStatusCode(StatusMessage message, StatusCodes code)
|
||||
{
|
||||
if (message.StatusCode == code)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Invalid status code.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
|
||||
namespace Renci.SshClient.Channels
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
internal enum ChannelTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// session
|
||||
/// </summary>
|
||||
Session,
|
||||
/// <summary>
|
||||
/// x11
|
||||
/// </summary>
|
||||
X11,
|
||||
/// <summary>
|
||||
/// forwarded-tcpip
|
||||
/// </summary>
|
||||
ForwardedTcpip,
|
||||
/// <summary>
|
||||
/// direct-tcpip
|
||||
/// </summary>
|
||||
DirectTcpip,
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace Renci.SshClient.Common
|
||||
{
|
||||
internal class DataReceivedEventArgs : EventArgs
|
||||
{
|
||||
public string Data { get; private set; }
|
||||
|
||||
public DataReceivedEventArgs(string data)
|
||||
{
|
||||
this.Data = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace Renci.SshClient
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks whether a collection is the same as another collection
|
||||
/// </summary>
|
||||
/// <param name="value">The current instance object</param>
|
||||
/// <param name="compareList">The collection to compare with</param>
|
||||
/// <param name="comparer">The comparer object to use to compare each item in the collection. If null uses EqualityComparer(T).Default</param>
|
||||
/// <returns>True if the two collections contain all the same items in the same order</returns>
|
||||
public static bool IsEqualTo<TSource>(this IEnumerable<TSource> value, IEnumerable<TSource> compareList, IEqualityComparer<TSource> comparer)
|
||||
{
|
||||
if (value == compareList)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (value == null || compareList == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (comparer == null)
|
||||
{
|
||||
comparer = EqualityComparer<TSource>.Default;
|
||||
}
|
||||
|
||||
IEnumerator<TSource> enumerator1 = value.GetEnumerator();
|
||||
IEnumerator<TSource> enumerator2 = compareList.GetEnumerator();
|
||||
|
||||
bool enum1HasValue = enumerator1.MoveNext();
|
||||
bool enum2HasValue = enumerator2.MoveNext();
|
||||
|
||||
try
|
||||
{
|
||||
while (enum1HasValue && enum2HasValue)
|
||||
{
|
||||
if (!comparer.Equals(enumerator1.Current, enumerator2.Current))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
enum1HasValue = enumerator1.MoveNext();
|
||||
enum2HasValue = enumerator2.MoveNext();
|
||||
}
|
||||
|
||||
return !(enum1HasValue || enum2HasValue);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (enumerator1 != null) enumerator1.Dispose();
|
||||
if (enumerator2 != null) enumerator2.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsEqualTo<TSource>(this IEnumerable<TSource> value, IEnumerable<TSource> compareList)
|
||||
{
|
||||
return IsEqualTo(value, compareList, null);
|
||||
}
|
||||
|
||||
public static bool IsEqualTo(this IEnumerable value, IEnumerable compareList)
|
||||
{
|
||||
return IsEqualTo<object>(value.OfType<object>(), compareList.OfType<object>());
|
||||
}
|
||||
|
||||
public static void DebugPrint(this IEnumerable<byte> bytes)
|
||||
{
|
||||
foreach (var b in bytes)
|
||||
{
|
||||
Debug.Write(string.Format("0x{0:x2}, ", b));
|
||||
}
|
||||
Debug.WriteLine(string.Empty);
|
||||
}
|
||||
|
||||
public static string GetSshString(this IEnumerable<byte> data)
|
||||
{
|
||||
List<char> bytes = new List<char>();
|
||||
foreach (var b in data)
|
||||
{
|
||||
bytes.Add((char)b);
|
||||
}
|
||||
|
||||
return new string(bytes.ToArray());
|
||||
}
|
||||
|
||||
public static IEnumerable<byte> GetSshBytes(this string data)
|
||||
{
|
||||
List<byte> bytes = new List<byte>();
|
||||
foreach (var c in data.ToCharArray())
|
||||
{
|
||||
bytes.Add((byte)c);
|
||||
}
|
||||
return bytes.ToArray();
|
||||
}
|
||||
|
||||
public static IEnumerable<byte> TrimLeadinZero(this IEnumerable<byte> data)
|
||||
{
|
||||
bool leadingZero = true;
|
||||
foreach (var item in data)
|
||||
{
|
||||
if (item == 0 & leadingZero)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
leadingZero = false;
|
||||
}
|
||||
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Renci.SshClient.Common
|
||||
{
|
||||
public class FtpFileInfo
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public string FullName { get; set; }
|
||||
|
||||
public DateTime CreationTime { get; set; }
|
||||
|
||||
public DateTime LastAccessTime { get; set; }
|
||||
|
||||
public DateTime LastModifyTime { get; set; }
|
||||
|
||||
public ulong Size { get; set; }
|
||||
|
||||
public uint UserId { get; set; }
|
||||
|
||||
public uint GroupId { get; set; }
|
||||
|
||||
public uint Permissions { get; set; }
|
||||
|
||||
public IDictionary<string, string> Extentions { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using Renci.SshClient.Messages;
|
||||
|
||||
namespace Renci.SshClient.Common
|
||||
{
|
||||
internal class MessageReceivedEventArgs : EventArgs
|
||||
{
|
||||
public Message Message { get; private set; }
|
||||
|
||||
public MessageReceivedEventArgs(Message message)
|
||||
{
|
||||
this.Message = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
|
||||
namespace Renci.SshClient.Common
|
||||
{
|
||||
public abstract class SshData
|
||||
{
|
||||
/// <summary>
|
||||
/// Data byte array that hold message unencrypted data
|
||||
/// </summary>
|
||||
private IList<byte> _data;
|
||||
|
||||
private int _readerIndex;
|
||||
|
||||
public bool IsEndOfData
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._readerIndex >= this._data.Count();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IEnumerable<byte> GetBytes()
|
||||
{
|
||||
this._data = new List<byte>();
|
||||
|
||||
this.SaveData();
|
||||
|
||||
return this._data;
|
||||
}
|
||||
|
||||
protected abstract void LoadData();
|
||||
|
||||
protected abstract void SaveData();
|
||||
|
||||
protected void LoadBytes(IEnumerable<byte> bytes)
|
||||
{
|
||||
this.ResetReader();
|
||||
this._data = new List<byte>(bytes);
|
||||
}
|
||||
|
||||
protected void ResetReader()
|
||||
{
|
||||
this._readerIndex = 1; // Set to 1 to skip first byte which specifies message type
|
||||
}
|
||||
|
||||
protected IEnumerable<byte> ReadBytes(int length)
|
||||
{
|
||||
var result = this._data.Skip(this._readerIndex).Take(length);
|
||||
this._readerIndex += length;
|
||||
return result;
|
||||
}
|
||||
|
||||
protected byte ReadByte()
|
||||
{
|
||||
return this.ReadBytes(1).FirstOrDefault();
|
||||
}
|
||||
|
||||
protected bool ReadBoolean()
|
||||
{
|
||||
return this.ReadByte() == 0 ? false : true;
|
||||
}
|
||||
|
||||
protected UInt16 ReadUInt16()
|
||||
{
|
||||
return BitConverter.ToUInt16(this.ReadBytes(2).Reverse().ToArray(), 0);
|
||||
}
|
||||
|
||||
protected UInt32 ReadUInt32()
|
||||
{
|
||||
return BitConverter.ToUInt32(this.ReadBytes(4).Reverse().ToArray(), 0);
|
||||
}
|
||||
|
||||
protected UInt64 ReadUInt64()
|
||||
{
|
||||
return BitConverter.ToUInt64(this.ReadBytes(8).Reverse().ToArray(), 0);
|
||||
}
|
||||
|
||||
protected Int64 ReadInt64()
|
||||
{
|
||||
return BitConverter.ToInt64(this.ReadBytes(8).Reverse().ToArray(), 0);
|
||||
|
||||
}
|
||||
|
||||
protected string ReadString()
|
||||
{
|
||||
var length = this.ReadUInt32();
|
||||
|
||||
if (length > (UInt32)int.MaxValue)
|
||||
{
|
||||
throw new NotSupportedException(string.Format("String that longer that {0} are not supported.", int.MaxValue));
|
||||
}
|
||||
|
||||
var result = this._data.Skip(this._readerIndex).Take((int)length).GetSshString();
|
||||
this._readerIndex += (int)length;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected BigInteger ReadBigInteger()
|
||||
{
|
||||
var length = this.ReadUInt32();
|
||||
|
||||
var data = this.ReadBytes((int)length);
|
||||
|
||||
return new BigInteger(data.Reverse().ToArray());
|
||||
}
|
||||
|
||||
protected IEnumerable<string> ReadNamesList()
|
||||
{
|
||||
var namesList = this.ReadString();
|
||||
return namesList.Split(',');
|
||||
}
|
||||
|
||||
protected IDictionary<string, string> ReadExtensionPair()
|
||||
{
|
||||
Dictionary<string, string> result = new Dictionary<string, string>();
|
||||
while (this._readerIndex < this._data.Count)
|
||||
{
|
||||
var extensionName = this.ReadString();
|
||||
var extensionData = this.ReadString();
|
||||
result.Add(extensionName, extensionData);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected void Write(IEnumerable<byte> data)
|
||||
{
|
||||
foreach (var b in data)
|
||||
this.Write(b);
|
||||
}
|
||||
|
||||
protected void Write(byte data)
|
||||
{
|
||||
this._data.Add(data);
|
||||
}
|
||||
|
||||
protected void Write(bool data)
|
||||
{
|
||||
if (data)
|
||||
{
|
||||
this.Write(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Write(0);
|
||||
}
|
||||
}
|
||||
|
||||
protected void Write(UInt16 data)
|
||||
{
|
||||
this.Write(BitConverter.GetBytes(data).Reverse());
|
||||
}
|
||||
|
||||
protected void Write(UInt32 data)
|
||||
{
|
||||
this.Write(BitConverter.GetBytes(data).Reverse());
|
||||
}
|
||||
|
||||
protected void Write(UInt64 data)
|
||||
{
|
||||
this.Write(BitConverter.GetBytes(data).Reverse());
|
||||
}
|
||||
|
||||
protected void Write(Int64 data)
|
||||
{
|
||||
this.Write(BitConverter.GetBytes(data).Reverse());
|
||||
}
|
||||
|
||||
protected void Write(string data, Encoding encoding)
|
||||
{
|
||||
this.Write((uint)data.Length);
|
||||
this.Write(encoding.GetBytes(data));
|
||||
}
|
||||
|
||||
protected void Write(string data)
|
||||
{
|
||||
this.Write((uint)data.Length);
|
||||
this.Write(data.GetSshBytes());
|
||||
}
|
||||
|
||||
protected void Write(BigInteger data)
|
||||
{
|
||||
var bytes = data.ToByteArray().Reverse().ToList();
|
||||
this.Write((uint)bytes.Count);
|
||||
this.Write(bytes);
|
||||
}
|
||||
|
||||
protected void Write(IEnumerable<string> data)
|
||||
{
|
||||
this.Write(string.Join(",", data));
|
||||
}
|
||||
|
||||
protected void Write(IDictionary<string, string> data)
|
||||
{
|
||||
foreach (var item in data)
|
||||
{
|
||||
this.Write(item.Key);
|
||||
this.Write(item.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
namespace Renci.SshClient
|
||||
{
|
||||
public class Connection
|
||||
{
|
||||
private Session _session;
|
||||
|
||||
public ConnectionInfo ConnectionInfo { get; private set; }
|
||||
|
||||
private Shell _shell;
|
||||
public Shell Shell
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this._shell == null)
|
||||
{
|
||||
this._shell = new Shell(this._session);
|
||||
}
|
||||
return this._shell;
|
||||
}
|
||||
}
|
||||
|
||||
private Sftp _sftp;
|
||||
|
||||
public Sftp Sftp
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this._sftp == null)
|
||||
{
|
||||
this._sftp = new Sftp(this._session);
|
||||
}
|
||||
return this._sftp;
|
||||
}
|
||||
}
|
||||
|
||||
public Connection(ConnectionInfo connectionInfo)
|
||||
{
|
||||
this._session = Session.CreateSession(connectionInfo);
|
||||
}
|
||||
|
||||
public void Connect()
|
||||
{
|
||||
this._session.Connect();
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
this._session.Disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace Renci.SshClient
|
||||
{
|
||||
public class ConnectionInfo
|
||||
{
|
||||
public string Host { get; set; }
|
||||
|
||||
public int Port { get; set; }
|
||||
|
||||
public string Username { get; set; }
|
||||
|
||||
public string Password { get; set; }
|
||||
|
||||
public KeyFile KeyFile { get; set; }
|
||||
|
||||
public ConnectionInfo()
|
||||
{
|
||||
// Set default connection values
|
||||
this.Port = 22;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Renci.SshClient
|
||||
{
|
||||
public class KeyFile
|
||||
{
|
||||
private Regex _beginKeyLine = new Regex(@"----[ ]*BEGIN (?<keyName>.+) PRIVATE KEY[ ]*----");
|
||||
private Regex _headerLine = new Regex(@"(?<headerTag>[^:]{1,64}):[ ](?<headerValue>[^:]+(?<continue>\\)?)");
|
||||
private Regex _headerLineContinue = new Regex(@"(?<headerValue>[^:]+(?<continue>\\)?)");
|
||||
private Regex _endKeyLine = new Regex(@"----[ ]*END (?<keyName>.+) PRIVATE KEY[ ]*----");
|
||||
|
||||
private PrivateKey _key;
|
||||
|
||||
public string AlgorithmName
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._key.AlgorithmName;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<byte> PublicKey
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._key.PublicKey;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<byte> GetSignature(IEnumerable<byte> sessionId)
|
||||
{
|
||||
return this._key.GetSignature(sessionId);
|
||||
}
|
||||
|
||||
public KeyFile()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Open(string fileName)
|
||||
{
|
||||
using (var keyFile = File.OpenText(fileName))
|
||||
{
|
||||
var headerTag = string.Empty;
|
||||
var headerValue = string.Empty;
|
||||
var headerValueContinue = false;
|
||||
var data = new StringBuilder();
|
||||
var keyName = string.Empty;
|
||||
|
||||
var fileLine = string.Empty;
|
||||
while ((fileLine = keyFile.ReadLine()) != null)
|
||||
{
|
||||
var match = _beginKeyLine.Match(fileLine);
|
||||
if (match.Success)
|
||||
{
|
||||
keyName = match.Result("${keyName}");
|
||||
continue;
|
||||
}
|
||||
|
||||
match = _endKeyLine.Match(fileLine);
|
||||
if (match.Success)
|
||||
{
|
||||
var endKeyName = match.Result("${keyName}");
|
||||
if (!endKeyName.Equals(keyName))
|
||||
throw new InvalidDataException("Invalid data key file.");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// Ignore everything if BEGIN was not found yet
|
||||
if (string.IsNullOrEmpty(keyName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
match = _headerLine.Match(fileLine);
|
||||
if (match.Success)
|
||||
{
|
||||
headerTag = match.Result("${headerTag}");
|
||||
headerValue = match.Result("${headerValue}");
|
||||
if (match.Result("${continue}") == @"\")
|
||||
{
|
||||
headerValueContinue = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
headerValueContinue = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (headerValueContinue)
|
||||
{
|
||||
headerValue += fileLine;
|
||||
if (match.Result("${continue}") == @"\")
|
||||
{
|
||||
headerValueContinue = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
headerValueContinue = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
data.Append(fileLine);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(keyName))
|
||||
{
|
||||
throw new InvalidDataException("Invalid Public key file");
|
||||
}
|
||||
|
||||
switch (keyName)
|
||||
{
|
||||
case "RSA":
|
||||
this._key = new PrivateKeyRsa(System.Convert.FromBase64String(data.ToString()));
|
||||
break;
|
||||
case "DSA":
|
||||
this._key = new PrivateKeyDsa(System.Convert.FromBase64String(data.ToString()));
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException(string.Format("Key '{0}' is not supported.", keyName));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Text;
|
||||
|
||||
namespace Renci.SshClient.Messages.Authentication
|
||||
{
|
||||
internal class BannerMessage : Message
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.UserAuthenticationBanner; }
|
||||
}
|
||||
|
||||
public string Message { get; set; }
|
||||
|
||||
public string Language { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
this.Message = this.ReadString();
|
||||
this.Language = this.ReadString();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
this.Write(this.Message, Encoding.UTF8);
|
||||
this.Write(this.Language);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Renci.SshClient.Messages.Authentication
|
||||
{
|
||||
internal class FailureMessage : Message
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.UserAuthenticationFailure; }
|
||||
}
|
||||
|
||||
public IEnumerable<string> AllowedAuthentications { get; set; }
|
||||
|
||||
public string Message { get; set; }
|
||||
|
||||
public bool PartialSuccess { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
this.AllowedAuthentications = this.ReadNamesList();
|
||||
this.PartialSuccess = this.ReadBoolean();
|
||||
if (this.PartialSuccess)
|
||||
{
|
||||
this.Message = string.Join(",", this.AllowedAuthentications);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
namespace Renci.SshClient.Messages.Authentication
|
||||
{
|
||||
internal class HostRequestMessage : RequestMessage
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
//byte SSH_MSG_USERAUTH_REQUEST
|
||||
//string user name
|
||||
//string service name
|
||||
//string "hostbased"
|
||||
//string public key algorithm for host key
|
||||
//string public host key and certificates for client host
|
||||
//string client host name expressed as the FQDN in US-ASCII
|
||||
//string user name on the client host in ISO-10646 UTF-8 encoding
|
||||
// [RFC3629]
|
||||
//string signature
|
||||
|
||||
|
||||
//string session identifier
|
||||
//byte SSH_MSG_USERAUTH_REQUEST
|
||||
//string user name
|
||||
//string service name
|
||||
//string "hostbased"
|
||||
//string public key algorithm for host key
|
||||
//string public host key and certificates for client host
|
||||
//string client host name expressed as the FQDN in US-ASCII
|
||||
//string user name on the client host in ISO-10646 UTF-8 encoding
|
||||
// [RFC3629]
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
|
||||
namespace Renci.SshClient.Messages.Authentication
|
||||
{
|
||||
internal class InformationRequestMessage : Message
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.UserAuthenticationInformationRequest; }
|
||||
}
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
|
||||
namespace Renci.SshClient.Messages.Authentication
|
||||
{
|
||||
internal class InformationResponseMessage : Message
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.UserAuthenticationInformationResponse; }
|
||||
}
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Renci.SshClient.Messages.Authentication
|
||||
{
|
||||
public enum Methods
|
||||
{
|
||||
None,
|
||||
PublicKey,
|
||||
Password,
|
||||
Hostbased
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Authentication
|
||||
{
|
||||
internal class PasswordChangeRequiredMessage : Message
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.UserAuthenticationPasswordChangeRequired; }
|
||||
}
|
||||
|
||||
public string Message { get; set; }
|
||||
|
||||
public string Language { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
this.Message = this.ReadString();
|
||||
this.Language = this.ReadString();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
this.Write(this.Message);
|
||||
this.Write(this.Language);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using System.Text;
|
||||
|
||||
namespace Renci.SshClient.Messages.Authentication
|
||||
{
|
||||
internal class PasswordRequestMessage : RequestMessage
|
||||
{
|
||||
public override string MethodName
|
||||
{
|
||||
get
|
||||
{
|
||||
return "password";
|
||||
}
|
||||
}
|
||||
|
||||
public string Password { get; set; }
|
||||
|
||||
public string NewPassword { get; set; }
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
|
||||
this.Write(!string.IsNullOrEmpty(this.NewPassword));
|
||||
|
||||
this.Write(this.Password, Encoding.UTF8);
|
||||
|
||||
if (!string.IsNullOrEmpty(this.NewPassword))
|
||||
{
|
||||
this.Write(this.NewPassword, Encoding.UTF8);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
|
||||
namespace Renci.SshClient.Messages.Authentication
|
||||
{
|
||||
internal class PublicKeyMessage : Message
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.UserAuthenticationPublicKey; }
|
||||
}
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
namespace Renci.SshClient.Messages.Authentication
|
||||
{
|
||||
internal class PublicKeyRequestMessage : RequestMessage
|
||||
{
|
||||
public override string MethodName
|
||||
{
|
||||
get
|
||||
{
|
||||
return "publickey";
|
||||
}
|
||||
}
|
||||
|
||||
public string PublicKeyAlgorithmName { get; set; }
|
||||
|
||||
public IEnumerable<byte> PublicKeyData { get; set; }
|
||||
|
||||
public IEnumerable<byte> Signature { get; set; }
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
|
||||
if (this.Signature == null)
|
||||
{
|
||||
this.Write(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Write(true);
|
||||
}
|
||||
this.Write(this.PublicKeyAlgorithmName);
|
||||
this.Write(this.PublicKeyData.GetSshString());
|
||||
if (this.Signature != null)
|
||||
this.Write(this.Signature.GetSshString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace Renci.SshClient.Messages.Authentication
|
||||
{
|
||||
internal class RequestMessage : Message
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.UserAuthenticationRequest; }
|
||||
}
|
||||
|
||||
public string Username { get; set; }
|
||||
|
||||
public ServiceNames ServiceName { get; set; }
|
||||
|
||||
public virtual string MethodName { get { return "none"; } }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
throw new InvalidOperationException("Load data is not supported.");
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
this.Write(this.Username, Encoding.UTF8);
|
||||
switch (this.ServiceName)
|
||||
{
|
||||
case ServiceNames.UserAuthentication:
|
||||
this.Write("ssh-userauth", Encoding.UTF8);
|
||||
break;
|
||||
case ServiceNames.Connection:
|
||||
this.Write("ssh-connection", Encoding.UTF8);
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException("Not supported service name");
|
||||
}
|
||||
this.Write(this.MethodName, Encoding.ASCII);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Authentication
|
||||
{
|
||||
internal class SuccessMessage : Message
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.UserAuthenticationSuccess; }
|
||||
}
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal class ChannelCloseMessage : ChannelMessage
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.ChannelClose; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal class ChannelDataMessage : ChannelMessage
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.ChannelData; }
|
||||
}
|
||||
|
||||
public string Data { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.Data = this.ReadString();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal class ChannelEofMessage : ChannelMessage
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.ChannelEof; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal class ChannelExtendedDataMessage : ChannelMessage
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.ChannelExtendedData; }
|
||||
}
|
||||
|
||||
public uint DataTypeCode { get; set; }
|
||||
|
||||
public string Data { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.DataTypeCode = this.ReadUInt32();
|
||||
this.Data = this.ReadString();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.DataTypeCode);
|
||||
this.Write(this.Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal class ChannelFailureMessage : ChannelMessage
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.ChannelFailure; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal abstract class ChannelMessage : Message
|
||||
{
|
||||
public uint ChannelNumber { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
this.ChannelNumber = this.ReadUInt32();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
this.Write(this.ChannelNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal class ChannelOpenConfirmationMessage : ChannelMessage
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.ChannelOpenConfirmation; }
|
||||
}
|
||||
|
||||
public uint ServerChannelNumber { get; set; }
|
||||
|
||||
public uint InitialWindowSize { get; set; }
|
||||
|
||||
public uint MaximumPacketSize { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.ServerChannelNumber = this.ReadUInt32();
|
||||
this.InitialWindowSize = this.ReadUInt32();
|
||||
this.MaximumPacketSize = this.ReadUInt32();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.ServerChannelNumber);
|
||||
this.Write(this.InitialWindowSize);
|
||||
this.Write(this.MaximumPacketSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal class ChannelOpenFailureMessage : ChannelMessage
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.ChannelOpenFailure; }
|
||||
}
|
||||
|
||||
public uint ReasconCode { get; set; }
|
||||
|
||||
public string Description { get; set; }
|
||||
|
||||
public string Language { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.ReasconCode = this.ReadUInt32();
|
||||
this.Description = this.ReadString();
|
||||
this.Language = this.ReadString();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.ReasconCode);
|
||||
this.Write(this.Description);
|
||||
this.Write(this.Language);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal enum ChannelOpenFailureReasons : uint
|
||||
{
|
||||
/// <summary>
|
||||
/// SSH_OPEN_ADMINISTRATIVELY_PROHIBITED
|
||||
/// </summary>
|
||||
AdministativelyProhibited = 1,
|
||||
/// <summary>
|
||||
/// SSH_OPEN_CONNECT_FAILED
|
||||
/// </summary>
|
||||
ConnectFailed = 2,
|
||||
/// <summary>
|
||||
/// SSH_OPEN_UNKNOWN_CHANNEL_TYPE
|
||||
/// </summary>
|
||||
UnknownChannelType = 3,
|
||||
/// <summary>
|
||||
/// SSH_OPEN_RESOURCE_SHORTAGE
|
||||
/// </summary>
|
||||
ResourceShortage = 4
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal class ChannelOpenMessage : ChannelMessage
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.ChannelOpen; }
|
||||
}
|
||||
|
||||
public string ChannelName { get; set; }
|
||||
|
||||
public uint InitialWindowSize { get; set; }
|
||||
|
||||
public uint MaximumPacketSize { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
this.ChannelName = this.ReadString();
|
||||
this.ChannelNumber = this.ReadUInt32();
|
||||
this.InitialWindowSize = this.ReadUInt32();
|
||||
this.MaximumPacketSize = this.ReadUInt32();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
this.Write(this.ChannelName);
|
||||
this.Write(this.ChannelNumber);
|
||||
this.Write(this.InitialWindowSize);
|
||||
this.Write(this.MaximumPacketSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal class ChannelRequestMessage : ChannelMessage
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.ChannelRequest; }
|
||||
}
|
||||
|
||||
public RequestNames RequestName { get; set; }
|
||||
|
||||
public bool WantReply { get; set; }
|
||||
|
||||
public string Command { get; set; }
|
||||
|
||||
public string SubsystemName { get; set; }
|
||||
|
||||
public uint ExitStatus { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
|
||||
var requestName = this.ReadString();
|
||||
switch (requestName)
|
||||
{
|
||||
case "pty-req":
|
||||
break;
|
||||
case "x11-req":
|
||||
break;
|
||||
case "env":
|
||||
break;
|
||||
case "shell":
|
||||
break;
|
||||
case "exec":
|
||||
this.RequestName = RequestNames.Exec;
|
||||
this.WantReply = this.ReadBoolean();
|
||||
this.Command = this.ReadString();
|
||||
break;
|
||||
case "subsystem":
|
||||
this.RequestName = RequestNames.Subsystem;
|
||||
this.WantReply = this.ReadBoolean();
|
||||
this.SubsystemName = this.ReadString();
|
||||
break;
|
||||
case "window-change":
|
||||
break;
|
||||
case "xon-xoff":
|
||||
break;
|
||||
case "signal":
|
||||
break;
|
||||
case "exit-status":
|
||||
this.RequestName = RequestNames.ExitStatus;
|
||||
this.WantReply = this.ReadBoolean();
|
||||
this.ExitStatus = this.ReadUInt32();
|
||||
break;
|
||||
case "exit-signal":
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
|
||||
switch (this.RequestName)
|
||||
{
|
||||
case RequestNames.PseudoTerminal:
|
||||
break;
|
||||
case RequestNames.X11Forwarding:
|
||||
break;
|
||||
case RequestNames.EnvironmentVariable:
|
||||
break;
|
||||
case RequestNames.Shell:
|
||||
break;
|
||||
case RequestNames.Exec:
|
||||
this.Write("exec");
|
||||
this.Write(this.WantReply);
|
||||
this.Write(this.Command);
|
||||
break;
|
||||
case RequestNames.Subsystem:
|
||||
this.Write("subsystem");
|
||||
this.Write(this.WantReply);
|
||||
this.Write(this.SubsystemName);
|
||||
break;
|
||||
case RequestNames.WindowChange:
|
||||
break;
|
||||
case RequestNames.XonXoff:
|
||||
break;
|
||||
case RequestNames.Signal:
|
||||
break;
|
||||
case RequestNames.ExitStatus:
|
||||
this.Write("exit-status");
|
||||
this.Write(this.WantReply);
|
||||
this.Write(this.ExitStatus);
|
||||
break;
|
||||
case RequestNames.ExitSignal:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal class ChannelSuccessMessage : ChannelMessage
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.ChannelSuccess; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal class ChannelWindowAdjustMessage : ChannelMessage
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.ChannelWindowAdjust; }
|
||||
}
|
||||
|
||||
public uint BytesToAdd { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.BytesToAdd = this.ReadUInt32();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.BytesToAdd);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal class GlobalRequestMessage : Message
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.GlobalRequest; }
|
||||
}
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal class RequestFailureMessage : Message
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.RequestFailure; }
|
||||
}
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal enum RequestNames
|
||||
{
|
||||
/// <summary>
|
||||
/// pty-req
|
||||
/// </summary>
|
||||
PseudoTerminal,
|
||||
///x11-req
|
||||
X11Forwarding,
|
||||
/// <summary>
|
||||
/// env
|
||||
/// </summary>
|
||||
EnvironmentVariable,
|
||||
/// <summary>
|
||||
/// shell
|
||||
/// </summary>
|
||||
Shell,
|
||||
/// <summary>
|
||||
/// exec
|
||||
/// </summary>
|
||||
Exec,
|
||||
/// <summary>
|
||||
/// subsystem
|
||||
/// </summary>
|
||||
Subsystem,
|
||||
/// <summary>
|
||||
/// window-change
|
||||
/// </summary>
|
||||
WindowChange,
|
||||
/// <summary>
|
||||
/// xon-xoff
|
||||
/// </summary>
|
||||
XonXoff,
|
||||
/// <summary>
|
||||
/// signal
|
||||
/// </summary>
|
||||
Signal,
|
||||
/// <summary>
|
||||
/// exit-status
|
||||
/// </summary>
|
||||
ExitStatus,
|
||||
/// <summary>
|
||||
/// exit-signal
|
||||
/// </summary>
|
||||
ExitSignal
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
|
||||
using System;
|
||||
namespace Renci.SshClient.Messages.Connection
|
||||
{
|
||||
internal class RequestSuccessMessage : Message
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.RequestSuccess; }
|
||||
}
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Renci.SshClient.Common;
|
||||
|
||||
namespace Renci.SshClient.Messages
|
||||
{
|
||||
public delegate void SendMessageDelegate(Message message);
|
||||
|
||||
public abstract class Message : SshData
|
||||
{
|
||||
private static object _lock = new object();
|
||||
|
||||
private delegate T LoadFunc<out T>(IEnumerable<byte> data);
|
||||
|
||||
public abstract MessageTypes MessageType { get; }
|
||||
|
||||
private static IDictionary<MessageTypes, LoadFunc<Message>> _registeredMessageTypes = new Dictionary<MessageTypes, LoadFunc<Message>>();
|
||||
|
||||
/// <summary>
|
||||
/// Registers the message type. This will allow message type to be recognized by and handled by the system.
|
||||
/// </summary>
|
||||
/// <remarks>Some message types are not allowed during cirtain times or same code can be used for different type of message</remarks>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="messageType">Type of the message.</param>
|
||||
public static void RegisterMessageType<T>(MessageTypes messageType) where T : Message, new()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (Message._registeredMessageTypes.ContainsKey(messageType))
|
||||
{
|
||||
Message.UnRegisterMessageType(messageType);
|
||||
}
|
||||
|
||||
Message._registeredMessageTypes.Add(messageType, new LoadFunc<Message>(Load<T>));
|
||||
}
|
||||
}
|
||||
|
||||
public static void UnRegisterMessageType(MessageTypes messageType)
|
||||
{
|
||||
Message._registeredMessageTypes.Remove(messageType);
|
||||
}
|
||||
|
||||
public static Message Load(IEnumerable<byte> data)
|
||||
{
|
||||
var messageType = (MessageTypes)data.FirstOrDefault();
|
||||
|
||||
return Load(data, messageType);
|
||||
}
|
||||
|
||||
private static Message Load(IEnumerable<byte> data, MessageTypes messageType)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (Message._registeredMessageTypes.ContainsKey(messageType))
|
||||
{
|
||||
return Message._registeredMessageTypes[messageType](data);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException(string.Format("Message type '{0}' is not registered.", messageType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static T Load<T>(IEnumerable<byte> data) where T : Message, new()
|
||||
{
|
||||
var messageType = (MessageTypes)data.FirstOrDefault();
|
||||
|
||||
T message = new T();
|
||||
|
||||
message.LoadBytes(data);
|
||||
|
||||
message.ResetReader();
|
||||
|
||||
message.LoadData();
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
public override IEnumerable<byte> GetBytes()
|
||||
{
|
||||
var data = new List<byte>(base.GetBytes());
|
||||
|
||||
data.Insert(0, (byte)this.MessageType);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
namespace Renci.SshClient.Messages
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public enum MessageTypes : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// {35A90EBF-F421-44A3-BE3A-47C72AFE47FE}
|
||||
/// </summary>
|
||||
None = 0,
|
||||
/// <summary>
|
||||
/// SSH_MSG_DISCONNECT
|
||||
/// </summary>
|
||||
Disconnect = 1,
|
||||
/// <summary>
|
||||
/// SSH_MSG_IGNORE
|
||||
/// </summary>
|
||||
Ignore = 2,
|
||||
/// <summary>
|
||||
/// SSH_MSG_UNIMPLEMENTED
|
||||
/// </summary>
|
||||
Unimplemented = 3,
|
||||
/// <summary>
|
||||
/// SSH_MSG_DEBUG
|
||||
/// </summary>
|
||||
Debug = 4,
|
||||
/// <summary>
|
||||
/// SSH_MSG_SERVICE_REQUEST
|
||||
/// </summary>
|
||||
ServiceRequest = 5,
|
||||
/// <summary>
|
||||
/// SSH_MSG_SERVICE_ACCEPT
|
||||
/// </summary>
|
||||
ServiceAcceptRequest = 6,
|
||||
|
||||
/// <summary>
|
||||
/// SSH_MSG_KEXINIT
|
||||
/// </summary>
|
||||
KeyExchangeInit = 20,
|
||||
/// <summary>
|
||||
/// SSH_MSG_NEWKEYS
|
||||
/// </summary>
|
||||
NewKeys = 21,
|
||||
/// <summary>
|
||||
/// SSH_MSG_KEXDH_INIT
|
||||
/// </summary>
|
||||
DiffieHellmanKeyExchangeInit = 30,
|
||||
/// <summary>
|
||||
/// SSH_MSG_KEXDH_REPLY
|
||||
/// </summary>
|
||||
KeyExchangeDhReply = 31,
|
||||
|
||||
SSH_MSG_KEX_DH_GEX_GROUP = 31,
|
||||
SSH_MSG_KEX_DH_GEX_INIT = 32,
|
||||
SSH_MSG_KEX_DH_GEX_REPLY = 33,
|
||||
SSH_MSG_KEX_DH_GEX_REQUEST = 34,
|
||||
|
||||
/// <summary>
|
||||
/// SSH_MSG_USERAUTH_REQUEST
|
||||
/// </summary>
|
||||
UserAuthenticationRequest = 50,
|
||||
/// <summary>
|
||||
/// SSH_MSG_USERAUTH_FAILURE
|
||||
/// </summary>
|
||||
UserAuthenticationFailure = 51,
|
||||
/// <summary>
|
||||
/// SSH_MSG_USERAUTH_SUCCESS
|
||||
/// </summary>
|
||||
UserAuthenticationSuccess = 52,
|
||||
/// <summary>
|
||||
/// SSH_MSG_USERAUTH_BANNER
|
||||
/// </summary>
|
||||
UserAuthenticationBanner = 53,
|
||||
/// <summary>
|
||||
/// SSH_MSG_USERAUTH_INFO_REQUEST
|
||||
/// </summary>
|
||||
UserAuthenticationInformationRequest = 60,
|
||||
/// <summary>
|
||||
/// SSH_MSG_USERAUTH_INFO_RESPONSE
|
||||
/// </summary>
|
||||
UserAuthenticationInformationResponse = 61,
|
||||
/// <summary>
|
||||
/// SSH_MSG_USERAUTH_PK_OK
|
||||
/// </summary>
|
||||
UserAuthenticationPublicKey = 60,
|
||||
/// <summary>
|
||||
/// SSH_MSG_USERAUTH_PASSWD_CHANGEREQ
|
||||
/// </summary>
|
||||
UserAuthenticationPasswordChangeRequired = 60,
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// SSH_MSG_GLOBAL_REQUEST
|
||||
/// </summary>
|
||||
GlobalRequest = 80,
|
||||
/// <summary>
|
||||
/// SSH_MSG_REQUEST_SUCCESS
|
||||
/// </summary>
|
||||
RequestSuccess = 81,
|
||||
/// <summary>
|
||||
/// SSH_MSG_REQUEST_FAILURE
|
||||
/// </summary>
|
||||
RequestFailure = 82,
|
||||
/// <summary>
|
||||
/// SSH_MSG_CHANNEL_OPEN
|
||||
/// </summary>
|
||||
ChannelOpen = 90,
|
||||
/// <summary>
|
||||
/// SSH_MSG_CHANNEL_OPEN_CONFIRMATION
|
||||
/// </summary>
|
||||
ChannelOpenConfirmation = 91,
|
||||
/// <summary>
|
||||
/// SSH_MSG_CHANNEL_OPEN_FAILURE
|
||||
/// </summary>
|
||||
ChannelOpenFailure = 92,
|
||||
/// <summary>
|
||||
/// SSH_MSG_CHANNEL_WINDOW_ADJUST
|
||||
/// </summary>
|
||||
ChannelWindowAdjust = 93,
|
||||
/// <summary>
|
||||
/// SSH_MSG_CHANNEL_DATA
|
||||
/// </summary>
|
||||
ChannelData = 94,
|
||||
/// <summary>
|
||||
/// SSH_MSG_CHANNEL_EXTENDED_DATA
|
||||
/// </summary>
|
||||
ChannelExtendedData = 95,
|
||||
/// <summary>
|
||||
/// SSH_MSG_CHANNEL_EOF
|
||||
/// </summary>
|
||||
ChannelEof = 96,
|
||||
/// <summary>
|
||||
/// SSH_MSG_CHANNEL_CLOSE
|
||||
/// </summary>
|
||||
ChannelClose = 97,
|
||||
/// <summary>
|
||||
/// SSH_MSG_CHANNEL_REQUEST
|
||||
/// </summary>
|
||||
ChannelRequest = 98,
|
||||
/// <summary>
|
||||
/// SSH_MSG_CHANNEL_SUCCESS
|
||||
/// </summary>
|
||||
ChannelSuccess = 99,
|
||||
/// <summary>
|
||||
/// SSH_MSG_CHANNEL_FAILURE
|
||||
/// </summary>
|
||||
ChannelFailure = 100,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace Renci.SshClient.Messages
|
||||
{
|
||||
internal enum ServiceNames
|
||||
{
|
||||
/// <summary>
|
||||
/// ssh-userauth
|
||||
/// </summary>
|
||||
UserAuthentication,
|
||||
|
||||
/// <summary>
|
||||
/// ssh-connection
|
||||
/// </summary>
|
||||
Connection
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal enum AceMasks
|
||||
{
|
||||
ACE4_READ_DATA = 0x00000001,
|
||||
ACE4_LIST_DIRECTORY = 0x00000001,
|
||||
ACE4_WRITE_DATA = 0x00000002,
|
||||
ACE4_ADD_FILE = 0x00000002,
|
||||
ACE4_APPEND_DATA = 0x00000004,
|
||||
ACE4_ADD_SUBDIRECTORY = 0x00000004,
|
||||
ACE4_READ_NAMED_ATTRS = 0x00000008,
|
||||
ACE4_WRITE_NAMED_ATTRS = 0x00000010,
|
||||
ACE4_EXECUTE = 0x00000020,
|
||||
ACE4_DELETE_CHILD = 0x00000040,
|
||||
ACE4_READ_ATTRIBUTES = 0x00000080,
|
||||
ACE4_WRITE_ATTRIBUTES = 0x00000100,
|
||||
ACE4_DELETE = 0x00010000,
|
||||
ACE4_READ_ACL = 0x00020000,
|
||||
ACE4_WRITE_ACL = 0x00040000,
|
||||
ACE4_WRITE_OWNER = 0x00080000,
|
||||
ACE4_SYNCHRONIZE = 0x00100000
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class Attributes
|
||||
{
|
||||
public UInt32 Flag { get; set; }
|
||||
|
||||
public ulong Size { get; set; }
|
||||
|
||||
public uint UserId { get; set; }
|
||||
|
||||
public uint GroupId { get; set; }
|
||||
|
||||
public uint Permissions { get; set; }
|
||||
|
||||
public DateTime AccessTime { get; set; }
|
||||
|
||||
public DateTime ModifyTime { get; set; }
|
||||
|
||||
public IDictionary<string, string> Extentions { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class AttrsMessage : SftpMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
get { return SftpMessageTypes.Attrs; }
|
||||
}
|
||||
|
||||
public Attributes Attributes { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.Attributes = this.ReadAttributes();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.Attributes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class CloseMessage : SftpMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
get { return SftpMessageTypes.Close; }
|
||||
}
|
||||
|
||||
public string Handle { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.Handle = this.ReadString();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.Handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class DataMessage : SftpMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
get { return SftpMessageTypes.Data; }
|
||||
}
|
||||
|
||||
public string Data { get; set; }
|
||||
|
||||
public bool IsEof { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.Data = this.ReadString();
|
||||
if (!this.IsEndOfData)
|
||||
{
|
||||
this.IsEof = this.ReadBoolean();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.Data);
|
||||
if (this.IsEof)
|
||||
{
|
||||
this.Write(this.IsEof);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class ExtendedMessage : SftpMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
get { return SftpMessageTypes.Extended; }
|
||||
}
|
||||
|
||||
public string ExtendedRequest { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.ExtendedRequest = this.ReadString();
|
||||
// TODO: Read extended request data
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.ExtendedRequest);
|
||||
// TODO: Save extended request data
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class ExtendedReplyMessage : SftpMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
get { return SftpMessageTypes.ExtendedReply; }
|
||||
}
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
// TODO: Load request specific reply
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
// TODO: Save request specific reply
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class FSetStat : SftpMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
get { return SftpMessageTypes.FSetStat; }
|
||||
}
|
||||
|
||||
public string Handle { get; set; }
|
||||
|
||||
public Attributes Attributes { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.Handle = this.ReadString();
|
||||
this.Attributes = this.ReadAttributes();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.Handle);
|
||||
this.Write(this.Attributes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class FSetStatMessage : SftpMessage
|
||||
{
|
||||
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
get { return SftpMessageTypes.FSetStat; }
|
||||
}
|
||||
|
||||
public string Handle { get; set; }
|
||||
|
||||
public Attributes Attributes { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.Handle = this.ReadString();
|
||||
this.Attributes = this.ReadAttributes();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.Handle);
|
||||
this.Write(this.Attributes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class FStatMessage : SftpMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
get { return SftpMessageTypes.FStat; }
|
||||
}
|
||||
|
||||
public string Handle { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.Handle = this.ReadString();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.Handle);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal enum Flags
|
||||
{
|
||||
/// <summary>
|
||||
/// SSH_FXF_READ
|
||||
/// </summary>
|
||||
Read = 0x00000001,
|
||||
/// <summary>
|
||||
/// SSH_FXF_WRITE
|
||||
/// </summary>
|
||||
Write = 0x00000002,
|
||||
/// <summary>
|
||||
/// SSH_FXF_APPEND
|
||||
/// </summary>
|
||||
Append = 0x00000004,
|
||||
/// <summary>
|
||||
/// SSH_FXF_CREAT
|
||||
/// </summary>
|
||||
CreateNewOrOpen = 0x00000008,
|
||||
/// <summary>
|
||||
/// SSH_FXF_TRUNC
|
||||
/// </summary>
|
||||
Truncate = 0x00000010,
|
||||
/// <summary>
|
||||
/// SSH_FXF_EXCL
|
||||
/// </summary>
|
||||
CreateNew = 0x00000028
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class HandleMessage : SftpMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
get { return SftpMessageTypes.Handle; }
|
||||
}
|
||||
|
||||
public string Handle { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.Handle = this.ReadString();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.Handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class InitMessage : SftpMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
get { return SftpMessageTypes.Init; }
|
||||
}
|
||||
|
||||
public uint Version { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.Version);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class LStatMessage : SftpMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
get { return SftpMessageTypes.LStat; }
|
||||
}
|
||||
|
||||
public string Path { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.Path = this.ReadString();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.Path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class MkDirMessage : SftpMessage
|
||||
{
|
||||
public MkDirMessage()
|
||||
{
|
||||
this.Attributes = new Attributes();
|
||||
}
|
||||
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
get { return SftpMessageTypes.MkDir; }
|
||||
}
|
||||
|
||||
public string Path { get; set; }
|
||||
|
||||
public Attributes Attributes { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.Path = this.ReadString();
|
||||
this.Attributes = this.ReadAttributes();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.Path);
|
||||
this.Write(this.Attributes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System.Collections.Generic;
|
||||
using Renci.SshClient.Common;
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class NameMessage : SftpMessage
|
||||
{
|
||||
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
get { return SftpMessageTypes.Name; }
|
||||
}
|
||||
|
||||
public uint Count { get; set; }
|
||||
|
||||
public IList<FtpFileInfo> Files { get; set; }
|
||||
|
||||
public NameMessage()
|
||||
{
|
||||
this.Files = new List<FtpFileInfo>();
|
||||
}
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.Count = this.ReadUInt32();
|
||||
for (int i = 0; i < this.Count; i++)
|
||||
{
|
||||
var fileName = this.ReadString();
|
||||
var fullName = this.ReadString();
|
||||
var attribute = this.ReadAttributes();
|
||||
|
||||
this.Files.Add(new FtpFileInfo
|
||||
{
|
||||
Name = fileName,
|
||||
FullName = fullName,
|
||||
Size = attribute.Size,
|
||||
UserId = attribute.UserId,
|
||||
GroupId = attribute.GroupId,
|
||||
LastAccessTime = attribute.AccessTime,
|
||||
LastModifyTime = attribute.ModifyTime,
|
||||
Extentions = attribute.Extentions,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
using System.Text;
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class OpenDirMessage : SftpMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
get { return SftpMessageTypes.OpenDir; }
|
||||
}
|
||||
|
||||
public string Path { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.Path = this.ReadString();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.Path, Encoding.UTF8);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class OpenMessage : SftpMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
get { return SftpMessageTypes.Open; }
|
||||
}
|
||||
|
||||
public string Filename { get; set; }
|
||||
|
||||
public Flags Flags { get; set; }
|
||||
|
||||
public Attributes Attributes { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.Filename);
|
||||
this.Write((uint)this.Flags);
|
||||
this.Write(this.Attributes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class ReadDirMessage : SftpMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
get { return SftpMessageTypes.ReadDir; }
|
||||
}
|
||||
|
||||
public string Handle { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.Handle = this.ReadString();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.Handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
using System.Text;
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class ReadLinkMessage : SftpMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
get { return SftpMessageTypes.ReadLink; }
|
||||
}
|
||||
|
||||
public string Path { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.Path = this.ReadString();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.Path, Encoding.UTF8);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
|
||||
using System;
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class ReadMessage : SftpMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
get { return SftpMessageTypes.Read; }
|
||||
}
|
||||
|
||||
public string Handle { get; set; }
|
||||
|
||||
public UInt64 Offset { get; set; }
|
||||
|
||||
public UInt32 Length { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.Handle = this.ReadString();
|
||||
this.Offset = this.ReadUInt64();
|
||||
this.Length = this.ReadUInt32();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.Handle);
|
||||
this.Write(this.Offset);
|
||||
this.Write(this.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class RealPathMessage : SftpMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
get { return SftpMessageTypes.RealPath; }
|
||||
}
|
||||
|
||||
public string Path { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.Path = this.ReadString();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.Path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
using System.Text;
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class RemoveMessage : SftpMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
get { return SftpMessageTypes.Remove; }
|
||||
}
|
||||
|
||||
public string Filename { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.Filename = this.ReadString();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.Filename, Encoding.UTF8);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class RenameMessage : SftpMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
get { return SftpMessageTypes.Rename; }
|
||||
}
|
||||
|
||||
public string OldPath { get; set; }
|
||||
|
||||
public string NewPath { get; set; }
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.OldPath = this.ReadString();
|
||||
this.NewPath = this.ReadString();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.OldPath);
|
||||
this.Write(this.NewPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Text;
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class RmDirMessage : SftpMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
get { return SftpMessageTypes.RmDir; }
|
||||
}
|
||||
|
||||
public string Path { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.Path = this.ReadString();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.Path, Encoding.UTF8);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class SetStatMessage : SftpMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
get { return SftpMessageTypes.SetStat; }
|
||||
}
|
||||
|
||||
public string Path { get; set; }
|
||||
|
||||
public Attributes Attributes { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.Path = this.ReadString();
|
||||
this.Attributes = this.ReadAttributes();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.Path);
|
||||
this.Write(this.Attributes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System.Linq;
|
||||
using Renci.SshClient.Messages.Connection;
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class SftpDataMessage : ChannelMessage
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.ChannelData; }
|
||||
}
|
||||
|
||||
public SftpMessage Data { get; set; }
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
var data = this.Data.GetBytes();
|
||||
this.Write((uint)data.Count() + 4);
|
||||
this.Write(data.GetSshString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Renci.SshClient.Common;
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal abstract class SftpMessage : SshData
|
||||
{
|
||||
private delegate T LoadFunc<out T>(IEnumerable<byte> data);
|
||||
|
||||
private static IDictionary<SftpMessageTypes, LoadFunc<SftpMessage>> _sftpMessageTypes = new Dictionary<SftpMessageTypes, LoadFunc<SftpMessage>>();
|
||||
|
||||
public static SftpMessage Load(IEnumerable<byte> data)
|
||||
{
|
||||
var messageType = (SftpMessageTypes)data.FirstOrDefault();
|
||||
|
||||
return Load(data, messageType);
|
||||
}
|
||||
|
||||
static SftpMessage()
|
||||
{
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.Init, new LoadFunc<SftpMessage>(Load<InitMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.Version, new LoadFunc<SftpMessage>(Load<VersionMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.Open, new LoadFunc<SftpMessage>(Load<OpenMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.Close, new LoadFunc<SftpMessage>(Load<CloseMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.Read, new LoadFunc<SftpMessage>(Load<ReadMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.Write, new LoadFunc<SftpMessage>(Load<WriteMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.LStat, new LoadFunc<SftpMessage>(Load<LStatMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.FStat, new LoadFunc<SftpMessage>(Load<FStatMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.SetStat, new LoadFunc<SftpMessage>(Load<SetStatMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.FSetStat, new LoadFunc<SftpMessage>(Load<FSetStatMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.OpenDir, new LoadFunc<SftpMessage>(Load<OpenDirMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.ReadDir, new LoadFunc<SftpMessage>(Load<ReadDirMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.Remove, new LoadFunc<SftpMessage>(Load<RemoveMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.MkDir, new LoadFunc<SftpMessage>(Load<MkDirMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.RmDir, new LoadFunc<SftpMessage>(Load<RmDirMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.RealPath, new LoadFunc<SftpMessage>(Load<RealPathMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.Stat, new LoadFunc<SftpMessage>(Load<StatMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.Rename, new LoadFunc<SftpMessage>(Load<RenameMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.ReadLink, new LoadFunc<SftpMessage>(Load<ReadLinkMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.SymLink, new LoadFunc<SftpMessage>(Load<SymLinkMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.Status, new LoadFunc<SftpMessage>(Load<StatusMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.Handle, new LoadFunc<SftpMessage>(Load<HandleMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.Data, new LoadFunc<SftpMessage>(Load<DataMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.Name, new LoadFunc<SftpMessage>(Load<NameMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.Attrs, new LoadFunc<SftpMessage>(Load<AttrsMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.Extended, new LoadFunc<SftpMessage>(Load<ExtendedMessage>));
|
||||
SftpMessage._sftpMessageTypes.Add(SftpMessageTypes.ExtendedReply, new LoadFunc<SftpMessage>(Load<ExtendedReplyMessage>));
|
||||
}
|
||||
|
||||
public abstract SftpMessageTypes SftpMessageType { get; }
|
||||
|
||||
public uint? RequestId { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
// SSH_FXP_INIT and SSH_FXP_VERSION doesnt have RequestID, all other messaages do
|
||||
if (!(this.SftpMessageType == SftpMessageTypes.Init || this.SftpMessageType == SftpMessageTypes.Version))
|
||||
{
|
||||
this.RequestId = this.ReadUInt32();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
this.Write((byte)this.SftpMessageType);
|
||||
if (this.RequestId.HasValue)
|
||||
this.Write(this.RequestId.Value);
|
||||
}
|
||||
|
||||
protected Attributes ReadAttributes()
|
||||
{
|
||||
var attributes = new Attributes();
|
||||
attributes.Flag = this.ReadUInt32();
|
||||
|
||||
var isSize = (attributes.Flag & 0x00000001) == 0x00000001; //SSH_FILEXFER_ATTR_SIZE 0x00000001
|
||||
var isUidGid = (attributes.Flag & 0x00000002) == 0x00000002; //SSH_FILEXFER_ATTR_UIDGID 0x00000002
|
||||
var isPermissions = (attributes.Flag & 0x00000004) == 0x00000004; //SSH_FILEXFER_ATTR_PERMISSIONS 0x00000004
|
||||
var isAccessModifyTime = (attributes.Flag & 0x00000008) == 0x00000008; //SSH_FILEXFER_ATTR_ACMODTIME 0x00000008
|
||||
|
||||
var isExtended = (attributes.Flag & 0x80000000) == 0x80000000; //SSH_FILEXFER_ATTR_EXTENDED 0x80000000
|
||||
|
||||
if (isSize)
|
||||
{
|
||||
attributes.Size = this.ReadUInt64();
|
||||
}
|
||||
|
||||
if (isUidGid)
|
||||
{
|
||||
attributes.UserId = this.ReadUInt32();
|
||||
|
||||
attributes.GroupId = this.ReadUInt32();
|
||||
}
|
||||
|
||||
if (isPermissions)
|
||||
{
|
||||
attributes.Permissions = this.ReadUInt32();
|
||||
}
|
||||
|
||||
if (isAccessModifyTime)
|
||||
{
|
||||
var time = this.ReadUInt32();
|
||||
attributes.AccessTime = DateTime.FromFileTime((time + 11644473600) * 10000000);
|
||||
time = this.ReadUInt32();
|
||||
attributes.ModifyTime = DateTime.FromFileTime((time + 11644473600) * 10000000);
|
||||
}
|
||||
|
||||
if (isExtended)
|
||||
{
|
||||
var extendedCount = this.ReadUInt32();
|
||||
attributes.Extentions = this.ReadExtensionPair();
|
||||
}
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
protected void Write(Attributes attributes)
|
||||
{
|
||||
// TODO: Complete attribute serialization, at this point we pass no attributes
|
||||
if (attributes == null)
|
||||
{
|
||||
this.Write((uint)0);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Need to be tested
|
||||
throw new NotImplementedException();
|
||||
|
||||
var isSize = (attributes.Flag & 0x00000001) == 0x00000001; //SSH_FILEXFER_ATTR_SIZE 0x00000001
|
||||
var isUidGid = (attributes.Flag & 0x00000002) == 0x00000002; //SSH_FILEXFER_ATTR_UIDGID 0x00000002
|
||||
var isPermissions = (attributes.Flag & 0x00000004) == 0x00000004; //SSH_FILEXFER_ATTR_PERMISSIONS 0x00000004
|
||||
var isAccessModifyTime = (attributes.Flag & 0x00000008) == 0x00000008; //SSH_FILEXFER_ATTR_ACMODTIME 0x00000008
|
||||
|
||||
var isExtended = (attributes.Flag & 0x80000000) == 0x80000000; //SSH_FILEXFER_ATTR_EXTENDED 0x80000000
|
||||
|
||||
if (isSize)
|
||||
{
|
||||
this.Write(attributes.Size);
|
||||
}
|
||||
|
||||
if (isUidGid)
|
||||
{
|
||||
this.Write(attributes.UserId);
|
||||
|
||||
this.Write(attributes.GroupId);
|
||||
}
|
||||
|
||||
if (isPermissions)
|
||||
{
|
||||
this.Write(attributes.Permissions);
|
||||
}
|
||||
|
||||
if (isAccessModifyTime)
|
||||
{
|
||||
uint time = (uint)(attributes.AccessTime.ToFileTime() - 11644473600) / 10000000;
|
||||
this.Write(time);
|
||||
time = (uint)(attributes.ModifyTime.ToFileTime() - 11644473600) / 10000000;
|
||||
this.Write(time);
|
||||
}
|
||||
|
||||
if (isExtended)
|
||||
{
|
||||
this.Write(attributes.Extentions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static SftpMessage Load(IEnumerable<byte> data, SftpMessageTypes messageType)
|
||||
{
|
||||
if (SftpMessage._sftpMessageTypes.ContainsKey(messageType))
|
||||
{
|
||||
return SftpMessage._sftpMessageTypes[messageType](data);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException(string.Format("Message type '{0}' is not registered.", messageType));
|
||||
}
|
||||
}
|
||||
|
||||
private static T Load<T>(IEnumerable<byte> data) where T : SftpMessage, new()
|
||||
{
|
||||
var messageType = (SftpMessageTypes)data.FirstOrDefault();
|
||||
|
||||
T message = new T();
|
||||
|
||||
message.LoadBytes(data);
|
||||
|
||||
message.ResetReader();
|
||||
|
||||
message.LoadData();
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal enum SftpMessageTypes : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// SSH_FXP_INIT
|
||||
/// </summary>
|
||||
Init = 1,
|
||||
/// <summary>
|
||||
/// SSH_FXP_VERSION
|
||||
/// </summary>
|
||||
Version = 2,
|
||||
/// <summary>
|
||||
/// SSH_FXP_OPEN
|
||||
/// </summary>
|
||||
Open = 3,
|
||||
/// <summary>
|
||||
/// SSH_FXP_CLOSE
|
||||
/// </summary>
|
||||
Close = 4,
|
||||
/// <summary>
|
||||
/// SSH_FXP_READ
|
||||
/// </summary>
|
||||
Read = 5,
|
||||
/// <summary>
|
||||
/// SSH_FXP_WRITE
|
||||
/// </summary>
|
||||
Write = 6,
|
||||
/// <summary>
|
||||
/// SSH_FXP_LSTAT
|
||||
/// </summary>
|
||||
LStat = 7,
|
||||
/// <summary>
|
||||
/// SSH_FXP_FSTAT
|
||||
/// </summary>
|
||||
FStat = 8,
|
||||
/// <summary>
|
||||
/// SSH_FXP_SETSTAT
|
||||
/// </summary>
|
||||
SetStat = 9,
|
||||
/// <summary>
|
||||
/// SSH_FXP_FSETSTAT
|
||||
/// </summary>
|
||||
FSetStat = 10,
|
||||
/// <summary>
|
||||
/// SSH_FXP_OPENDIR
|
||||
/// </summary>
|
||||
OpenDir = 11,
|
||||
/// <summary>
|
||||
/// SSH_FXP_READDIR
|
||||
/// </summary>
|
||||
ReadDir = 12,
|
||||
/// <summary>
|
||||
/// SSH_FXP_REMOVE
|
||||
/// </summary>
|
||||
Remove = 13,
|
||||
/// <summary>
|
||||
/// SSH_FXP_MKDIR
|
||||
/// </summary>
|
||||
MkDir = 14,
|
||||
/// <summary>
|
||||
/// SSH_FXP_RMDIR
|
||||
/// </summary>
|
||||
RmDir = 15,
|
||||
/// <summary>
|
||||
/// SSH_FXP_REALPATH
|
||||
/// </summary>
|
||||
RealPath = 16,
|
||||
/// <summary>
|
||||
/// SSH_FXP_STAT
|
||||
/// </summary>
|
||||
Stat = 17,
|
||||
/// <summary>
|
||||
/// SSH_FXP_RENAME
|
||||
/// </summary>
|
||||
Rename = 18,
|
||||
/// <summary>
|
||||
/// SSH_FXP_READLINK
|
||||
/// </summary>
|
||||
ReadLink = 19,
|
||||
/// <summary>
|
||||
/// SSH_FXP_SYMLINK
|
||||
/// </summary>
|
||||
SymLink = 20,
|
||||
/// <summary>
|
||||
/// SSH_FXP_STATUS
|
||||
/// </summary>
|
||||
Status = 101,
|
||||
/// <summary>
|
||||
/// SSH_FXP_HANDLE
|
||||
/// </summary>
|
||||
Handle = 102,
|
||||
/// <summary>
|
||||
/// SSH_FXP_DATA
|
||||
/// </summary>
|
||||
Data = 103,
|
||||
/// <summary>
|
||||
/// SSH_FXP_NAME
|
||||
/// </summary>
|
||||
Name = 104,
|
||||
/// <summary>
|
||||
/// SSH_FXP_ATTRS
|
||||
/// </summary>
|
||||
Attrs = 105,
|
||||
|
||||
/// <summary>
|
||||
/// SSH_FXP_EXTENDED
|
||||
/// </summary>
|
||||
Extended = 200,
|
||||
/// <summary>
|
||||
/// SSH_FXP_EXTENDED_REPLY
|
||||
/// </summary>
|
||||
ExtendedReply = 201
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class StatMessage : SftpMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
get { return SftpMessageTypes.Stat; }
|
||||
}
|
||||
|
||||
public string Path { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.Path = this.ReadString();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.Path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal enum StatusCodes : uint
|
||||
{
|
||||
/// <summary>
|
||||
/// SSH_FX_OK
|
||||
/// </summary>
|
||||
Ok = 0,
|
||||
/// <summary>
|
||||
/// SSH_FX_EOF
|
||||
/// </summary>
|
||||
Eof = 1,
|
||||
/// <summary>
|
||||
/// SSH_FX_NO_SUCH_FILE
|
||||
/// </summary>
|
||||
NoSuchFile = 2,
|
||||
/// <summary>
|
||||
/// SSH_FX_PERMISSION_DENIED
|
||||
/// </summary>
|
||||
PermissionDenied = 3,
|
||||
/// <summary>
|
||||
/// SSH_FX_FAILURE
|
||||
/// </summary>
|
||||
Failure = 4,
|
||||
/// <summary>
|
||||
/// SSH_FX_BAD_MESSAGE
|
||||
/// </summary>
|
||||
BadMessage = 5,
|
||||
/// <summary>
|
||||
/// SSH_FX_NO_CONNECTION
|
||||
/// </summary>
|
||||
NoConnection = 6,
|
||||
/// <summary>
|
||||
/// SSH_FX_CONNECTION_LOST
|
||||
/// </summary>
|
||||
ConnectionLost = 7,
|
||||
/// <summary>
|
||||
/// SSH_FX_OP_UNSUPPORTED
|
||||
/// </summary>
|
||||
OperationUnsupported = 8,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class StatusMessage : SftpMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
get { return SftpMessageTypes.Status; }
|
||||
}
|
||||
|
||||
public StatusCodes StatusCode { get; set; }
|
||||
|
||||
public string ErrorMessage { get; set; }
|
||||
|
||||
public string Language { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.StatusCode = (StatusCodes)this.ReadUInt32();
|
||||
|
||||
switch (this.StatusCode)
|
||||
{
|
||||
case StatusCodes.Ok:
|
||||
break;
|
||||
case StatusCodes.Eof:
|
||||
break;
|
||||
case StatusCodes.NoSuchFile:
|
||||
break;
|
||||
case StatusCodes.PermissionDenied:
|
||||
break;
|
||||
case StatusCodes.Failure:
|
||||
break;
|
||||
case StatusCodes.BadMessage:
|
||||
break;
|
||||
case StatusCodes.NoConnection:
|
||||
break;
|
||||
case StatusCodes.ConnectionLost:
|
||||
break;
|
||||
case StatusCodes.OperationUnsupported:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!this.IsEndOfData)
|
||||
{
|
||||
this.ErrorMessage = this.ReadString();
|
||||
this.Language = this.ReadString();
|
||||
}
|
||||
// TODO: Load error specific data
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write((uint)this.StatusCode);
|
||||
if (this.StatusCode == StatusCodes.Ok)
|
||||
{
|
||||
// No more data need to be written
|
||||
return;
|
||||
}
|
||||
this.Write(this.ErrorMessage);
|
||||
this.Write(this.Language);
|
||||
// TODO: Save error specific data
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using System.Text;
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class SymLinkMessage : SftpMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
get { return SftpMessageTypes.SymLink; }
|
||||
}
|
||||
|
||||
public string NewLinkPath { get; set; }
|
||||
|
||||
public string ExistingPath { get; set; }
|
||||
|
||||
public bool IsSymLink { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.NewLinkPath = this.ReadString();
|
||||
this.ExistingPath = this.ReadString();
|
||||
this.IsSymLink = this.ReadBoolean();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.NewLinkPath, Encoding.UTF8);
|
||||
this.Write(this.ExistingPath, Encoding.UTF8);
|
||||
this.Write(this.IsSymLink);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class VersionMessage : SftpMessage
|
||||
{
|
||||
public VersionMessage()
|
||||
{
|
||||
this.Extentions = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
get { return SftpMessageTypes.Version; }
|
||||
}
|
||||
|
||||
public uint Version { get; set; }
|
||||
|
||||
public IDictionary<string, string> Extentions { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.Version = this.ReadUInt32();
|
||||
this.Extentions = this.ReadExtensionPair();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.Version);
|
||||
this.Write(this.Extentions);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
|
||||
namespace Renci.SshClient.Messages.Sftp
|
||||
{
|
||||
internal class WriteMessage : SftpMessage
|
||||
{
|
||||
public override SftpMessageTypes SftpMessageType
|
||||
{
|
||||
get { return SftpMessageTypes.Write; }
|
||||
}
|
||||
|
||||
public string Handle { get; set; }
|
||||
|
||||
public UInt64 Offset { get; set; }
|
||||
|
||||
public string Data { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
base.LoadData();
|
||||
this.Handle = this.ReadString();
|
||||
this.Offset = this.ReadUInt64();
|
||||
this.Data = this.ReadString();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
base.SaveData();
|
||||
this.Write(this.Handle);
|
||||
this.Write(this.Offset);
|
||||
this.Write(this.Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
|
||||
namespace Renci.SshClient.Messages.Transport
|
||||
{
|
||||
internal class DebugMessage : Message
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.Debug; }
|
||||
}
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.Text;
|
||||
|
||||
namespace Renci.SshClient.Messages.Transport
|
||||
{
|
||||
internal class DisconnectMessage : Message
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.Disconnect; }
|
||||
}
|
||||
|
||||
public DisconnectReasonCodes ReasonCode { get; set; }
|
||||
|
||||
public string Description { get; set; }
|
||||
|
||||
public string Language { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
this.ReasonCode = (DisconnectReasonCodes)this.ReadUInt32();
|
||||
this.Description = this.ReadString();
|
||||
this.Language = this.ReadString();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
this.Write((uint)this.ReasonCode);
|
||||
this.Write(this.Description, Encoding.UTF8);
|
||||
this.Write(this.Language ?? "en");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
namespace Renci.SshClient.Messages.Transport
|
||||
{
|
||||
public enum DisconnectReasonCodes : uint
|
||||
{
|
||||
/// <summary>
|
||||
/// SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT
|
||||
/// </summary>
|
||||
HostNotAllowedToConnect = 1,
|
||||
/// <summary>
|
||||
/// SSH_DISCONNECT_PROTOCOL_ERROR
|
||||
/// </summary>
|
||||
ProtocolError = 2,
|
||||
/// <summary>
|
||||
/// SSH_DISCONNECT_KEY_EXCHANGE_FAILED
|
||||
/// </summary>
|
||||
KeyExchangeFailed = 3,
|
||||
/// <summary>
|
||||
/// SSH_DISCONNECT_RESERVED
|
||||
/// </summary>
|
||||
Reserved = 4,
|
||||
/// <summary>
|
||||
/// SSH_DISCONNECT_MAC_ERROR
|
||||
/// </summary>
|
||||
MacError = 5,
|
||||
/// <summary>
|
||||
/// SSH_DISCONNECT_COMPRESSION_ERROR
|
||||
/// </summary>
|
||||
CompressionError = 6,
|
||||
/// <summary>
|
||||
/// SSH_DISCONNECT_SERVICE_NOT_AVAILABLE
|
||||
/// </summary>
|
||||
ServiceNotAvailable = 7,
|
||||
/// <summary>
|
||||
/// SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED
|
||||
/// </summary>
|
||||
ProtocolVersionNotSupported = 8,
|
||||
/// <summary>
|
||||
/// SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE
|
||||
/// </summary>
|
||||
HostKeyNotVerifiable = 9,
|
||||
/// <summary>
|
||||
/// SSH_DISCONNECT_CONNECTION_LOST
|
||||
/// </summary>
|
||||
ConnectionLost = 10,
|
||||
/// <summary>
|
||||
/// SSH_DISCONNECT_BY_APPLICATION
|
||||
/// </summary>
|
||||
ByApplication = 11,
|
||||
/// <summary>
|
||||
/// SSH_DISCONNECT_TOO_MANY_CONNECTIONS
|
||||
/// </summary>
|
||||
TooManyConnections = 12,
|
||||
/// <summary>
|
||||
/// SSH_DISCONNECT_AUTH_CANCELLED_BY_USER
|
||||
/// </summary>
|
||||
AuthenticationCancelledByUser = 13,
|
||||
/// <summary>
|
||||
/// SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE
|
||||
/// </summary>
|
||||
NoMoreAuthenticationMethodsAvailable = 14,
|
||||
/// <summary>
|
||||
/// SSH_DISCONNECT_ILLEGAL_USER_NAME
|
||||
/// </summary>
|
||||
IllegalUserName = 15,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
|
||||
namespace Renci.SshClient.Messages.Transport
|
||||
{
|
||||
internal class IgnoreMessage : Message
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.Ignore; }
|
||||
}
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace Renci.SshClient.Messages.Transport
|
||||
{
|
||||
internal class KeyExchangeDhInitMessage : Message
|
||||
{
|
||||
public BigInteger E { get; set; }
|
||||
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get
|
||||
{
|
||||
return MessageTypes.DiffieHellmanKeyExchangeInit;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
this.ResetReader();
|
||||
this.E = this.ReadBigInteger();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
this.Write(this.E);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Renci.SshClient.Messages.Transport
|
||||
{
|
||||
internal class KeyExchangeDhReplyMessage : Message
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets server public host key and certificates
|
||||
/// </summary>
|
||||
/// <value>The host key.</value>
|
||||
public string HostKey { get; private set; }
|
||||
|
||||
public BigInteger F { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the signature of H.
|
||||
/// </summary>
|
||||
/// <value>The signature.</value>
|
||||
public string Signature { get; private set; }
|
||||
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get
|
||||
{
|
||||
return MessageTypes.KeyExchangeDhReply;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
this.ResetReader();
|
||||
this.HostKey = this.ReadString();
|
||||
this.F = this.ReadBigInteger();
|
||||
this.Signature = this.ReadString();
|
||||
|
||||
// TODO: Determine which algorithms to use from signature
|
||||
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
throw new NotSupportedException("SaveData is not supported for KeyExchangeDhReplyMessage class");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Renci.SshClient.Messages.Transport
|
||||
{
|
||||
internal class KeyExchangeInitMessage : Message
|
||||
{
|
||||
private static RNGCryptoServiceProvider _randomizer = new System.Security.Cryptography.RNGCryptoServiceProvider();
|
||||
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.KeyExchangeInit; }
|
||||
}
|
||||
|
||||
public KeyExchangeInitMessage()
|
||||
{
|
||||
var cookie = new byte[16];
|
||||
_randomizer.GetBytes(cookie);
|
||||
this.Cookie = cookie;
|
||||
}
|
||||
|
||||
#region Message Properties
|
||||
|
||||
public IEnumerable<byte> Cookie { get; private set; }
|
||||
|
||||
public IEnumerable<string> KeyExchangeAlgorithms { get; set; }
|
||||
|
||||
public IEnumerable<string> ServerHostKeyAlgorithms { get; set; }
|
||||
|
||||
public IEnumerable<string> EncryptionAlgorithmsClientToServer { get; set; }
|
||||
|
||||
public IEnumerable<string> EncryptionAlgorithmsServerToClient { get; set; }
|
||||
|
||||
public IEnumerable<string> MacAlgorithmsClientToSserver { get; set; }
|
||||
|
||||
public IEnumerable<string> MacAlgorithmsServerToClient { get; set; }
|
||||
|
||||
public IEnumerable<string> CompressionAlgorithmsClientToServer { get; set; }
|
||||
|
||||
public IEnumerable<string> CompressionAlgorithmsServerToClient { get; set; }
|
||||
|
||||
public IEnumerable<string> LanguagesClientToServer { get; set; }
|
||||
|
||||
public IEnumerable<string> LanguagesServerToClient { get; set; }
|
||||
|
||||
public bool FirstKexPacketFollows { get; set; }
|
||||
|
||||
public UInt32 Reserved { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
this.ResetReader();
|
||||
|
||||
this.Cookie = this.ReadBytes(16);
|
||||
this.KeyExchangeAlgorithms = this.ReadNamesList();
|
||||
this.ServerHostKeyAlgorithms = this.ReadNamesList();
|
||||
this.EncryptionAlgorithmsClientToServer = this.ReadNamesList();
|
||||
this.EncryptionAlgorithmsServerToClient = this.ReadNamesList();
|
||||
this.MacAlgorithmsClientToSserver = this.ReadNamesList();
|
||||
this.MacAlgorithmsServerToClient = this.ReadNamesList();
|
||||
this.CompressionAlgorithmsClientToServer = this.ReadNamesList();
|
||||
this.CompressionAlgorithmsServerToClient = this.ReadNamesList();
|
||||
this.LanguagesClientToServer = this.ReadNamesList();
|
||||
this.LanguagesServerToClient = this.ReadNamesList();
|
||||
this.FirstKexPacketFollows = this.ReadBoolean();
|
||||
this.Reserved = this.ReadUInt32();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
this.Write(this.Cookie);
|
||||
this.Write(this.KeyExchangeAlgorithms);
|
||||
this.Write(this.ServerHostKeyAlgorithms);
|
||||
this.Write(this.EncryptionAlgorithmsClientToServer);
|
||||
this.Write(this.EncryptionAlgorithmsServerToClient);
|
||||
this.Write(this.MacAlgorithmsClientToSserver);
|
||||
this.Write(this.MacAlgorithmsServerToClient);
|
||||
this.Write(this.CompressionAlgorithmsClientToServer);
|
||||
this.Write(this.CompressionAlgorithmsServerToClient);
|
||||
this.Write(this.LanguagesClientToServer);
|
||||
this.Write(this.LanguagesServerToClient);
|
||||
this.Write(this.FirstKexPacketFollows);
|
||||
this.Write(this.Reserved);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace Renci.SshClient.Messages.Transport
|
||||
{
|
||||
internal class NewKeysMessage : Message
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.NewKeys; }
|
||||
}
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
|
||||
namespace Renci.SshClient.Messages.Transport
|
||||
{
|
||||
/// <summary>
|
||||
/// SSH_MSG_SERVICE_ACCEPT
|
||||
/// </summary>
|
||||
internal class ServiceAcceptMessage : Message
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.ServiceAcceptRequest; }
|
||||
}
|
||||
|
||||
public ServiceNames ServiceName { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
var serviceName = this.ReadString();
|
||||
switch (serviceName)
|
||||
{
|
||||
case "ssh-userauth":
|
||||
this.ServiceName = ServiceNames.UserAuthentication;
|
||||
break;
|
||||
case "ssh-connection":
|
||||
this.ServiceName = ServiceNames.Connection;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
throw new InvalidOperationException("Save data is not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
|
||||
namespace Renci.SshClient.Messages.Transport
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains SSH_MSG_SERVICE_REQUEST message information
|
||||
/// </summary>
|
||||
internal class ServiceRequestMessage : Message
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.ServiceRequest; }
|
||||
}
|
||||
|
||||
public ServiceNames ServiceName { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
throw new InvalidOperationException("Load data is not supported.");
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
switch (this.ServiceName)
|
||||
{
|
||||
case ServiceNames.UserAuthentication:
|
||||
this.Write("ssh-userauth");
|
||||
break;
|
||||
case ServiceNames.Connection:
|
||||
this.Write("ssh-connection");
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException("Not supported service name");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
|
||||
namespace Renci.SshClient.Messages.Transport
|
||||
{
|
||||
internal class UnimplementedMessage : Message
|
||||
{
|
||||
public override MessageTypes MessageType
|
||||
{
|
||||
get { return MessageTypes.Unimplemented; }
|
||||
}
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user