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.Exception;
using Shadowsocks.Net.Crypto.Stream; using Shadowsocks.Net.Crypto.Stream;
using Splat;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net; using System.Net;
@@ -8,9 +9,8 @@ using System.Text;


namespace Shadowsocks.Net.Crypto.AEAD 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 // We are using the same saltLen and keyLen
private const string Info = "ss-subkey"; private const string Info = "ss-subkey";
private static readonly byte[] InfoBytes = Encoding.ASCII.GetBytes(Info); 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 // Initialize all-zero nonce for each connection
nonce = new byte[nonceLen]; 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(); protected abstract Dictionary<string, CipherInfo> GetCiphers();
@@ -92,8 +92,8 @@ namespace Shadowsocks.Net.Crypto.AEAD


CryptoUtils.HKDF(keyLen, masterKey, salt, InfoBytes).CopyTo(sessionKey, 0); 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); 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 not, keep buf for next run, at this condition, buffer is not empty
if (outlength + ChunkOverhead > cipher.Length) 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 // write rest data to head of shared buffer
tmp.CopyTo(buffer); tmp.CopyTo(buffer);
@@ -150,7 +150,7 @@ namespace Shadowsocks.Net.Crypto.AEAD
bufSize = tmp.Length; bufSize = tmp.Length;
if (bufSize <= 0) if (bufSize <= 0)
{ {
logger.Debug("No more data to encrypt, leaving");
this.Log().Debug("No more data to encrypt, leaving");
return outlength; return outlength;
} }
} }
@@ -165,7 +165,7 @@ namespace Shadowsocks.Net.Crypto.AEAD
cipher.CopyTo(tmp.Slice(bufPtr)); cipher.CopyTo(tmp.Slice(bufPtr));
int bufSize = tmp.Length; int bufSize = tmp.Length;


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


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


// drop decrypted data // drop decrypted data
tmp = tmp.Slice(ChunkLengthBytes + tagLen + len + tagLen); tmp = tmp.Slice(ChunkLengthBytes + tagLen + len + tagLen);
@@ -225,7 +225,7 @@ namespace Shadowsocks.Net.Crypto.AEAD
// logger.Debug("aead dec outlength " + outlength); // logger.Debug("aead dec outlength " + outlength);
if (outlength + ChunkOverhead > cipher.Length) 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); tmp.CopyTo(buffer);
bufPtr = tmp.Length; bufPtr = tmp.Length;
return outlength; return outlength;
@@ -235,7 +235,7 @@ namespace Shadowsocks.Net.Crypto.AEAD
if (bufSize <= 0) if (bufSize <= 0)
{ {
bufPtr = 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; return outlength;
} }
} }
@@ -266,7 +266,7 @@ namespace Shadowsocks.Net.Crypto.AEAD
{ {
if (plain.Length > ChunkLengthMask) if (plain.Length > ChunkLengthMask)
{ {
logger.Error("enc chunk too big");
this.Log().Error("enc chunk too big");
throw new CryptoErrorException(); throw new CryptoErrorException();
} }


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


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

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


namespace Shadowsocks.Net.Crypto.Stream 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 // shared by TCP decrypt UDP encrypt and decrypt
protected static byte[] sharedBuffer = new byte[65536]; protected static byte[] sharedBuffer = new byte[65536];


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


InitKey(password); InitKey(password);


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


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


@@ -116,7 +115,7 @@ namespace Shadowsocks.Net.Crypto.Stream
[MethodImpl(MethodImplOptions.Synchronized)] [MethodImpl(MethodImplOptions.Synchronized)]
public override int Decrypt(Span<byte> plain, ReadOnlySpan<byte> cipher) 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; int cipherOffset = 0;
// is first packet, need read iv // is first packet, need read iv
@@ -149,9 +148,9 @@ namespace Shadowsocks.Net.Crypto.Stream
// read all data from buffer // read all data from buffer
int len = CipherDecrypt(plain, cipher.Slice(cipherOffset)); 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; return len;
} }




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

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


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


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


if (_respondLineCount == 0) if (_respondLineCount == 0)
{ {


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

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

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


</Project> </Project>

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

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


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

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


Configuration _config;
bool _shareOverLAN;
IPEndPoint _localEndPoint;
Socket _tcpSocket; Socket _tcpSocket;
IEnumerable<IStreamService> _services; 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; _services = services;
} }


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


