|
|
@@ -1,10 +1,12 @@ |
|
|
|
using NLog;
|
|
|
|
using System;
|
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Linq;
|
|
|
|
using System.Net;
|
|
|
|
using System.Net.Sockets;
|
|
|
|
using System.Timers;
|
|
|
|
|
|
|
|
using NLog;
|
|
|
|
|
|
|
|
using Shadowsocks.Controller.Strategy;
|
|
|
|
using Shadowsocks.Encryption;
|
|
|
|
using Shadowsocks.Encryption.AEAD;
|
|
|
@@ -12,16 +14,22 @@ using Shadowsocks.Encryption.Exception; |
|
|
|
using Shadowsocks.Model;
|
|
|
|
using Shadowsocks.Proxy;
|
|
|
|
using Shadowsocks.Util.Sockets;
|
|
|
|
|
|
|
|
using static Shadowsocks.Encryption.EncryptorBase;
|
|
|
|
|
|
|
|
namespace Shadowsocks.Controller
|
|
|
|
{
|
|
|
|
class TCPRelay : Listener.Service
|
|
|
|
internal class TCPRelay : Listener.Service
|
|
|
|
{
|
|
|
|
private static Logger logger = LogManager.GetCurrentClassLogger();
|
|
|
|
private ShadowsocksController _controller;
|
|
|
|
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 DateTime _lastSweepTime;
|
|
|
|
private Configuration _config;
|
|
|
|
private readonly Configuration _config;
|
|
|
|
|
|
|
|
public ISet<TCPHandler> Handlers { get; set; }
|
|
|
|
|
|
|
@@ -37,9 +45,24 @@ namespace Shadowsocks.Controller |
|
|
|
{
|
|
|
|
if (socket.ProtocolType != ProtocolType.Tcp
|
|
|
|
|| (length < 2 || firstPacket[0] != 5))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
|
|
|
|
TCPHandler handler = new TCPHandler(_controller, _config, this, socket);
|
|
|
|
TCPHandler handler = new TCPHandler(_controller, _config, socket);
|
|
|
|
|
|
|
|
handler.OnConnected += OnConnected;
|
|
|
|
handler.OnInbound += OnInbound;
|
|
|
|
handler.OnOutbound += OnOutbound;
|
|
|
|
handler.OnFailed += OnFailed;
|
|
|
|
handler.OnClosed += (h, arg) =>
|
|
|
|
{
|
|
|
|
lock (Handlers)
|
|
|
|
{
|
|
|
|
Handlers.Remove(handler);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
IList<TCPHandler> handlersToClose = new List<TCPHandler>();
|
|
|
|
lock (Handlers)
|
|
|
@@ -50,8 +73,12 @@ namespace Shadowsocks.Controller |
|
|
|
{
|
|
|
|
_lastSweepTime = now;
|
|
|
|
foreach (TCPHandler handler1 in Handlers)
|
|
|
|
{
|
|
|
|
if (now - handler1.lastActivity > TimeSpan.FromSeconds(900))
|
|
|
|
{
|
|
|
|
handlersToClose.Add(handler1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
foreach (TCPHandler handler1 in handlersToClose)
|
|
|
@@ -80,26 +107,46 @@ namespace Shadowsocks.Controller |
|
|
|
}
|
|
|
|
handlersToClose.ForEach(h => h.Close());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void UpdateInboundCounter(Server server, long n)
|
|
|
|
public class SSRelayEventArgs : EventArgs
|
|
|
|
{
|
|
|
|
public readonly Server server;
|
|
|
|
|
|
|
|
public SSRelayEventArgs(Server server)
|
|
|
|
{
|
|
|
|
_controller.UpdateInboundCounter(server, n);
|
|
|
|
this.server = server;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void UpdateOutboundCounter(Server server, long n)
|
|
|
|
public class SSTransmitEventArgs : SSRelayEventArgs
|
|
|
|
{
|
|
|
|
public readonly long length;
|
|
|
|
public SSTransmitEventArgs(Server server, long length) : base(server)
|
|
|
|
{
|
|
|
|
_controller.UpdateOutboundCounter(server, n);
|
|
|
|
this.length = length;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public class SSTCPConnectedEventArgs : SSRelayEventArgs
|
|
|
|
{
|
|
|
|
public readonly TimeSpan latency;
|
|
|
|
|
|
|
|
public void UpdateLatency(Server server, TimeSpan latency)
|
|
|
|
public SSTCPConnectedEventArgs(Server server, TimeSpan latency) : base(server)
|
|
|
|
{
|
|
|
|
_controller.UpdateLatency(server, latency);
|
|
|
|
this.latency = latency;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal class TCPHandler
|
|
|
|
{
|
|
|
|
class AsyncSession
|
|
|
|
public event EventHandler<SSTCPConnectedEventArgs> OnConnected;
|
|
|
|
public event EventHandler<SSTransmitEventArgs> OnInbound;
|
|
|
|
public event EventHandler<SSTransmitEventArgs> OnOutbound;
|
|
|
|
public event EventHandler<SSRelayEventArgs> OnClosed;
|
|
|
|
public event EventHandler<SSRelayEventArgs> OnFailed;
|
|
|
|
|
|
|
|
private class AsyncSession
|
|
|
|
{
|
|
|
|
public IProxy Remote { get; }
|
|
|
|
|
|
|
@@ -109,7 +156,7 @@ namespace Shadowsocks.Controller |
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class AsyncSession<T> : AsyncSession
|
|
|
|
private class AsyncSession<T> : AsyncSession
|
|
|
|
{
|
|
|
|
public T State { get; set; }
|
|
|
|
|
|
|
@@ -124,7 +171,7 @@ namespace Shadowsocks.Controller |
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static Logger Logger = LogManager.GetCurrentClassLogger();
|
|
|
|
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
|
|
|
|
|
|
|
private readonly int _serverTimeout;
|
|
|
|
private readonly int _proxyTimeout;
|
|
|
@@ -143,10 +190,9 @@ namespace Shadowsocks.Controller |
|
|
|
|
|
|
|
public DateTime lastActivity;
|
|
|
|
|
|
|
|
private ShadowsocksController _controller;
|
|
|
|
private Configuration _config;
|
|
|
|
private TCPRelay _tcprelay;
|
|
|
|
private Socket _connection;
|
|
|
|
private readonly ShadowsocksController _controller;
|
|
|
|
private readonly ProxyConfig _config;
|
|
|
|
private readonly Socket _connection;
|
|
|
|
|
|
|
|
private IEncryptor _encryptor;
|
|
|
|
private Server _server;
|
|
|
@@ -170,16 +216,16 @@ namespace Shadowsocks.Controller |
|
|
|
private int _totalWrite = 0;
|
|
|
|
|
|
|
|
// remote -> local proxy (ciphertext, before decrypt)
|
|
|
|
private byte[] _remoteRecvBuffer = new byte[BufferSize];
|
|
|
|
private readonly byte[] _remoteRecvBuffer = new byte[BufferSize];
|
|
|
|
|
|
|
|
// client -> local proxy (plaintext, before encrypt)
|
|
|
|
private byte[] _connetionRecvBuffer = new byte[BufferSize];
|
|
|
|
private readonly byte[] _connetionRecvBuffer = new byte[BufferSize];
|
|
|
|
|
|
|
|
// local proxy -> remote (plaintext, after decrypt)
|
|
|
|
private byte[] _remoteSendBuffer = new byte[BufferSize];
|
|
|
|
private readonly byte[] _remoteSendBuffer = new byte[BufferSize];
|
|
|
|
|
|
|
|
// local proxy -> client (ciphertext, before decrypt)
|
|
|
|
private byte[] _connetionSendBuffer = new byte[BufferSize];
|
|
|
|
private readonly byte[] _connetionSendBuffer = new byte[BufferSize];
|
|
|
|
|
|
|
|
private bool _connectionShutdown = false;
|
|
|
|
private bool _remoteShutdown = false;
|
|
|
@@ -197,11 +243,11 @@ namespace Shadowsocks.Controller |
|
|
|
|
|
|
|
private EndPoint _destEndPoint = null;
|
|
|
|
|
|
|
|
public TCPHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket)
|
|
|
|
// TODO: decouple controller
|
|
|
|
public TCPHandler(ShadowsocksController controller, Configuration config, Socket socket)
|
|
|
|
{
|
|
|
|
_controller = controller;
|
|
|
|
_config = config;
|
|
|
|
_tcprelay = tcprelay;
|
|
|
|
_config = config.proxy;
|
|
|
|
_connection = socket;
|
|
|
|
_proxyTimeout = config.proxy.proxyTimeout * 1000;
|
|
|
|
_serverTimeout = config.GetCurrentServer().timeout * 1000;
|
|
|
@@ -214,11 +260,13 @@ namespace Shadowsocks.Controller |
|
|
|
Server server = _controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)_connection.RemoteEndPoint,
|
|
|
|
_destEndPoint);
|
|
|
|
if (server == null || server.server == "")
|
|
|
|
{
|
|
|
|
throw new ArgumentException("No server configured");
|
|
|
|
}
|
|
|
|
|
|
|
|
_encryptor = EncryptorFactory.GetEncryptor(server.method, server.password);
|
|
|
|
|
|
|
|
this._server = server;
|
|
|
|
_server = server;
|
|
|
|
|
|
|
|
/* prepare address buffer length for AEAD */
|
|
|
|
Logger.Trace($"_addrBufLength={_addrBufLength}");
|
|
|
@@ -235,20 +283,31 @@ namespace Shadowsocks.Controller |
|
|
|
private void CheckClose()
|
|
|
|
{
|
|
|
|
if (_connectionShutdown && _remoteShutdown)
|
|
|
|
{
|
|
|
|
Close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void ErrorClose(Exception e)
|
|
|
|
{
|
|
|
|
Logger.LogUsefulException(e);
|
|
|
|
Close();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void Close()
|
|
|
|
{
|
|
|
|
lock (_closeConnLock)
|
|
|
|
{
|
|
|
|
if (_closed) return;
|
|
|
|
if (_closed)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_closed = true;
|
|
|
|
}
|
|
|
|
lock (_tcprelay.Handlers)
|
|
|
|
{
|
|
|
|
_tcprelay.Handlers.Remove(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
OnClosed?.Invoke(this, new SSRelayEventArgs(_server));
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
_connection.Shutdown(SocketShutdown.Both);
|
|
|
@@ -263,7 +322,7 @@ namespace Shadowsocks.Controller |
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
var remote = _currentRemoteSession.Remote;
|
|
|
|
IProxy remote = _currentRemoteSession.Remote;
|
|
|
|
remote.Shutdown(SocketShutdown.Both);
|
|
|
|
remote.Close();
|
|
|
|
}
|
|
|
@@ -284,7 +343,11 @@ namespace Shadowsocks.Controller |
|
|
|
|
|
|
|
private void HandshakeReceive()
|
|
|
|
{
|
|
|
|
if (_closed) return;
|
|
|
|
if (_closed)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
int bytesRead = _firstPacketLength;
|
|
|
@@ -301,18 +364,23 @@ namespace Shadowsocks.Controller |
|
|
|
HandshakeSendCallback, null);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
Logger.LogUsefulException(e);
|
|
|
|
Close();
|
|
|
|
ErrorClose(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void HandshakeSendCallback(IAsyncResult ar)
|
|
|
|
{
|
|
|
|
if (_closed) return;
|
|
|
|
if (_closed)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
_connection.EndSend(ar);
|
|
|
@@ -324,27 +392,30 @@ namespace Shadowsocks.Controller |
|
|
|
// +-----+-----+-------+------+----------+----------+
|
|
|
|
// Skip first 3 bytes, and read 2 more bytes to analysis the address.
|
|
|
|
// 2 more bytes is designed if address is domain then we don't need to read once more to get the addr length.
|
|
|
|
// TODO validate
|
|
|
|
// validate is unnecessary, we did it in first packet, but we can do it in future version
|
|
|
|
_connection.BeginReceive(_connetionRecvBuffer, 0, 3 + ADDR_ATYP_LEN + 1, SocketFlags.None,
|
|
|
|
HandshakeReceive2Callback, null);
|
|
|
|
AddressReceiveCallback, null);
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
Logger.LogUsefulException(e);
|
|
|
|
Close();
|
|
|
|
ErrorClose(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void HandshakeReceive2Callback(IAsyncResult ar)
|
|
|
|
private void AddressReceiveCallback(IAsyncResult ar)
|
|
|
|
{
|
|
|
|
if (_closed) return;
|
|
|
|
if (_closed)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
int bytesRead = _connection.EndReceive(ar);
|
|
|
|
if (bytesRead >= 5)
|
|
|
|
{
|
|
|
|
_command = _connetionRecvBuffer[1];
|
|
|
|
switch(_command)
|
|
|
|
switch (_command)
|
|
|
|
{
|
|
|
|
case CMD_CONNECT:
|
|
|
|
|
|
|
@@ -355,7 +426,7 @@ namespace Shadowsocks.Controller |
|
|
|
// +----+-----+-------+------+----------+----------+
|
|
|
|
byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 };
|
|
|
|
_connection.BeginSend(response, 0, response.Length, SocketFlags.None,
|
|
|
|
ResponseCallback, null);
|
|
|
|
ConnectResponseCallback, null);
|
|
|
|
break;
|
|
|
|
case CMD_UDP_ASSOC:
|
|
|
|
ReadAddress(HandleUDPAssociate);
|
|
|
@@ -376,12 +447,11 @@ namespace Shadowsocks.Controller |
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
Logger.LogUsefulException(e);
|
|
|
|
Close();
|
|
|
|
ErrorClose(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void ResponseCallback(IAsyncResult ar)
|
|
|
|
private void ConnectResponseCallback(IAsyncResult ar)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
@@ -391,8 +461,7 @@ namespace Shadowsocks.Controller |
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
Logger.LogUsefulException(e);
|
|
|
|
Close();
|
|
|
|
ErrorClose(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
@@ -431,15 +500,19 @@ namespace Shadowsocks.Controller |
|
|
|
|
|
|
|
private void OnAddressFullyRead(IAsyncResult ar)
|
|
|
|
{
|
|
|
|
if (_closed) return;
|
|
|
|
if (_closed)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
int bytesRead = _connection.EndReceive(ar);
|
|
|
|
|
|
|
|
var states = (object[])ar.AsyncState;
|
|
|
|
object[] states = (object[])ar.AsyncState;
|
|
|
|
|
|
|
|
int bytesRemain = (int)states[0];
|
|
|
|
var onSuccess = (Action)states[1];
|
|
|
|
Action onSuccess = (Action)states[1];
|
|
|
|
|
|
|
|
if (bytesRead >= bytesRemain)
|
|
|
|
{
|
|
|
@@ -472,7 +545,7 @@ namespace Shadowsocks.Controller |
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
Logger.Debug($"connect to {dstAddr}:{dstPort}");
|
|
|
|
Logger.Debug($"connect to {dstAddr}:{dstPort}");
|
|
|
|
|
|
|
|
_destEndPoint = SocketUtil.GetEndPoint(dstAddr, dstPort);
|
|
|
|
|
|
|
@@ -486,8 +559,7 @@ namespace Shadowsocks.Controller |
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
Logger.LogUsefulException(e);
|
|
|
|
Close();
|
|
|
|
ErrorClose(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
@@ -515,7 +587,11 @@ namespace Shadowsocks.Controller |
|
|
|
|
|
|
|
private void ReadAll(IAsyncResult ar)
|
|
|
|
{
|
|
|
|
if (_closed) return;
|
|
|
|
if (_closed)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
if (ar.AsyncState != null)
|
|
|
@@ -533,13 +609,14 @@ namespace Shadowsocks.Controller |
|
|
|
ReadAll, null);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
Logger.LogUsefulException(e);
|
|
|
|
Close();
|
|
|
|
ErrorClose(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
@@ -584,9 +661,9 @@ namespace Shadowsocks.Controller |
|
|
|
serverEP = pluginEP;
|
|
|
|
remote = new DirectConnect();
|
|
|
|
}
|
|
|
|
else if (_config.proxy.useProxy)
|
|
|
|
else if (_config.useProxy)
|
|
|
|
{
|
|
|
|
switch (_config.proxy.proxyType)
|
|
|
|
switch (_config.proxyType)
|
|
|
|
{
|
|
|
|
case ProxyConfig.PROXY_SOCKS5:
|
|
|
|
remote = new Socks5Proxy();
|
|
|
@@ -597,14 +674,14 @@ namespace Shadowsocks.Controller |
|
|
|
default:
|
|
|
|
throw new NotSupportedException("Unknown forward proxy.");
|
|
|
|
}
|
|
|
|
proxyEP = SocketUtil.GetEndPoint(_config.proxy.proxyServer, _config.proxy.proxyPort);
|
|
|
|
proxyEP = SocketUtil.GetEndPoint(_config.proxyServer, _config.proxyPort);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
remote = new DirectConnect();
|
|
|
|
}
|
|
|
|
|
|
|
|
var session = new AsyncSession(remote);
|
|
|
|
AsyncSession session = new AsyncSession(remote);
|
|
|
|
lock (_closeConnLock)
|
|
|
|
{
|
|
|
|
if (_closed)
|
|
|
@@ -632,14 +709,13 @@ namespace Shadowsocks.Controller |
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
Logger.LogUsefulException(e);
|
|
|
|
Close();
|
|
|
|
ErrorClose(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void ProxyConnectTimer_Elapsed(object sender, ElapsedEventArgs e)
|
|
|
|
{
|
|
|
|
var timer = (ProxyTimer)sender;
|
|
|
|
ProxyTimer timer = (ProxyTimer)sender;
|
|
|
|
timer.Elapsed -= ProxyConnectTimer_Elapsed;
|
|
|
|
timer.Enabled = false;
|
|
|
|
timer.Dispose();
|
|
|
@@ -649,7 +725,7 @@ namespace Shadowsocks.Controller |
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var proxy = timer.Session.Remote;
|
|
|
|
IProxy proxy = timer.Session.Remote;
|
|
|
|
|
|
|
|
Logger.Info($"Proxy {proxy.ProxyEndPoint} timed out");
|
|
|
|
proxy.Close();
|
|
|
@@ -664,15 +740,15 @@ namespace Shadowsocks.Controller |
|
|
|
}
|
|
|
|
try
|
|
|
|
{
|
|
|
|
var session = (AsyncSession<ProxyTimer>)ar.AsyncState;
|
|
|
|
AsyncSession<ProxyTimer> session = (AsyncSession<ProxyTimer>)ar.AsyncState;
|
|
|
|
ProxyTimer timer = session.State;
|
|
|
|
var destEndPoint = timer.DestEndPoint;
|
|
|
|
var server = timer.Server;
|
|
|
|
EndPoint destEndPoint = timer.DestEndPoint;
|
|
|
|
Server server = timer.Server;
|
|
|
|
timer.Elapsed -= ProxyConnectTimer_Elapsed;
|
|
|
|
timer.Enabled = false;
|
|
|
|
timer.Dispose();
|
|
|
|
|
|
|
|
var remote = session.Remote;
|
|
|
|
IProxy remote = session.Remote;
|
|
|
|
|
|
|
|
// Complete the connection.
|
|
|
|
remote.EndConnectProxy(ar);
|
|
|
@@ -694,9 +770,9 @@ namespace Shadowsocks.Controller |
|
|
|
_destConnected = false;
|
|
|
|
|
|
|
|
NetworkCredential auth = null;
|
|
|
|
if (_config.proxy.useAuth)
|
|
|
|
if (_config.useAuth)
|
|
|
|
{
|
|
|
|
auth = new NetworkCredential(_config.proxy.authUser, _config.proxy.authPwd);
|
|
|
|
auth = new NetworkCredential(_config.authUser, _config.authPwd);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Connect to the remote endpoint.
|
|
|
@@ -708,14 +784,13 @@ namespace Shadowsocks.Controller |
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
Logger.LogUsefulException(e);
|
|
|
|
Close();
|
|
|
|
ErrorClose(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void DestConnectTimer_Elapsed(object sender, ElapsedEventArgs e)
|
|
|
|
{
|
|
|
|
var timer = (ServerTimer)sender;
|
|
|
|
ServerTimer timer = (ServerTimer)sender;
|
|
|
|
timer.Elapsed -= DestConnectTimer_Elapsed;
|
|
|
|
timer.Enabled = false;
|
|
|
|
timer.Dispose();
|
|
|
@@ -725,10 +800,9 @@ namespace Shadowsocks.Controller |
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var session = timer.Session;
|
|
|
|
AsyncSession session = timer.Session;
|
|
|
|
Server server = timer.Server;
|
|
|
|
IStrategy strategy = _controller.GetCurrentStrategy();
|
|
|
|
strategy?.SetFailure(server);
|
|
|
|
OnFailed?.Invoke(this, new SSRelayEventArgs(_server));
|
|
|
|
Logger.Info($"{server.FriendlyName()} timed out");
|
|
|
|
session.Remote.Close();
|
|
|
|
Close();
|
|
|
@@ -736,17 +810,21 @@ namespace Shadowsocks.Controller |
|
|
|
|
|
|
|
private void ConnectCallback(IAsyncResult ar)
|
|
|
|
{
|
|
|
|
if (_closed) return;
|
|
|
|
if (_closed)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
var session = (AsyncSession<ServerTimer>)ar.AsyncState;
|
|
|
|
AsyncSession<ServerTimer> session = (AsyncSession<ServerTimer>)ar.AsyncState;
|
|
|
|
ServerTimer timer = session.State;
|
|
|
|
_server = timer.Server;
|
|
|
|
timer.Elapsed -= DestConnectTimer_Elapsed;
|
|
|
|
timer.Enabled = false;
|
|
|
|
timer.Dispose();
|
|
|
|
|
|
|
|
var remote = session.Remote;
|
|
|
|
IProxy remote = session.Remote;
|
|
|
|
// Complete the connection.
|
|
|
|
remote.EndConnectDest(ar);
|
|
|
|
|
|
|
@@ -754,10 +832,9 @@ namespace Shadowsocks.Controller |
|
|
|
|
|
|
|
Logger.Debug($"Socket connected to ss server: {_server.FriendlyName()}");
|
|
|
|
|
|
|
|
var latency = DateTime.Now - _startConnectTime;
|
|
|
|
IStrategy strategy = _controller.GetCurrentStrategy();
|
|
|
|
strategy?.UpdateLatency(_server, latency);
|
|
|
|
_tcprelay.UpdateLatency(_server, latency);
|
|
|
|
TimeSpan latency = DateTime.Now - _startConnectTime;
|
|
|
|
|
|
|
|
OnConnected?.Invoke(this, new SSTCPConnectedEventArgs(_server, latency));
|
|
|
|
|
|
|
|
StartPipe(session);
|
|
|
|
}
|
|
|
@@ -768,11 +845,9 @@ namespace Shadowsocks.Controller |
|
|
|
{
|
|
|
|
if (_server != null)
|
|
|
|
{
|
|
|
|
IStrategy strategy = _controller.GetCurrentStrategy();
|
|
|
|
strategy?.SetFailure(_server);
|
|
|
|
OnFailed?.Invoke(this, new SSRelayEventArgs(_server));
|
|
|
|
}
|
|
|
|
Logger.LogUsefulException(e);
|
|
|
|
Close();
|
|
|
|
ErrorClose(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
@@ -781,7 +856,7 @@ namespace Shadowsocks.Controller |
|
|
|
int available = Math.Min(_connection.Available, RecvSize - _firstPacketLength);
|
|
|
|
if (available > 0)
|
|
|
|
{
|
|
|
|
var size = _connection.Receive(_connetionRecvBuffer, _firstPacketLength, available,
|
|
|
|
int size = _connection.Receive(_connetionRecvBuffer, _firstPacketLength, available,
|
|
|
|
SocketFlags.None);
|
|
|
|
|
|
|
|
_firstPacketLength += size;
|
|
|
@@ -790,7 +865,11 @@ namespace Shadowsocks.Controller |
|
|
|
|
|
|
|
private void StartPipe(AsyncSession session)
|
|
|
|
{
|
|
|
|
if (_closed) return;
|
|
|
|
if (_closed)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
_startReceivingTime = DateTime.Now;
|
|
|
@@ -803,20 +882,24 @@ namespace Shadowsocks.Controller |
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
Logger.LogUsefulException(e);
|
|
|
|
Close();
|
|
|
|
ErrorClose(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void PipeRemoteReceiveCallback(IAsyncResult ar)
|
|
|
|
{
|
|
|
|
if (_closed) return;
|
|
|
|
if (_closed)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
var session = (AsyncSession)ar.AsyncState;
|
|
|
|
AsyncSession session = (AsyncSession)ar.AsyncState;
|
|
|
|
int bytesRead = session.Remote.EndReceive(ar);
|
|
|
|
_totalRead += bytesRead;
|
|
|
|
_tcprelay.UpdateInboundCounter(_server, bytesRead);
|
|
|
|
|
|
|
|
OnInbound?.Invoke(this, new SSTransmitEventArgs(_server, bytesRead));
|
|
|
|
if (bytesRead > 0)
|
|
|
|
{
|
|
|
|
lastActivity = DateTime.Now;
|
|
|
@@ -845,8 +928,6 @@ namespace Shadowsocks.Controller |
|
|
|
Logger.Trace($"start sending {bytesToSend}");
|
|
|
|
_connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None,
|
|
|
|
PipeConnectionSendCallback, new object[] { session, bytesToSend });
|
|
|
|
IStrategy strategy = _controller.GetCurrentStrategy();
|
|
|
|
strategy?.UpdateLastRead(_server);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
@@ -857,20 +938,23 @@ namespace Shadowsocks.Controller |
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
Logger.LogUsefulException(e);
|
|
|
|
Close();
|
|
|
|
ErrorClose(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void PipeConnectionReceiveCallback(IAsyncResult ar)
|
|
|
|
{
|
|
|
|
if (_closed) return;
|
|
|
|
if (_closed)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
int bytesRead = _connection.EndReceive(ar);
|
|
|
|
|
|
|
|
var session = (AsyncSession)ar.AsyncState;
|
|
|
|
var remote = session.Remote;
|
|
|
|
AsyncSession session = (AsyncSession)ar.AsyncState;
|
|
|
|
IProxy remote = session.Remote;
|
|
|
|
|
|
|
|
if (bytesRead > 0)
|
|
|
|
{
|
|
|
@@ -885,8 +969,7 @@ namespace Shadowsocks.Controller |
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
Logger.LogUsefulException(e);
|
|
|
|
Close();
|
|
|
|
ErrorClose(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
@@ -907,22 +990,25 @@ namespace Shadowsocks.Controller |
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_tcprelay.UpdateOutboundCounter(_server, bytesToSend);
|
|
|
|
|
|
|
|
OnOutbound?.Invoke(this, new SSTransmitEventArgs(_server, bytesToSend));
|
|
|
|
_startSendingTime = DateTime.Now;
|
|
|
|
session.Remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None,
|
|
|
|
PipeRemoteSendCallback, new object[] { session, bytesToSend });
|
|
|
|
IStrategy strategy = _controller.GetCurrentStrategy();
|
|
|
|
strategy?.UpdateLastWrite(_server);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void PipeRemoteSendCallback(IAsyncResult ar)
|
|
|
|
{
|
|
|
|
if (_closed) return;
|
|
|
|
if (_closed)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
var container = (object[])ar.AsyncState;
|
|
|
|
var session = (AsyncSession)container[0];
|
|
|
|
var bytesShouldSend = (int)container[1];
|
|
|
|
object[] container = (object[])ar.AsyncState;
|
|
|
|
AsyncSession session = (AsyncSession)container[0];
|
|
|
|
int bytesShouldSend = (int)container[1];
|
|
|
|
int bytesSent = session.Remote.EndSend(ar);
|
|
|
|
|
|
|
|
if (bytesSent > 0)
|
|
|
@@ -944,8 +1030,7 @@ namespace Shadowsocks.Controller |
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
Logger.LogUsefulException(e);
|
|
|
|
Close();
|
|
|
|
ErrorClose(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
@@ -954,11 +1039,11 @@ namespace Shadowsocks.Controller |
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
var container = (object[])ar.AsyncState;
|
|
|
|
var session = (AsyncSession)container[0];
|
|
|
|
var bytesShouldSend = (int)container[1];
|
|
|
|
var bytesSent = _connection.EndSend(ar);
|
|
|
|
var bytesRemaining = bytesShouldSend - bytesSent;
|
|
|
|
object[] container = (object[])ar.AsyncState;
|
|
|
|
AsyncSession session = (AsyncSession)container[0];
|
|
|
|
int bytesShouldSend = (int)container[1];
|
|
|
|
int bytesSent = _connection.EndSend(ar);
|
|
|
|
int bytesRemaining = bytesShouldSend - bytesSent;
|
|
|
|
if (bytesRemaining > 0)
|
|
|
|
{
|
|
|
|
Logger.Info("reconstruct _remoteSendBuffer to re-send");
|
|
|
@@ -972,8 +1057,7 @@ namespace Shadowsocks.Controller |
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
Logger.LogUsefulException(e);
|
|
|
|
Close();
|
|
|
|
ErrorClose(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|