using System; using System.Globalization; using System.IO; using System.Text; namespace Renci.SshNet.Common { /// /// Specialized for reading and writing data SSH data. /// public class SshDataStream : MemoryStream { /// /// Initializes a new instance of the class with an expandable capacity initialized /// as specified. /// /// The initial size of the internal array in bytes. public SshDataStream(int capacity) : base(capacity) { } /// /// Initializes a new instance of the class for the specified byte array. /// /// The array of unsigned bytes from which to create the current stream. /// is null. public SshDataStream(byte[] buffer) : base(buffer) { } /// /// Initializes a new instance of the class for the specified byte array. /// /// The array of unsigned bytes from which to create the current stream. /// The zero-based offset in at which to begin reading SSH data. /// The number of bytes to load. /// is null. public SshDataStream(byte[] buffer, int offset, int count) : base(buffer, offset, count) { } /// /// Gets a value indicating whether all data from the SSH data stream has been read. /// /// /// true if this instance is end of data; otherwise, false. /// public bool IsEndOfData { get { return Position >= Length; } } /// /// Writes an to the SSH data stream. /// /// data to write. public void Write(uint value) { var bytes = Pack.UInt32ToBigEndian(value); Write(bytes, 0, bytes.Length); } /// /// Writes an to the SSH data stream. /// /// data to write. public void Write(ulong value) { var bytes = Pack.UInt64ToBigEndian(value); Write(bytes, 0, bytes.Length); } /// /// Writes a into the SSH data stream. /// /// The to write. public void Write(BigInteger data) { var bytes = data.ToByteArray().Reverse(); WriteBinary(bytes, 0, bytes.Length); } /// /// Writes bytes array data into the SSH data stream. /// /// Byte array data to write. /// is null. public void Write(byte[] data) { if (data is null) { throw new ArgumentNullException(nameof(data)); } Write(data, 0, data.Length); } /// /// Reads a byte array from the SSH data stream. /// /// /// The byte array read from the SSH data stream. /// public byte[] ReadBinary() { var length = ReadUInt32(); if (length > int.MaxValue) { throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Data longer than {0} is not supported.", int.MaxValue)); } return ReadBytes((int)length); } /// /// Writes a buffer preceded by its length into the SSH data stream. /// /// The data to write. /// is null. public void WriteBinary(byte[] buffer) { if (buffer is null) { throw new ArgumentNullException(nameof(buffer)); } WriteBinary(buffer, 0, buffer.Length); } /// /// Writes a buffer preceded by its length into the SSH data stream. /// /// An array of bytes. This method write bytes from buffer to the current SSH data stream. /// The zero-based byte offset in at which to begin writing bytes to the SSH data stream. /// The number of bytes to be written to the current SSH data stream. /// is null. /// The sum of and is greater than the buffer length. /// or is negative. public void WriteBinary(byte[] buffer, int offset, int count) { Write((uint) count); Write(buffer, offset, count); } /// /// Writes string data to the SSH data stream using the specified encoding. /// /// The string data to write. /// The character encoding to use. /// is null. /// is null. public void Write(string s, Encoding encoding) { if (encoding is null) { throw new ArgumentNullException(nameof(encoding)); } var bytes = encoding.GetBytes(s); WriteBinary(bytes, 0, bytes.Length); } /// /// Reads a from the SSH datastream. /// /// /// The read from the SSH data stream. /// public BigInteger ReadBigInt() { var length = ReadUInt32(); var data = ReadBytes((int) length); return new BigInteger(data.Reverse()); } /// /// Reads the next data type from the SSH data stream. /// /// /// The read from the SSH data stream. /// public uint ReadUInt32() { var data = ReadBytes(4); return Pack.BigEndianToUInt32(data); } /// /// Reads the next data type from the SSH data stream. /// /// /// The read from the SSH data stream. /// public ulong ReadUInt64() { var data = ReadBytes(8); return Pack.BigEndianToUInt64(data); } /// /// Reads the next data type from the SSH data stream. /// /// The character encoding to use. /// /// The read from the SSH data stream. /// public string ReadString(Encoding encoding) { var length = ReadUInt32(); if (length > int.MaxValue) { throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Strings longer than {0} is not supported.", int.MaxValue)); } var bytes = ReadBytes((int) length); return encoding.GetString(bytes, 0, bytes.Length); } /// /// Writes the stream contents to a byte array, regardless of the . /// /// /// This method returns the contents of the as a byte array. /// /// /// If the current instance was constructed on a provided byte array, a copy of the section of the array /// to which this instance has access is returned. /// public override byte[] ToArray() { if (Capacity == Length) { return GetBuffer(); } return base.ToArray(); } /// /// Reads next specified number of bytes data type from internal buffer. /// /// Number of bytes to read. /// /// An array of bytes that was read from the internal buffer. /// /// is greater than the internal buffer size. private byte[] ReadBytes(int length) { var data = new byte[length]; var bytesRead = Read(data, 0, length); if (bytesRead < length) { throw new ArgumentOutOfRangeException(nameof(length), string.Format(CultureInfo.InvariantCulture, "The requested length ({0}) is greater than the actual number of bytes read ({1}).", length, bytesRead)); } return data; } } }