From 465947dbbbdedf0d5c2e9190ab7e544c606ea4d9 Mon Sep 17 00:00:00 2001 From: Student Main Date: Fri, 24 Jul 2020 17:04:12 +0800 Subject: [PATCH 1/5] fix udp uplink. --- .../Controller/Service/UDPListener.cs | 56 +++++++------------ .../Controller/Service/UDPRelay.cs | 35 ++++++------ 2 files changed, 37 insertions(+), 54 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/UDPListener.cs b/shadowsocks-csharp/Controller/Service/UDPListener.cs index dfc8b5df..53b92723 100644 --- a/shadowsocks-csharp/Controller/Service/UDPListener.cs +++ b/shadowsocks-csharp/Controller/Service/UDPListener.cs @@ -4,6 +4,8 @@ using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; using NLog; using Shadowsocks.Model; @@ -49,6 +51,7 @@ namespace Shadowsocks.Controller bool _shareOverLAN; Socket _udpSocket; IEnumerable _services; + CancellationTokenSource tokenSource = new CancellationTokenSource(); public UDPListener(Configuration config, IEnumerable services) { @@ -84,54 +87,35 @@ namespace Shadowsocks.Controller logger.Info($"Shadowsocks started UDP ({UpdateChecker.Version})"); logger.Debug(Encryption.EncryptorFactory.DumpRegisteredEncryptor()); UDPState udpState = new UDPState(_udpSocket); - _udpSocket.BeginReceiveFrom(udpState.buffer, 0, udpState.buffer.Length, 0, ref udpState.remoteEndPoint, new AsyncCallback(RecvFromCallback), udpState); - - } - - public void Stop() - { - _udpSocket?.Close(); - foreach (var s in _services) - { - s.Stop(); - } + // _udpSocket.BeginReceiveFrom(udpState.buffer, 0, udpState.buffer.Length, 0, ref udpState.remoteEndPoint, new AsyncCallback(RecvFromCallback), udpState); + Task.Run(() => WorkLoop(tokenSource.Token)); } - public void RecvFromCallback(IAsyncResult ar) + private async Task WorkLoop(CancellationToken token) { - UDPState state = (UDPState)ar.AsyncState; - var socket = state.socket; - try + byte[] buffer = new byte[4096]; + EndPoint remote = new IPEndPoint(_udpSocket.AddressFamily == AddressFamily.InterNetworkV6 ? IPAddress.IPv6Any : IPAddress.Any, 0); + while (!token.IsCancellationRequested) { - int bytesRead = socket.EndReceiveFrom(ar, ref state.remoteEndPoint); + var result = await _udpSocket.ReceiveFromAsync(buffer, SocketFlags.None, remote); + var len = result.ReceivedBytes; foreach (IDatagramService service in _services) { - if (service.Handle(state.buffer, bytesRead, socket, state)) + if (service.Handle(buffer, len, _udpSocket, remote)) { break; } } } - catch (ObjectDisposedException) - { - } - catch (Exception ex) - { - logger.Debug(ex); - } - finally + } + + public void Stop() + { + tokenSource.Cancel(); + _udpSocket?.Close(); + foreach (var s in _services) { - try - { - socket.BeginReceiveFrom(state.buffer, 0, state.buffer.Length, 0, ref state.remoteEndPoint, new AsyncCallback(RecvFromCallback), state); - } - catch (ObjectDisposedException) - { - // do nothing - } - catch (Exception) - { - } + s.Stop(); } } } diff --git a/shadowsocks-csharp/Controller/Service/UDPRelay.cs b/shadowsocks-csharp/Controller/Service/UDPRelay.cs index fb5b5854..ecef6cb2 100644 --- a/shadowsocks-csharp/Controller/Service/UDPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/UDPRelay.cs @@ -44,8 +44,8 @@ namespace Shadowsocks.Controller { return false; } - UDPListener.UDPState udpState = (UDPListener.UDPState)state; - IPEndPoint remoteEndPoint = (IPEndPoint)udpState.remoteEndPoint; + // UDPListener.UDPState udpState = (UDPListener.UDPState)state; + IPEndPoint remoteEndPoint = (IPEndPoint)state; UDPHandler handler = _cache.get(remoteEndPoint); if (handler == null) { @@ -70,16 +70,16 @@ namespace Shadowsocks.Controller private IPEndPoint _localEndPoint; private IPEndPoint _remoteEndPoint; - private IPAddress GetIPAddress() + private IPAddress ListenAddress { - switch (_remote.AddressFamily) + get { - case AddressFamily.InterNetwork: - return IPAddress.Any; - case AddressFamily.InterNetworkV6: - return IPAddress.IPv6Any; - default: - return IPAddress.Any; + return _remote.AddressFamily switch + { + AddressFamily.InterNetwork => IPAddress.Any, + AddressFamily.InterNetworkV6 => IPAddress.IPv6Any, + _ => throw new NotSupportedException(), + }; } } @@ -99,24 +99,23 @@ namespace Shadowsocks.Controller } _remoteEndPoint = new IPEndPoint(ipAddress, server.server_port); _remote = new Socket(_remoteEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp); - _remote.Bind(new IPEndPoint(GetIPAddress(), 0)); + _remote.Bind(new IPEndPoint(ListenAddress, 0)); } public void Send(byte[] data, int length) { IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password); - // byte[] dataIn = new byte[length - 3]; - // Array.Copy(data, 3, dataIn, 0, length - 3); - byte[] dataOut = new byte[65536]; // enough space for AEAD ciphers - // encryptor.EncryptUDP(dataIn, length - 3, dataOut, out outlen); - int outlen = encryptor.EncryptUDP(data.AsSpan(3), dataOut); + var slicedData = data.AsSpan(0, length); + byte[] dataOut = new byte[slicedData.Length + 1000]; + var dataToSend = slicedData[3..]; + int outlen = encryptor.EncryptUDP(slicedData[3..], dataOut); logger.Debug(_localEndPoint, _remoteEndPoint, outlen, "UDP Relay"); _remote?.SendTo(dataOut, outlen, SocketFlags.None, _remoteEndPoint); } public void Receive() { - EndPoint remoteEndPoint = new IPEndPoint(GetIPAddress(), 0); + EndPoint remoteEndPoint = new IPEndPoint(ListenAddress, 0); logger.Debug($"++++++Receive Server Port, size:" + _buffer.Length); _remote?.BeginReceiveFrom(_buffer, 0, _buffer.Length, 0, ref remoteEndPoint, new AsyncCallback(RecvFromCallback), null); } @@ -126,7 +125,7 @@ namespace Shadowsocks.Controller try { if (_remote == null) return; - EndPoint remoteEndPoint = new IPEndPoint(GetIPAddress(), 0); + EndPoint remoteEndPoint = new IPEndPoint(ListenAddress, 0); int bytesRead = _remote.EndReceiveFrom(ar, ref remoteEndPoint); byte[] dataOut = new byte[bytesRead]; From 47105ec5ef95cc23b1287edcf2dea0f9a73ac973 Mon Sep 17 00:00:00 2001 From: Student Main Date: Fri, 24 Jul 2020 17:52:47 +0800 Subject: [PATCH 2/5] add none cipher --- .../Encryption/Stream/StreamPlainNativeEncryptor.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/shadowsocks-csharp/Encryption/Stream/StreamPlainNativeEncryptor.cs b/shadowsocks-csharp/Encryption/Stream/StreamPlainNativeEncryptor.cs index 3b4e1ece..8293db18 100644 --- a/shadowsocks-csharp/Encryption/Stream/StreamPlainNativeEncryptor.cs +++ b/shadowsocks-csharp/Encryption/Stream/StreamPlainNativeEncryptor.cs @@ -26,6 +26,7 @@ namespace Shadowsocks.Encryption.Stream private static readonly Dictionary _ciphers = new Dictionary { {"plain", new CipherInfo("plain", 0, 0, CipherFamily.Plain) }, + {"none", new CipherInfo("none", 0, 0, CipherFamily.Plain) }, }; public static Dictionary SupportedCiphers() From d04adc10b6bfae8956541c163fa2b790127652ae Mon Sep 17 00:00:00 2001 From: Student Main Date: Sat, 25 Jul 2020 02:05:23 +0800 Subject: [PATCH 3/5] fix udp downlink. --- shadowsocks-csharp/Controller/Service/UDPListener.cs | 2 +- shadowsocks-csharp/Controller/Service/UDPRelay.cs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/UDPListener.cs b/shadowsocks-csharp/Controller/Service/UDPListener.cs index 53b92723..52f375a9 100644 --- a/shadowsocks-csharp/Controller/Service/UDPListener.cs +++ b/shadowsocks-csharp/Controller/Service/UDPListener.cs @@ -101,7 +101,7 @@ namespace Shadowsocks.Controller var len = result.ReceivedBytes; foreach (IDatagramService service in _services) { - if (service.Handle(buffer, len, _udpSocket, remote)) + if (service.Handle(buffer, len, _udpSocket, result.RemoteEndPoint)) { break; } diff --git a/shadowsocks-csharp/Controller/Service/UDPRelay.cs b/shadowsocks-csharp/Controller/Service/UDPRelay.cs index ecef6cb2..2068d226 100644 --- a/shadowsocks-csharp/Controller/Service/UDPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/UDPRelay.cs @@ -46,6 +46,7 @@ namespace Shadowsocks.Controller } // UDPListener.UDPState udpState = (UDPListener.UDPState)state; IPEndPoint remoteEndPoint = (IPEndPoint)state; + // IPEndPoint remoteEndPoint = (IPEndPoint)socket.RemoteEndPoint; UDPHandler handler = _cache.get(remoteEndPoint); if (handler == null) { @@ -109,7 +110,7 @@ namespace Shadowsocks.Controller byte[] dataOut = new byte[slicedData.Length + 1000]; var dataToSend = slicedData[3..]; int outlen = encryptor.EncryptUDP(slicedData[3..], dataOut); - logger.Debug(_localEndPoint, _remoteEndPoint, outlen, "UDP Relay"); + logger.Debug(_localEndPoint, _remoteEndPoint, outlen, "UDP Relay up"); _remote?.SendTo(dataOut, outlen, SocketFlags.None, _remoteEndPoint); } @@ -137,7 +138,7 @@ namespace Shadowsocks.Controller byte[] sendBuf = new byte[outlen + 3]; Array.Copy(dataOut, 0, sendBuf, 3, outlen); - logger.Debug(_localEndPoint, _remoteEndPoint, outlen, "UDP Relay"); + logger.Debug(_remoteEndPoint, _localEndPoint, outlen, "UDP Relay down"); _local?.SendTo(sendBuf, outlen + 3, 0, _localEndPoint); Receive(); From 9ced417d54d20f9493e7bdac07d48d1ad7aba620 Mon Sep 17 00:00:00 2001 From: Student Main Date: Sat, 25 Jul 2020 02:17:31 +0800 Subject: [PATCH 4/5] async await UDP downlink --- .../Controller/Service/UDPRelay.cs | 50 ++++++++----------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/UDPRelay.cs b/shadowsocks-csharp/Controller/Service/UDPRelay.cs index 2068d226..3c0e5afa 100644 --- a/shadowsocks-csharp/Controller/Service/UDPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/UDPRelay.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Runtime.CompilerServices; +using System.Threading.Tasks; using NLog; using Shadowsocks.Controller.Strategy; using Shadowsocks.Encryption; @@ -114,48 +115,39 @@ namespace Shadowsocks.Controller _remote?.SendTo(dataOut, outlen, SocketFlags.None, _remoteEndPoint); } - public void Receive() + public async Task ReceiveAsync() { EndPoint remoteEndPoint = new IPEndPoint(ListenAddress, 0); logger.Debug($"++++++Receive Server Port, size:" + _buffer.Length); - _remote?.BeginReceiveFrom(_buffer, 0, _buffer.Length, 0, ref remoteEndPoint, new AsyncCallback(RecvFromCallback), null); - } - - public void RecvFromCallback(IAsyncResult ar) - { try { - if (_remote == null) return; - EndPoint remoteEndPoint = new IPEndPoint(ListenAddress, 0); - int bytesRead = _remote.EndReceiveFrom(ar, ref remoteEndPoint); - byte[] dataOut = new byte[bytesRead]; - int outlen; + while (true) + { - IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password); - // encryptor.DecryptUDP(_buffer, bytesRead, dataOut, out outlen); - outlen = encryptor.DecryptUDP(dataOut, _buffer.AsSpan(0, bytesRead)); - byte[] sendBuf = new byte[outlen + 3]; - Array.Copy(dataOut, 0, sendBuf, 3, outlen); + var result = await _remote.ReceiveFromAsync(_buffer, SocketFlags.None, remoteEndPoint); + int bytesRead = result.ReceivedBytes; + byte[] dataOut = new byte[bytesRead]; + int outlen; - logger.Debug(_remoteEndPoint, _localEndPoint, outlen, "UDP Relay down"); - _local?.SendTo(sendBuf, outlen + 3, 0, _localEndPoint); + IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password); + outlen = encryptor.DecryptUDP(dataOut, _buffer.AsSpan(0, bytesRead)); + byte[] sendBuf = new byte[outlen + 3]; + Array.Copy(dataOut, 0, sendBuf, 3, outlen); - Receive(); - } - catch (ObjectDisposedException) - { - // TODO: handle the ObjectDisposedException + logger.Debug(_remoteEndPoint, _localEndPoint, outlen, "UDP Relay down"); + await _local?.SendToAsync(sendBuf, SocketFlags.None, _localEndPoint); + } } - catch (Exception) + catch (Exception e) { - // TODO: need more think about handle other Exceptions, or should remove this catch(). + logger.LogUsefulException(e); } - finally - { - // No matter success or failed, we keep receiving + } - } + public void Receive() + { + _ = ReceiveAsync(); } public void Close() From bff6760a9b0d6e6306783b6890b03a155c6fa5ad Mon Sep 17 00:00:00 2001 From: Student Main Date: Sat, 25 Jul 2020 16:32:19 +0800 Subject: [PATCH 5/5] async await style udp operation --- .../Controller/Service/UDPListener.cs | 12 +--- .../Controller/Service/UDPRelay.cs | 61 +++++++++---------- 2 files changed, 31 insertions(+), 42 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/UDPListener.cs b/shadowsocks-csharp/Controller/Service/UDPListener.cs index 52f375a9..0ed972fe 100644 --- a/shadowsocks-csharp/Controller/Service/UDPListener.cs +++ b/shadowsocks-csharp/Controller/Service/UDPListener.cs @@ -13,20 +13,14 @@ namespace Shadowsocks.Controller { public interface IDatagramService { - [Obsolete] - bool Handle(byte[] firstPacket, int length, Socket socket, object state); - - public abstract bool Handle(CachedNetworkStream stream, object state); + public abstract Task< bool> Handle(Memory packet, Socket socket, EndPoint client); void Stop(); } public abstract class DatagramService : IDatagramService { - [Obsolete] - public abstract bool Handle(byte[] firstPacket, int length, Socket socket, object state); - - public abstract bool Handle(CachedNetworkStream stream, object state); + public abstract Task Handle(Memory packet, Socket socket, EndPoint client); public virtual void Stop() { } } @@ -101,7 +95,7 @@ namespace Shadowsocks.Controller var len = result.ReceivedBytes; foreach (IDatagramService service in _services) { - if (service.Handle(buffer, len, _udpSocket, result.RemoteEndPoint)) + if (await service.Handle(new Memory(buffer)[..len], _udpSocket, result.RemoteEndPoint)) { break; } diff --git a/shadowsocks-csharp/Controller/Service/UDPRelay.cs b/shadowsocks-csharp/Controller/Service/UDPRelay.cs index 3c0e5afa..3f8e6f2c 100644 --- a/shadowsocks-csharp/Controller/Service/UDPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/UDPRelay.cs @@ -1,8 +1,10 @@ using System; +using System.Buffers; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Threading.Tasks; using NLog; using Shadowsocks.Controller.Strategy; @@ -26,28 +28,17 @@ namespace Shadowsocks.Controller this._controller = controller; } - // TODO: UDP is datagram protocol not stream protocol - public override bool Handle(CachedNetworkStream stream, object state) - { - byte[] fp = new byte[256]; - int len = stream.ReadFirstBlock(fp); - return Handle(fp, len, stream.Socket, state); - } - - [Obsolete] - public override bool Handle(byte[] firstPacket, int length, Socket socket, object state) + public override async Task Handle(Memory packet, Socket socket, EndPoint client) { if (socket.ProtocolType != ProtocolType.Udp) { return false; } - if (length < 4) + if (packet.Length < 4) { return false; } - // UDPListener.UDPState udpState = (UDPListener.UDPState)state; - IPEndPoint remoteEndPoint = (IPEndPoint)state; - // IPEndPoint remoteEndPoint = (IPEndPoint)socket.RemoteEndPoint; + IPEndPoint remoteEndPoint = (IPEndPoint)client; UDPHandler handler = _cache.get(remoteEndPoint); if (handler == null) { @@ -55,14 +46,14 @@ namespace Shadowsocks.Controller handler.Receive(); _cache.add(remoteEndPoint, handler); } - handler.Send(firstPacket, length); + await handler.SendAsync(packet); return true; } public class UDPHandler { private static Logger logger = LogManager.GetCurrentClassLogger(); - + private static MemoryPool pool = MemoryPool.Shared; private Socket _local; private Socket _remote; @@ -92,8 +83,7 @@ namespace Shadowsocks.Controller _localEndPoint = localEndPoint; // TODO async resolving - IPAddress ipAddress; - bool parsed = IPAddress.TryParse(server.server, out ipAddress); + bool parsed = IPAddress.TryParse(server.server, out IPAddress ipAddress); if (!parsed) { IPHostEntry ipHostInfo = Dns.GetHostEntry(server.server); @@ -104,15 +94,19 @@ namespace Shadowsocks.Controller _remote.Bind(new IPEndPoint(ListenAddress, 0)); } - public void Send(byte[] data, int length) + public async Task SendAsync(ReadOnlyMemory data) { IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password); - var slicedData = data.AsSpan(0, length); - byte[] dataOut = new byte[slicedData.Length + 1000]; - var dataToSend = slicedData[3..]; - int outlen = encryptor.EncryptUDP(slicedData[3..], dataOut); + using IMemoryOwner mem = pool.Rent(data.Length + 1000); + + // byte[] dataOut = new byte[slicedData.Length + 1000]; + int outlen = encryptor.EncryptUDP(data.Span[3..], mem.Memory.Span); logger.Debug(_localEndPoint, _remoteEndPoint, outlen, "UDP Relay up"); - _remote?.SendTo(dataOut, outlen, SocketFlags.None, _remoteEndPoint); + if (!MemoryMarshal.TryGetArray(mem.Memory[..outlen], out ArraySegment outData)) + { + throw new InvalidOperationException("Can't extract underly array segment"); + }; + await _remote?.SendToAsync(outData, SocketFlags.None, _remoteEndPoint); } public async Task ReceiveAsync() @@ -121,22 +115,23 @@ namespace Shadowsocks.Controller logger.Debug($"++++++Receive Server Port, size:" + _buffer.Length); try { - while (true) { - var result = await _remote.ReceiveFromAsync(_buffer, SocketFlags.None, remoteEndPoint); int bytesRead = result.ReceivedBytes; - byte[] dataOut = new byte[bytesRead]; - int outlen; - IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password); - outlen = encryptor.DecryptUDP(dataOut, _buffer.AsSpan(0, bytesRead)); - byte[] sendBuf = new byte[outlen + 3]; - Array.Copy(dataOut, 0, sendBuf, 3, outlen); + using IMemoryOwner owner = pool.Rent(bytesRead + 3); + Memory o = owner.Memory; + IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password); + int outlen = encryptor.DecryptUDP(o.Span[3..], _buffer.AsSpan(0, bytesRead)); logger.Debug(_remoteEndPoint, _localEndPoint, outlen, "UDP Relay down"); - await _local?.SendToAsync(sendBuf, SocketFlags.None, _localEndPoint); + if (!MemoryMarshal.TryGetArray(o[..(outlen + 3)], out ArraySegment data)) + { + throw new InvalidOperationException("Can't extract underly array segment"); + }; + await _local?.SendToAsync(data, SocketFlags.None, _localEndPoint); + } } catch (Exception e)