ForwardedPortDynamic.NET.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. using System;
  2. using System.Linq;
  3. using System.Text;
  4. using System.Net;
  5. using System.Net.Sockets;
  6. using System.Threading;
  7. using Renci.SshNet.Abstractions;
  8. using Renci.SshNet.Channels;
  9. using Renci.SshNet.Common;
  10. namespace Renci.SshNet
  11. {
  12. public partial class ForwardedPortDynamic
  13. {
  14. private Socket _listener;
  15. private CountdownEvent _pendingChannelCountdown;
  16. partial void InternalStart()
  17. {
  18. InitializePendingChannelCountdown();
  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) {NoDelay = true};
  26. _listener.Bind(ep);
  27. _listener.Listen(5);
  28. Session.ErrorOccured += Session_ErrorOccured;
  29. Session.Disconnected += Session_Disconnected;
  30. StartAccept(null);
  31. // consider port started when we're listening for inbound connections
  32. _status = ForwardedPortStatus.Started;
  33. }
  34. private void StartAccept(SocketAsyncEventArgs e)
  35. {
  36. if (e == null)
  37. {
  38. e = new SocketAsyncEventArgs();
  39. e.Completed += AcceptCompleted;
  40. }
  41. else
  42. {
  43. // clear the socket as we're reusing the context object
  44. e.AcceptSocket = null;
  45. }
  46. // only accept new connections while we are started
  47. if (IsStarted)
  48. {
  49. try
  50. {
  51. if (!_listener.AcceptAsync(e))
  52. {
  53. AcceptCompleted(null, e);
  54. }
  55. }
  56. catch (ObjectDisposedException)
  57. {
  58. if (_status == ForwardedPortStatus.Stopped || _status == ForwardedPortStatus.Stopped)
  59. {
  60. // ignore ObjectDisposedException while stopping or stopped
  61. return;
  62. }
  63. throw;
  64. }
  65. }
  66. }
  67. private void AcceptCompleted(object sender, SocketAsyncEventArgs e)
  68. {
  69. if (e.SocketError == SocketError.OperationAborted || e.SocketError == SocketError.NotSocket)
  70. {
  71. // server was stopped
  72. return;
  73. }
  74. // capture client socket
  75. var clientSocket = e.AcceptSocket;
  76. if (e.SocketError != SocketError.Success)
  77. {
  78. // accept new connection
  79. StartAccept(e);
  80. // dispose broken client socket
  81. CloseClientSocket(clientSocket);
  82. return;
  83. }
  84. // accept new connection
  85. StartAccept(e);
  86. // process connection
  87. ProcessAccept(clientSocket);
  88. }
  89. private void ProcessAccept(Socket clientSocket)
  90. {
  91. // close the client socket if we're no longer accepting new connections
  92. if (!IsStarted)
  93. {
  94. CloseClientSocket(clientSocket);
  95. return;
  96. }
  97. // capture the countdown event that we're adding a count to, as we need to make sure that we'll be signaling
  98. // that same instance; the instance field for the countdown event is re-initialized when the port is restarted
  99. // and at that time there may still be pending requests
  100. var pendingChannelCountdown = _pendingChannelCountdown;
  101. pendingChannelCountdown.AddCount();
  102. try
  103. {
  104. using (var channel = Session.CreateChannelDirectTcpip())
  105. {
  106. channel.Exception += Channel_Exception;
  107. try
  108. {
  109. if (!HandleSocks(channel, clientSocket, Session.ConnectionInfo.Timeout))
  110. {
  111. CloseClientSocket(clientSocket);
  112. return;
  113. }
  114. // start receiving from client socket (and sending to server)
  115. channel.Bind();
  116. }
  117. finally
  118. {
  119. channel.Close();
  120. }
  121. }
  122. }
  123. catch (Exception exp)
  124. {
  125. RaiseExceptionEvent(exp);
  126. CloseClientSocket(clientSocket);
  127. }
  128. finally
  129. {
  130. // take into account that CountdownEvent has since been disposed; when stopping the port we
  131. // wait for a given time for the channels to close, but once that timeout period has elapsed
  132. // the CountdownEvent will be disposed
  133. try
  134. {
  135. pendingChannelCountdown.Signal();
  136. }
  137. catch (ObjectDisposedException)
  138. {
  139. }
  140. }
  141. }
  142. /// <summary>
  143. /// Initializes the <see cref="CountdownEvent"/>.
  144. /// </summary>
  145. /// <remarks>
  146. /// <para>
  147. /// When the port is started for the first time, a <see cref="CountdownEvent"/> is created with an initial count
  148. /// of <c>1</c>.
  149. /// </para>
  150. /// <para>
  151. /// On subsequent (re)starts, we'll dispose the current <see cref="CountdownEvent"/> and create a new one with
  152. /// initial count of <c>1</c>.
  153. /// </para>
  154. /// </remarks>
  155. private void InitializePendingChannelCountdown()
  156. {
  157. var original = Interlocked.Exchange(ref _pendingChannelCountdown, new CountdownEvent(1));
  158. if (original != null)
  159. {
  160. original.Dispose();
  161. }
  162. }
  163. private bool HandleSocks(IChannelDirectTcpip channel, Socket clientSocket, TimeSpan timeout)
  164. {
  165. // create eventhandler which is to be invoked to interrupt a blocking receive
  166. // when we're closing the forwarded port
  167. EventHandler closeClientSocket = (_, args) => CloseClientSocket(clientSocket);
  168. Closing += closeClientSocket;
  169. try
  170. {
  171. var version = SocketAbstraction.ReadByte(clientSocket, timeout);
  172. switch (version)
  173. {
  174. case -1:
  175. // SOCKS client closed connection
  176. return false;
  177. case 4:
  178. return HandleSocks4(clientSocket, channel, timeout);
  179. case 5:
  180. return HandleSocks5(clientSocket, channel, timeout);
  181. default:
  182. throw new NotSupportedException(string.Format("SOCKS version {0} is not supported.", version));
  183. }
  184. }
  185. catch (SocketException ex)
  186. {
  187. // ignore exception thrown by interrupting the blocking receive as part of closing
  188. // the forwarded port
  189. if (ex.SocketErrorCode != SocketError.Interrupted)
  190. {
  191. RaiseExceptionEvent(ex);
  192. }
  193. return false;
  194. }
  195. finally
  196. {
  197. // interrupt of blocking receive is now handled by channel (SOCKS4 and SOCKS5)
  198. // or no longer necessary
  199. Closing -= closeClientSocket;
  200. }
  201. }
  202. private static void CloseClientSocket(Socket clientSocket)
  203. {
  204. if (clientSocket.Connected)
  205. {
  206. try
  207. {
  208. clientSocket.Shutdown(SocketShutdown.Send);
  209. }
  210. catch (Exception)
  211. {
  212. // ignore exception when client socket was already closed
  213. }
  214. }
  215. clientSocket.Dispose();
  216. }
  217. /// <summary>
  218. /// Interrupts the listener, and unsubscribes from <see cref="Session"/> events.
  219. /// </summary>
  220. partial void StopListener()
  221. {
  222. // close listener socket
  223. var listener = _listener;
  224. if (listener != null)
  225. {
  226. listener.Dispose();
  227. }
  228. // unsubscribe from session events
  229. var session = Session;
  230. if (session != null)
  231. {
  232. session.ErrorOccured -= Session_ErrorOccured;
  233. session.Disconnected -= Session_Disconnected;
  234. }
  235. }
  236. /// <summary>
  237. /// Waits for pending channels to close.
  238. /// </summary>
  239. /// <param name="timeout">The maximum time to wait for the pending channels to close.</param>
  240. partial void InternalStop(TimeSpan timeout)
  241. {
  242. _pendingChannelCountdown.Signal();
  243. _pendingChannelCountdown.Wait(timeout);
  244. }
  245. partial void InternalDispose(bool disposing)
  246. {
  247. if (disposing)
  248. {
  249. var listener = _listener;
  250. if (listener != null)
  251. {
  252. _listener = null;
  253. listener.Dispose();
  254. }
  255. var pendingRequestsCountdown = _pendingChannelCountdown;
  256. if (pendingRequestsCountdown != null)
  257. {
  258. _pendingChannelCountdown = null;
  259. pendingRequestsCountdown.Dispose();
  260. }
  261. }
  262. }
  263. private void Session_Disconnected(object sender, EventArgs e)
  264. {
  265. var session = Session;
  266. if (session != null)
  267. {
  268. StopPort(session.ConnectionInfo.Timeout);
  269. }
  270. }
  271. private void Session_ErrorOccured(object sender, ExceptionEventArgs e)
  272. {
  273. var session = Session;
  274. if (session != null)
  275. {
  276. StopPort(session.ConnectionInfo.Timeout);
  277. }
  278. }
  279. private void Channel_Exception(object sender, ExceptionEventArgs e)
  280. {
  281. RaiseExceptionEvent(e.Exception);
  282. }
  283. private bool HandleSocks4(Socket socket, IChannelDirectTcpip channel, TimeSpan timeout)
  284. {
  285. var commandCode = SocketAbstraction.ReadByte(socket, timeout);
  286. if (commandCode == -1)
  287. {
  288. // SOCKS client closed connection
  289. return false;
  290. }
  291. // TODO: See what need to be done depends on the code
  292. var portBuffer = new byte[2];
  293. if (SocketAbstraction.Read(socket, portBuffer, 0, portBuffer.Length, timeout) == 0)
  294. {
  295. // SOCKS client closed connection
  296. return false;
  297. }
  298. var port = (uint)(portBuffer[0] * 256 + portBuffer[1]);
  299. var ipBuffer = new byte[4];
  300. if (SocketAbstraction.Read(socket, ipBuffer, 0, ipBuffer.Length, timeout) == 0)
  301. {
  302. // SOCKS client closed connection
  303. return false;
  304. }
  305. var ipAddress = new IPAddress(ipBuffer);
  306. var username = ReadString(socket, timeout);
  307. if (username == null)
  308. {
  309. // SOCKS client closed connection
  310. return false;
  311. }
  312. var host = ipAddress.ToString();
  313. RaiseRequestReceived(host, port);
  314. channel.Open(host, port, this, socket);
  315. SocketAbstraction.SendByte(socket, 0x00);
  316. if (channel.IsOpen)
  317. {
  318. SocketAbstraction.SendByte(socket, 0x5a);
  319. SocketAbstraction.Send(socket, portBuffer, 0, portBuffer.Length);
  320. SocketAbstraction.Send(socket, ipBuffer, 0, ipBuffer.Length);
  321. return true;
  322. }
  323. // signal that request was rejected or failed
  324. SocketAbstraction.SendByte(socket, 0x5b);
  325. return false;
  326. }
  327. private bool HandleSocks5(Socket socket, IChannelDirectTcpip channel, TimeSpan timeout)
  328. {
  329. var authenticationMethodsCount = SocketAbstraction.ReadByte(socket, timeout);
  330. if (authenticationMethodsCount == -1)
  331. {
  332. // SOCKS client closed connection
  333. return false;
  334. }
  335. var authenticationMethods = new byte[authenticationMethodsCount];
  336. if (SocketAbstraction.Read(socket, authenticationMethods, 0, authenticationMethods.Length, timeout) == 0)
  337. {
  338. // SOCKS client closed connection
  339. return false;
  340. }
  341. if (authenticationMethods.Min() == 0)
  342. {
  343. // no user authentication is one of the authentication methods supported
  344. // by the SOCKS client
  345. SocketAbstraction.Send(socket, new byte[] { 0x05, 0x00 }, 0, 2);
  346. }
  347. else
  348. {
  349. // the SOCKS client requires authentication, which we currently do not support
  350. SocketAbstraction.Send(socket, new byte[] { 0x05, 0xFF }, 0, 2);
  351. // we continue business as usual but expect the client to close the connection
  352. // so one of the subsequent reads should return -1 signaling that the client
  353. // has effectively closed the connection
  354. }
  355. var version = SocketAbstraction.ReadByte(socket, timeout);
  356. if (version == -1)
  357. {
  358. // SOCKS client closed connection
  359. return false;
  360. }
  361. if (version != 5)
  362. throw new ProxyException("SOCKS5: Version 5 is expected.");
  363. var commandCode = SocketAbstraction.ReadByte(socket, timeout);
  364. if (commandCode == -1)
  365. {
  366. // SOCKS client closed connection
  367. return false;
  368. }
  369. var reserved = SocketAbstraction.ReadByte(socket, timeout);
  370. if (reserved == -1)
  371. {
  372. // SOCKS client closed connection
  373. return false;
  374. }
  375. if (reserved != 0)
  376. {
  377. throw new ProxyException("SOCKS5: 0 is expected for reserved byte.");
  378. }
  379. var addressType = SocketAbstraction.ReadByte(socket, timeout);
  380. if (addressType == -1)
  381. {
  382. // SOCKS client closed connection
  383. return false;
  384. }
  385. IPAddress ipAddress;
  386. byte[] addressBuffer;
  387. switch (addressType)
  388. {
  389. case 0x01:
  390. {
  391. addressBuffer = new byte[4];
  392. if (SocketAbstraction.Read(socket, addressBuffer, 0, 4, timeout) == 0)
  393. {
  394. // SOCKS client closed connection
  395. return false;
  396. }
  397. ipAddress = new IPAddress(addressBuffer);
  398. }
  399. break;
  400. case 0x03:
  401. {
  402. var length = SocketAbstraction.ReadByte(socket, timeout);
  403. if (length == -1)
  404. {
  405. // SOCKS client closed connection
  406. return false;
  407. }
  408. addressBuffer = new byte[length];
  409. if (SocketAbstraction.Read(socket, addressBuffer, 0, addressBuffer.Length, timeout) == 0)
  410. {
  411. // SOCKS client closed connection
  412. return false;
  413. }
  414. ipAddress = IPAddress.Parse(SshData.Ascii.GetString(addressBuffer, 0, addressBuffer.Length));
  415. //var hostName = new Common.ASCIIEncoding().GetString(addressBuffer);
  416. //ipAddress = Dns.GetHostEntry(hostName).AddressList[0];
  417. }
  418. break;
  419. case 0x04:
  420. {
  421. addressBuffer = new byte[16];
  422. if (SocketAbstraction.Read(socket, addressBuffer, 0, 16, timeout) == 0)
  423. {
  424. // SOCKS client closed connection
  425. return false;
  426. }
  427. ipAddress = new IPAddress(addressBuffer);
  428. }
  429. break;
  430. default:
  431. throw new ProxyException(string.Format("SOCKS5: Address type '{0}' is not supported.", addressType));
  432. }
  433. var portBuffer = new byte[2];
  434. if (SocketAbstraction.Read(socket, portBuffer, 0, portBuffer.Length, timeout) == 0)
  435. {
  436. // SOCKS client closed connection
  437. return false;
  438. }
  439. var port = (uint)(portBuffer[0] * 256 + portBuffer[1]);
  440. var host = ipAddress.ToString();
  441. RaiseRequestReceived(host, port);
  442. channel.Open(host, port, this, socket);
  443. SocketAbstraction.SendByte(socket, 0x05);
  444. if (channel.IsOpen)
  445. {
  446. SocketAbstraction.SendByte(socket, 0x00);
  447. }
  448. else
  449. {
  450. SocketAbstraction.SendByte(socket, 0x01);
  451. }
  452. // reserved
  453. SocketAbstraction.SendByte(socket, 0x00);
  454. if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
  455. {
  456. SocketAbstraction.SendByte(socket, 0x01);
  457. }
  458. else if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
  459. {
  460. SocketAbstraction.SendByte(socket, 0x04);
  461. }
  462. else
  463. {
  464. throw new NotSupportedException("Not supported address family.");
  465. }
  466. var addressBytes = ipAddress.GetAddressBytes();
  467. SocketAbstraction.Send(socket, addressBytes, 0, addressBytes.Length);
  468. SocketAbstraction.Send(socket, portBuffer, 0, portBuffer.Length);
  469. return true;
  470. }
  471. /// <summary>
  472. /// Reads a null terminated string from a socket.
  473. /// </summary>
  474. /// <param name="socket">The <see cref="Socket"/> to read from.</param>
  475. /// <param name="timeout">The timeout to apply to individual reads.</param>
  476. /// <returns>
  477. /// The <see cref="string"/> read, or <c>null</c> when the socket was closed.
  478. /// </returns>
  479. private static string ReadString(Socket socket, TimeSpan timeout)
  480. {
  481. var text = new StringBuilder();
  482. var buffer = new byte[1];
  483. while (true)
  484. {
  485. if (SocketAbstraction.Read(socket, buffer, 0, 1, timeout) == 0)
  486. {
  487. // SOCKS client closed connection
  488. return null;
  489. }
  490. var byteRead = buffer[0];
  491. if (byteRead == 0)
  492. {
  493. // end of the string
  494. break;
  495. }
  496. var c = (char) byteRead;
  497. text.Append(c);
  498. }
  499. return text.ToString();
  500. }
  501. }
  502. }