Extensions.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Globalization;
  5. using System.Net;
  6. using System.Net.Sockets;
  7. using System.Text;
  8. using Renci.SshNet.Abstractions;
  9. using Renci.SshNet.Messages;
  10. namespace Renci.SshNet.Common
  11. {
  12. /// <summary>
  13. /// Collection of different extension methods.
  14. /// </summary>
  15. internal static partial class Extensions
  16. {
  17. internal static byte[] ToArray(this ServiceName serviceName)
  18. {
  19. switch (serviceName)
  20. {
  21. case ServiceName.UserAuthentication:
  22. return SshData.Ascii.GetBytes("ssh-userauth");
  23. case ServiceName.Connection:
  24. return SshData.Ascii.GetBytes("ssh-connection");
  25. default:
  26. throw new NotSupportedException(string.Format("Service name '{0}' is not supported.", serviceName));
  27. }
  28. }
  29. internal static ServiceName ToServiceName(this byte[] data)
  30. {
  31. var sshServiceName = SshData.Ascii.GetString(data, 0, data.Length);
  32. switch (sshServiceName)
  33. {
  34. case "ssh-userauth":
  35. return ServiceName.UserAuthentication;
  36. case "ssh-connection":
  37. return ServiceName.Connection;
  38. default:
  39. throw new NotSupportedException(string.Format("Service name '{0}' is not supported.", sshServiceName));
  40. }
  41. }
  42. internal static BigInteger ToBigInteger(this byte[] data)
  43. {
  44. var reversed = new byte[data.Length];
  45. Buffer.BlockCopy(data, 0, reversed, 0, data.Length);
  46. return new BigInteger(reversed.Reverse());
  47. }
  48. /// <summary>
  49. /// Initializes a new instance of the <see cref="BigInteger"/> structure using the SSH BigNum2 Format.
  50. /// </summary>
  51. public static BigInteger ToBigInteger2(this byte[] data)
  52. {
  53. if ((data[0] & (1 << 7)) != 0)
  54. {
  55. var buf = new byte[data.Length + 1];
  56. Buffer.BlockCopy(data, 0, buf, 1, data.Length);
  57. data = buf;
  58. }
  59. return data.ToBigInteger();
  60. }
  61. /// <summary>
  62. /// Reverses the sequence of the elements in the entire one-dimensional <see cref="Array"/>.
  63. /// </summary>
  64. /// <param name="array">The one-dimensional <see cref="Array"/> to reverse.</param>
  65. /// <returns>
  66. /// The <see cref="Array"/> with its elements reversed.
  67. /// </returns>
  68. internal static T[] Reverse<T>(this T[] array)
  69. {
  70. Array.Reverse(array);
  71. return array;
  72. }
  73. /// <summary>
  74. /// Prints out the specified bytes.
  75. /// </summary>
  76. /// <param name="bytes">The bytes.</param>
  77. internal static void DebugPrint(this IEnumerable<byte> bytes)
  78. {
  79. var sb = new StringBuilder();
  80. foreach (var b in bytes)
  81. {
  82. _ = sb.AppendFormat(CultureInfo.CurrentCulture, "0x{0:x2}, ", b);
  83. }
  84. Debug.WriteLine(sb.ToString());
  85. }
  86. internal static void ValidatePort(this uint value, string argument)
  87. {
  88. if (value > IPEndPoint.MaxPort)
  89. {
  90. throw new ArgumentOutOfRangeException(argument,
  91. string.Format(CultureInfo.InvariantCulture, "Specified value cannot be greater than {0}.", IPEndPoint.MaxPort));
  92. }
  93. }
  94. internal static void ValidatePort(this int value, string argument)
  95. {
  96. if (value < IPEndPoint.MinPort)
  97. {
  98. throw new ArgumentOutOfRangeException(argument, string.Format(CultureInfo.InvariantCulture, "Specified value cannot be less than {0}.", IPEndPoint.MinPort));
  99. }
  100. if (value > IPEndPoint.MaxPort)
  101. {
  102. throw new ArgumentOutOfRangeException(argument, string.Format(CultureInfo.InvariantCulture, "Specified value cannot be greater than {0}.", IPEndPoint.MaxPort));
  103. }
  104. }
  105. /// <summary>
  106. /// Returns a specified number of contiguous bytes from a given offset.
  107. /// </summary>
  108. /// <param name="value">The array to return a number of bytes from.</param>
  109. /// <param name="offset">The zero-based offset in <paramref name="value"/> at which to begin taking bytes.</param>
  110. /// <param name="count">The number of bytes to take from <paramref name="value"/>.</param>
  111. /// <returns>
  112. /// A <see cref="byte"/> array that contains the specified number of bytes at the specified offset
  113. /// of the input array.
  114. /// </returns>
  115. /// <exception cref="ArgumentNullException"><paramref name="value"/> is <see langword="null"/>.</exception>
  116. /// <remarks>
  117. /// When <paramref name="offset"/> is zero and <paramref name="count"/> equals the length of <paramref name="value"/>,
  118. /// then <paramref name="value"/> is returned.
  119. /// </remarks>
  120. public static byte[] Take(this byte[] value, int offset, int count)
  121. {
  122. if (value is null)
  123. {
  124. throw new ArgumentNullException(nameof(value));
  125. }
  126. if (count == 0)
  127. {
  128. return Array.Empty<byte>();
  129. }
  130. if (offset == 0 && value.Length == count)
  131. {
  132. return value;
  133. }
  134. var taken = new byte[count];
  135. Buffer.BlockCopy(value, offset, taken, 0, count);
  136. return taken;
  137. }
  138. /// <summary>
  139. /// Returns a specified number of contiguous bytes from the start of the specified byte array.
  140. /// </summary>
  141. /// <param name="value">The array to return a number of bytes from.</param>
  142. /// <param name="count">The number of bytes to take from <paramref name="value"/>.</param>
  143. /// <returns>
  144. /// A <see cref="byte"/> array that contains the specified number of bytes at the start of the input array.
  145. /// </returns>
  146. /// <exception cref="ArgumentNullException"><paramref name="value"/> is <see langword="null"/>.</exception>
  147. /// <remarks>
  148. /// When <paramref name="count"/> equals the length of <paramref name="value"/>, then <paramref name="value"/>
  149. /// is returned.
  150. /// </remarks>
  151. public static byte[] Take(this byte[] value, int count)
  152. {
  153. if (value is null)
  154. {
  155. throw new ArgumentNullException(nameof(value));
  156. }
  157. if (count == 0)
  158. {
  159. return Array.Empty<byte>();
  160. }
  161. if (value.Length == count)
  162. {
  163. return value;
  164. }
  165. var taken = new byte[count];
  166. Buffer.BlockCopy(value, 0, taken, 0, count);
  167. return taken;
  168. }
  169. public static bool IsEqualTo(this byte[] left, byte[] right)
  170. {
  171. if (left is null)
  172. {
  173. throw new ArgumentNullException(nameof(left));
  174. }
  175. if (right is null)
  176. {
  177. throw new ArgumentNullException(nameof(right));
  178. }
  179. if (left == right)
  180. {
  181. return true;
  182. }
  183. #if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER
  184. return left.AsSpan().SequenceEqual(right);
  185. #else
  186. if (left.Length != right.Length)
  187. {
  188. return false;
  189. }
  190. for (var i = 0; i < left.Length; i++)
  191. {
  192. if (left[i] != right[i])
  193. {
  194. return false;
  195. }
  196. }
  197. return true;
  198. #endif
  199. }
  200. /// <summary>
  201. /// Trims the leading zero from a byte array.
  202. /// </summary>
  203. /// <param name="value">The value.</param>
  204. /// <returns>
  205. /// <paramref name="value"/> without leading zeros.
  206. /// </returns>
  207. public static byte[] TrimLeadingZeros(this byte[] value)
  208. {
  209. if (value is null)
  210. {
  211. throw new ArgumentNullException(nameof(value));
  212. }
  213. for (var i = 0; i < value.Length; i++)
  214. {
  215. if (value[i] == 0)
  216. {
  217. continue;
  218. }
  219. // if the first byte is non-zero, then we return the byte array as is
  220. if (i == 0)
  221. {
  222. return value;
  223. }
  224. var remainingBytes = value.Length - i;
  225. var cleaned = new byte[remainingBytes];
  226. Buffer.BlockCopy(value, i, cleaned, 0, remainingBytes);
  227. return cleaned;
  228. }
  229. return value;
  230. }
  231. /// <summary>
  232. /// Pads with leading zeros if needed.
  233. /// </summary>
  234. /// <param name="data">The data.</param>
  235. /// <param name="length">The length to pad to.</param>
  236. public static byte[] Pad(this byte[] data, int length)
  237. {
  238. if (length <= data.Length)
  239. {
  240. return data;
  241. }
  242. var newData = new byte[length];
  243. Buffer.BlockCopy(data, 0, newData, newData.Length - data.Length, data.Length);
  244. return newData;
  245. }
  246. public static byte[] Concat(this byte[] first, byte[] second)
  247. {
  248. if (first is null || first.Length == 0)
  249. {
  250. return second;
  251. }
  252. if (second is null || second.Length == 0)
  253. {
  254. return first;
  255. }
  256. var concat = new byte[first.Length + second.Length];
  257. Buffer.BlockCopy(first, 0, concat, 0, first.Length);
  258. Buffer.BlockCopy(second, 0, concat, first.Length, second.Length);
  259. return concat;
  260. }
  261. internal static bool CanRead(this Socket socket)
  262. {
  263. return SocketAbstraction.CanRead(socket);
  264. }
  265. internal static bool CanWrite(this Socket socket)
  266. {
  267. return SocketAbstraction.CanWrite(socket);
  268. }
  269. internal static bool IsConnected(this Socket socket)
  270. {
  271. if (socket is null)
  272. {
  273. return false;
  274. }
  275. return socket.Connected;
  276. }
  277. }
  278. }