ForwardedPortDynamic.NET.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666
  1. using System;
  2. using System.Diagnostics;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Net;
  6. using System.Net.Sockets;
  7. using System.Threading;
  8. using Renci.SshNet.Abstractions;
  9. using Renci.SshNet.Channels;
  10. using Renci.SshNet.Common;
  11. namespace Renci.SshNet
  12. {
  13. public partial class ForwardedPortDynamic
  14. {
  15. private Socket _listener;
  16. private int _pendingRequests;
  17. #if FEATURE_SOCKET_EAP
  18. private ManualResetEvent _stoppingListener;
  19. #endif // FEATURE_SOCKET_EAP
  20. partial void InternalStart()
  21. {
  22. var ip = IPAddress.Any;
  23. if (!string.IsNullOrEmpty(BoundHost))
  24. {
  25. ip = DnsAbstraction.GetHostAddresses(BoundHost)[0];
  26. }
  27. var ep = new IPEndPoint(ip, (int) BoundPort);
  28. _listener = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
  29. // TODO: decide if we want to have blocking socket
  30. #if FEATURE_SOCKET_SETSOCKETOPTION
  31. _listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);
  32. _listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true);
  33. #endif //FEATURE_SOCKET_SETSOCKETOPTION
  34. _listener.Bind(ep);
  35. _listener.Listen(5);
  36. Session.ErrorOccured += Session_ErrorOccured;
  37. Session.Disconnected += Session_Disconnected;
  38. _listenerCompleted = new ManualResetEvent(false);
  39. ThreadAbstraction.ExecuteThread(() =>
  40. {
  41. try
  42. {
  43. #if FEATURE_SOCKET_EAP
  44. _stoppingListener = new ManualResetEvent(false);
  45. StartAccept();
  46. _stoppingListener.WaitOne();
  47. #elif FEATURE_SOCKET_APM
  48. while (true)
  49. {
  50. // accept new inbound connection
  51. var asyncResult = _listener.BeginAccept(AcceptCallback, _listener);
  52. // wait for the connection to be established
  53. asyncResult.AsyncWaitHandle.WaitOne();
  54. }
  55. }
  56. catch (ObjectDisposedException)
  57. {
  58. // BeginAccept will throw an ObjectDisposedException when the
  59. // socket is closed
  60. #elif FEATURE_SOCKET_TAP
  61. #error Accepting new socket connections is not implemented.
  62. #else
  63. #error Accepting new socket connections is not implemented.
  64. #endif
  65. }
  66. catch (Exception ex)
  67. {
  68. RaiseExceptionEvent(ex);
  69. }
  70. finally
  71. {
  72. if (Session != null)
  73. {
  74. Session.ErrorOccured -= Session_ErrorOccured;
  75. Session.Disconnected -= Session_Disconnected;
  76. }
  77. // mark listener stopped
  78. _listenerCompleted.Set();
  79. }
  80. });
  81. }
  82. private void Session_Disconnected(object sender, EventArgs e)
  83. {
  84. StopListener();
  85. }
  86. private void Session_ErrorOccured(object sender, ExceptionEventArgs e)
  87. {
  88. StopListener();
  89. }
  90. #if FEATURE_SOCKET_EAP
  91. private void StartAccept()
  92. {
  93. var args = new SocketAsyncEventArgs();
  94. args.Completed += AcceptCompleted;
  95. if (!_listener.AcceptAsync(args))
  96. {
  97. AcceptCompleted(null, args);
  98. }
  99. }
  100. private void AcceptCompleted(object sender, SocketAsyncEventArgs acceptAsyncEventArgs)
  101. {
  102. if (acceptAsyncEventArgs.SocketError != SocketError.Success)
  103. {
  104. StartAccept();
  105. acceptAsyncEventArgs.AcceptSocket.Dispose();
  106. return;
  107. }
  108. StartAccept();
  109. ProcessAccept(acceptAsyncEventArgs.AcceptSocket);
  110. }
  111. #elif FEATURE_SOCKET_APM
  112. private void AcceptCallback(IAsyncResult ar)
  113. {
  114. // Get the socket that handles the client request
  115. var serverSocket = (Socket) ar.AsyncState;
  116. Socket clientSocket;
  117. try
  118. {
  119. clientSocket = serverSocket.EndAccept(ar);
  120. }
  121. catch (ObjectDisposedException)
  122. {
  123. // when the socket is closed, an ObjectDisposedException is thrown
  124. // by Socket.EndAccept(IAsyncResult)
  125. return;
  126. }
  127. ProcessAccept(clientSocket);
  128. }
  129. #endif
  130. private void ProcessAccept(Socket remoteSocket)
  131. {
  132. Interlocked.Increment(ref _pendingRequests);
  133. #if DEBUG_GERT
  134. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | " + remoteSocket.RemoteEndPoint + " | ForwardedPortDynamic.ProcessAccept | " + DateTime.Now.ToString("hh:mm:ss.fff"));
  135. #endif // DEBUG_GERT
  136. try
  137. {
  138. #if FEATURE_SOCKET_SETSOCKETOPTION
  139. remoteSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);
  140. remoteSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true);
  141. #endif //FEATURE_SOCKET_SETSOCKETOPTION
  142. using (var channel = Session.CreateChannelDirectTcpip())
  143. {
  144. channel.Exception += Channel_Exception;
  145. try
  146. {
  147. if (!HandleSocks(channel, remoteSocket, Session.ConnectionInfo.Timeout))
  148. {
  149. CloseSocket(remoteSocket);
  150. return;
  151. }
  152. // start receiving from client socket (and sending to server)
  153. channel.Bind();
  154. }
  155. #if DEBUG_GERT
  156. catch (SocketException ex)
  157. {
  158. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | " + ex.SocketErrorCode + " | " + DateTime.Now.ToString("hh:mm:ss.fff") + " | " + ex);
  159. }
  160. #endif // DEBUG_GERT
  161. finally
  162. {
  163. channel.Close();
  164. }
  165. }
  166. }
  167. catch (SocketException ex)
  168. {
  169. // ignore exception thrown by interrupting the blocking receive as part of closing
  170. // the forwarded port
  171. if (ex.SocketErrorCode != SocketError.Interrupted)
  172. {
  173. #if DEBUG_GERT
  174. RaiseExceptionEvent(new Exception("ID: " + Thread.CurrentThread.ManagedThreadId, ex));
  175. #else
  176. RaiseExceptionEvent(ex);
  177. #endif // DEBUG_GERT
  178. }
  179. CloseSocket(remoteSocket);
  180. }
  181. catch (Exception exp)
  182. {
  183. #if DEBUG_GERT
  184. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | " + exp + " | " + DateTime.Now.ToString("hh:mm:ss.fff"));
  185. #endif // DEBUG_GERT
  186. RaiseExceptionEvent(exp);
  187. CloseSocket(remoteSocket);
  188. }
  189. finally
  190. {
  191. Interlocked.Decrement(ref _pendingRequests);
  192. }
  193. }
  194. private bool HandleSocks(IChannelDirectTcpip channel, Socket remoteSocket, TimeSpan timeout)
  195. {
  196. // create eventhandler which is to be invoked to interrupt a blocking receive
  197. // when we're closing the forwarded port
  198. EventHandler closeClientSocket = (_, args) => CloseSocket(remoteSocket);
  199. Closing += closeClientSocket;
  200. try
  201. {
  202. #if DEBUG_GERT
  203. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | Before ReadByte for version | " + DateTime.Now.ToString("hh:mm:ss.fff"));
  204. #endif // DEBUG_GERT
  205. var version = SocketAbstraction.ReadByte(remoteSocket, timeout);
  206. if (version == -1)
  207. {
  208. return false;
  209. }
  210. #if DEBUG_GERT
  211. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | After ReadByte for version | " + DateTime.Now.ToString("hh:mm:ss.fff"));
  212. #endif // DEBUG_GERT
  213. if (version == 4)
  214. {
  215. return HandleSocks4(remoteSocket, channel, timeout);
  216. }
  217. else if (version == 5)
  218. {
  219. return HandleSocks5(remoteSocket, channel, timeout);
  220. }
  221. else
  222. {
  223. throw new NotSupportedException(string.Format("SOCKS version {0} is not supported.", version));
  224. }
  225. }
  226. finally
  227. {
  228. // interrupt of blocking receive is now handled by channel (SOCKS4 and SOCKS5)
  229. // or no longer necessary
  230. Closing -= closeClientSocket;
  231. }
  232. }
  233. private static void CloseSocket(Socket socket)
  234. {
  235. #if DEBUG_GERT
  236. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | ForwardedPortDynamic.CloseSocket | " + DateTime.Now.ToString("hh:mm:ss.fff"));
  237. #endif // DEBUG_GERT
  238. if (socket.Connected)
  239. {
  240. socket.Shutdown(SocketShutdown.Both);
  241. socket.Dispose();
  242. }
  243. }
  244. partial void StopListener()
  245. {
  246. // if the port is not started then there's nothing to stop
  247. if (!IsStarted)
  248. return;
  249. #if FEATURE_SOCKET_EAP
  250. _stoppingListener.Set();
  251. #endif // FEATURE_SOCKET_EAP
  252. // close listener socket
  253. _listener.Dispose();
  254. // wait for listener loop to finish
  255. _listenerCompleted.WaitOne();
  256. }
  257. /// <summary>
  258. /// Waits for pending requests to finish, and channels to close.
  259. /// </summary>
  260. /// <param name="timeout">The maximum time to wait for the forwarded port to stop.</param>
  261. partial void InternalStop(TimeSpan timeout)
  262. {
  263. if (timeout == TimeSpan.Zero)
  264. return;
  265. var stopWatch = new Stopwatch();
  266. stopWatch.Start();
  267. // break out of loop when one of the following conditions are met:
  268. // * the forwarded port is restarted
  269. // * all pending requests have been processed and corresponding channel are closed
  270. // * the specified timeout has elapsed
  271. while (!IsStarted)
  272. {
  273. // break out of loop when all pending requests have been processed
  274. if (Interlocked.CompareExchange(ref _pendingRequests, 0, 0) == 0)
  275. break;
  276. // break out of loop when specified timeout has elapsed
  277. if (stopWatch.Elapsed >= timeout && timeout != SshNet.Session.InfiniteTimeSpan)
  278. break;
  279. // give channels time to process pending requests
  280. ThreadAbstraction.Sleep(50);
  281. }
  282. stopWatch.Stop();
  283. }
  284. partial void InternalDispose(bool disposing)
  285. {
  286. if (disposing)
  287. {
  288. if (_listener != null)
  289. {
  290. _listener.Dispose();
  291. _listener = null;
  292. }
  293. #if FEATURE_SOCKET_EAP
  294. if (_stoppingListener != null)
  295. {
  296. _stoppingListener.Dispose();
  297. _stoppingListener = null;
  298. }
  299. #endif // FEATURE_SOCKET_EAP
  300. }
  301. }
  302. private bool HandleSocks4(Socket socket, IChannelDirectTcpip channel, TimeSpan timeout)
  303. {
  304. var commandCode = SocketAbstraction.ReadByte(socket, timeout);
  305. if (commandCode == 0)
  306. {
  307. // SOCKS client closed connection
  308. return false;
  309. }
  310. // TODO: See what need to be done depends on the code
  311. var portBuffer = new byte[2];
  312. if (SocketAbstraction.Read(socket, portBuffer, 0, portBuffer.Length, timeout) == 0)
  313. {
  314. // SOCKS client closed connection
  315. return false;
  316. }
  317. var port = (uint)(portBuffer[0] * 256 + portBuffer[1]);
  318. var ipBuffer = new byte[4];
  319. if (SocketAbstraction.Read(socket, ipBuffer, 0, ipBuffer.Length, timeout) == 0)
  320. {
  321. // SOCKS client closed connection
  322. return false;
  323. }
  324. var ipAddress = new IPAddress(ipBuffer);
  325. var username = ReadString(socket, timeout);
  326. if (username == null)
  327. {
  328. // SOCKS client closed connection
  329. return false;
  330. }
  331. var host = ipAddress.ToString();
  332. RaiseRequestReceived(host, port);
  333. channel.Open(host, port, this, socket);
  334. SocketAbstraction.SendByte(socket, 0x00);
  335. if (channel.IsOpen)
  336. {
  337. SocketAbstraction.SendByte(socket, 0x5a);
  338. SocketAbstraction.Send(socket, portBuffer, 0, portBuffer.Length);
  339. SocketAbstraction.Send(socket, ipBuffer, 0, ipBuffer.Length);
  340. return true;
  341. }
  342. // signal that request was rejected or failed
  343. SocketAbstraction.SendByte(socket, 0x5b);
  344. return false;
  345. }
  346. private bool HandleSocks5(Socket socket, IChannelDirectTcpip channel, TimeSpan timeout)
  347. {
  348. #if DEBUG_GERT
  349. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | Handling Socks5: " + socket.LocalEndPoint + " | " + socket.RemoteEndPoint + " | " + DateTime.Now.ToString("hh:mm:ss.fff"));
  350. #endif // DEBUG_GERT
  351. var authenticationMethodsCount = SocketAbstraction.ReadByte(socket, timeout);
  352. if (authenticationMethodsCount == -1)
  353. {
  354. // SOCKS client closed connection
  355. return false;
  356. }
  357. #if DEBUG_GERT
  358. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | After ReadByte for authenticationMethodsCount | " + DateTime.Now.ToString("hh:mm:ss.fff"));
  359. #endif // DEBUG_GERT
  360. var authenticationMethods = new byte[authenticationMethodsCount];
  361. if (SocketAbstraction.Read(socket, authenticationMethods, 0, authenticationMethods.Length, timeout) == 0)
  362. {
  363. // SOCKS client closed connection
  364. return false;
  365. }
  366. #if DEBUG_GERT
  367. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | After Read for authenticationMethods | " + DateTime.Now.ToString("hh:mm:ss.fff"));
  368. #endif // DEBUG_GERT
  369. if (authenticationMethods.Min() == 0)
  370. {
  371. // no user authentication is one of the authentication methods supported
  372. // by the SOCKS client
  373. SocketAbstraction.Send(socket, new byte[] { 0x05, 0x00 }, 0, 2);
  374. #if DEBUG_GERT
  375. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | After Send for authenticationMethods 0 | " + DateTime.Now.ToString("hh:mm:ss.fff"));
  376. #endif // DEBUG_GERT
  377. }
  378. else
  379. {
  380. // the SOCKS client requires authentication, which we currently do not support
  381. SocketAbstraction.Send(socket, new byte[] { 0x05, 0xFF }, 0, 2);
  382. // we continue business as usual but expect the client to close the connection
  383. // so one of the subsequent reads should return -1 signaling that the client
  384. // has effectively closed the connection
  385. #if DEBUG_GERT
  386. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | After Send for authenticationMethods 2 | " + DateTime.Now.ToString("hh:mm:ss.fff"));
  387. #endif // DEBUG_GERT
  388. }
  389. var version = SocketAbstraction.ReadByte(socket, timeout);
  390. if (version == -1)
  391. {
  392. // SOCKS client closed connection
  393. return false;
  394. }
  395. if (version != 5)
  396. throw new ProxyException("SOCKS5: Version 5 is expected.");
  397. var commandCode = SocketAbstraction.ReadByte(socket, timeout);
  398. if (commandCode == -1)
  399. {
  400. // SOCKS client closed connection
  401. return false;
  402. }
  403. var reserved = SocketAbstraction.ReadByte(socket, timeout);
  404. if (reserved == -1)
  405. {
  406. // SOCKS client closed connection
  407. return false;
  408. }
  409. if (reserved != 0)
  410. {
  411. throw new ProxyException("SOCKS5: 0 is expected for reserved byte.");
  412. }
  413. var addressType = SocketAbstraction.ReadByte(socket, timeout);
  414. if (addressType == -1)
  415. {
  416. // SOCKS client closed connection
  417. return false;
  418. }
  419. IPAddress ipAddress;
  420. byte[] addressBuffer;
  421. switch (addressType)
  422. {
  423. case 0x01:
  424. {
  425. addressBuffer = new byte[4];
  426. if (SocketAbstraction.Read(socket, addressBuffer, 0, 4, timeout) == 0)
  427. {
  428. // SOCKS client closed connection
  429. return false;
  430. }
  431. ipAddress = new IPAddress(addressBuffer);
  432. }
  433. break;
  434. case 0x03:
  435. {
  436. var length = SocketAbstraction.ReadByte(socket, timeout);
  437. addressBuffer = new byte[length];
  438. if (SocketAbstraction.Read(socket, addressBuffer, 0, addressBuffer.Length, timeout) == 0)
  439. {
  440. // SOCKS client closed connection
  441. return false;
  442. }
  443. ipAddress = IPAddress.Parse(SshData.Ascii.GetString(addressBuffer));
  444. //var hostName = new Common.ASCIIEncoding().GetString(addressBuffer);
  445. //ipAddress = Dns.GetHostEntry(hostName).AddressList[0];
  446. }
  447. break;
  448. case 0x04:
  449. {
  450. addressBuffer = new byte[16];
  451. if (SocketAbstraction.Read(socket, addressBuffer, 0, 16, timeout) == 0)
  452. {
  453. // SOCKS client closed connection
  454. return false;
  455. }
  456. ipAddress = new IPAddress(addressBuffer);
  457. }
  458. break;
  459. default:
  460. throw new ProxyException(string.Format("SOCKS5: Address type '{0}' is not supported.", addressType));
  461. }
  462. var portBuffer = new byte[2];
  463. if (SocketAbstraction.Read(socket, portBuffer, 0, portBuffer.Length, timeout) == 0)
  464. {
  465. // SOCKS client closed connection
  466. return false;
  467. }
  468. var port = (uint)(portBuffer[0] * 256 + portBuffer[1]);
  469. var host = ipAddress.ToString();
  470. RaiseRequestReceived(host, port);
  471. #if DEBUG_GERT
  472. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | Before channel open | " + DateTime.Now.ToString("hh:mm:ss.fff"));
  473. var stopWatch = new Stopwatch();
  474. stopWatch.Start();
  475. #endif // DEBUG_GERT
  476. channel.Open(host, port, this, socket);
  477. #if DEBUG_GERT
  478. stopWatch.Stop();
  479. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | After channel open | " + DateTime.Now.ToString("hh:mm:ss.fff") + " => " + stopWatch.ElapsedMilliseconds);
  480. #endif // DEBUG_GERT
  481. var replyBuffer = new byte[10];
  482. replyBuffer[0] = 0x05;
  483. // SocketAbstraction.SendByte(socket, 0x05);
  484. if (channel.IsOpen)
  485. {
  486. replyBuffer[1] = 0x00;
  487. // SocketAbstraction.SendByte(socket, 0x00);
  488. }
  489. else
  490. {
  491. replyBuffer[1] = 0x01;
  492. //SocketAbstraction.SendByte(socket, 0x01);
  493. #if DEBUG_GERT
  494. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | Channel not open");
  495. #endif // DEBUG_GERT
  496. }
  497. // reserved
  498. replyBuffer[2] = 0x00;
  499. // reserved
  500. //SocketAbstraction.SendByte(socket, 0x00);
  501. //if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
  502. //{
  503. // SocketAbstraction.SendByte(socket, 0x01);
  504. //}
  505. //else if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
  506. //{
  507. // SocketAbstraction.SendByte(socket, 0x04);
  508. //}
  509. //else
  510. //{
  511. // throw new NotSupportedException("Not supported address family.");
  512. //}
  513. // IPv4
  514. replyBuffer[3] = 0x01;
  515. SocketAbstraction.Send(socket, replyBuffer, 0, replyBuffer.Length);
  516. //var addressBytes = IPAddress.Any.GetAddressBytes();
  517. //SocketAbstraction.Send(socket, addressBytes, 0, addressBytes.Length);
  518. //SocketAbstraction.Send(socket, new byte[] {0x00, 0x00}, 0, 2);
  519. //var addressBytes = ipAddress.GetAddressBytes();
  520. //SocketAbstraction.Send(socket, addressBytes, 0, addressBytes.Length);
  521. //SocketAbstraction.Send(socket, portBuffer, 0, portBuffer.Length);
  522. return true;
  523. }
  524. private void Channel_Exception(object sender, ExceptionEventArgs e)
  525. {
  526. #if DEBUG_GERT
  527. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | Channel_Exception | " +
  528. DateTime.Now.ToString("hh:mm:ss.fff"));
  529. #endif // DEBUG_GERT
  530. RaiseExceptionEvent(e.Exception);
  531. }
  532. /// <summary>
  533. /// Reads a null terminated string from a socket.
  534. /// </summary>
  535. /// <param name="socket">The <see cref="Socket"/> to read from.</param>
  536. /// <param name="timeout">The timeout to apply to individual reads.</param>
  537. /// <returns>
  538. /// The <see cref="string"/> read, or <c>null</c> when the socket was closed.
  539. /// </returns>
  540. private static string ReadString(Socket socket, TimeSpan timeout)
  541. {
  542. var text = new StringBuilder();
  543. var buffer = new byte[1];
  544. while (true)
  545. {
  546. if (SocketAbstraction.Read(socket, buffer, 0, 1, timeout) == 0)
  547. {
  548. // SOCKS client closed connection
  549. return null;
  550. }
  551. var byteRead = buffer[0];
  552. if (byteRead == 0)
  553. {
  554. // end of the string
  555. break;
  556. }
  557. var c = (char) byteRead;
  558. text.Append(c);
  559. }
  560. return text.ToString();
  561. }
  562. }
  563. }