public void Start() 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 try
{ {
// Create a TCP/IP socket. // 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); _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. // Bind the socket to the local endpoint and listen for incoming connections.
_tcpSocket.Bind(localEndPoint);
_tcpSocket.Bind(_localEndPoint);
_tcpSocket.Listen(1024); _tcpSocket.Listen(1024);


// Start an asynchronous socket to listen for connections. // 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); _tcpSocket.BeginAccept(new AsyncCallback(AcceptCallback), _tcpSocket);
} }
catch (SocketException) catch (SocketException)
@@ -126,7 +116,7 @@ namespace Shadowsocks.Net
} }
catch (Exception e) catch (Exception e)
{ {
logger.LogUsefulException(e);
this.Log().Error(e, "");
} }
finally finally
{ {
@@ -142,7 +132,7 @@ namespace Shadowsocks.Net
} }
catch (Exception e) catch (Exception e)
{ {
logger.LogUsefulException(e);
this.Log().Error(e, "");
} }
} }
} }
@@ -177,7 +167,7 @@ namespace Shadowsocks.Net
} }
catch (Exception e) catch (Exception e)
{ {
logger.LogUsefulException(e);
this.Log().Error(e, "");
conn.Close(); 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;
using System.Buffers; using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; 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 static Shadowsocks.Net.Crypto.CryptoBase;
using Shadowsocks.Models;


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


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


public ISet<TCPHandler> Handlers { get; set; } 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>(); Handlers = new HashSet<TCPHandler>();
_lastSweepTime = DateTime.Now; _lastSweepTime = DateTime.Now;
} }
@@ -58,7 +49,7 @@ namespace Shadowsocks.Net


socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); 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>(); IList<TCPHandler> handlersToClose = new List<TCPHandler>();
lock (Handlers) lock (Handlers)
@@ -75,7 +66,7 @@ namespace Shadowsocks.Net
} }
foreach (TCPHandler handler1 in handlersToClose) foreach (TCPHandler handler1 in handlersToClose)
{ {
logger.Debug("Closing timed out TCP connection.");
this.Log().Debug("Closing timed out TCP connection.");
handler1.Close(); handler1.Close();
} }


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


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


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


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


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


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

private readonly int _serverTimeout; private readonly int _serverTimeout;
private readonly int _proxyTimeout; private readonly int _proxyTimeout;


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


public DateTime lastActivity; 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 readonly Socket _connection;
private IProxy _remote; private IProxy _remote;
private IEncryptor encryptor;
private ICrypto encryptor;
// workaround // workaround
private IEncryptor decryptor;
private Server _server;
private ICrypto decryptor;


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


// TODO: decouple controller // 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; _connection = socket;
_proxyTimeout = config.proxy.proxyTimeout * 1000;
_serverTimeout = config.GetCurrentServer().timeout * 1000;
_proxyTimeout = 5000;
_serverTimeout = 5000;


lastActivity = DateTime.Now; lastActivity = DateTime.Now;
} }


public void CreateRemote(EndPoint destination) 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"); 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) public async Task StartAsync(byte[] firstPacket, int length)
@@ -283,7 +269,7 @@ namespace Shadowsocks.Net


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


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


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


@@ -452,11 +438,12 @@ namespace Shadowsocks.Net
CreateRemote(destination); CreateRemote(destination);
IProxy remote; IProxy remote;
EndPoint proxyEP = null; 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; NetworkCredential auth = null;
if (_config.useAuth)
/*if (_config.useAuth)
{ {
auth = new NetworkCredential(_config.authUser, _config.authPwd); auth = new NetworkCredential(_config.authUser, _config.authPwd);
} }
@@ -478,8 +465,7 @@ namespace Shadowsocks.Net
else else
{ {
remote = new DirectConnect(); remote = new DirectConnect();
}

}*/


CancellationTokenSource cancelProxy = new CancellationTokenSource(_proxyTimeout * 1000); CancellationTokenSource cancelProxy = new CancellationTokenSource(_proxyTimeout * 1000);


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


