SshData.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701
  1. using System;
  2. using System.Collections.Generic;
  3. #if !TUNING
  4. using System.Linq;
  5. #endif
  6. using System.Text;
  7. using System.Globalization;
  8. namespace Renci.SshNet.Common
  9. {
  10. /// <summary>
  11. /// Base ssh data serialization type
  12. /// </summary>
  13. public abstract class SshData
  14. {
  15. internal const int DefaultCapacity = 64;
  16. internal static readonly Encoding Ascii = new ASCIIEncoding();
  17. #if SILVERLIGHT
  18. internal static readonly Encoding Utf8 = Encoding.UTF8;
  19. #else
  20. internal static readonly Encoding Utf8 = Encoding.Default;
  21. #endif
  22. #if TUNING
  23. private SshDataStream _stream;
  24. protected SshDataStream DataStream
  25. {
  26. get { return _stream; }
  27. }
  28. #else
  29. /// <summary>
  30. /// Data byte array that hold message unencrypted data
  31. /// </summary>
  32. private List<byte> _data;
  33. private int _readerIndex;
  34. #endif
  35. /// <summary>
  36. /// Gets a value indicating whether all data from the buffer has been read.
  37. /// </summary>
  38. /// <value>
  39. /// <c>true</c> if this instance is end of data; otherwise, <c>false</c>.
  40. /// </value>
  41. protected bool IsEndOfData
  42. {
  43. get
  44. {
  45. #if TUNING
  46. return _stream.Position >= _stream.Length;
  47. #else
  48. return _readerIndex >= _data.Count();
  49. #endif
  50. }
  51. }
  52. private byte[] _loadedData;
  53. #if TUNING
  54. private int _offset;
  55. #endif
  56. /// <summary>
  57. /// Gets the index that represents zero in current data type.
  58. /// </summary>
  59. /// <value>
  60. /// The index of the zero reader.
  61. /// </value>
  62. protected virtual int ZeroReaderIndex
  63. {
  64. get
  65. {
  66. return 0;
  67. }
  68. }
  69. #if TUNING
  70. /// <summary>
  71. /// Gets the size of the message in bytes.
  72. /// </summary>
  73. /// <value>
  74. /// The size of the messages in bytes.
  75. /// </value>
  76. protected virtual int BufferCapacity
  77. {
  78. get { return 0; }
  79. }
  80. #endif
  81. /// <summary>
  82. /// Gets data bytes array
  83. /// </summary>
  84. /// <returns>Byte array representation of data structure.</returns>
  85. public
  86. #if !TUNING
  87. virtual
  88. #endif
  89. byte[] GetBytes()
  90. {
  91. #if TUNING
  92. var messageLength = BufferCapacity;
  93. var capacity = messageLength != -1 ? messageLength : DefaultCapacity;
  94. var dataStream = new SshDataStream(capacity);
  95. WriteBytes(dataStream);
  96. return dataStream.ToArray();
  97. #else
  98. _data = new List<byte>();
  99. SaveData();
  100. return _data.ToArray();
  101. #endif
  102. }
  103. #if TUNING
  104. /// <summary>
  105. /// Writes the current message to the specified <see cref="SshDataStream"/>.
  106. /// </summary>
  107. /// <param name="stream">The <see cref="SshDataStream"/> to write the message to.</param>
  108. protected virtual void WriteBytes(SshDataStream stream)
  109. {
  110. _stream = stream;
  111. SaveData();
  112. }
  113. #endif
  114. internal T OfType<T>() where T : SshData, new()
  115. {
  116. var result = new T();
  117. #if TUNING
  118. result.LoadBytes(_loadedData, _offset);
  119. #else
  120. result.LoadBytes(_loadedData);
  121. #endif
  122. result.LoadData();
  123. return result;
  124. }
  125. /// <summary>
  126. /// Loads data from specified bytes.
  127. /// </summary>
  128. /// <param name="value">Bytes array.</param>
  129. /// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
  130. public void Load(byte[] value)
  131. {
  132. #if TUNING
  133. Load(value, 0);
  134. #else
  135. if (value == null)
  136. throw new ArgumentNullException("value");
  137. LoadBytes(value);
  138. LoadData();
  139. #endif
  140. }
  141. #if TUNING
  142. /// <summary>
  143. /// Loads data from the specified buffer.
  144. /// </summary>
  145. /// <param name="value">Bytes array.</param>
  146. /// <param name="offset">The zero-based offset in <paramref name="value"/> at which to begin reading SSH data.</param>
  147. /// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
  148. public void Load(byte[] value, int offset)
  149. {
  150. LoadBytes(value, offset);
  151. LoadData();
  152. }
  153. #endif
  154. /// <summary>
  155. /// Called when type specific data need to be loaded.
  156. /// </summary>
  157. protected abstract void LoadData();
  158. /// <summary>
  159. /// Called when type specific data need to be saved.
  160. /// </summary>
  161. protected abstract void SaveData();
  162. /// <summary>
  163. /// Loads data bytes into internal buffer.
  164. /// </summary>
  165. /// <param name="bytes">The bytes.</param>
  166. /// <exception cref="ArgumentNullException"><paramref name="bytes"/> is null.</exception>
  167. protected void LoadBytes(byte[] bytes)
  168. {
  169. #if TUNING
  170. LoadBytes(bytes, 0);
  171. #else
  172. // Note about why I check for null here, and in Load(byte[]) in this class.
  173. // This method is called by several other classes, such as SshNet.Messages.Message, SshNet.Sftp.SftpMessage.
  174. if (bytes == null)
  175. throw new ArgumentNullException("bytes");
  176. ResetReader();
  177. _loadedData = bytes;
  178. _data = new List<byte>(bytes);
  179. #endif
  180. }
  181. #if TUNING
  182. /// <summary>
  183. /// Loads data bytes into internal buffer.
  184. /// </summary>
  185. /// <param name="bytes">The bytes.</param>
  186. /// <param name="offset">The zero-based offset in <paramref name="bytes"/> at which to begin reading SSH data.</param>
  187. /// <exception cref="ArgumentNullException"><paramref name="bytes"/> is null.</exception>
  188. protected void LoadBytes(byte[] bytes, int offset)
  189. {
  190. if (bytes == null)
  191. throw new ArgumentNullException("bytes");
  192. _loadedData = bytes;
  193. _offset = offset;
  194. _stream = new SshDataStream(bytes);
  195. ResetReader();
  196. }
  197. #endif
  198. /// <summary>
  199. /// Resets internal data reader index.
  200. /// </summary>
  201. protected void ResetReader()
  202. {
  203. #if TUNING
  204. _stream.Position = ZeroReaderIndex + _offset;
  205. #else
  206. _readerIndex = ZeroReaderIndex; // Set to 1 to skip first byte which specifies message type
  207. #endif
  208. }
  209. /// <summary>
  210. /// Reads all data left in internal buffer at current position.
  211. /// </summary>
  212. /// <returns>An array of bytes containing the remaining data in the internal buffer.</returns>
  213. protected byte[] ReadBytes()
  214. {
  215. #if TUNING
  216. var bytesLength = (int) (_stream.Length - _stream.Position);
  217. var data = new byte[bytesLength];
  218. _stream.Read(data, 0, bytesLength);
  219. return data;
  220. #else
  221. var data = new byte[_data.Count - _readerIndex];
  222. _data.CopyTo(_readerIndex, data, 0, data.Length);
  223. return data;
  224. #endif
  225. }
  226. /// <summary>
  227. /// Reads next specified number of bytes data type from internal buffer.
  228. /// </summary>
  229. /// <param name="length">Number of bytes to read.</param>
  230. /// <returns>An array of bytes that was read from the internal buffer.</returns>
  231. /// <exception cref="ArgumentOutOfRangeException"><paramref name="length"/> is greater than the internal buffer size.</exception>
  232. protected byte[] ReadBytes(int length)
  233. {
  234. // Note that this also prevents allocating non-relevant lengths, such as if length is greater than _data.Count but less than int.MaxValue.
  235. // For the nerds, the condition translates to: if (length > data.Count && length < int.MaxValue)
  236. // Which probably would cause all sorts of exception, most notably OutOfMemoryException.
  237. #if TUNING
  238. var data = new byte[length];
  239. var bytesRead = _stream.Read(data, 0, length);
  240. if (bytesRead < length)
  241. throw new ArgumentOutOfRangeException("length");
  242. return data;
  243. #else
  244. if (length > _data.Count)
  245. throw new ArgumentOutOfRangeException("length");
  246. var result = new byte[length];
  247. _data.CopyTo(_readerIndex, result, 0, length);
  248. _readerIndex += length;
  249. return result;
  250. #endif
  251. }
  252. /// <summary>
  253. /// Reads next byte data type from internal buffer.
  254. /// </summary>
  255. /// <returns>Byte read.</returns>
  256. protected byte ReadByte()
  257. {
  258. #if TUNING
  259. var byteRead = _stream.ReadByte();
  260. if (byteRead == -1)
  261. throw new InvalidOperationException("Attempt to read past the end of the SSH data stream.");
  262. return (byte) byteRead;
  263. #else
  264. return ReadBytes(1).FirstOrDefault();
  265. #endif
  266. }
  267. /// <summary>
  268. /// Reads next boolean data type from internal buffer.
  269. /// </summary>
  270. /// <returns>Boolean read.</returns>
  271. protected bool ReadBoolean()
  272. {
  273. return ReadByte() != 0;
  274. }
  275. /// <summary>
  276. /// Reads next uint16 data type from internal buffer.
  277. /// </summary>
  278. /// <returns>uint16 read</returns>
  279. protected ushort ReadUInt16()
  280. {
  281. var data = ReadBytes(2);
  282. return (ushort)(data[0] << 8 | data[1]);
  283. }
  284. /// <summary>
  285. /// Reads next uint32 data type from internal buffer.
  286. /// </summary>
  287. /// <returns>uint32 read</returns>
  288. protected uint ReadUInt32()
  289. {
  290. var data = ReadBytes(4);
  291. return (uint)(data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]);
  292. }
  293. /// <summary>
  294. /// Reads next uint64 data type from internal buffer.
  295. /// </summary>
  296. /// <returns>uint64 read</returns>
  297. protected ulong ReadUInt64()
  298. {
  299. var data = ReadBytes(8);
  300. return ((ulong)data[0] << 56 | (ulong)data[1] << 48 | (ulong)data[2] << 40 | (ulong)data[3] << 32 | (ulong)data[4] << 24 | (ulong)data[5] << 16 | (ulong)data[6] << 8 | data[7]);
  301. }
  302. /// <summary>
  303. /// Reads next int64 data type from internal buffer.
  304. /// </summary>
  305. /// <returns>int64 read</returns>
  306. protected long ReadInt64()
  307. {
  308. var data = ReadBytes(8);
  309. return (int)(data[0] << 56 | data[1] << 48 | data[2] << 40 | data[3] << 32 | data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]);
  310. }
  311. #if !TUNING
  312. /// <summary>
  313. /// Reads next string data type from internal buffer.
  314. /// </summary>
  315. /// <returns>string read</returns>
  316. protected string ReadAsciiString()
  317. {
  318. var length = ReadUInt32();
  319. if (length > int.MaxValue)
  320. {
  321. throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Strings longer than {0} is not supported.", int.MaxValue));
  322. }
  323. return Ascii.GetString(ReadBytes((int)length), 0, (int)length);
  324. }
  325. #endif
  326. /// <summary>
  327. /// Reads next string data type from internal buffer.
  328. /// </summary>
  329. /// <returns>string read</returns>
  330. protected string ReadString()
  331. {
  332. return ReadString(Utf8);
  333. }
  334. /// <summary>
  335. /// Reads next string data type from internal buffer.
  336. /// </summary>
  337. /// <returns>string read</returns>
  338. protected string ReadString(Encoding encoding)
  339. {
  340. var length = ReadUInt32();
  341. if (length > int.MaxValue)
  342. {
  343. throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Strings longer than {0} is not supported.", int.MaxValue));
  344. }
  345. return encoding.GetString(ReadBytes((int)length), 0, (int)length);
  346. }
  347. #if TUNING
  348. /// <summary>
  349. /// Reads next data type as byte array from internal buffer.
  350. /// </summary>
  351. /// <returns>
  352. /// The bytes read.
  353. /// </returns>
  354. protected byte[] ReadBinary()
  355. {
  356. var length = ReadUInt32();
  357. if (length > int.MaxValue)
  358. {
  359. throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Data longer than {0} is not supported.", int.MaxValue));
  360. }
  361. return ReadBytes((int) length);
  362. }
  363. #else
  364. /// <summary>
  365. /// Reads next string data type from internal buffer.
  366. /// </summary>
  367. /// <returns>string read</returns>
  368. protected byte[] ReadBinaryString()
  369. {
  370. var length = ReadUInt32();
  371. if (length > int.MaxValue)
  372. {
  373. throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Strings longer than {0} is not supported.", int.MaxValue));
  374. }
  375. return ReadBytes((int)length);
  376. }
  377. #endif
  378. /// <summary>
  379. /// Reads next mpint data type from internal buffer.
  380. /// </summary>
  381. /// <returns>mpint read.</returns>
  382. protected BigInteger ReadBigInt()
  383. {
  384. var length = ReadUInt32();
  385. var data = ReadBytes((int)length);
  386. #if TUNING
  387. return new BigInteger(data.Reverse());
  388. #else
  389. return new BigInteger(data.Reverse().ToArray());
  390. #endif
  391. }
  392. /// <summary>
  393. /// Reads next name-list data type from internal buffer.
  394. /// </summary>
  395. /// <returns>String array or read data..</returns>
  396. protected string[] ReadNamesList()
  397. {
  398. var namesList = ReadString();
  399. return namesList.Split(',');
  400. }
  401. /// <summary>
  402. /// Reads next extension-pair data type from internal buffer.
  403. /// </summary>
  404. /// <returns>Extensions pair dictionary.</returns>
  405. protected IDictionary<string, string> ReadExtensionPair()
  406. {
  407. var result = new Dictionary<string, string>();
  408. while (!IsEndOfData)
  409. {
  410. var extensionName = ReadString();
  411. var extensionData = ReadString();
  412. result.Add(extensionName, extensionData);
  413. }
  414. return result;
  415. }
  416. #if TUNING
  417. /// <summary>
  418. /// Writes bytes array data into internal buffer.
  419. /// </summary>
  420. /// <param name="data">Byte array data to write.</param>
  421. /// <exception cref="ArgumentNullException"><paramref name="data"/> is null.</exception>
  422. protected void Write(byte[] data)
  423. {
  424. _stream.Write(data, 0, data.Length);
  425. }
  426. #else
  427. /// <summary>
  428. /// Writes bytes array data into internal buffer.
  429. /// </summary>
  430. /// <param name="data">Byte array data to write.</param>
  431. /// <exception cref="ArgumentNullException"><paramref name="data"/> is null.</exception>
  432. protected void Write(IEnumerable<byte> data)
  433. {
  434. _data.AddRange(data);
  435. }
  436. #endif
  437. #if TUNING
  438. /// <summary>
  439. /// Writes a sequence of bytes to the current SSH data stream and advances the current position
  440. /// within this stream by the number of bytes written.
  441. /// </summary>
  442. /// <param name="buffer">An array of bytes. This method write <paramref name="count"/> bytes from buffer to the current SSH data stream.</param>
  443. /// <param name="offset">The zero-based offset in <paramref name="buffer"/> at which to begin writing bytes to the SSH data stream.</param>
  444. /// <param name="count">The number of bytes to be written to the current SSH data stream.</param>
  445. /// <exception cref="ArgumentNullException"><paramref name="buffer"/> is null.</exception>
  446. /// <exception cref="ArgumentException">The sum of <paramref name="offset"/> and <paramref name="count"/> is greater than the buffer length.</exception>
  447. /// <exception cref="ArgumentOutOfRangeException"><paramref name="offset"/> or <paramref name="count"/> is negative.</exception>
  448. protected void Write(byte[] buffer, int offset, int count)
  449. {
  450. _stream.Write(buffer, offset, count);
  451. }
  452. #endif
  453. /// <summary>
  454. /// Writes byte data into internal buffer.
  455. /// </summary>
  456. /// <param name="data">Byte data to write.</param>
  457. protected void Write(byte data)
  458. {
  459. #if TUNING
  460. _stream.WriteByte(data);
  461. #else
  462. _data.Add(data);
  463. #endif
  464. }
  465. /// <summary>
  466. /// Writes boolean data into internal buffer.
  467. /// </summary>
  468. /// <param name="data">Boolean data to write.</param>
  469. protected void Write(bool data)
  470. {
  471. Write(data ? (byte) 1 : (byte) 0);
  472. }
  473. /// <summary>
  474. /// Writes uint16 data into internal buffer.
  475. /// </summary>
  476. /// <param name="data">uint16 data to write.</param>
  477. protected void Write(ushort data)
  478. {
  479. Write(data.GetBytes());
  480. }
  481. /// <summary>
  482. /// Writes uint32 data into internal buffer.
  483. /// </summary>
  484. /// <param name="data">uint32 data to write.</param>
  485. protected void Write(uint data)
  486. {
  487. Write(data.GetBytes());
  488. }
  489. /// <summary>
  490. /// Writes uint64 data into internal buffer.
  491. /// </summary>
  492. /// <param name="data">uint64 data to write.</param>
  493. protected void Write(ulong data)
  494. {
  495. Write(data.GetBytes());
  496. }
  497. /// <summary>
  498. /// Writes int64 data into internal buffer.
  499. /// </summary>
  500. /// <param name="data">int64 data to write.</param>
  501. protected void Write(long data)
  502. {
  503. Write(data.GetBytes());
  504. }
  505. /// <summary>
  506. /// Writes string data into internal buffer as ASCII.
  507. /// </summary>
  508. /// <param name="data">string data to write.</param>
  509. protected void WriteAscii(string data)
  510. {
  511. Write(data, Ascii);
  512. }
  513. /// <summary>
  514. /// Writes string data into internal buffer using default encoding.
  515. /// </summary>
  516. /// <param name="data">string data to write.</param>
  517. /// <exception cref="ArgumentNullException"><paramref name="data"/> is null.</exception>
  518. protected void Write(string data)
  519. {
  520. Write(data, Utf8);
  521. }
  522. /// <summary>
  523. /// Writes string data into internal buffer using the specified encoding.
  524. /// </summary>
  525. /// <param name="data">string data to write.</param>
  526. /// <param name="encoding">The character encoding to use.</param>
  527. /// <exception cref="ArgumentNullException"><paramref name="data"/> is null.</exception>
  528. /// <exception cref="ArgumentNullException"><paramref name="encoding"/> is null.</exception>
  529. protected void Write(string data, Encoding encoding)
  530. {
  531. if (data == null)
  532. throw new ArgumentNullException("data");
  533. if (encoding == null)
  534. throw new ArgumentNullException("encoding");
  535. var bytes = encoding.GetBytes(data);
  536. #if TUNING
  537. var bytesLength = bytes.Length;
  538. Write((uint) bytesLength);
  539. Write(bytes, 0, bytesLength);
  540. #else
  541. Write((uint)bytes.Length);
  542. Write(bytes);
  543. #endif
  544. }
  545. #if TUNING
  546. /// <summary>
  547. /// Writes data into internal buffer.
  548. /// </summary>
  549. /// <param name="buffer">The data to write.</param>
  550. /// <exception cref="ArgumentNullException"><paramref name="buffer"/> is null.</exception>
  551. protected void WriteBinaryString(byte[] buffer)
  552. {
  553. if (buffer == null)
  554. throw new ArgumentNullException("buffer");
  555. var bufferLength = buffer.Length;
  556. Write((uint)bufferLength);
  557. Write(buffer, 0, bufferLength);
  558. }
  559. /// <summary>
  560. /// Writes data into internal buffer.
  561. /// </summary>
  562. /// <param name="buffer">An array of bytes. This method write <paramref name="count"/> bytes from buffer to the current SSH data stream.</param>
  563. /// <param name="offset">The zero-based byte offset in <paramref name="buffer"/> at which to begin writing bytes to the SSH data stream.</param>
  564. /// <param name="count">The number of bytes to be written to the current SSH data stream.</param>
  565. /// <exception cref="ArgumentNullException"><paramref name="buffer"/> is null.</exception>
  566. /// <exception cref="ArgumentException">The sum of <paramref name="offset"/> and <paramref name="count"/> is greater than the buffer length.</exception>
  567. /// <exception cref="ArgumentOutOfRangeException"><paramref name="offset"/> or <paramref name="count"/> is negative.</exception>
  568. protected void WriteBinary(byte[] buffer, int offset, int count)
  569. {
  570. if (buffer == null)
  571. throw new ArgumentNullException("buffer");
  572. Write((uint) count);
  573. Write(buffer, offset, count);
  574. }
  575. #else
  576. /// <summary>
  577. /// Writes string data into internal buffer.
  578. /// </summary>
  579. /// <param name="data">string data to write.</param>
  580. /// <exception cref="ArgumentNullException"><paramref name="data"/> is null.</exception>
  581. protected void WriteBinaryString(byte[] data)
  582. {
  583. if (data == null)
  584. throw new ArgumentNullException("data");
  585. Write((uint)data.Length);
  586. _data.AddRange(data);
  587. }
  588. #endif
  589. /// <summary>
  590. /// Writes mpint data into internal buffer.
  591. /// </summary>
  592. /// <param name="data">mpint data to write.</param>
  593. protected void Write(BigInteger data)
  594. {
  595. #if TUNING
  596. var bytes = data.ToByteArray().Reverse();
  597. var bytesLength = bytes.Length;
  598. Write((uint) bytesLength);
  599. Write(bytes, 0, bytesLength);
  600. #else
  601. var bytes = data.ToByteArray().Reverse().ToList();
  602. Write((uint)bytes.Count);
  603. Write(bytes);
  604. #endif
  605. }
  606. /// <summary>
  607. /// Writes name-list data into internal buffer.
  608. /// </summary>
  609. /// <param name="data">name-list data to write.</param>
  610. protected void Write(string[] data)
  611. {
  612. WriteAscii(string.Join(",", data));
  613. }
  614. /// <summary>
  615. /// Writes extension-pair data into internal buffer.
  616. /// </summary>
  617. /// <param name="data">extension-pair data to write.</param>
  618. protected void Write(IDictionary<string, string> data)
  619. {
  620. foreach (var item in data)
  621. {
  622. WriteAscii(item.Key);
  623. WriteAscii(item.Value);
  624. }
  625. }
  626. }
  627. }