diff --git a/shadowsocks-csharp/Controller/Service/TCPRelay.cs b/shadowsocks-csharp/Controller/Service/TCPRelay.cs index 9c162780..28d7ee8b 100644 --- a/shadowsocks-csharp/Controller/Service/TCPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/TCPRelay.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Net; using System.Net.Sockets; using System.Timers; @@ -15,10 +16,7 @@ namespace Shadowsocks.Controller private ShadowsocksController _controller; private DateTime _lastSweepTime; - public ISet Handlers - { - get; set; - } + public ISet Handlers { get; set; } public TCPRelay(ShadowsocksController controller) { @@ -29,44 +27,37 @@ namespace Shadowsocks.Controller public bool Handle(byte[] firstPacket, int length, Socket socket, object state) { - if (socket.ProtocolType != ProtocolType.Tcp) - { + if (socket.ProtocolType != ProtocolType.Tcp + || (length < 2 || firstPacket[0] != 5)) return false; - } - if (length < 2 || firstPacket[0] != 5) - { - return false; - } - socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); - TCPHandler handler = new TCPHandler(this); - handler.connection = socket; - handler.controller = _controller; - handler.relay = this; - - handler.Start(firstPacket, length); - IList handlersToClose = new List(); - lock (Handlers) + else { - Handlers.Add(handler); - DateTime now = DateTime.Now; - if (now - _lastSweepTime > TimeSpan.FromSeconds(1)) + socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); + TCPHandler handler = new TCPHandler(this); + handler.connection = socket; + handler.controller = _controller; + handler.tcprelay = this; + handler.Start(firstPacket, length); + IList handlersToClose = new List(); + lock (Handlers) { - _lastSweepTime = now; - foreach (TCPHandler handler1 in Handlers) + Handlers.Add(handler); + DateTime now = DateTime.Now; + if (now - _lastSweepTime > TimeSpan.FromSeconds(1)) { - if (now - handler1.lastActivity > TimeSpan.FromSeconds(900)) - { - handlersToClose.Add(handler1); - } + _lastSweepTime = now; + foreach (TCPHandler handler1 in Handlers) + if (now - handler1.lastActivity > TimeSpan.FromSeconds(900)) + handlersToClose.Add(handler1); } } + foreach (TCPHandler handler1 in handlersToClose) + { + Logging.Debug("Closing timed out TCP connection."); + handler1.Close(); + } + return true; } - foreach (TCPHandler handler1 in handlersToClose) - { - Logging.Debug("Closing timed out TCP connection."); - handler1.Close(); - } - return true; } public void UpdateInboundCounter(Server server, long n) @@ -87,6 +78,11 @@ namespace Shadowsocks.Controller class TCPHandler { + // Size of receive buffer. + static public readonly int RecvSize = 8192; + static public readonly int RecvReserveSize = IVEncryptor.ONETIMEAUTH_BYTES + IVEncryptor.AUTH_BYTES; // reserve for one-time auth + static public readonly int BufferSize = RecvSize + RecvReserveSize + 32; + // public Encryptor encryptor; public IEncryptor encryptor; public Server server; @@ -94,60 +90,43 @@ namespace Shadowsocks.Controller public Socket remote; public Socket connection; public ShadowsocksController controller; - public TCPRelay relay; + public TCPRelay tcprelay; public DateTime lastActivity; - private const int maxRetry = 4; - private int retryCount = 0; - private bool connected; - - private byte command; + private const int _maxRetry = 4; + private int _retryCount = 0; + private bool _connected; + private byte _command; private byte[] _firstPacket; private int _firstPacketLength; - // Size of receive buffer. - public const int RecvSize = 8192; - public const int RecvReserveSize = IVEncryptor.ONETIMEAUTH_BYTES + IVEncryptor.AUTH_BYTES; // reserve for one-time auth - public const int BufferSize = RecvSize + RecvReserveSize + 32; - - private int totalRead = 0; - private int totalWrite = 0; - - // remote receive buffer - private byte[] remoteRecvBuffer = new byte[BufferSize]; - // remote send buffer - private byte[] remoteSendBuffer = new byte[BufferSize]; - // connection receive buffer - private byte[] connetionRecvBuffer = new byte[BufferSize]; - // connection send buffer - private byte[] connetionSendBuffer = new byte[BufferSize]; - // Received data string. - - private bool connectionShutdown = false; - private bool remoteShutdown = false; - private bool closed = false; - - private object encryptionLock = new object(); - private object decryptionLock = new object(); - + private int _totalRead = 0; + private int _totalWrite = 0; + private byte[] _remoteRecvBuffer = new byte[BufferSize]; + private byte[] _remoteSendBuffer = new byte[BufferSize]; + private byte[] _connetionRecvBuffer = new byte[BufferSize]; + private byte[] _connetionSendBuffer = new byte[BufferSize]; + private bool _connectionShutdown = false; + private bool _remoteShutdown = false; + private bool _closed = false; + private object _encryptionLock = new object(); + private object _decryptionLock = new object(); private DateTime _startConnectTime; private DateTime _startReceivingTime; private DateTime _startSendingTime; private int _bytesToSend; - private TCPRelay tcprelay; // TODO: tcprelay ?= relay + private TCPRelay _tcprelay; // TODO: is _tcprelay equals tcprelay declared above? public TCPHandler(TCPRelay tcprelay) { - this.tcprelay = tcprelay; + _tcprelay = tcprelay; } public void CreateRemote() { Server server = controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)connection.RemoteEndPoint); if (server == null || server.server == "") - { throw new ArgumentException("No server configured"); - } encryptor = EncryptorFactory.GetEncryptor(server.method, server.password, server.auth, false); this.server = server; } @@ -162,72 +141,56 @@ namespace Shadowsocks.Controller private void CheckClose() { - if (connectionShutdown && remoteShutdown) - { + if (_connectionShutdown && _remoteShutdown) Close(); - } } public void Close() { - lock (relay.Handlers) + lock (tcprelay.Handlers) { - relay.Handlers.Remove(this); + tcprelay.Handlers.Remove(this); } lock (this) { - if (closed) - { + if (_closed) return; - } - closed = true; + else + _closed = true; } - if (connection != null) + try { - try - { - connection.Shutdown(SocketShutdown.Both); - connection.Close(); - } - catch (Exception e) - { - Logging.LogUsefulException(e); - } + connection?.Shutdown(SocketShutdown.Both); + connection?.Close(); } - if (remote != null) + catch (Exception e) { - try - { - remote.Shutdown(SocketShutdown.Both); - remote.Close(); - } - catch (Exception e) - { - Logging.LogUsefulException(e); - } + Logging.LogUsefulException(e); + } + try + { + remote?.Shutdown(SocketShutdown.Both); + remote?.Close(); + } + catch (Exception e) + { + Logging.LogUsefulException(e); } - lock (encryptionLock) + lock (_encryptionLock) { - lock (decryptionLock) + lock (_decryptionLock) { - if (encryptor != null) - { - ((IDisposable)encryptor).Dispose(); - } + encryptor?.Dispose(); } } } private void HandshakeReceive() { - if (closed) - { - return; - } + if (_closed) return; try { int bytesRead = _firstPacketLength; - if (bytesRead > 1) { byte[] response = { 5, 0 }; @@ -237,12 +200,10 @@ namespace Shadowsocks.Controller response = new byte[] { 0, 91 }; Logging.Error("socks 5 protocol error"); } - connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(HandshakeSendCallback), null); + connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(HandshakeSendCallback), null); } else - { Close(); - } } catch (Exception e) { @@ -253,10 +214,7 @@ namespace Shadowsocks.Controller private void HandshakeSendCallback(IAsyncResult ar) { - if (closed) - { - return; - } + if (_closed) return; try { connection.EndSend(ar); @@ -268,7 +226,7 @@ namespace Shadowsocks.Controller // +-----+-----+-------+------+----------+----------+ // Skip first 3 bytes // TODO validate - connection.BeginReceive(connetionRecvBuffer, 0, 3, 0, new AsyncCallback(handshakeReceive2Callback), null); + connection.BeginReceive(_connetionRecvBuffer, 0, 3, SocketFlags.None, new AsyncCallback(handshakeReceive2Callback), null); } catch (Exception e) { @@ -279,26 +237,20 @@ namespace Shadowsocks.Controller private void handshakeReceive2Callback(IAsyncResult ar) { - if (closed) - { - return; - } + if (_closed) return; try { int bytesRead = connection.EndReceive(ar); - if (bytesRead >= 3) { - command = connetionRecvBuffer[1]; - if (command == 1) + _command = _connetionRecvBuffer[1]; + if (_command == 1) { byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 }; - connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(ResponseCallback), null); + connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(ResponseCallback), null); } - else if (command == 3) - { + else if (_command == 3) HandleUDPAssociate(); - } } else { @@ -320,33 +272,31 @@ namespace Shadowsocks.Controller int port = endPoint.Port; byte[] response = new byte[4 + address.Length + 2]; response[0] = 5; - if (endPoint.AddressFamily == AddressFamily.InterNetwork) - { - response[3] = 1; - } - else if (endPoint.AddressFamily == AddressFamily.InterNetworkV6) + switch (endPoint.AddressFamily) { - response[3] = 4; + case AddressFamily.InterNetwork: + response[3] = 1; + break; + case AddressFamily.InterNetworkV6: + response[3] = 4; + break; } address.CopyTo(response, 4); response[response.Length - 1] = (byte)(port & 0xFF); response[response.Length - 2] = (byte)((port >> 8) & 0xFF); - connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(ReadAll), true); + connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(ReadAll), true); } private void ReadAll(IAsyncResult ar) { - if (closed) - { - return; - } + if (_closed) return; try { if (ar.AsyncState != null) { connection.EndSend(ar); Logging.Debug(remote, RecvSize, "TCP Relay"); - connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(ReadAll), null); + connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null); } else { @@ -354,12 +304,10 @@ namespace Shadowsocks.Controller if (bytesRead > 0) { Logging.Debug(remote, RecvSize, "TCP Relay"); - connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(ReadAll), null); + connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null); } else - { Close(); - } } } catch (Exception e) @@ -374,10 +322,8 @@ namespace Shadowsocks.Controller try { connection.EndSend(ar); - StartConnect(); } - catch (Exception e) { Logging.LogUsefulException(e); @@ -389,10 +335,7 @@ namespace Shadowsocks.Controller private class ServerTimer : Timer { public Server Server; - - public ServerTimer(int p) : base(p) - { - } + public ServerTimer(int p) : base(p) { } } private void StartConnect() @@ -411,8 +354,7 @@ namespace Shadowsocks.Controller } IPEndPoint remoteEP = new IPEndPoint(ipAddress, server.server_port); - remote = new Socket(ipAddress.AddressFamily, - SocketType.Stream, ProtocolType.Tcp); + remote = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); _startConnectTime = DateTime.Now; @@ -422,7 +364,7 @@ namespace Shadowsocks.Controller connectTimer.Enabled = true; connectTimer.Server = server; - connected = false; + _connected = false; // Connect to the remote endpoint. remote.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), connectTimer); } @@ -435,16 +377,10 @@ namespace Shadowsocks.Controller private void connectTimer_Elapsed(object sender, ElapsedEventArgs e) { - if (connected) - { - return; - } + if (_connected) return; Server server = ((ServerTimer)sender).Server; IStrategy strategy = controller.GetCurrentStrategy(); - if (strategy != null) - { - strategy.SetFailure(server); - } + strategy?.SetFailure(server); Logging.Info($"{server.FriendlyName()} timed out"); remote.Close(); RetryConnect(); @@ -452,29 +388,23 @@ namespace Shadowsocks.Controller private void RetryConnect() { - if (retryCount < maxRetry) + if (_retryCount < _maxRetry) { - Logging.Debug($"Connection failed, retry ({retryCount})"); + Logging.Debug($"Connection failed, retry ({_retryCount})"); StartConnect(); - retryCount++; + _retryCount++; } else - { Close(); - } } private void ConnectCallback(IAsyncResult ar) { - Server server = null; - if (closed) - { - return; - } + if (_closed) return; try { ServerTimer timer = (ServerTimer)ar.AsyncState; - server = timer.Server; + Server server = timer.Server; timer.Elapsed -= connectTimer_Elapsed; timer.Enabled = false; timer.Dispose(); @@ -482,14 +412,12 @@ namespace Shadowsocks.Controller // Complete the connection. remote.EndConnect(ar); - connected = true; - - Logging.Debug($"Socket connected to {remote.RemoteEndPoint}"); + _connected = true; var latency = DateTime.Now - _startConnectTime; IStrategy strategy = controller.GetCurrentStrategy(); strategy?.UpdateLatency(server, latency); - tcprelay.UpdateLatency(server, latency); + _tcprelay.UpdateLatency(server, latency); StartPipe(); } @@ -501,10 +429,7 @@ namespace Shadowsocks.Controller if (server != null) { IStrategy strategy = controller.GetCurrentStrategy(); - if (strategy != null) - { - strategy.SetFailure(server); - } + strategy?.SetFailure(server); } Logging.LogUsefulException(e); RetryConnect(); @@ -513,15 +438,12 @@ namespace Shadowsocks.Controller private void StartPipe() { - if (closed) - { - return; - } + if (_closed) return; try { _startReceivingTime = DateTime.Now; - remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeRemoteReceiveCallback), null); - connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeConnectionReceiveCallback), null); + remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), null); + connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback), null); } catch (Exception e) { @@ -532,49 +454,30 @@ namespace Shadowsocks.Controller private void PipeRemoteReceiveCallback(IAsyncResult ar) { - if (closed) - { - return; - } + if (_closed) return; try { int bytesRead = remote.EndReceive(ar); - totalRead += bytesRead; - tcprelay.UpdateInboundCounter(server, bytesRead); - + _totalRead += bytesRead; + _tcprelay.UpdateInboundCounter(server, bytesRead); if (bytesRead > 0) { lastActivity = DateTime.Now; int bytesToSend; - lock (decryptionLock) + lock (_decryptionLock) { - if (closed) - { - return; - } - encryptor.Decrypt(remoteRecvBuffer, bytesRead, remoteSendBuffer, out bytesToSend); + if (_closed) return; + encryptor.Decrypt(_remoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend); } - Logging.Debug(remote, bytesToSend, "TCP Relay", "@PipeRemoteReceiveCallback() (download)"); - connection.BeginSend(remoteSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeConnectionSendCallback), null); - + connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeConnectionSendCallback), null); IStrategy strategy = controller.GetCurrentStrategy(); - if (strategy != null) - { - strategy.UpdateLastRead(server); - } + strategy?.UpdateLastRead(server); } else { connection.Shutdown(SocketShutdown.Send); - connectionShutdown = true; + _connectionShutdown = true; CheckClose(); - - //if (totalRead == 0) - //{ - // // closed before anything received, reports as failure - // // disable this feature - // controller.GetCurrentStrategy().SetFailure(this.server); - //} } } catch (Exception e) @@ -586,39 +489,52 @@ namespace Shadowsocks.Controller private void PipeConnectionReceiveCallback(IAsyncResult ar) { - if (closed) - { - return; - } + if (_closed) return; try { int bytesRead = connection.EndReceive(ar); - totalWrite += bytesRead; - + _totalWrite += bytesRead; if (bytesRead > 0) { + int atyp = _connetionRecvBuffer[0]; + string dst_addr; + int dst_port; + switch (atyp) + { + case 1: // IPv4 address, 4 bytes + dst_addr = new IPAddress(_connetionRecvBuffer.Skip(1).Take(4).ToArray()).ToString(); + dst_port = (_connetionRecvBuffer[5] << 8) + _connetionRecvBuffer[6]; + Logging.Debug($"connect to {dst_addr}:{dst_port}"); + break; + case 3: // domain name, length + str + int len = _connetionRecvBuffer[1]; + dst_addr = System.Text.Encoding.UTF8.GetString(_connetionRecvBuffer, 2, len); + dst_port = (_connetionRecvBuffer[len + 2] << 8) + _connetionRecvBuffer[len + 3]; + Logging.Debug($"connect to {dst_addr}:{dst_port}"); + break; + case 4: // IPv6 address, 16 bytes + dst_addr = new IPAddress(_connetionRecvBuffer.Skip(1).Take(16).ToArray()).ToString(); + dst_port = (_connetionRecvBuffer[17] << 8) + _connetionRecvBuffer[18]; + Logging.Debug($"connect to [{dst_addr}]:{dst_port}"); + break; + } int bytesToSend; - lock (encryptionLock) + lock (_encryptionLock) { - if (closed) - { - return; - } - encryptor.Encrypt(connetionRecvBuffer, bytesRead, connetionSendBuffer, out bytesToSend); + if (_closed) return; + encryptor.Encrypt(_connetionRecvBuffer, bytesRead, _connetionSendBuffer, out bytesToSend); } - Logging.Debug(remote, bytesToSend, "TCP Relay", "@PipeConnectionReceiveCallback() (upload)"); - tcprelay.UpdateOutboundCounter(server, bytesToSend); + _tcprelay.UpdateOutboundCounter(server, bytesToSend); _startSendingTime = DateTime.Now; _bytesToSend = bytesToSend; - remote.BeginSend(connetionSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeRemoteSendCallback), null); - + remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeRemoteSendCallback), null); IStrategy strategy = controller.GetCurrentStrategy(); strategy?.UpdateLastWrite(server); } else { remote.Shutdown(SocketShutdown.Send); - remoteShutdown = true; + _remoteShutdown = true; CheckClose(); } } @@ -631,14 +547,11 @@ namespace Shadowsocks.Controller private void PipeRemoteSendCallback(IAsyncResult ar) { - if (closed) - { - return; - } + if (_closed) return; try { remote.EndSend(ar); - connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeConnectionReceiveCallback), null); + connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback), null); } catch (Exception e) { @@ -649,14 +562,11 @@ namespace Shadowsocks.Controller private void PipeConnectionSendCallback(IAsyncResult ar) { - if (closed) - { - return; - } + if (_closed) return; try { connection.EndSend(ar); - remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeRemoteReceiveCallback), null); + remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), null); } catch (Exception e) { diff --git a/shadowsocks-csharp/Util/Util.cs b/shadowsocks-csharp/Util/Util.cs index 207e5289..44ae5e8f 100755 --- a/shadowsocks-csharp/Util/Util.cs +++ b/shadowsocks-csharp/Util/Util.cs @@ -76,7 +76,8 @@ namespace Shadowsocks.Util // // just kidding SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle, - (UIntPtr)0xFFFFFFFF, (UIntPtr)0xFFFFFFFF); + (UIntPtr)0xFFFFFFFF, + (UIntPtr)0xFFFFFFFF); } } @@ -87,7 +88,8 @@ namespace Shadowsocks.Util using (MemoryStream sb = new MemoryStream()) { using (GZipStream input = new GZipStream(new MemoryStream(buf), - CompressionMode.Decompress, false)) + CompressionMode.Decompress, + false)) { while ((n = input.Read(buffer, 0, buffer.Length)) > 0) {