ForwardedPortDynamic.NET.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  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. partial void InternalStart()
  18. {
  19. var ip = IPAddress.Any;
  20. if (!string.IsNullOrEmpty(BoundHost))
  21. {
  22. ip = DnsAbstraction.GetHostAddresses(BoundHost)[0];
  23. }
  24. var ep = new IPEndPoint(ip, (int) BoundPort);
  25. _listener = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
  26. // TODO: decide if we want to have blocking socket
  27. #if FEATURE_SOCKET_SETSOCKETOPTION
  28. _listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);
  29. _listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true);
  30. #endif //FEATURE_SOCKET_SETSOCKETOPTION
  31. _listener.Bind(ep);
  32. _listener.Listen(5);
  33. Session.ErrorOccured += Session_ErrorOccured;
  34. Session.Disconnected += Session_Disconnected;
  35. _listenerCompleted = new ManualResetEvent(false);
  36. ThreadAbstraction.ExecuteThread(() =>
  37. {
  38. try
  39. {
  40. #if FEATURE_SOCKET_EAP
  41. StartAccept();
  42. #elif FEATURE_SOCKET_APM
  43. _listener.BeginAccept(AcceptCallback, _listener);
  44. #elif FEATURE_SOCKET_TAP
  45. #error Accepting new socket connections is not implemented.
  46. #else
  47. #error Accepting new socket connections is not implemented.
  48. #endif
  49. // wait until listener is stopped
  50. _listenerCompleted.WaitOne();
  51. }
  52. catch (ObjectDisposedException)
  53. {
  54. // BeginAccept / AcceptAsync will throw an ObjectDisposedException when the
  55. // server is closed before the listener has started accepting connections.
  56. //
  57. // As we start accepting connection on a separate thread, this is possible
  58. // when the listener is stopped right after it was started.
  59. // mark listener stopped
  60. _listenerCompleted.Set();
  61. }
  62. catch (Exception ex)
  63. {
  64. RaiseExceptionEvent(ex);
  65. // mark listener stopped
  66. _listenerCompleted.Set();
  67. }
  68. finally
  69. {
  70. var session = Session;
  71. if (session != null)
  72. {
  73. session.ErrorOccured -= Session_ErrorOccured;
  74. session.Disconnected -= Session_Disconnected;
  75. }
  76. }
  77. });
  78. }
  79. private void Session_Disconnected(object sender, EventArgs e)
  80. {
  81. StopListener();
  82. }
  83. private void Session_ErrorOccured(object sender, ExceptionEventArgs e)
  84. {
  85. StopListener();
  86. }
  87. #if FEATURE_SOCKET_EAP
  88. private void StartAccept()
  89. {
  90. var args = new SocketAsyncEventArgs();
  91. args.Completed += AcceptCompleted;
  92. if (!_listener.AcceptAsync(args))
  93. {
  94. AcceptCompleted(null, args);
  95. }
  96. }
  97. private void AcceptCompleted(object sender, SocketAsyncEventArgs acceptAsyncEventArgs)
  98. {
  99. if (acceptAsyncEventArgs.SocketError == SocketError.OperationAborted)
  100. {
  101. // server was stopped
  102. return;
  103. }
  104. if (acceptAsyncEventArgs.SocketError != SocketError.Success)
  105. {
  106. // accept new connection
  107. StartAccept();
  108. // dispose broken socket
  109. acceptAsyncEventArgs.AcceptSocket.Dispose();
  110. return;
  111. }
  112. // accept new connection
  113. StartAccept();
  114. // process connection
  115. ProcessAccept(acceptAsyncEventArgs.AcceptSocket);
  116. }
  117. #elif FEATURE_SOCKET_APM
  118. private void AcceptCallback(IAsyncResult ar)
  119. {
  120. // Get the socket that handles the client request
  121. var serverSocket = (Socket) ar.AsyncState;
  122. Socket clientSocket;
  123. try
  124. {
  125. clientSocket = serverSocket.EndAccept(ar);
  126. }
  127. catch (ObjectDisposedException)
  128. {
  129. // when the server socket is closed, an ObjectDisposedException is thrown
  130. // by Socket.EndAccept(IAsyncResult)
  131. return;
  132. }
  133. // accept new connection
  134. _listener.BeginAccept(AcceptCallback, _listener);
  135. // process connection
  136. ProcessAccept(clientSocket);
  137. }
  138. #endif
  139. private void ProcessAccept(Socket remoteSocket)
  140. {
  141. Interlocked.Increment(ref _pendingRequests);
  142. #if DEBUG_GERT
  143. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | " + remoteSocket.RemoteEndPoint + " | ForwardedPortDynamic.ProcessAccept | " + DateTime.Now.ToString("hh:mm:ss.fff"));
  144. #endif // DEBUG_GERT
  145. try
  146. {
  147. #if FEATURE_SOCKET_SETSOCKETOPTION
  148. remoteSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);
  149. remoteSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true);
  150. #endif //FEATURE_SOCKET_SETSOCKETOPTION
  151. using (var channel = Session.CreateChannelDirectTcpip())
  152. {
  153. channel.Exception += Channel_Exception;
  154. try
  155. {
  156. if (!HandleSocks(channel, remoteSocket, Session.ConnectionInfo.Timeout))
  157. {
  158. CloseSocket(remoteSocket);
  159. return;
  160. }
  161. // start receiving from client socket (and sending to server)
  162. channel.Bind();
  163. }
  164. #if DEBUG_GERT
  165. catch (SocketException ex)
  166. {
  167. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | " + ex.SocketErrorCode + " | " + DateTime.Now.ToString("hh:mm:ss.fff") + " | " + ex);
  168. }
  169. #endif // DEBUG_GERT
  170. finally
  171. {
  172. channel.Close();
  173. }
  174. }
  175. }
  176. catch (SocketException ex)
  177. {
  178. // ignore exception thrown by interrupting the blocking receive as part of closing
  179. // the forwarded port
  180. if (ex.SocketErrorCode != SocketError.Interrupted)
  181. {
  182. #if DEBUG_GERT
  183. RaiseExceptionEvent(new Exception("ID: " + Thread.CurrentThread.ManagedThreadId, ex));
  184. #else
  185. RaiseExceptionEvent(ex);
  186. #endif // DEBUG_GERT
  187. }
  188. CloseSocket(remoteSocket);
  189. }
  190. catch (Exception exp)
  191. {
  192. #if DEBUG_GERT
  193. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | " + exp + " | " + DateTime.Now.ToString("hh:mm:ss.fff"));
  194. #endif // DEBUG_GERT
  195. RaiseExceptionEvent(exp);
  196. CloseSocket(remoteSocket);
  197. }
  198. finally
  199. {
  200. Interlocked.Decrement(ref _pendingRequests);
  201. }
  202. }
  203. private bool HandleSocks(IChannelDirectTcpip channel, Socket remoteSocket, TimeSpan timeout)
  204. {
  205. // create eventhandler which is to be invoked to interrupt a blocking receive
  206. // when we're closing the forwarded port
  207. EventHandler closeClientSocket = (_, args) => CloseSocket(remoteSocket);
  208. Closing += closeClientSocket;
  209. try
  210. {
  211. #if DEBUG_GERT
  212. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | Before ReadByte for version | " + DateTime.Now.ToString("hh:mm:ss.fff"));
  213. #endif // DEBUG_GERT
  214. var version = SocketAbstraction.ReadByte(remoteSocket, timeout);
  215. if (version == -1)
  216. {
  217. return false;
  218. }
  219. #if DEBUG_GERT
  220. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | After ReadByte for version | " + DateTime.Now.ToString("hh:mm:ss.fff"));
  221. #endif // DEBUG_GERT
  222. if (version == 4)
  223. {
  224. return HandleSocks4(remoteSocket, channel, timeout);
  225. }
  226. else if (version == 5)
  227. {
  228. return HandleSocks5(remoteSocket, channel, timeout);
  229. }
  230. else
  231. {
  232. throw new NotSupportedException(string.Format("SOCKS version {0} is not supported.", version));
  233. }
  234. }
  235. finally
  236. {
  237. // interrupt of blocking receive is now handled by channel (SOCKS4 and SOCKS5)
  238. // or no longer necessary
  239. Closing -= closeClientSocket;
  240. }
  241. }
  242. private static void CloseSocket(Socket socket)
  243. {
  244. #if DEBUG_GERT
  245. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | ForwardedPortDynamic.CloseSocket | " + DateTime.Now.ToString("hh:mm:ss.fff"));
  246. #endif // DEBUG_GERT
  247. if (socket.Connected)
  248. {
  249. socket.Shutdown(SocketShutdown.Both);
  250. socket.Dispose();
  251. }
  252. }
  253. partial void StopListener()
  254. {
  255. // if the port is not started then there's nothing to stop
  256. if (!IsStarted)
  257. return;
  258. // close listener socket
  259. _listener.Dispose();
  260. // allow listener thread to stop
  261. _listenerCompleted.Set();
  262. }
  263. /// <summary>
  264. /// Waits for pending requests to finish, and channels to close.
  265. /// </summary>
  266. /// <param name="timeout">The maximum time to wait for the forwarded port to stop.</param>
  267. partial void InternalStop(TimeSpan timeout)
  268. {
  269. if (timeout == TimeSpan.Zero)
  270. return;
  271. var stopWatch = new Stopwatch();
  272. stopWatch.Start();
  273. // break out of loop when one of the following conditions are met:
  274. // * the forwarded port is restarted
  275. // * all pending requests have been processed and corresponding channel are closed
  276. // * the specified timeout has elapsed
  277. while (!IsStarted)
  278. {
  279. // break out of loop when all pending requests have been processed
  280. if (Interlocked.CompareExchange(ref _pendingRequests, 0, 0) == 0)
  281. break;
  282. // break out of loop when specified timeout has elapsed
  283. if (stopWatch.Elapsed >= timeout && timeout != SshNet.Session.InfiniteTimeSpan)
  284. break;
  285. // give channels time to process pending requests
  286. ThreadAbstraction.Sleep(50);
  287. }
  288. stopWatch.Stop();
  289. }
  290. partial void InternalDispose(bool disposing)
  291. {
  292. if (disposing)
  293. {
  294. var listener = _listener;
  295. if (listener != null)
  296. {
  297. _listener = null;
  298. listener.Dispose();
  299. }
  300. }
  301. }
  302. private bool HandleSocks4(Socket socket, IChannelDirectTcpip channel, TimeSpan timeout)
  303. {
  304. var commandCode = SocketAbstraction.ReadByte(socket, timeout);
  305. if (commandCode == -1)
  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. if (length == -1)
  438. {
  439. // SOCKS client closed connection
  440. return false;
  441. }
  442. addressBuffer = new byte[length];
  443. if (SocketAbstraction.Read(socket, addressBuffer, 0, addressBuffer.Length, timeout) == 0)
  444. {
  445. // SOCKS client closed connection
  446. return false;
  447. }
  448. ipAddress = IPAddress.Parse(SshData.Ascii.GetString(addressBuffer));
  449. //var hostName = new Common.ASCIIEncoding().GetString(addressBuffer);
  450. //ipAddress = Dns.GetHostEntry(hostName).AddressList[0];
  451. }
  452. break;
  453. case 0x04:
  454. {
  455. addressBuffer = new byte[16];
  456. if (SocketAbstraction.Read(socket, addressBuffer, 0, 16, timeout) == 0)
  457. {
  458. // SOCKS client closed connection
  459. return false;
  460. }
  461. ipAddress = new IPAddress(addressBuffer);
  462. }
  463. break;
  464. default:
  465. throw new ProxyException(string.Format("SOCKS5: Address type '{0}' is not supported.", addressType));
  466. }
  467. var portBuffer = new byte[2];
  468. if (SocketAbstraction.Read(socket, portBuffer, 0, portBuffer.Length, timeout) == 0)
  469. {
  470. // SOCKS client closed connection
  471. return false;
  472. }
  473. var port = (uint)(portBuffer[0] * 256 + portBuffer[1]);
  474. var host = ipAddress.ToString();
  475. RaiseRequestReceived(host, port);
  476. #if DEBUG_GERT
  477. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | Before channel open | " + DateTime.Now.ToString("hh:mm:ss.fff"));
  478. var stopWatch = new Stopwatch();
  479. stopWatch.Start();
  480. #endif // DEBUG_GERT
  481. channel.Open(host, port, this, socket);
  482. #if DEBUG_GERT
  483. stopWatch.Stop();
  484. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | After channel open | " + DateTime.Now.ToString("hh:mm:ss.fff") + " => " + stopWatch.ElapsedMilliseconds);
  485. #endif // DEBUG_GERT
  486. SocketAbstraction.SendByte(socket, 0x05);
  487. if (channel.IsOpen)
  488. {
  489. SocketAbstraction.SendByte(socket, 0x00);
  490. }
  491. else
  492. {
  493. SocketAbstraction.SendByte(socket, 0x01);
  494. }
  495. // reserved
  496. SocketAbstraction.SendByte(socket, 0x00);
  497. if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
  498. {
  499. SocketAbstraction.SendByte(socket, 0x01);
  500. }
  501. else if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
  502. {
  503. SocketAbstraction.SendByte(socket, 0x04);
  504. }
  505. else
  506. {
  507. throw new NotSupportedException("Not supported address family.");
  508. }
  509. var addressBytes = ipAddress.GetAddressBytes();
  510. SocketAbstraction.Send(socket, addressBytes, 0, addressBytes.Length);
  511. SocketAbstraction.Send(socket, portBuffer, 0, portBuffer.Length);
  512. return true;
  513. }
  514. private void Channel_Exception(object sender, ExceptionEventArgs e)
  515. {
  516. #if DEBUG_GERT
  517. Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | Channel_Exception | " +
  518. DateTime.Now.ToString("hh:mm:ss.fff"));
  519. #endif // DEBUG_GERT
  520. RaiseExceptionEvent(e.Exception);
  521. }
  522. /// <summary>
  523. /// Reads a null terminated string from a socket.
  524. /// </summary>
  525. /// <param name="socket">The <see cref="Socket"/> to read from.</param>
  526. /// <param name="timeout">The timeout to apply to individual reads.</param>
  527. /// <returns>
  528. /// The <see cref="string"/> read, or <c>null</c> when the socket was closed.
  529. /// </returns>
  530. private static string ReadString(Socket socket, TimeSpan timeout)
  531. {
  532. var text = new StringBuilder();
  533. var buffer = new byte[1];
  534. while (true)
  535. {
  536. if (SocketAbstraction.Read(socket, buffer, 0, 1, timeout) == 0)
  537. {
  538. // SOCKS client closed connection
  539. return null;
  540. }
  541. var byteRead = buffer[0];
  542. if (byteRead == 0)
  543. {
  544. // end of the string
  545. break;
  546. }
  547. var c = (char) byteRead;
  548. text.Append(c);
  549. }
  550. return text.ToString();
  551. }
  552. }
  553. }