diff --git a/shadowsocks-csharp/Controller/Service/TCPRelay.cs b/shadowsocks-csharp/Controller/Service/TCPRelay.cs index 71bb47e9..2348ab6b 100644 --- a/shadowsocks-csharp/Controller/Service/TCPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/TCPRelay.cs @@ -322,24 +322,15 @@ namespace Shadowsocks.Controller } else { - int atyp = _connetionRecvBuffer[3]; - - switch (atyp) + if (_command == 1) + { + byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 }; + _connection.BeginSend(response, 0, response.Length, SocketFlags.None, + new AsyncCallback(ResponseCallback), null); + } + else if (_command == 3) { - case 1: // IPv4 address, 4 bytes - ReadAddress(4 + 2 - 1); - break; - case 3: // domain name, length + str - int len = _connetionRecvBuffer[4]; - ReadAddress(len + 2); - break; - case 4: // IPv6 address, 16 bytes - ReadAddress(16 + 2 - 1); - break; - default: - Logging.Debug("Unsupported ATYP=" + atyp); - Close(); - break; + ReadAddress(HandleUDPAssociate); } } } @@ -356,12 +347,50 @@ namespace Shadowsocks.Controller } } - private void ReadAddress(int bytesRemain) + private void ResponseCallback(IAsyncResult ar) + { + try + { + _connection.EndSend(ar); + + ReadAddress(StartConnect); + } + catch (Exception e) + { + Logging.LogUsefulException(e); + Close(); + } + } + + private void ReadAddress(Action onSuccess) + { + int atyp = _connetionRecvBuffer[3]; + + switch (atyp) + { + case 1: // IPv4 address, 4 bytes + ReadAddress(4 + 2 - 1, onSuccess); + break; + case 3: // domain name, length + str + int len = _connetionRecvBuffer[4]; + ReadAddress(len + 2, onSuccess); + break; + case 4: // IPv6 address, 16 bytes + ReadAddress(16 + 2 - 1, onSuccess); + break; + default: + Logging.Debug("Unsupported ATYP=" + atyp); + Close(); + break; + } + } + + private void ReadAddress(int bytesRemain, Action onSuccess) { Array.Copy(_connetionRecvBuffer, 3, _connetionRecvBuffer, 0, 2); // Read the remain address bytes - _connection.BeginReceive(_connetionRecvBuffer, 2, RecvSize - 2, SocketFlags.None, OnAddressFullyRead, bytesRemain); + _connection.BeginReceive(_connetionRecvBuffer, 2, RecvSize - 2, SocketFlags.None, OnAddressFullyRead, new object[] {bytesRemain, onSuccess}); } private void OnAddressFullyRead(IAsyncResult ar) @@ -370,7 +399,12 @@ namespace Shadowsocks.Controller try { int bytesRead = _connection.EndReceive(ar); - int bytesRemain = (int) ar.AsyncState; + + var states = (object[]) ar.AsyncState; + + int bytesRemain = (int)states[0]; + var onSuccess = (Action) states[1]; + if (bytesRead >= bytesRemain) { _firstPacketLength = bytesRead + 2; @@ -405,16 +439,7 @@ namespace Shadowsocks.Controller _destEndPoint = SocketUtil.GetEndPoint(dst_addr, dst_port); - if (_command == 1) - { - byte[] response = {5, 0, 0, 1, 0, 0, 0, 0, 0, 0}; - _connection.BeginSend(response, 0, response.Length, SocketFlags.None, - new AsyncCallback(ResponseCallback), null); - } - else if (_command == 3) - { - HandleUDPAssociate(); - } + onSuccess.Invoke(); } else { @@ -479,20 +504,6 @@ namespace Shadowsocks.Controller } } - private void ResponseCallback(IAsyncResult ar) - { - try - { - _connection.EndSend(ar); - StartConnect(); - } - catch (Exception e) - { - Logging.LogUsefulException(e); - Close(); - } - } - // inner class private class ProxyTimer : Timer { @@ -716,6 +727,26 @@ namespace Shadowsocks.Controller } } + // private static readonly Random Rnd = new Random(); + + private void TryReadAvailableData() + { + int available = Math.Min(_connection.Available, RecvSize - _firstPacketLength); + if (available > 0) + { + // Pick a random chunk size, or is it truly necessary? Random packet size is some sort of 'characteristic' itself. + //lock (Rnd) + //{ + // available = Rnd.Next(available + 1); + //} + + var size = _connection.Receive(_connetionRecvBuffer, _firstPacketLength, available, + SocketFlags.None); + + _firstPacketLength += size; + } + } + private void StartPipe(AsyncSession session) { if (_closed) return; @@ -723,6 +754,9 @@ namespace Shadowsocks.Controller { _startReceivingTime = DateTime.Now; session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session); + + TryReadAvailableData(); + Logging.Debug($"_firstPacketLength = {_firstPacketLength}"); SendToServer(_firstPacketLength, session); } catch (Exception e)