@@ -14,9 +14,18 @@ namespace Shadowsocks.Controller | |||||
class TCPRelay : Listener.Service | class TCPRelay : Listener.Service | ||||
{ | { | ||||
private ShadowsocksController _controller; | private ShadowsocksController _controller; | ||||
private DateTime _lastSweepTime; | |||||
public ISet<Handler> Handlers | |||||
{ | |||||
get; set; | |||||
} | |||||
public TCPRelay(ShadowsocksController controller) | public TCPRelay(ShadowsocksController controller) | ||||
{ | { | ||||
this._controller = controller; | this._controller = controller; | ||||
this.Handlers = new HashSet<Handler>(); | |||||
this._lastSweepTime = DateTime.Now; | |||||
} | } | ||||
public bool Handle(byte[] firstPacket, int length, Socket socket, object state) | public bool Handle(byte[] firstPacket, int length, Socket socket, object state) | ||||
@@ -33,9 +42,33 @@ namespace Shadowsocks.Controller | |||||
Handler handler = new Handler(); | Handler handler = new Handler(); | ||||
handler.connection = socket; | handler.connection = socket; | ||||
handler.controller = _controller; | handler.controller = _controller; | ||||
handler.relay = this; | |||||
handler.Start(firstPacket, length); | handler.Start(firstPacket, length); | ||||
return true; | |||||
IList<Handler> handlersToClose = new List<Handler>(); | |||||
lock (this.Handlers) | |||||
{ | |||||
this.Handlers.Add(handler); | |||||
Logging.Debug($"connections: {Handlers.Count}"); | |||||
DateTime now = DateTime.Now; | |||||
if (now - _lastSweepTime > TimeSpan.FromSeconds(1)) | |||||
{ | |||||
_lastSweepTime = now; | |||||
foreach (Handler handler1 in this.Handlers) | |||||
{ | |||||
if (now - handler1.lastActivity > TimeSpan.FromSeconds(1800)) | |||||
{ | |||||
handlersToClose.Add(handler1); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
foreach (Handler handler1 in handlersToClose) | |||||
{ | |||||
Logging.Debug("Closing timed out connection"); | |||||
handler1.Close(); | |||||
} | |||||
return true; | |||||
} | } | ||||
} | } | ||||
@@ -48,6 +81,10 @@ namespace Shadowsocks.Controller | |||||
public Socket remote; | public Socket remote; | ||||
public Socket connection; | public Socket connection; | ||||
public ShadowsocksController controller; | public ShadowsocksController controller; | ||||
public TCPRelay relay; | |||||
public DateTime lastActivity; | |||||
private int retryCount = 0; | private int retryCount = 0; | ||||
private bool connected; | private bool connected; | ||||
@@ -55,7 +92,7 @@ namespace Shadowsocks.Controller | |||||
private byte[] _firstPacket; | private byte[] _firstPacket; | ||||
private int _firstPacketLength; | private int _firstPacketLength; | ||||
// Size of receive buffer. | // Size of receive buffer. | ||||
public const int RecvSize = 16384; | |||||
public const int RecvSize = 8192; | |||||
public const int BufferSize = RecvSize + 32; | public const int BufferSize = RecvSize + 32; | ||||
private int totalRead = 0; | private int totalRead = 0; | ||||
@@ -96,6 +133,7 @@ namespace Shadowsocks.Controller | |||||
this._firstPacket = firstPacket; | this._firstPacket = firstPacket; | ||||
this._firstPacketLength = length; | this._firstPacketLength = length; | ||||
this.HandshakeReceive(); | this.HandshakeReceive(); | ||||
this.lastActivity = DateTime.Now; | |||||
} | } | ||||
private void CheckClose() | private void CheckClose() | ||||
@@ -108,6 +146,11 @@ namespace Shadowsocks.Controller | |||||
public void Close() | public void Close() | ||||
{ | { | ||||
lock (relay.Handlers) | |||||
{ | |||||
Logging.Debug($"connections: {relay.Handlers.Count}"); | |||||
relay.Handlers.Remove(this); | |||||
} | |||||
lock (this) | lock (this) | ||||
{ | { | ||||
if (closed) | if (closed) | ||||
@@ -485,6 +528,7 @@ namespace Shadowsocks.Controller | |||||
if (bytesRead > 0) | if (bytesRead > 0) | ||||
{ | { | ||||
this.lastActivity = DateTime.Now; | |||||
int bytesToSend; | int bytesToSend; | ||||
lock (decryptionLock) | lock (decryptionLock) | ||||
{ | { | ||||
@@ -1,6 +1,7 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Text; | using System.Text; | ||||
using System.Threading; | |||||
namespace Shadowsocks.Encryption | namespace Shadowsocks.Encryption | ||||
{ | { | ||||
@@ -12,19 +13,17 @@ namespace Shadowsocks.Encryption | |||||
const int SODIUM_BLOCK_SIZE = 64; | const int SODIUM_BLOCK_SIZE = 64; | ||||
static byte[] sodiumBuf = new byte[MAX_INPUT_SIZE + SODIUM_BLOCK_SIZE]; | |||||
protected int _encryptBytesRemaining; | protected int _encryptBytesRemaining; | ||||
protected int _decryptBytesRemaining; | protected int _decryptBytesRemaining; | ||||
protected ulong _encryptIC; | protected ulong _encryptIC; | ||||
protected ulong _decryptIC; | protected ulong _decryptIC; | ||||
protected byte[] _encryptBuf; | |||||
protected byte[] _decryptBuf; | |||||
public SodiumEncryptor(string method, string password) | public SodiumEncryptor(string method, string password) | ||||
: base(method, password) | : base(method, password) | ||||
{ | { | ||||
InitKey(method, password); | InitKey(method, password); | ||||
_encryptBuf = new byte[MAX_INPUT_SIZE + SODIUM_BLOCK_SIZE]; | |||||
_decryptBuf = new byte[MAX_INPUT_SIZE + SODIUM_BLOCK_SIZE]; | |||||
} | } | ||||
private static Dictionary<string, int[]> _ciphers = new Dictionary<string, int[]> { | private static Dictionary<string, int[]> _ciphers = new Dictionary<string, int[]> { | ||||
@@ -47,48 +46,51 @@ namespace Shadowsocks.Encryption | |||||
// TODO write a unidirection cipher so we don't have to if if if | // TODO write a unidirection cipher so we don't have to if if if | ||||
int bytesRemaining; | int bytesRemaining; | ||||
ulong ic; | ulong ic; | ||||
byte[] sodiumBuf; | |||||
byte[] iv; | byte[] iv; | ||||
if (isCipher) | |||||
{ | |||||
bytesRemaining = _encryptBytesRemaining; | |||||
ic = _encryptIC; | |||||
sodiumBuf = _encryptBuf; | |||||
iv = _encryptIV; | |||||
} | |||||
else | |||||
{ | |||||
bytesRemaining = _decryptBytesRemaining; | |||||
ic = _decryptIC; | |||||
sodiumBuf = _decryptBuf; | |||||
iv = _decryptIV; | |||||
} | |||||
int padding = bytesRemaining; | |||||
Buffer.BlockCopy(buf, 0, sodiumBuf, padding, length); | |||||
switch (_cipher) | |||||
// I'm tired. just add a big lock | |||||
// let's optimize for RAM instead of CPU | |||||
lock(sodiumBuf) | |||||
{ | { | ||||
case CIPHER_SALSA20: | |||||
Sodium.crypto_stream_salsa20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key); | |||||
break; | |||||
case CIPHER_CHACHA20: | |||||
Sodium.crypto_stream_chacha20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key); | |||||
break; | |||||
} | |||||
Buffer.BlockCopy(sodiumBuf, padding, outbuf, 0, length); | |||||
padding += length; | |||||
ic += (ulong)padding / SODIUM_BLOCK_SIZE; | |||||
bytesRemaining = padding % SODIUM_BLOCK_SIZE; | |||||
if (isCipher) | |||||
{ | |||||
bytesRemaining = _encryptBytesRemaining; | |||||
ic = _encryptIC; | |||||
iv = _encryptIV; | |||||
} | |||||
else | |||||
{ | |||||
bytesRemaining = _decryptBytesRemaining; | |||||
ic = _decryptIC; | |||||
iv = _decryptIV; | |||||
} | |||||
int padding = bytesRemaining; | |||||
Buffer.BlockCopy(buf, 0, sodiumBuf, padding, length); | |||||
if (isCipher) | |||||
{ | |||||
_encryptBytesRemaining = bytesRemaining; | |||||
_encryptIC = ic; | |||||
} | |||||
else | |||||
{ | |||||
_decryptBytesRemaining = bytesRemaining; | |||||
_decryptIC = ic; | |||||
switch (_cipher) | |||||
{ | |||||
case CIPHER_SALSA20: | |||||
Sodium.crypto_stream_salsa20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key); | |||||
break; | |||||
case CIPHER_CHACHA20: | |||||
Sodium.crypto_stream_chacha20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key); | |||||
break; | |||||
} | |||||
Buffer.BlockCopy(sodiumBuf, padding, outbuf, 0, length); | |||||
padding += length; | |||||
ic += (ulong)padding / SODIUM_BLOCK_SIZE; | |||||
bytesRemaining = padding % SODIUM_BLOCK_SIZE; | |||||
if (isCipher) | |||||
{ | |||||
_encryptBytesRemaining = bytesRemaining; | |||||
_encryptIC = ic; | |||||
} | |||||
else | |||||
{ | |||||
_decryptBytesRemaining = bytesRemaining; | |||||
_decryptIC = ic; | |||||
} | |||||
} | } | ||||
} | } | ||||