Browse Source

🔨 `Shadowsocks.Net` now builds

- Use `Splat`'s logging interface
- Cleanups
pull/3006/head
database64128 3 years ago
parent
commit
98e8554839
No known key found for this signature in database GPG Key ID: 1CA27546BEDB8B01
8 changed files with 121 additions and 154 deletions
  1. +20
    -20
      Shadowsocks.Net/Crypto/AEAD/AEADCrypto.cs
  2. +11
    -12
      Shadowsocks.Net/Crypto/Stream/StreamCrypto.cs
  3. +3
    -5
      Shadowsocks.Net/Proxy/HttpProxy.cs
  4. +5
    -0
      Shadowsocks.Net/Shadowsocks.Net.csproj
  5. +14
    -24
      Shadowsocks.Net/TCPListener.cs
  6. +36
    -50
      Shadowsocks.Net/TCPRelay.cs
  7. +14
    -24
      Shadowsocks.Net/UDPListener.cs
  8. +18
    -19
      Shadowsocks.Net/UDPRelay.cs

+ 20
- 20
Shadowsocks.Net/Crypto/AEAD/AEADCrypto.cs View File

@@ -1,5 +1,6 @@
using Shadowsocks.Net.Crypto.Exception;
using Shadowsocks.Net.Crypto.Stream;
using Splat;
using System;
using System.Collections.Generic;
using System.Net;
@@ -8,9 +9,8 @@ using System.Text;

namespace Shadowsocks.Net.Crypto.AEAD
{
public abstract class AEADCrypto : CryptoBase
public abstract class AEADCrypto : CryptoBase, IEnableLogger
{
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
// We are using the same saltLen and keyLen
private const string Info = "ss-subkey";
private static readonly byte[] InfoBytes = Encoding.ASCII.GetBytes(Info);
@@ -60,8 +60,8 @@ namespace Shadowsocks.Net.Crypto.AEAD
// Initialize all-zero nonce for each connection
nonce = new byte[nonceLen];

logger.Dump($"masterkey {instanceId}", masterKey, keyLen);
logger.Dump($"nonce {instanceId}", nonce, keyLen);
this.Log().Debug($"masterkey {instanceId} {masterKey} {keyLen}");
this.Log().Debug($"nonce {instanceId} {nonce} {keyLen}");
}

protected abstract Dictionary<string, CipherInfo> GetCiphers();
@@ -92,8 +92,8 @@ namespace Shadowsocks.Net.Crypto.AEAD

CryptoUtils.HKDF(keyLen, masterKey, salt, InfoBytes).CopyTo(sessionKey, 0);

logger.Dump($"salt {instanceId}", salt, saltLen);
logger.Dump($"sessionkey {instanceId}", sessionKey, keyLen);
this.Log().Debug($"salt {instanceId}", salt, saltLen);
this.Log().Debug($"sessionkey {instanceId}", sessionKey, keyLen);
}

public abstract int CipherEncrypt(ReadOnlySpan<byte> plain, Span<byte> cipher);
@@ -138,7 +138,7 @@ namespace Shadowsocks.Net.Crypto.AEAD
// if not, keep buf for next run, at this condition, buffer is not empty
if (outlength + ChunkOverhead > cipher.Length)
{
logger.Debug("enc outbuf almost full, giving up");
this.Log().Debug("enc outbuf almost full, giving up");

// write rest data to head of shared buffer
tmp.CopyTo(buffer);
@@ -150,7 +150,7 @@ namespace Shadowsocks.Net.Crypto.AEAD
bufSize = tmp.Length;
if (bufSize <= 0)
{
logger.Debug("No more data to encrypt, leaving");
this.Log().Debug("No more data to encrypt, leaving");
return outlength;
}
}
@@ -165,7 +165,7 @@ namespace Shadowsocks.Net.Crypto.AEAD
cipher.CopyTo(tmp.Slice(bufPtr));
int bufSize = tmp.Length;

