SshClient.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Collections.ObjectModel;
  5. using System.Text;
  6. namespace Renci.SshNet
  7. {
  8. /// <summary>
  9. /// Provides client connection to SSH server.
  10. /// </summary>
  11. public class SshClient : BaseClient
  12. {
  13. /// <summary>
  14. /// Holds the list of forwarded ports
  15. /// </summary>
  16. private List<ForwardedPort> _forwardedPorts = new List<ForwardedPort>();
  17. /// <summary>
  18. /// Gets the list of forwarded ports.
  19. /// </summary>
  20. public IEnumerable<ForwardedPort> ForwardedPorts
  21. {
  22. get
  23. {
  24. return this._forwardedPorts.AsReadOnly();
  25. }
  26. }
  27. #region Constructors
  28. /// <summary>
  29. /// Initializes a new instance of the <see cref="SshClient"/> class.
  30. /// </summary>
  31. /// <param name="connectionInfo">The connection info.</param>
  32. /// <exception cref="ArgumentNullException"><paramref name="connectionInfo"/> is null.</exception>
  33. public SshClient(ConnectionInfo connectionInfo)
  34. : base(connectionInfo)
  35. {
  36. }
  37. /// <summary>
  38. /// Initializes a new instance of the <see cref="SshClient"/> class.
  39. /// </summary>
  40. /// <param name="host">Connection host.</param>
  41. /// <param name="port">Connection port.</param>
  42. /// <param name="username">Authentication username.</param>
  43. /// <param name="password">Authentication password.</param>
  44. /// <exception cref="ArgumentNullException"><paramref name="password"/> is null.</exception>
  45. /// <exception cref="ArgumentException"><paramref name="host"/> is invalid, or <paramref name="username"/> is null or contains whitespace characters.</exception>
  46. /// <exception cref="ArgumentOutOfRangeException"><paramref name="port"/> is not within <see cref="System.Net.IPEndPoint.MinPort"/> and <see cref="System.Net.IPEndPoint.MaxPort"/>.</exception>
  47. public SshClient(string host, int port, string username, string password)
  48. : this(new PasswordConnectionInfo(host, port, username, password))
  49. {
  50. }
  51. /// <summary>
  52. /// Initializes a new instance of the <see cref="SshClient"/> class.
  53. /// </summary>
  54. /// <param name="host">Connection host.</param>
  55. /// <param name="username">Authentication username.</param>
  56. /// <param name="password">Authentication password.</param>
  57. /// <exception cref="ArgumentNullException"><paramref name="password"/> is null.</exception>
  58. /// <exception cref="ArgumentException"><paramref name="host"/> is invalid, or <paramref name="username"/> is null or contains whitespace characters.</exception>
  59. public SshClient(string host, string username, string password)
  60. : this(host, 22, username, password)
  61. {
  62. }
  63. /// <summary>
  64. /// Initializes a new instance of the <see cref="SshClient"/> class.
  65. /// </summary>
  66. /// <param name="host">Connection host.</param>
  67. /// <param name="port">Connection port.</param>
  68. /// <param name="username">Authentication username.</param>
  69. /// <param name="keyFiles">Authentication private key file(s) .</param>
  70. /// <exception cref="ArgumentNullException"><paramref name="keyFiles"/> is null.</exception>
  71. /// <exception cref="ArgumentException"><paramref name="host"/> is invalid, -or- <paramref name="username"/> is null or contains whitespace characters.</exception>
  72. /// <exception cref="ArgumentOutOfRangeException"><paramref name="port"/> is not within <see cref="System.Net.IPEndPoint.MinPort"/> and <see cref="System.Net.IPEndPoint.MaxPort"/>.</exception>
  73. public SshClient(string host, int port, string username, params PrivateKeyFile[] keyFiles)
  74. : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles))
  75. {
  76. }
  77. /// <summary>
  78. /// Initializes a new instance of the <see cref="SshClient"/> class.
  79. /// </summary>
  80. /// <param name="host">Connection host.</param>
  81. /// <param name="username">Authentication username.</param>
  82. /// <param name="keyFiles">Authentication private key file(s) .</param>
  83. /// <exception cref="ArgumentNullException"><paramref name="keyFiles"/> is null.</exception>
  84. /// <exception cref="ArgumentException"><paramref name="host"/> is invalid, -or- <paramref name="username"/> is null or contains whitespace characters.</exception>
  85. public SshClient(string host, string username, params PrivateKeyFile[] keyFiles)
  86. : this(host, 22, username, keyFiles)
  87. {
  88. }
  89. #endregion
  90. /// <summary>
  91. /// Called when client is disconnecting from the server.
  92. /// </summary>
  93. protected override void OnDisconnecting()
  94. {
  95. base.OnDisconnecting();
  96. foreach (var port in this._forwardedPorts)
  97. {
  98. port.Stop();
  99. }
  100. }
  101. /// <summary>
  102. /// Adds forwarded port to the list.
  103. /// </summary>
  104. /// <typeparam name="T">Type of forwarded port to add</typeparam>
  105. /// <param name="boundHost">The bound host.</param>
  106. /// <param name="boundPort">The bound port.</param>
  107. /// <param name="connectedHost">The connected host.</param>
  108. /// <param name="connectedPort">The connected port.</param>
  109. /// <returns>
  110. /// Forwarded port
  111. /// </returns>
  112. /// <exception cref="ArgumentNullException"><paramref name="boundHost"/> or <paramref name="connectedHost"/> is null.</exception>
  113. /// <exception cref="ArgumentException"><paramref name="boundHost"/> or <paramref name="connectedHost"/> is invalid.</exception>
  114. /// <exception cref="ArgumentOutOfRangeException"><paramref name="boundPort"/> or <paramref name="connectedPort"/> is not within <see cref="System.Net.IPEndPoint.MinPort"/> and <see cref="System.Net.IPEndPoint.MaxPort"/>.</exception>
  115. /// <exception cref="Renci.SshNet.Common.SshConnectionException">Client is not connected.</exception>
  116. public T AddForwardedPort<T>(string boundHost, uint boundPort, string connectedHost, uint connectedPort) where T : ForwardedPort, new()
  117. {
  118. if (boundHost == null)
  119. throw new ArgumentNullException("boundHost");
  120. if (connectedHost == null)
  121. throw new ArgumentNullException("connectedHost");
  122. if (!boundHost.IsValidHost())
  123. throw new ArgumentException("boundHost");
  124. if (!boundPort.IsValidPort())
  125. throw new ArgumentOutOfRangeException("boundPort");
  126. if (!connectedHost.IsValidHost())
  127. throw new ArgumentException("connectedHost");
  128. if (!connectedPort.IsValidPort())
  129. throw new ArgumentOutOfRangeException("connectedPort");
  130. // Ensure that connection is established.
  131. this.EnsureConnection();
  132. T port = new T();
  133. port.Session = this.Session;
  134. port.BoundHost = boundHost;
  135. port.BoundPort = boundPort;
  136. port.Host = connectedHost;
  137. port.Port = connectedPort;
  138. this._forwardedPorts.Add(port);
  139. return port;
  140. }
  141. /// <summary>
  142. /// Adds forwarded port to the list bound to "localhost".
  143. /// </summary>
  144. /// <typeparam name="T">Type of forwarded port to add</typeparam>
  145. /// <param name="boundPort">The bound port.</param>
  146. /// <param name="connectedHost">The connected host.</param>
  147. /// <param name="connectedPort">The connected port.</param>
  148. /// <returns></returns>
  149. /// <exception cref="ArgumentNullException"><paramref name="connectedHost"/> is null.</exception>
  150. /// <exception cref="ArgumentException"><paramref name="boundPort"/>, <paramref name="connectedPort"/> or <paramref name="connectedHost"/> is invalid.</exception>
  151. /// <exception cref="ArgumentOutOfRangeException"><paramref name="boundPort"/> or <paramref name="connectedPort"/> is not within <see cref="System.Net.IPEndPoint.MinPort"/> and <see cref="System.Net.IPEndPoint.MaxPort"/>.</exception>
  152. /// <exception cref="Renci.SshNet.Common.SshConnectionException">Client is not connected.</exception>
  153. public T AddForwardedPort<T>(uint boundPort, string connectedHost, uint connectedPort) where T : ForwardedPort, new()
  154. {
  155. return this.AddForwardedPort<T>("localhost", boundPort, connectedHost, connectedPort);
  156. }
  157. /// <summary>
  158. /// Stops and removes the forwarded port from the list.
  159. /// </summary>
  160. /// <param name="port">Forwarded port.</param>
  161. /// <exception cref="ArgumentNullException"><paramref name="port"/> is null.</exception>
  162. public void RemoveForwardedPort(ForwardedPort port)
  163. {
  164. if (port == null)
  165. throw new ArgumentNullException("port");
  166. // Stop port forwarding before removing it
  167. port.Stop();
  168. this._forwardedPorts.Remove(port);
  169. }
  170. /// <summary>
  171. /// Creates the command to be executed.
  172. /// </summary>
  173. /// <param name="commandText">The command text.</param>
  174. /// <returns><see cref="SshCommand"/> object.</returns>
  175. public SshCommand CreateCommand(string commandText)
  176. {
  177. return this.CreateCommand(commandText, Encoding.UTF8);
  178. }
  179. /// <summary>
  180. /// Creates the command to be executed with specified encoding.
  181. /// </summary>
  182. /// <param name="commandText">The command text.</param>
  183. /// <param name="encoding">The encoding to use for results.</param>
  184. /// <returns><see cref="SshCommand"/> object which uses specified encoding.</returns>
  185. public SshCommand CreateCommand(string commandText, Encoding encoding)
  186. {
  187. // Ensure that connection is established.
  188. this.EnsureConnection();
  189. return new SshCommand(this.Session, commandText, encoding);
  190. }
  191. /// <summary>
  192. /// Creates and executes the command.
  193. /// </summary>
  194. /// <param name="commandText">The command text.</param>
  195. /// <returns>Returns an instance of <see cref="SshCommand"/> with execution results.</returns>
  196. /// <remarks>This method internally uses asynchronous calls.</remarks>
  197. /// <exception cref="ArgumentException">CommandText property is empty.</exception>
  198. /// <exception cref="Renci.SshNet.Common.SshException">Invalid Operation - An existing channel was used to execute this command.</exception>
  199. /// <exception cref="InvalidOperationException">Asynchronous operation is already in progress.</exception>
  200. public SshCommand RunCommand(string commandText)
  201. {
  202. var cmd = this.CreateCommand(commandText);
  203. cmd.Execute();
  204. return cmd;
  205. }
  206. /// <summary>
  207. /// Creates the shell.
  208. /// </summary>
  209. /// <param name="input">The input.</param>
  210. /// <param name="output">The output.</param>
  211. /// <param name="extendedOutput">The extended output.</param>
  212. /// <param name="terminalName">Name of the terminal.</param>
  213. /// <param name="columns">The columns.</param>
  214. /// <param name="rows">The rows.</param>
  215. /// <param name="width">The width.</param>
  216. /// <param name="height">The height.</param>
  217. /// <param name="terminalMode">The terminal mode.</param>
  218. /// <param name="bufferSize">Size of the internal read buffer.</param>
  219. /// <returns>Returns a representation of a <see cref="Shell"/> object.</returns>
  220. public Shell CreateShell(Stream input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, string terminalMode, int bufferSize)
  221. {
  222. // Ensure that connection is established.
  223. this.EnsureConnection();
  224. return new Shell(this.Session, input, output, extendedOutput, terminalName, columns, rows, width, height, terminalMode, bufferSize);
  225. }
  226. /// <summary>
  227. /// Creates the shell.
  228. /// </summary>
  229. /// <param name="input">The input.</param>
  230. /// <param name="output">The output.</param>
  231. /// <param name="extendedOutput">The extended output.</param>
  232. /// <param name="terminalName">Name of the terminal.</param>
  233. /// <param name="columns">The columns.</param>
  234. /// <param name="rows">The rows.</param>
  235. /// <param name="width">The width.</param>
  236. /// <param name="height">The height.</param>
  237. /// <param name="terminalMode">The terminal mode.</param>
  238. /// <returns>Returns a representation of a <see cref="Shell"/> object.</returns>
  239. public Shell CreateShell(Stream input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, string terminalMode)
  240. {
  241. return this.CreateShell(input, output, extendedOutput, terminalName, columns, rows, width, height, terminalMode, 1024);
  242. }
  243. /// <summary>
  244. /// Creates the shell.
  245. /// </summary>
  246. /// <param name="input">The input.</param>
  247. /// <param name="output">The output.</param>
  248. /// <param name="extendedOutput">The extended output.</param>
  249. /// <returns>Returns a representation of a <see cref="Shell"/> object.</returns>
  250. public Shell CreateShell(Stream input, Stream output, Stream extendedOutput)
  251. {
  252. return this.CreateShell(input, output, extendedOutput, string.Empty, 0, 0, 0, 0, string.Empty, 1024);
  253. }
  254. /// <summary>
  255. /// Creates the shell.
  256. /// </summary>
  257. /// <param name="encoding">The encoding to use to send the input.</param>
  258. /// <param name="input">The input.</param>
  259. /// <param name="output">The output.</param>
  260. /// <param name="extendedOutput">The extended output.</param>
  261. /// <param name="terminalName">Name of the terminal.</param>
  262. /// <param name="columns">The columns.</param>
  263. /// <param name="rows">The rows.</param>
  264. /// <param name="width">The width.</param>
  265. /// <param name="height">The height.</param>
  266. /// <param name="terminalMode">The terminal mode.</param>
  267. /// <param name="bufferSize">Size of the internal read buffer.</param>
  268. /// <returns>Returns a representation of a <see cref="Shell"/> object.</returns>
  269. public Shell CreateShell(Encoding encoding, string input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width , uint height , string terminalMode, int bufferSize)
  270. {
  271. // Ensure that connection is established.
  272. this.EnsureConnection();
  273. var inputStream = new MemoryStream();
  274. var writer = new StreamWriter(inputStream, encoding);
  275. writer.Write(input);
  276. writer.Flush();
  277. inputStream.Seek(0, SeekOrigin.Begin);
  278. return this.CreateShell(inputStream, output, extendedOutput, terminalName, columns, rows, width, height, terminalMode, bufferSize);
  279. }
  280. /// <summary>
  281. /// Creates the shell.
  282. /// </summary>
  283. /// <param name="encoding">The encoding.</param>
  284. /// <param name="input">The input.</param>
  285. /// <param name="output">The output.</param>
  286. /// <param name="extendedOutput">The extended output.</param>
  287. /// <param name="terminalName">Name of the terminal.</param>
  288. /// <param name="columns">The columns.</param>
  289. /// <param name="rows">The rows.</param>
  290. /// <param name="width">The width.</param>
  291. /// <param name="height">The height.</param>
  292. /// <param name="terminalMode">The terminal mode.</param>
  293. /// <returns>Returns a representation of a <see cref="Shell"/> object.</returns>
  294. public Shell CreateShell(Encoding encoding, string input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, string terminalMode)
  295. {
  296. return this.CreateShell(encoding, input, output, extendedOutput, terminalName, columns, rows, width, height, terminalMode, 1024);
  297. }
  298. /// <summary>
  299. /// Creates the shell.
  300. /// </summary>
  301. /// <param name="encoding">The encoding.</param>
  302. /// <param name="input">The input.</param>
  303. /// <param name="output">The output.</param>
  304. /// <param name="extendedOutput">The extended output.</param>
  305. /// <returns>Returns a representation of a <see cref="Shell"/> object.</returns>
  306. public Shell CreateShell(Encoding encoding, string input, Stream output, Stream extendedOutput)
  307. {
  308. return this.CreateShell(encoding, input, output, extendedOutput, string.Empty, 0, 0, 0, 0, string.Empty, 1024);
  309. }
  310. }
  311. }