Browse Source

split Listener to TCP and UDP Listener

pull/2865/head
Student Main 4 years ago
parent
commit
b0b7a9205e
7 changed files with 345 additions and 251 deletions
  1. +2
    -2
      shadowsocks-csharp/Controller/Service/PACServer.cs
  2. +1
    -1
      shadowsocks-csharp/Controller/Service/PortForwarder.cs
  3. +186
    -232
      shadowsocks-csharp/Controller/Service/TCPListener.cs
  4. +1
    -1
      shadowsocks-csharp/Controller/Service/TCPRelay.cs
  5. +138
    -0
      shadowsocks-csharp/Controller/Service/UDPListener.cs
  6. +2
    -2
      shadowsocks-csharp/Controller/Service/UDPRelay.cs
  7. +15
    -13
      shadowsocks-csharp/Controller/ShadowsocksController.cs

+ 2
- 2
shadowsocks-csharp/Controller/Service/PACServer.cs View File

@@ -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)));
}


+ 1
- 1
shadowsocks-csharp/Controller/Service/PortForwarder.cs View File

@@ -6,7 +6,7 @@ using Shadowsocks.Util.Sockets;
namespace Shadowsocks.Controller
{
class PortForwarder : Listener.Service
class PortForwarder : StreamService
{
private readonly int _targetPort;


shadowsocks-csharp/Controller/Service/Listener.cs → shadowsocks-csharp/Controller/Service/TCPListener.cs View File

@@ -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<IService> _services;
public Listener(List<IService> 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<IStreamService> _services;

public TCPListener(Configuration config, IEnumerable<IStreamService> 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();
}
}
}
}

+ 1
- 1
shadowsocks-csharp/Controller/Service/TCPRelay.cs View File

@@ -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;


+ 138
- 0
shadowsocks-csharp/Controller/Service/UDPListener.cs View File

@@ -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<IDatagramService> _services;

public UDPListener(Configuration config, IEnumerable<IDatagramService> 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)
{
}
}
}
}
}

+ 2
- 2
shadowsocks-csharp/Controller/Service/UDPRelay.cs View File

@@ -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)


+ 15
- 13
shadowsocks-csharp/Controller/ShadowsocksController.cs View File

@@ -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<Listener.IService> services = new List<Listener.IService>
_tcpListener = new TCPListener(_config, new List<IStreamService>
{
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<IDatagramService>
{
udpRelay,
});
_udpListener.Start();
}
catch (Exception e)
{


Loading…
Cancel
Save