logger.Debug($"{instanceId} decrypt tcp, read salt: {!saltReady}");
this.Log().Debug($"{instanceId} decrypt tcp, read salt: {!saltReady}");
if (!saltReady)
{
// check if we get the leading salt
@@ -191,7 +191,7 @@ namespace Shadowsocks.Net.Crypto.AEAD
// check if we have any data
if (bufSize <= 0)
{
logger.Trace("No data in buffer");
this.Log().Debug("No data in buffer");
return outlength;
}

@@ -200,23 +200,23 @@ namespace Shadowsocks.Net.Crypto.AEAD
{
// so we only have chunk length and its tag?
// wait more
logger.Trace($"{instanceId} not enough data to decrypt chunk. write {tmp.Length} byte back to buffer.");
this.Log().Debug($"{instanceId} not enough data to decrypt chunk. write {tmp.Length} byte back to buffer.");
tmp.CopyTo(buffer);
bufPtr = tmp.Length;
return outlength;
}
logger.Trace($"{instanceId} try decrypt to offset {outlength}");
this.Log().Debug($"{instanceId} try decrypt to offset {outlength}");
int len = ChunkDecrypt(plain.Slice(outlength), tmp);
if (len <= 0)
{
logger.Trace($"{instanceId} no chunk decrypted, write {tmp.Length} byte back to buffer.");
this.Log().Debug($"{instanceId} no chunk decrypted, write {tmp.Length} byte back to buffer.");

// no chunk decrypted
tmp.CopyTo(buffer);
bufPtr = tmp.Length;
return outlength;
}
logger.Trace($"{instanceId} decrypted {len} to offset {outlength}");
this.Log().Debug($"{instanceId} decrypted {len} to offset {outlength}");

// drop decrypted data
tmp = tmp.Slice(ChunkLengthBytes + tagLen + len + tagLen);
@@ -225,7 +225,7 @@ namespace Shadowsocks.Net.Crypto.AEAD
// logger.Debug("aead dec outlength " + outlength);
if (outlength + ChunkOverhead > cipher.Length)
{
logger.Trace($"{instanceId} output almost full, write {tmp.Length} byte back to buffer.");
this.Log().Debug($"{instanceId} output almost full, write {tmp.Length} byte back to buffer.");
tmp.CopyTo(buffer);
bufPtr = tmp.Length;
return outlength;
@@ -235,7 +235,7 @@ namespace Shadowsocks.Net.Crypto.AEAD
if (bufSize <= 0)
{
bufPtr = 0;
logger.Debug($"{instanceId} no data in buffer, already all done");
this.Log().Debug($"{instanceId} no data in buffer, already all done");
return outlength;
}
}
@@ -266,7 +266,7 @@ namespace Shadowsocks.Net.Crypto.AEAD
{
if (plain.Length > ChunkLengthMask)
{
logger.Error("enc chunk too big");
this.Log().Error("enc chunk too big");
throw new CryptoErrorException();
}

@@ -289,14 +289,14 @@ namespace Shadowsocks.Net.Crypto.AEAD
if (chunkLength > ChunkLengthMask)
{
// we get invalid chunk
logger.Error($"{instanceId} Invalid chunk length: {chunkLength}");
this.Log().Error($"{instanceId} Invalid chunk length: {chunkLength}");
throw new CryptoErrorException();
}
// logger.Debug("Get the real chunk len:" + chunkLength);
int bufSize = cipher.Length;
if (bufSize < ChunkLengthBytes + tagLen /* we haven't remove them */+ chunkLength + tagLen)
{
logger.Debug($"{instanceId} need {ChunkLengthBytes + tagLen + chunkLength + tagLen}, but have {cipher.Length}");
this.Log().Debug($"{instanceId} need {ChunkLengthBytes + tagLen + chunkLength + tagLen}, but have {cipher.Length}");
return 0;
}
CryptoUtils.SodiumIncrement(nonce);
@@ -304,7 +304,7 @@ namespace Shadowsocks.Net.Crypto.AEAD
// drop chunk len and its tag from buffer
int len = CipherDecrypt(plain, cipher.Slice(ChunkLengthBytes + tagLen, chunkLength + tagLen));
CryptoUtils.SodiumIncrement(nonce);
logger.Trace($"{instanceId} decrypted {len} byte chunk used {ChunkLengthBytes + tagLen + chunkLength + tagLen} from {cipher.Length}");
this.Log().Debug($"{instanceId} decrypted {len} byte chunk used {ChunkLengthBytes + tagLen + chunkLength + tagLen} from {cipher.Length}");
return len;
}
}


+ 11
- 12
Shadowsocks.Net/Crypto/Stream/StreamCrypto.cs View File

@@ -1,3 +1,4 @@
using Splat;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
@@ -5,10 +6,8 @@ using System.Text;

