Session.SilverlightShared.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.Linq;
  5. using System.Net;
  6. using System.Net.Sockets;
  7. using System.Threading;
  8. using Renci.SshNet.Common;
  9. using Renci.SshNet.Messages.Transport;
  10. namespace Renci.SshNet
  11. {
  12. public partial class Session
  13. {
  14. private const byte Null = 0x00;
  15. private const byte CarriageReturn = 0x0d;
  16. private const byte LineFeed = 0x0a;
  17. private static readonly TimeSpan Infinite = new TimeSpan(0, 0, 0, 0, -1);
  18. private readonly AutoResetEvent _connectEvent = new AutoResetEvent(false);
  19. private readonly AutoResetEvent _sendEvent = new AutoResetEvent(false);
  20. private readonly AutoResetEvent _receiveEvent = new AutoResetEvent(false);
  21. /// <summary>
  22. /// Gets a value indicating whether the socket is connected.
  23. /// </summary>
  24. /// <param name="isConnected"><c>true</c> if the socket is connected; otherwise, <c>false</c></param>
  25. partial void IsSocketConnected(ref bool isConnected)
  26. {
  27. isConnected = (_socket != null && _socket.Connected);
  28. }
  29. /// <summary>
  30. /// Establishes a socket connection to the specified host and port.
  31. /// </summary>
  32. /// <param name="host">The host name of the server to connect to.</param>
  33. /// <param name="port">The port to connect to.</param>
  34. /// <exception cref="SshOperationTimeoutException">The connection failed to establish within the configured <see cref="Renci.SshNet.ConnectionInfo.Timeout"/>.</exception>
  35. /// <exception cref="SocketException">An error occurred trying to establish the connection.</exception>
  36. partial void SocketConnect(string host, int port)
  37. {
  38. var timeout = ConnectionInfo.Timeout;
  39. var ipAddress = host.GetIPAddress();
  40. var ep = new IPEndPoint(ipAddress, port);
  41. _socket = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
  42. var args = CreateSocketAsyncEventArgs(_connectEvent);
  43. if (_socket.ConnectAsync(args))
  44. {
  45. if (!_connectEvent.WaitOne(timeout))
  46. throw new SshOperationTimeoutException(string.Format(CultureInfo.InvariantCulture,
  47. "Connection failed to establish within {0:F0} milliseconds.", timeout.TotalMilliseconds));
  48. }
  49. if (args.SocketError != SocketError.Success)
  50. throw new SocketException((int) args.SocketError);
  51. }
  52. /// <summary>
  53. /// Closes the socket.
  54. /// </summary>
  55. /// <remarks>
  56. /// This method will wait up to <c>10</c> seconds to send any remaining data.
  57. /// </remarks>
  58. partial void SocketDisconnect()
  59. {
  60. _socket.Close(10);
  61. }
  62. /// <summary>
  63. /// Performs a blocking read on the socket until a line is read.
  64. /// </summary>
  65. /// <param name="response">The line read from the socket, or <c>null</c> when the remote server has shutdown and all data has been received.</param>
  66. /// <param name="timeout">A <see cref="TimeSpan"/> that represents the time to wait until a line is read.</param>
  67. /// <exception cref="SshOperationTimeoutException">The read has timed-out.</exception>
  68. /// <exception cref="SocketException">An error occurred when trying to access the socket.</exception>
  69. partial void SocketReadLine(ref string response, TimeSpan timeout)
  70. {
  71. var encoding = new ASCIIEncoding();
  72. var buffer = new List<byte>();
  73. var data = new byte[1];
  74. // read data one byte at a time to find end of line and leave any unhandled information in the buffer
  75. // to be processed by subsequent invocations
  76. do
  77. {
  78. var args = CreateSocketAsyncEventArgs(_receiveEvent, data, 0, data.Length);
  79. if (_socket.ReceiveAsync(args))
  80. {
  81. if (!_receiveEvent.WaitOne(timeout))
  82. throw new SshOperationTimeoutException(string.Format(CultureInfo.InvariantCulture,
  83. "Socket read operation has timed out after {0:F0} milliseconds.", timeout.TotalMilliseconds));
  84. }
  85. if (args.SocketError != SocketError.Success)
  86. throw new SocketException((int) args.SocketError);
  87. if (args.BytesTransferred == 0)
  88. // the remote server shut down the socket
  89. break;
  90. buffer.Add(data[0]);
  91. }
  92. while (!(buffer.Count > 0 && (buffer[buffer.Count - 1] == LineFeed || buffer[buffer.Count - 1] == Null)));
  93. if (buffer.Count == 0)
  94. response = null;
  95. else if (buffer.Count == 1 && buffer[buffer.Count - 1] == 0x00)
  96. // return an empty version string if the buffer consists of only a 0x00 character
  97. response = string.Empty;
  98. else if (buffer.Count > 1 && buffer[buffer.Count - 2] == CarriageReturn)
  99. // strip trailing CRLF
  100. response = encoding.GetString(buffer.ToArray(), 0, buffer.Count - 2);
  101. else if (buffer.Count > 1 && buffer[buffer.Count - 1] == LineFeed)
  102. // strip trailing LF
  103. response = encoding.GetString(buffer.ToArray(), 0, buffer.Count - 1);
  104. else
  105. response = encoding.GetString(buffer.ToArray(), 0, buffer.Count);
  106. }
  107. /// <summary>
  108. /// Performs a blocking read on the socket until <paramref name="length"/> bytes are received.
  109. /// </summary>
  110. /// <param name="length">The number of bytes to read.</param>
  111. /// <param name="buffer">The buffer to read to.</param>
  112. /// <exception cref="SshConnectionException">The socket is closed.</exception>
  113. /// <exception cref="SshOperationTimeoutException">The read has timed-out.</exception>
  114. /// <exception cref="SocketException">The read failed.</exception>
  115. partial void SocketRead(int length, ref byte[] buffer)
  116. {
  117. var timeout = Infinite;
  118. var totalBytesReceived = 0; // how many bytes are already received
  119. do
  120. {
  121. var args = CreateSocketAsyncEventArgs(_receiveEvent, buffer, totalBytesReceived,
  122. length - totalBytesReceived);
  123. if (_socket.ReceiveAsync(args))
  124. {
  125. if (!_receiveEvent.WaitOne(timeout))
  126. // currently we wait indefinitely, so this exception will never be thrown
  127. // but let's leave this here anyway as we may revisit this later
  128. throw new SshOperationTimeoutException(string.Format(CultureInfo.InvariantCulture,
  129. "Socket read operation has timed out after {0:F0} milliseconds.", timeout.TotalMilliseconds));
  130. }
  131. switch (args.SocketError)
  132. {
  133. case SocketError.WouldBlock:
  134. case SocketError.IOPending:
  135. case SocketError.NoBufferSpaceAvailable:
  136. // socket buffer is probably full, wait and try again
  137. Thread.Sleep(30);
  138. break;
  139. case SocketError.Success:
  140. var bytesReceived = args.BytesTransferred;
  141. if (bytesReceived > 0)
  142. {
  143. totalBytesReceived += bytesReceived;
  144. continue;
  145. }
  146. if (_isDisconnecting)
  147. throw new SshConnectionException(
  148. "An established connection was aborted by the software in your host machine.",
  149. DisconnectReason.ConnectionLost);
  150. throw new SshConnectionException("An established connection was aborted by the server.",
  151. DisconnectReason.ConnectionLost);
  152. default:
  153. throw new SocketException((int) args.SocketError);
  154. }
  155. } while (totalBytesReceived < length);
  156. }
  157. /// <summary>
  158. /// Writes the specified data to the server.
  159. /// </summary>
  160. /// <param name="data">The data to write to the server.</param>
  161. /// <exception cref="SshOperationTimeoutException">The write has timed-out.</exception>
  162. /// <exception cref="SocketException">The write failed.</exception>
  163. partial void SocketWrite(byte[] data)
  164. {
  165. var timeout = ConnectionInfo.Timeout;
  166. var totalBytesSent = 0; // how many bytes are already sent
  167. var totalBytesToSend = data.Length;
  168. do
  169. {
  170. var args = CreateSocketAsyncEventArgs(_sendEvent, data, 0, totalBytesToSend - totalBytesSent);
  171. if (_socket.SendAsync(args))
  172. {
  173. if (!_sendEvent.WaitOne(timeout))
  174. throw new SshOperationTimeoutException(string.Format(CultureInfo.InvariantCulture,
  175. "Socket write operation has timed out after {0:F0} milliseconds.", timeout.TotalMilliseconds));
  176. }
  177. switch (args.SocketError)
  178. {
  179. case SocketError.WouldBlock:
  180. case SocketError.IOPending:
  181. case SocketError.NoBufferSpaceAvailable:
  182. // socket buffer is probably full, wait and try again
  183. Thread.Sleep(30);
  184. break;
  185. case SocketError.Success:
  186. totalBytesSent += args.BytesTransferred;
  187. break;
  188. default:
  189. throw new SocketException((int) args.SocketError);
  190. }
  191. } while (totalBytesSent < totalBytesToSend);
  192. }
  193. partial void ExecuteThread(Action action)
  194. {
  195. ThreadPool.QueueUserWorkItem(o => action());
  196. }
  197. partial void InternalRegisterMessage(string messageName)
  198. {
  199. lock (_messagesMetadata)
  200. {
  201. foreach (var item in from m in _messagesMetadata where m.Name == messageName select m)
  202. {
  203. item.Enabled = true;
  204. item.Activated = true;
  205. }
  206. }
  207. }
  208. partial void InternalUnRegisterMessage(string messageName)
  209. {
  210. lock (_messagesMetadata)
  211. {
  212. foreach (var item in from m in _messagesMetadata where m.Name == messageName select m)
  213. {
  214. item.Enabled = false;
  215. item.Activated = false;
  216. }
  217. }
  218. }
  219. private SocketAsyncEventArgs CreateSocketAsyncEventArgs(EventWaitHandle waitHandle)
  220. {
  221. var args = new SocketAsyncEventArgs();
  222. args.UserToken = _socket;
  223. args.RemoteEndPoint = _socket.RemoteEndPoint;
  224. args.Completed += (sender, eventArgs) => waitHandle.Set();
  225. return args;
  226. }
  227. private SocketAsyncEventArgs CreateSocketAsyncEventArgs(EventWaitHandle waitHandle, byte[] data, int offset, int count)
  228. {
  229. var args = CreateSocketAsyncEventArgs(waitHandle);
  230. args.SetBuffer(data, offset, count);
  231. return args;
  232. }
  233. }
  234. }