Extensions.cs 13 KB

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