namespace Shadowsocks.Net.Crypto.Stream
{
public abstract class StreamCrypto : CryptoBase
public abstract class StreamCrypto : CryptoBase, IEnableLogger
{
private static readonly Logger logger = LogManager.GetCurrentClassLogger();

// shared by TCP decrypt UDP encrypt and decrypt
protected static byte[] sharedBuffer = new byte[65536];

@@ -34,7 +33,7 @@ namespace Shadowsocks.Net.Crypto.Stream

InitKey(password);

logger.Dump($"key {instanceId}", key, keyLen);
this.Log().Debug($"key {instanceId} {key} {keyLen}");
}

protected abstract Dictionary<string, CipherInfo> GetCiphers();
@@ -93,7 +92,7 @@ namespace Shadowsocks.Net.Crypto.Stream
public override int Encrypt(ReadOnlySpan<byte> plain, Span<byte> cipher)
{
int cipherOffset = 0;
logger.Trace($"{instanceId} encrypt TCP, generate iv: {!ivReady}");
this.Log().Debug($"{instanceId} encrypt TCP, generate iv: {!ivReady}");
if (!ivReady)
{
// Generate IV
@@ -106,9 +105,9 @@ namespace Shadowsocks.Net.Crypto.Stream
}
int clen = CipherEncrypt(plain, cipher);

logger.DumpBase64($"plain {instanceId}", plain);
logger.DumpBase64($"cipher {instanceId}", cipher.Slice(0, clen));
logger.Dump($"iv {instanceId}", iv, ivLen);
this.Log().Debug($"plain {instanceId} {Convert.ToBase64String(plain)}");
this.Log().Debug($"cipher {instanceId} {Convert.ToBase64String(cipher.Slice(0, clen))}");
this.Log().Debug($"iv {instanceId} {iv} {ivLen}");
return clen + cipherOffset;
}

@@ -116,7 +115,7 @@ namespace Shadowsocks.Net.Crypto.Stream
[MethodImpl(MethodImplOptions.Synchronized)]
public override int Decrypt(Span<byte> plain, ReadOnlySpan<byte> cipher)
{
logger.Trace($"{instanceId} decrypt TCP, read iv: {!ivReady}");
this.Log().Debug($"{instanceId} decrypt TCP, read iv: {!ivReady}");

int cipherOffset = 0;
// is first packet, need read iv
@@ -149,9 +148,9 @@ namespace Shadowsocks.Net.Crypto.Stream
// read all data from buffer
int len = CipherDecrypt(plain, cipher.Slice(cipherOffset));

logger.DumpBase64($"cipher {instanceId}", cipher.Slice(cipherOffset));
logger.DumpBase64($"plain {instanceId}", plain.Slice(0, len));
logger.Dump($"iv {instanceId}", iv, ivLen);
this.Log().Debug($"cipher {instanceId} {Convert.ToBase64String(cipher.Slice(cipherOffset))}");
this.Log().Debug($"plain {instanceId} {Convert.ToBase64String(plain.Slice(0, len))}");
this.Log().Debug($"iv {instanceId} {iv} {ivLen}");
return len;
}



+ 3
- 5
Shadowsocks.Net/Proxy/HttpProxy.cs View File

@@ -1,3 +1,4 @@
using Splat;
using System;
using System.Net;
using System.Net.Sockets;
@@ -5,14 +6,11 @@ using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using NLog;

namespace Shadowsocks.Net.Proxy
{
public class HttpProxy : IProxy
public class HttpProxy : IProxy, IEnableLogger
{
private static readonly Logger logger = LogManager.GetCurrentClassLogger();

public EndPoint LocalEndPoint => _remote.LocalEndPoint;
public EndPoint ProxyEndPoint { get; private set; }
public EndPoint DestEndPoint { get; private set; }
@@ -45,7 +43,7 @@ namespace Shadowsocks.Net.Proxy

private bool OnLineRead(string line, object state)
{
logger.Trace(line);
this.Log().Debug(line);

if (_respondLineCount == 0)
{


+ 5
- 0
Shadowsocks.Net/Shadowsocks.Net.csproj View File

@@ -7,6 +7,11 @@
<ItemGroup>
<PackageReference Include="BouncyCastle.NetCore" Version="1.8.6" />
<PackageReference Include="NaCl.Core" Version="2.0.0" />
<PackageReference Include="Splat" Version="9.6.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Shadowsocks\Shadowsocks.csproj" />
</ItemGroup>

</Project>

+ 14
- 24
Shadowsocks.Net/TCPListener.cs View File

@@ -1,4 +1,4 @@
using NLog;
using Splat;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -28,10 +28,8 @@ namespace Shadowsocks.Net
public virtual void Stop() { }
}

public class TCPListener
public class TCPListener : IEnableLogger
{
private static Logger logger = LogManager.GetCurrentClassLogger();

public class UDPState
{
public UDPState(Socket s)
@@ -44,15 +42,13 @@ namespace Shadowsocks.Net
public EndPoint remoteEndPoint;
}

Configuration _config;
bool _shareOverLAN;
IPEndPoint _localEndPoint;
Socket _tcpSocket;
IEnumerable<IStreamService> _services;

public TCPListener(Configuration config, IEnumerable<IStreamService> services)
public TCPListener(IPEndPoint localEndPoint, IEnumerable<IStreamService> services)
{
_config = config;
_shareOverLAN = config.shareOverLan;
_localEndPoint = localEndPoint;
_services = services;
}

@@ -64,28 +60,22 @@ namespace Shadowsocks.Net

public void Start()
{
if (CheckIfPortInUse(_config.localPort))
{
throw new Exception(I18N.GetString("Port {0} already in use", this._config.localPort));
}
if (CheckIfPortInUse(_localEndPoint.Port))
throw new Exception($"Port {_localEndPoint.Port} already in use");

try
{
// Create a TCP/IP socket.
_tcpSocket = new Socket(_config.isIPv6Enabled ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_tcpSocket = new Socket(_localEndPoint.AddressFamily, 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.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());
this.Log().Info($"Shadowsocks started TCP");
this.Log().Debug(Crypto.CryptoFactory.DumpRegisteredEncryptor());
_tcpSocket.BeginAccept(new AsyncCallback(AcceptCallback), _tcpSocket);
}
catch (SocketException)
@@ -126,7 +116,7 @@ namespace Shadowsocks.Net
}
catch (Exception e)
{
logger.LogUsefulException(e);
this.Log().Error(e, "");
}
finally
{
@@ -142,7 +132,7 @@ namespace Shadowsocks.Net
}
catch (Exception e)
{
logger.LogUsefulException(e);
this.Log().Error(e, "");
}
}
}
@@ -177,7 +167,7 @@ namespace Shadowsocks.Net
}
catch (Exception e)
{
logger.LogUsefulException(e);
this.Log().Error(e, "");
conn.Close();
}
}


+ 36
- 50
Shadowsocks.Net/TCPRelay.cs View File

@@ -1,45 +1,36 @@
using Splat;
using Shadowsocks.Net.Crypto;
using Shadowsocks.Net.Crypto.AEAD;
using Shadowsocks.Net.Proxy;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

using NLog;

using Shadowsocks.Controller.Strategy;
using Shadowsocks.Net.Crypto;
using Shadowsocks.Net.Crypto.AEAD;
using Shadowsocks.Model;
using Shadowsocks.Net.Proxy;
using Shadowsocks.Net.Sockets;

using static Shadowsocks.Net.Crypto.CryptoBase;
using Shadowsocks.Models;

namespace Shadowsocks.Net
{
class TCPRelay : StreamService
class TCPRelay : StreamService, IEnableLogger
{
public event EventHandler<SSTCPConnectedEventArgs> OnConnected;
public event EventHandler<SSTransmitEventArgs> OnInbound;
public event EventHandler<SSTransmitEventArgs> OnOutbound;
public event EventHandler<SSRelayEventArgs> OnFailed;

private static readonly Logger logger = LogManager.GetCurrentClassLogger();
private readonly ShadowsocksController _controller;
private Server _server;
private DateTime _lastSweepTime;
private readonly Configuration _config;

public ISet<TCPHandler> Handlers { get; set; }

public TCPRelay(ShadowsocksController controller, Configuration conf)
public TCPRelay(Server server)
{
_controller = controller;
_config = conf;
_server = server;
Handlers = new HashSet<TCPHandler>();
_lastSweepTime = DateTime.Now;
}
@@ -58,7 +49,7 @@ namespace Shadowsocks.Net

socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);

TCPHandler handler = new TCPHandler(_controller, _config, socket);
TCPHandler handler = new TCPHandler(_server, socket);

IList<TCPHandler> handlersToClose = new List<TCPHandler>();
lock (Handlers)
@@ -75,7 +66,7 @@ namespace Shadowsocks.Net
}
foreach (TCPHandler handler1 in handlersToClose)
{
logger.Debug("Closing timed out TCP connection.");
this.Log().Debug("Closing timed out TCP connection.");
handler1.Close();
}

@@ -102,7 +93,7 @@ namespace Shadowsocks.Net
}

socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
TCPHandler handler = new TCPHandler(_controller, _config, socket);
TCPHandler handler = new TCPHandler(_server, socket);

handler.OnConnected += OnConnected;
handler.OnInbound += OnInbound;
@@ -135,7 +126,7 @@ namespace Shadowsocks.Net
}
foreach (TCPHandler handler1 in handlersToClose)
{
logger.Debug("Closing timed out TCP connection.");
this.Log().Debug("Closing timed out TCP connection.");
handler1.Close();
}

