diff --git a/shadowsocks-csharp/Controller/Service/PACServer.cs b/shadowsocks-csharp/Controller/Service/PACServer.cs index 102064cf..cf31010a 100644 --- a/shadowsocks-csharp/Controller/Service/PACServer.cs +++ b/shadowsocks-csharp/Controller/Service/PACServer.cs @@ -10,7 +10,7 @@ using NLog; namespace Shadowsocks.Controller { - public class PACServer : Listener.Service + public class PACServer : StreamService { private static Logger logger = LogManager.GetCurrentClassLogger(); @@ -49,7 +49,7 @@ namespace Shadowsocks.Controller private static string GetHash(string content) { - + return HttpServerUtilityUrlToken.Encode(CryptoUtils.MD5(Encoding.ASCII.GetBytes(content))); } diff --git a/shadowsocks-csharp/Controller/Service/PortForwarder.cs b/shadowsocks-csharp/Controller/Service/PortForwarder.cs index ab468c54..5dc5d8d4 100644 --- a/shadowsocks-csharp/Controller/Service/PortForwarder.cs +++ b/shadowsocks-csharp/Controller/Service/PortForwarder.cs @@ -6,7 +6,7 @@ using Shadowsocks.Util.Sockets; namespace Shadowsocks.Controller { - class PortForwarder : Listener.Service + class PortForwarder : StreamService { private readonly int _targetPort; diff --git a/shadowsocks-csharp/Controller/Service/Listener.cs b/shadowsocks-csharp/Controller/Service/TCPListener.cs similarity index 52% rename from shadowsocks-csharp/Controller/Service/Listener.cs rename to shadowsocks-csharp/Controller/Service/TCPListener.cs index 16920ea8..6e699184 100644 --- a/shadowsocks-csharp/Controller/Service/Listener.cs +++ b/shadowsocks-csharp/Controller/Service/TCPListener.cs @@ -1,232 +1,186 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.NetworkInformation; -using System.Net.Sockets; -using NLog; -using Shadowsocks.Model; - -namespace Shadowsocks.Controller -{ - // TODO: Stream/Dgram Listener, so we needn't put TCP/UDP service together - public class Listener - { - private static Logger logger = LogManager.GetCurrentClassLogger(); - - public interface IService - { - [Obsolete] - bool Handle(byte[] firstPacket, int length, Socket socket, object state); - - public abstract bool Handle(CachedNetworkStream stream, object state); - - void Stop(); - } - - public abstract class Service : IService - { - [Obsolete] - public abstract bool Handle(byte[] firstPacket, int length, Socket socket, object state); - - public abstract bool Handle(CachedNetworkStream stream, object state); - - public virtual void Stop() { } - } - - public class UDPState - { - public UDPState(Socket s) - { - socket = s; - remoteEndPoint = new IPEndPoint(s.AddressFamily == AddressFamily.InterNetworkV6 ? IPAddress.IPv6Any : IPAddress.Any, 0); - } - public Socket socket; - public byte[] buffer = new byte[4096]; - public EndPoint remoteEndPoint; - } - - Configuration _config; - bool _shareOverLAN; - Socket _tcpSocket; - Socket _udpSocket; - List _services; - - public Listener(List services) - { - this._services = services; - } - - private bool CheckIfPortInUse(int port) - { - IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties(); - return ipProperties.GetActiveTcpListeners().Any(endPoint => endPoint.Port == port); - } - - public void Start(Configuration config) - { - this._config = config; - this._shareOverLAN = config.shareOverLan; - - if (CheckIfPortInUse(_config.localPort)) - throw new Exception(I18N.GetString("Port {0} already in use", _config.localPort)); - - try - { - // Create a TCP/IP socket. - _tcpSocket = new Socket(config.isIPv6Enabled ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - _udpSocket = new Socket(config.isIPv6Enabled ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - _tcpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); - _udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); - IPEndPoint localEndPoint = null; - localEndPoint = _shareOverLAN - ? new IPEndPoint(config.isIPv6Enabled ? IPAddress.IPv6Any : IPAddress.Any, _config.localPort) - : new IPEndPoint(config.isIPv6Enabled ? IPAddress.IPv6Loopback : IPAddress.Loopback, _config.localPort); - - // Bind the socket to the local endpoint and listen for incoming connections. - _tcpSocket.Bind(localEndPoint); - _udpSocket.Bind(localEndPoint); - _tcpSocket.Listen(1024); - - // Start an asynchronous socket to listen for connections. - logger.Info($"Shadowsocks started ({UpdateChecker.Version})"); - logger.Debug(Encryption.EncryptorFactory.DumpRegisteredEncryptor()); - _tcpSocket.BeginAccept(new AsyncCallback(AcceptCallback), _tcpSocket); - UDPState udpState = new UDPState(_udpSocket); - _udpSocket.BeginReceiveFrom(udpState.buffer, 0, udpState.buffer.Length, 0, ref udpState.remoteEndPoint, new AsyncCallback(RecvFromCallback), udpState); - } - catch (SocketException) - { - _tcpSocket.Close(); - throw; - } - } - - public void Stop() - { - if (_tcpSocket != null) - { - _tcpSocket.Close(); - _tcpSocket = null; - } - if (_udpSocket != null) - { - _udpSocket.Close(); - _udpSocket = null; - } - - _services.ForEach(s => s.Stop()); - } - - public void RecvFromCallback(IAsyncResult ar) - { - UDPState state = (UDPState)ar.AsyncState; - var socket = state.socket; - try - { - int bytesRead = socket.EndReceiveFrom(ar, ref state.remoteEndPoint); - foreach (IService service in _services) - { - if (service.Handle(state.buffer, bytesRead, socket, state)) - { - break; - } - } - } - catch (ObjectDisposedException) - { - } - catch (Exception ex) - { - logger.Debug(ex); - } - finally - { - try - { - socket.BeginReceiveFrom(state.buffer, 0, state.buffer.Length, 0, ref state.remoteEndPoint, new AsyncCallback(RecvFromCallback), state); - } - catch (ObjectDisposedException) - { - // do nothing - } - catch (Exception) - { - } - } - } - - public void AcceptCallback(IAsyncResult ar) - { - Socket listener = (Socket)ar.AsyncState; - try - { - Socket conn = listener.EndAccept(ar); - - byte[] buf = new byte[4096]; - object[] state = new object[] { - conn, - buf - }; - - conn.BeginReceive(buf, 0, buf.Length, 0, - new AsyncCallback(ReceiveCallback), state); - } - catch (ObjectDisposedException) - { - } - catch (Exception e) - { - logger.LogUsefulException(e); - } - finally - { - try - { - listener.BeginAccept( - new AsyncCallback(AcceptCallback), - listener); - } - catch (ObjectDisposedException) - { - // do nothing - } - catch (Exception e) - { - logger.LogUsefulException(e); - } - } - } - - private void ReceiveCallback(IAsyncResult ar) - { - object[] state = (object[])ar.AsyncState; - - Socket conn = (Socket)state[0]; - byte[] buf = (byte[])state[1]; - try - { - int bytesRead = conn.EndReceive(ar); - if (bytesRead <= 0) goto Shutdown; - foreach (IService service in _services) - { - if (service.Handle(buf, bytesRead, conn, null)) - { - return; - } - } - Shutdown: - // no service found for this - if (conn.ProtocolType == ProtocolType.Tcp) - { - conn.Close(); - } - } - catch (Exception e) - { - logger.LogUsefulException(e); - conn.Close(); - } - } - } -} +using NLog; +using Shadowsocks.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.NetworkInformation; +using System.Net.Sockets; + +namespace Shadowsocks.Controller +{ + public interface IStreamService + { + [Obsolete] + bool Handle(byte[] firstPacket, int length, Socket socket, object state); + + public abstract bool Handle(CachedNetworkStream stream, object state); + + void Stop(); + } + + public abstract class StreamService : IStreamService + { + [Obsolete] + public abstract bool Handle(byte[] firstPacket, int length, Socket socket, object state); + + public abstract bool Handle(CachedNetworkStream stream, object state); + + public virtual void Stop() { } + } + + public class TCPListener + { + private static Logger logger = LogManager.GetCurrentClassLogger(); + + public class UDPState + { + public UDPState(Socket s) + { + socket = s; + remoteEndPoint = new IPEndPoint(s.AddressFamily == AddressFamily.InterNetworkV6 ? IPAddress.IPv6Any : IPAddress.Any, 0); + } + public Socket socket; + public byte[] buffer = new byte[4096]; + public EndPoint remoteEndPoint; + } + + Configuration _config; + bool _shareOverLAN; + Socket _tcpSocket; + IEnumerable _services; + + public TCPListener(Configuration config, IEnumerable services) + { + _config = config; + _shareOverLAN = config.shareOverLan; + _services = services; + } + + private bool CheckIfPortInUse(int port) + { + IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties(); + return ipProperties.GetActiveTcpListeners().Any(endPoint => endPoint.Port == port); + } + + public void Start() + { + if (CheckIfPortInUse(_config.localPort)) + { + throw new Exception(I18N.GetString("Port {0} already in use", this._config.localPort)); + } + + try + { + // Create a TCP/IP socket. + _tcpSocket = new Socket(_config.isIPv6Enabled ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + _tcpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); + IPEndPoint localEndPoint = null; + localEndPoint = _shareOverLAN + ? new IPEndPoint(_config.isIPv6Enabled ? IPAddress.IPv6Any : IPAddress.Any, this._config.localPort) + : new IPEndPoint(_config.isIPv6Enabled ? IPAddress.IPv6Loopback : IPAddress.Loopback, this._config.localPort); + + // Bind the socket to the local endpoint and listen for incoming connections. + _tcpSocket.Bind(localEndPoint); + _tcpSocket.Listen(1024); + + // Start an asynchronous socket to listen for connections. + logger.Info($"Shadowsocks started TCP ({UpdateChecker.Version})"); + logger.Debug(Encryption.EncryptorFactory.DumpRegisteredEncryptor()); + _tcpSocket.BeginAccept(new AsyncCallback(AcceptCallback), _tcpSocket); + } + catch (SocketException) + { + _tcpSocket.Close(); + throw; + } + } + + public void Stop() + { + _tcpSocket?.Close(); + + foreach (IStreamService s in _services) + { + s.Stop(); + } + } + + public void AcceptCallback(IAsyncResult ar) + { + Socket listener = (Socket)ar.AsyncState; + try + { + Socket conn = listener.EndAccept(ar); + + byte[] buf = new byte[4096]; + object[] state = new object[] { + conn, + buf + }; + + conn.BeginReceive(buf, 0, buf.Length, 0, + new AsyncCallback(ReceiveCallback), state); + } + catch (ObjectDisposedException) + { + } + catch (Exception e) + { + logger.LogUsefulException(e); + } + finally + { + try + { + listener.BeginAccept( + new AsyncCallback(AcceptCallback), + listener); + } + catch (ObjectDisposedException) + { + // do nothing + } + catch (Exception e) + { + logger.LogUsefulException(e); + } + } + } + + private void ReceiveCallback(IAsyncResult ar) + { + object[] state = (object[])ar.AsyncState; + + Socket conn = (Socket)state[0]; + byte[] buf = (byte[])state[1]; + try + { + int bytesRead = conn.EndReceive(ar); + if (bytesRead <= 0) + { + goto Shutdown; + } + + foreach (IStreamService service in _services) + { + if (service.Handle(buf, bytesRead, conn, null)) + { + return; + } + } + Shutdown: + // no service found for this + if (conn.ProtocolType == ProtocolType.Tcp) + { + conn.Close(); + } + } + catch (Exception e) + { + logger.LogUsefulException(e); + conn.Close(); + } + } + } +} diff --git a/shadowsocks-csharp/Controller/Service/TCPRelay.cs b/shadowsocks-csharp/Controller/Service/TCPRelay.cs index 8bfac583..fc12c4dd 100644 --- a/shadowsocks-csharp/Controller/Service/TCPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/TCPRelay.cs @@ -16,7 +16,7 @@ using static Shadowsocks.Encryption.EncryptorBase; namespace Shadowsocks.Controller { - class TCPRelay : Listener.Service + class TCPRelay : StreamService { private static Logger logger = LogManager.GetCurrentClassLogger(); private ShadowsocksController _controller; diff --git a/shadowsocks-csharp/Controller/Service/UDPListener.cs b/shadowsocks-csharp/Controller/Service/UDPListener.cs new file mode 100644 index 00000000..dfc8b5df --- /dev/null +++ b/shadowsocks-csharp/Controller/Service/UDPListener.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.NetworkInformation; +using System.Net.Sockets; +using NLog; +using Shadowsocks.Model; + +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); + + 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 virtual void Stop() { } + } + + public class UDPListener + { + private static Logger logger = LogManager.GetCurrentClassLogger(); + + public class UDPState + { + public UDPState(Socket s) + { + socket = s; + remoteEndPoint = new IPEndPoint(s.AddressFamily == AddressFamily.InterNetworkV6 ? IPAddress.IPv6Any : IPAddress.Any, 0); + } + public Socket socket; + public byte[] buffer = new byte[4096]; + public EndPoint remoteEndPoint; + } + + Configuration _config; + bool _shareOverLAN; + Socket _udpSocket; + IEnumerable _services; + + public UDPListener(Configuration config, IEnumerable services) + { + this._config = config; + this._shareOverLAN = _config.shareOverLan; + + this._services = services; + } + + private bool CheckIfPortInUse(int port) + { + IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties(); + return ipProperties.GetActiveUdpListeners().Any(endPoint => endPoint.Port == port); + } + + public void Start() + { + if (CheckIfPortInUse(this._config.localPort)) + throw new Exception(I18N.GetString("Port {0} already in use", this._config.localPort)); + + // Create a TCP/IP socket. + _udpSocket = new Socket(_config.isIPv6Enabled ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + _udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); + IPEndPoint localEndPoint = null; + localEndPoint = _shareOverLAN + ? new IPEndPoint(_config.isIPv6Enabled ? IPAddress.IPv6Any : IPAddress.Any, this._config.localPort) + : new IPEndPoint(_config.isIPv6Enabled ? IPAddress.IPv6Loopback : IPAddress.Loopback, this._config.localPort); + + // Bind the socket to the local endpoint and listen for incoming connections. + _udpSocket.Bind(localEndPoint); + + // Start an asynchronous socket to listen for connections. + 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(); + } + } + + public void RecvFromCallback(IAsyncResult ar) + { + UDPState state = (UDPState)ar.AsyncState; + var socket = state.socket; + try + { + int bytesRead = socket.EndReceiveFrom(ar, ref state.remoteEndPoint); + foreach (IDatagramService service in _services) + { + if (service.Handle(state.buffer, bytesRead, socket, state)) + { + break; + } + } + } + catch (ObjectDisposedException) + { + } + catch (Exception ex) + { + logger.Debug(ex); + } + finally + { + try + { + socket.BeginReceiveFrom(state.buffer, 0, state.buffer.Length, 0, ref state.remoteEndPoint, new AsyncCallback(RecvFromCallback), state); + } + catch (ObjectDisposedException) + { + // do nothing + } + catch (Exception) + { + } + } + } + } +} diff --git a/shadowsocks-csharp/Controller/Service/UDPRelay.cs b/shadowsocks-csharp/Controller/Service/UDPRelay.cs index 807398c0..fb5b5854 100644 --- a/shadowsocks-csharp/Controller/Service/UDPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/UDPRelay.cs @@ -10,7 +10,7 @@ using Shadowsocks.Model; namespace Shadowsocks.Controller { - class UDPRelay : Listener.Service + class UDPRelay : DatagramService { private ShadowsocksController _controller; @@ -44,7 +44,7 @@ namespace Shadowsocks.Controller { return false; } - Listener.UDPState udpState = (Listener.UDPState)state; + UDPListener.UDPState udpState = (UDPListener.UDPState)state; IPEndPoint remoteEndPoint = (IPEndPoint)udpState.remoteEndPoint; UDPHandler handler = _cache.get(remoteEndPoint); if (handler == null) diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs index b7a53cc9..248d1441 100644 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -26,10 +26,10 @@ namespace Shadowsocks.Controller // manipulates UI // interacts with low level logic - private Thread _ramThread; private Thread _trafficThread; - private Listener _listener; + private TCPListener _tcpListener; + private UDPListener _udpListener; private PACDaemon _pacDaemon; private PACServer _pacServer; private Configuration _config; @@ -311,10 +311,8 @@ namespace Shadowsocks.Controller return; } stopped = true; - if (_listener != null) - { - _listener.Stop(); - } + _tcpListener?.Stop(); + _udpListener?.Stop(); StopPlugins(); if (privoxyRunner != null) { @@ -518,7 +516,8 @@ namespace Shadowsocks.Controller gfwListUpdater.Error += PacServer_PACUpdateError; availabilityStatistics.UpdateConfiguration(this); - _listener?.Stop(); + _tcpListener?.Stop(); + _udpListener?.Stop(); StopPlugins(); // don't put PrivoxyRunner.Start() before pacServer.Stop() @@ -536,15 +535,18 @@ namespace Shadowsocks.Controller TCPRelay tcpRelay = new TCPRelay(this, _config); UDPRelay udpRelay = new UDPRelay(this); - List services = new List + _tcpListener = new TCPListener(_config, new List { tcpRelay, - udpRelay, _pacServer, - new PortForwarder(privoxyRunner.RunningPort) - }; - _listener = new Listener(services); - _listener.Start(_config); + new PortForwarder(privoxyRunner.RunningPort), + }); + _tcpListener.Start(); + _udpListener = new UDPListener(_config, new List + { + udpRelay, + }); + _udpListener.Start(); } catch (Exception e) {