if (!(remote is DirectConnect)) 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; var _startConnectTime = DateTime.Now;
CancellationTokenSource cancelServer = new CancellationTokenSource(_serverTimeout * 1000); CancellationTokenSource cancelServer = new CancellationTokenSource(_serverTimeout * 1000);
await remote.ConnectRemoteAsync(serverEP, cancelServer.Token); 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; TimeSpan latency = DateTime.Now - _startConnectTime;
OnConnected?.Invoke(this, new SSTCPConnectedEventArgs(_server, latency)); 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.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
@@ -6,10 +7,8 @@ using System.Net.NetworkInformation;
using System.Net.Sockets; using System.Net.Sockets;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using NLog;
using Shadowsocks.Model;


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


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

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


Configuration _config;
bool _shareOverLAN;
IPEndPoint _localEndPoint;
Socket _udpSocket; Socket _udpSocket;
IEnumerable<IDatagramService> _services; IEnumerable<IDatagramService> _services;
CancellationTokenSource tokenSource = new CancellationTokenSource(); 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) private bool CheckIfPortInUse(int port)
@@ -63,23 +57,19 @@ namespace Shadowsocks.Controller


public void Start() 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. // 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); _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. // 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. // 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); UDPState udpState = new UDPState(_udpSocket);
// _udpSocket.BeginReceiveFrom(udpState.buffer, 0, udpState.buffer.Length, 0, ref udpState.remoteEndPoint, new AsyncCallback(RecvFromCallback), udpState); // _udpSocket.BeginReceiveFrom(udpState.buffer, 0, udpState.buffer.Length, 0, ref udpState.remoteEndPoint, new AsyncCallback(RecvFromCallback), udpState);
Task.Run(() => WorkLoop(tokenSource.Token)); 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;
using System.Buffers; using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
@@ -6,24 +9,21 @@ using System.Net.Sockets;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using NLog;
using Shadowsocks.Net.Crypto;


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

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


public long outbound = 0; public long outbound = 0;
public long inbound = 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) 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); UDPHandler handler = _cache.get(remoteEndPoint);
if (handler == null) 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(); handler.Receive();
_cache.add(remoteEndPoint, handler); _cache.add(remoteEndPoint, handler);
} }
@@ -48,9 +48,8 @@ namespace Shadowsocks.Controller
return true; return true;
} }


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


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


public async Task SendAsync(ReadOnlyMemory<byte> data) 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); using IMemoryOwner<byte> mem = pool.Rent(data.Length + 1000);


// byte[] dataOut = new byte[slicedData.Length + 1000]; // byte[] dataOut = new byte[slicedData.Length + 1000];
int outlen = encryptor.EncryptUDP(data.Span[3..], mem.Memory.Span); 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)) if (!MemoryMarshal.TryGetArray(mem.Memory[..outlen], out ArraySegment<byte> outData))
{ {
throw new InvalidOperationException("Can't extract underly array segment"); throw new InvalidOperationException("Can't extract underly array segment");
@@ -110,7 +109,7 @@ namespace Shadowsocks.Controller
public async Task ReceiveAsync() public async Task ReceiveAsync()
{ {
EndPoint remoteEndPoint = new IPEndPoint(ListenAddress, 0); EndPoint remoteEndPoint = new IPEndPoint(ListenAddress, 0);
logger.Debug($"++++++Receive Server Port, size:" + _buffer.Length);
this.Log().Debug($"++++++Receive Server Port, size:" + _buffer.Length);
try try
{ {
while (true) while (true)
@@ -121,9 +120,9 @@ namespace Shadowsocks.Controller
using IMemoryOwner<byte> owner = pool.Rent(bytesRead + 3); using IMemoryOwner<byte> owner = pool.Rent(bytesRead + 3);
Memory<byte> o = owner.Memory; 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)); 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)) if (!MemoryMarshal.TryGetArray(o[..(outlen + 3)], out ArraySegment<byte> data))
{ {
throw new InvalidOperationException("Can't extract underly array segment"); throw new InvalidOperationException("Can't extract underly array segment");
@@ -134,7 +133,7 @@ namespace Shadowsocks.Controller
} }
catch (Exception e) catch (Exception e)
{ {
logger.LogUsefulException(e);
this.Log().Warn(e, "");
} }
} }




Loading…
Cancel
Save