@@ -191,7 +182,7 @@ namespace Shadowsocks.Net
}
}

internal class TCPHandler
internal class TCPHandler : IEnableLogger
{
public event EventHandler<SSTCPConnectedEventArgs> OnConnected;
public event EventHandler<SSTransmitEventArgs> OnInbound;
@@ -199,8 +190,6 @@ namespace Shadowsocks.Net
public event EventHandler<SSRelayEventArgs> OnClosed;
public event EventHandler<SSRelayEventArgs> OnFailed;

private static readonly Logger Logger = LogManager.GetCurrentClassLogger();

private readonly int _serverTimeout;
private readonly int _proxyTimeout;

@@ -216,14 +205,14 @@ namespace Shadowsocks.Net

public DateTime lastActivity;

private readonly ShadowsocksController _controller;
private readonly ForwardProxyConfig _config;
// TODO: forward proxy
//private readonly ForwardProxyConfig _config;
private readonly Server _server;
private readonly Socket _connection;
private IProxy _remote;
private IEncryptor encryptor;
private ICrypto encryptor;
// workaround
private IEncryptor decryptor;
private Server _server;
private ICrypto decryptor;

private byte[] _firstPacket;
private int _firstPacketLength;
@@ -240,28 +229,25 @@ namespace Shadowsocks.Net
private readonly object _closeConnLock = new object();

// TODO: decouple controller
public TCPHandler(ShadowsocksController controller, Configuration config, Socket socket)
public TCPHandler(Server server, Socket socket)
{
_controller = controller;
_config = config.proxy;
_server = server;
_connection = socket;
_proxyTimeout = config.proxy.proxyTimeout * 1000;
_serverTimeout = config.GetCurrentServer().timeout * 1000;
_proxyTimeout = 5000;
_serverTimeout = 5000;

lastActivity = DateTime.Now;
}

public void CreateRemote(EndPoint destination)
{
Server server = _controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)_connection.RemoteEndPoint, destination);
if (server == null || server.server == "")
if (_server == null || _server.Host == "")
{
throw new ArgumentException("No server configured");
}

encryptor = EncryptorFactory.GetEncryptor(server.method, server.password);
decryptor = EncryptorFactory.GetEncryptor(server.method, server.password);
_server = server;
encryptor = CryptoFactory.GetEncryptor(_server.Method, _server.Password);
decryptor = CryptoFactory.GetEncryptor(_server.Method, _server.Password);
}

public async Task StartAsync(byte[] firstPacket, int length)
@@ -283,7 +269,7 @@ namespace Shadowsocks.Net

private void ErrorClose(Exception e)
{
Logger.LogUsefulException(e);
this.Log().Error(e, "");
Close();
}

@@ -308,7 +294,7 @@ namespace Shadowsocks.Net
}
catch (Exception e)
{
Logger.LogUsefulException(e);
this.Log().Error(e, "");
}
}

@@ -330,7 +316,7 @@ namespace Shadowsocks.Net
{
// reject socks 4
response = new byte[] { 0, 91 };
Logger.Error("socks 5 protocol error");
this.Log().Error("socks5 protocol error");
}
await _connection.SendAsync(response, SocketFlags.None);

@@ -452,11 +438,12 @@ namespace Shadowsocks.Net
CreateRemote(destination);
IProxy remote;
EndPoint proxyEP = null;
EndPoint serverEP = new DnsEndPoint(_server.server, _server.server_port);
EndPoint pluginEP = _controller.GetPluginLocalEndPointIfConfigured(_server);
EndPoint serverEP = new DnsEndPoint(_server.Host, _server.Port);
EndPoint pluginEP = null; // TODO: plugin local end point

remote = new DirectConnect(); // TODO: forward proxy
NetworkCredential auth = null;
if (_config.useAuth)
/*if (_config.useAuth)
{
auth = new NetworkCredential(_config.authUser, _config.authPwd);
}
@@ -478,8 +465,7 @@ namespace Shadowsocks.Net
else
{
remote = new DirectConnect();
}

}*/

CancellationTokenSource cancelProxy = new CancellationTokenSource(_proxyTimeout * 1000);

@@ -488,13 +474,13 @@ namespace Shadowsocks.Net

if (!(remote is DirectConnect))
{
Logger.Debug($"Socket connected to proxy {remote.ProxyEndPoint}");
this.Log().Debug($"Socket connected to proxy {remote.ProxyEndPoint}");
}

var _startConnectTime = DateTime.Now;
CancellationTokenSource cancelServer = new CancellationTokenSource(_serverTimeout * 1000);
await remote.ConnectRemoteAsync(serverEP, cancelServer.Token);
Logger.Debug($"Socket connected to ss server: {_server}");
this.Log().Debug($"Socket connected to ss server: {_server}");
TimeSpan latency = DateTime.Now - _startConnectTime;
OnConnected?.Invoke(this, new SSTCPConnectedEventArgs(_server, latency));



+ 14
- 24
Shadowsocks.Net/UDPListener.cs View File

@@ -1,4 +1,5 @@
using System;
using Splat;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
@@ -6,10 +7,8 @@ using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using NLog;
using Shadowsocks.Model;

namespace Shadowsocks.Controller
namespace Shadowsocks.Net
{
public interface IDatagramService
{
@@ -25,10 +24,8 @@ namespace Shadowsocks.Controller
public virtual void Stop() { }
}

public class UDPListener
public class UDPListener : IEnableLogger
{
private static Logger logger = LogManager.GetCurrentClassLogger();

public class UDPState
{
public UDPState(Socket s)
@@ -41,18 +38,15 @@ namespace Shadowsocks.Controller
public EndPoint remoteEndPoint;
}

Configuration _config;
bool _shareOverLAN;
IPEndPoint _localEndPoint;
Socket _udpSocket;
IEnumerable<IDatagramService> _services;
CancellationTokenSource tokenSource = new CancellationTokenSource();

public UDPListener(Configuration config, IEnumerable<IDatagramService> services)
public UDPListener(IPEndPoint localEndPoint, IEnumerable<IDatagramService> services)
{
this._config = config;
this._shareOverLAN = _config.shareOverLan;

this._services = services;
_localEndPoint = localEndPoint;
_services = services;
}

private bool CheckIfPortInUse(int port)
@@ -63,23 +57,19 @@ namespace Shadowsocks.Controller

public void Start()
{
if (CheckIfPortInUse(this._config.localPort))
throw new Exception(I18N.GetString("Port {0} already in use", this._config.localPort));
if (CheckIfPortInUse(_localEndPoint.Port))
throw new Exception($"Port {_localEndPoint.Port} already in use");

// Create a TCP/IP socket.
_udpSocket = new Socket(_config.isIPv6Enabled ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
_udpSocket = new Socket(_localEndPoint.AddressFamily, 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);
_udpSocket.Bind(_localEndPoint);

// Start an asynchronous socket to listen for connections.
logger.Info($"Shadowsocks started UDP ({UpdateChecker.Version})");
logger.Debug(Encryption.EncryptorFactory.DumpRegisteredEncryptor());
this.Log().Info($"Shadowsocks started UDP");
this.Log().Debug(Crypto.CryptoFactory.DumpRegisteredEncryptor());
UDPState udpState = new UDPState(_udpSocket);
// _udpSocket.BeginReceiveFrom(udpState.buffer, 0, udpState.buffer.Length, 0, ref udpState.remoteEndPoint, new AsyncCallback(RecvFromCallback), udpState);
Task.Run(() => WorkLoop(tokenSource.Token));


+ 18
- 19
Shadowsocks.Net/UDPRelay.cs View File

@@ -1,3 +1,6 @@
using Shadowsocks.Models;
using Shadowsocks.Net.Crypto;
using Splat;
using System;
using System.Buffers;
using System.Collections.Generic;
@@ -6,24 +9,21 @@ using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using NLog;
using Shadowsocks.Net.Crypto;

namespace Shadowsocks.Controller
namespace Shadowsocks.Net
{
class UDPRelay : DatagramService
{
private ShadowsocksController _controller;

Server _server;
// TODO: choose a smart number
private LRUCache<IPEndPoint, UDPHandler> _cache = new LRUCache<IPEndPoint, UDPHandler>(512);

public long outbound = 0;
public long inbound = 0;

public UDPRelay(ShadowsocksController controller)
public UDPRelay(Server server)
{
this._controller = controller;
_server = server;
}

public override async Task<bool> Handle(Memory<byte> packet, Socket socket, EndPoint client)
@@ -40,7 +40,7 @@ namespace Shadowsocks.Controller
UDPHandler handler = _cache.get(remoteEndPoint);
if (handler == null)
{
handler = new UDPHandler(socket, _controller.GetAServer(IStrategyCallerType.UDP, remoteEndPoint, null/*TODO: fix this*/), remoteEndPoint);
handler = new UDPHandler(socket, _server, remoteEndPoint);
handler.Receive();
_cache.add(remoteEndPoint, handler);
}
@@ -48,9 +48,8 @@ namespace Shadowsocks.Controller
return true;
}

public class UDPHandler
public class UDPHandler : IEnableLogger
{
private static Logger logger = LogManager.GetCurrentClassLogger();
private static MemoryPool<byte> pool = MemoryPool<byte>.Shared;
private Socket _local;
private Socket _remote;
@@ -81,25 +80,25 @@ namespace Shadowsocks.Controller
_localEndPoint = localEndPoint;

// TODO async resolving
bool parsed = IPAddress.TryParse(server.server, out IPAddress ipAddress);
bool parsed = IPAddress.TryParse(server.Host, out IPAddress ipAddress);
if (!parsed)
{
IPHostEntry ipHostInfo = Dns.GetHostEntry(server.server);
IPHostEntry ipHostInfo = Dns.GetHostEntry(server.Host);
ipAddress = ipHostInfo.AddressList[0];
}
_remoteEndPoint = new IPEndPoint(ipAddress, server.server_port);
_remoteEndPoint = new IPEndPoint(ipAddress, server.Port);
_remote = new Socket(_remoteEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
_remote.Bind(new IPEndPoint(ListenAddress, 0));
}

public async Task SendAsync(ReadOnlyMemory<byte> data)
{
IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password);
ICrypto encryptor = CryptoFactory.GetEncryptor(_server.Method, _server.Password);
using IMemoryOwner<byte> 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");
this.Log().Debug($"{_localEndPoint} {_remoteEndPoint} {outlen} UDP Relay up");
if (!MemoryMarshal.TryGetArray(mem.Memory[..outlen], out ArraySegment<byte> outData))
{
throw new InvalidOperationException("Can't extract underly array segment");
@@ -110,7 +109,7 @@ namespace Shadowsocks.Controller
public async Task ReceiveAsync()
{
EndPoint remoteEndPoint = new IPEndPoint(ListenAddress, 0);
logger.Debug($"++++++Receive Server Port, size:" + _buffer.Length);
this.Log().Debug($"++++++Receive Server Port, size:" + _buffer.Length);
try
{
while (true)
@@ -121,9 +120,9 @@ namespace Shadowsocks.Controller
using IMemoryOwner<byte> owner = pool.Rent(bytesRead + 3);
Memory<byte> o = owner.Memory;

IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password);
ICrypto encryptor = CryptoFactory.GetEncryptor(_server.Method, _server.Password);
int outlen = encryptor.DecryptUDP(o.Span[3..], _buffer.AsSpan(0, bytesRead));
logger.Debug(_remoteEndPoint, _localEndPoint, outlen, "UDP Relay down");
this.Log().Debug($"{_remoteEndPoint} {_localEndPoint} {outlen} UDP Relay down");
if (!MemoryMarshal.TryGetArray(o[..(outlen + 3)], out ArraySegment<byte> data))
{
throw new InvalidOperationException("Can't extract underly array segment");
@@ -134,7 +133,7 @@ namespace Shadowsocks.Controller
}
catch (Exception e)
{
logger.LogUsefulException(e);
this.Log().Warn(e, "");
}
}



Loading…
Cancel
Save