Add proxy support

This commit is contained in:
noisyfox
2016-06-29 22:38:51 +08:00
parent 2307c9515e
commit 95d4e3a0c0
5 changed files with 235 additions and 26 deletions
@@ -7,6 +7,7 @@ using System.Timers;
using Shadowsocks.Controller.Strategy;
using Shadowsocks.Encryption;
using Shadowsocks.Model;
using Shadowsocks.Proxy;
namespace Shadowsocks.Controller
{
@@ -14,15 +15,17 @@ namespace Shadowsocks.Controller
{
private ShadowsocksController _controller;
private DateTime _lastSweepTime;
private Configuration _config;
public ISet<TCPHandler> Handlers
{
get; set;
}
public TCPRelay(ShadowsocksController controller)
public TCPRelay(ShadowsocksController controller, Configuration conf)
{
_controller = controller;
_config = conf;
Handlers = new HashSet<TCPHandler>();
_lastSweepTime = DateTime.Now;
}
@@ -91,7 +94,7 @@ namespace Shadowsocks.Controller
public IEncryptor encryptor;
public Server server;
// Client socket.
public Socket remote;
public IProxy remote;
public Socket connection;
public ShadowsocksController controller;
public TCPRelay relay;
@@ -100,7 +103,8 @@ namespace Shadowsocks.Controller
private const int maxRetry = 4;
private int retryCount = 0;
private bool connected;
private bool proxyConnected;
private bool destConnected;
private byte command;
private byte[] _firstPacket;
@@ -345,7 +349,7 @@ namespace Shadowsocks.Controller
if (ar.AsyncState != null)
{
connection.EndSend(ar);
Logging.Debug(remote, RecvSize, "TCP Relay");
Logging.Debug(remote.LocalEndPoint, remote.DestEndPoint, RecvSize, "TCP Relay");
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(ReadAll), null);
}
else
@@ -353,7 +357,7 @@ namespace Shadowsocks.Controller
int bytesRead = connection.EndReceive(ar);
if (bytesRead > 0)
{
Logging.Debug(remote, RecvSize, "TCP Relay");
Logging.Debug(remote.LocalEndPoint, remote.DestEndPoint, RecvSize, "TCP Relay");
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(ReadAll), null);
}
else
@@ -386,6 +390,16 @@ namespace Shadowsocks.Controller
}
// inner class
private class ProxyTimer : Timer
{
public EndPoint DestEndPoint;
public Server Server;
public ProxyTimer(int p) : base(p)
{
}
}
private class ServerTimer : Timer
{
public Server Server;
@@ -411,20 +425,19 @@ namespace Shadowsocks.Controller
}
IPEndPoint remoteEP = new IPEndPoint(ipAddress, server.server_port);
remote = new Socket(ipAddress.AddressFamily,
SocketType.Stream, ProtocolType.Tcp);
remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
remote = new DirectConnect();
_startConnectTime = DateTime.Now;
ServerTimer connectTimer = new ServerTimer(3000);
connectTimer.AutoReset = false;
connectTimer.Elapsed += connectTimer_Elapsed;
connectTimer.Enabled = true;
connectTimer.Server = server;
ProxyTimer proxyTimer = new ProxyTimer(3000);
proxyTimer.AutoReset = false;
proxyTimer.Elapsed += proxyConnectTimer_Elapsed;
proxyTimer.Enabled = true;
proxyTimer.DestEndPoint = remoteEP;
proxyTimer.Server = server;
connected = false;
// Connect to the remote endpoint.
remote.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), connectTimer);
proxyConnected = false;
// Connect to the proxy server.
remote.BeginConnectProxy(remoteEP, new AsyncCallback(ProxyConnectCallback), proxyTimer);
}
catch (Exception e)
{
@@ -433,9 +446,67 @@ namespace Shadowsocks.Controller
}
}
private void connectTimer_Elapsed(object sender, ElapsedEventArgs e)
private void proxyConnectTimer_Elapsed(object sender, ElapsedEventArgs e)
{
if (connected)
if (proxyConnected || destConnected)
{
return;
}
var ep = ((ProxyTimer)sender).DestEndPoint;
Logging.Info($"Proxy {ep} timed out");
remote.Close();
RetryConnect();
}
private void ProxyConnectCallback(IAsyncResult ar)
{
Server server = null;
if (closed)
{
return;
}
try
{
ProxyTimer timer = (ProxyTimer)ar.AsyncState;
var destEP = timer.DestEndPoint;
server = timer.Server;
timer.Elapsed -= proxyConnectTimer_Elapsed;
timer.Enabled = false;
timer.Dispose();
// Complete the connection.
remote.EndConnectProxy(ar);
proxyConnected = true;
Logging.Debug($"Socket connected to proxy {remote.ProxyEndPoint}");
_startConnectTime = DateTime.Now;
ServerTimer connectTimer = new ServerTimer(3000);
connectTimer.AutoReset = false;
connectTimer.Elapsed += destConnectTimer_Elapsed;
connectTimer.Enabled = true;
connectTimer.Server = server;
destConnected = false;
// Connect to the remote endpoint.
remote.BeginConnectDest(destEP, new AsyncCallback(ConnectCallback), connectTimer);
}
catch (ArgumentException)
{
}
catch (Exception e)
{
Logging.LogUsefulException(e);
RetryConnect();
}
}
private void destConnectTimer_Elapsed(object sender, ElapsedEventArgs e)
{
if (destConnected)
{
return;
}
@@ -475,16 +546,16 @@ namespace Shadowsocks.Controller
{
ServerTimer timer = (ServerTimer)ar.AsyncState;
server = timer.Server;
timer.Elapsed -= connectTimer_Elapsed;
timer.Elapsed -= destConnectTimer_Elapsed;
timer.Enabled = false;
timer.Dispose();
// Complete the connection.
remote.EndConnect(ar);
remote.EndConnectDest(ar);
connected = true;
destConnected = true;
Logging.Debug($"Socket connected to {remote.RemoteEndPoint}");
Logging.Debug($"Socket connected to {remote.DestEndPoint}");
var latency = DateTime.Now - _startConnectTime;
IStrategy strategy = controller.GetCurrentStrategy();
@@ -554,7 +625,7 @@ namespace Shadowsocks.Controller
}
encryptor.Decrypt(remoteRecvBuffer, bytesRead, remoteSendBuffer, out bytesToSend);
}
Logging.Debug(remote, bytesToSend, "TCP Relay", "@PipeRemoteReceiveCallback() (download)");
Logging.Debug(remote.LocalEndPoint, remote.DestEndPoint, bytesToSend, "TCP Relay", "@PipeRemoteReceiveCallback() (download)");
connection.BeginSend(remoteSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeConnectionSendCallback), null);
IStrategy strategy = controller.GetCurrentStrategy();
@@ -606,7 +677,7 @@ namespace Shadowsocks.Controller
}
encryptor.Encrypt(connetionRecvBuffer, bytesRead, connetionSendBuffer, out bytesToSend);
}
Logging.Debug(remote, bytesToSend, "TCP Relay", "@PipeConnectionReceiveCallback() (upload)");
Logging.Debug(remote.LocalEndPoint, remote.DestEndPoint, bytesToSend, "TCP Relay", "@PipeConnectionReceiveCallback() (upload)");
tcprelay.UpdateOutboundCounter(server, bytesToSend);
_startSendingTime = DateTime.Now;
_bytesToSend = bytesToSend;
@@ -394,7 +394,7 @@ namespace Shadowsocks.Controller
polipoRunner.Start(_config);
TCPRelay tcpRelay = new TCPRelay(this);
TCPRelay tcpRelay = new TCPRelay(this, _config);
UDPRelay udpRelay = new UDPRelay(this);
List<Listener.Service> services = new List<Listener.Service>();
services.Add(tcpRelay);
+98
View File
@@ -0,0 +1,98 @@
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace Shadowsocks.Proxy
{
public class DirectConnect : IProxy
{
private class FakeAsyncResult : IAsyncResult
{
public FakeAsyncResult(object state)
{
AsyncState = state;
}
public bool IsCompleted { get; } = true;
public WaitHandle AsyncWaitHandle { get; } = null;
public object AsyncState { get; }
public bool CompletedSynchronously { get; } = true;
}
private Socket _remote;
public EndPoint LocalEndPoint => _remote.LocalEndPoint;
public EndPoint ProxyEndPoint { get; private set; }
public EndPoint DestEndPoint { get; private set; }
public IAsyncResult BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state)
{
// do nothing
ProxyEndPoint = remoteEP;
var r = new FakeAsyncResult(state);
callback?.Invoke(r);
return r;
}
public void EndConnectProxy(IAsyncResult asyncResult)
{
// do nothing
}
public IAsyncResult BeginConnectDest(EndPoint remoteEP, AsyncCallback callback, object state)
{
if (_remote == null)
{
_remote = new Socket(remoteEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
_remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
}
DestEndPoint = remoteEP;
return _remote.BeginConnect(remoteEP, callback, state);
}
public void EndConnectDest(IAsyncResult asyncResult)
{
_remote.EndConnect(asyncResult);
}
public IAsyncResult BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
object state)
{
return _remote.BeginSend(buffer, offset, size, socketFlags, callback, state);
}
public int EndSend(IAsyncResult asyncResult)
{
return _remote.EndSend(asyncResult);
}
public IAsyncResult BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
object state)
{
return _remote.BeginReceive(buffer, offset, size, socketFlags, callback, state);
}
public int EndReceive(IAsyncResult asyncResult)
{
return _remote.EndReceive(asyncResult);
}
public void Shutdown(SocketShutdown how)
{
_remote?.Shutdown(how);
}
public void Close()
{
_remote?.Close();
}
}
}
+38
View File
@@ -0,0 +1,38 @@
using System;
using System.Net;
using System.Net.Sockets;
namespace Shadowsocks.Proxy
{
public interface IProxy
{
EndPoint LocalEndPoint { get; }
EndPoint ProxyEndPoint { get; }
EndPoint DestEndPoint { get; }
IAsyncResult BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state);
void EndConnectProxy(IAsyncResult asyncResult);
IAsyncResult BeginConnectDest(EndPoint remoteEP, AsyncCallback callback, object state);
void EndConnectDest(IAsyncResult asyncResult);
IAsyncResult BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
object state);
int EndSend(IAsyncResult asyncResult);
IAsyncResult BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
object state);
int EndReceive(IAsyncResult asyncResult);
void Shutdown(SocketShutdown how);
void Close();
}
}
@@ -170,6 +170,8 @@
<Compile Include="3rd\zxing\ResultPoint.cs" />
<Compile Include="3rd\zxing\ResultPointCallback.cs" />
<Compile Include="3rd\zxing\WriterException.cs" />
<Compile Include="Proxy\DirectConnect.cs" />
<Compile Include="Proxy\IProxy.cs" />
<Compile Include="Controller\Service\AvailabilityStatistics.cs" />
<Compile Include="Controller\Strategy\HighAvailabilityStrategy.cs" />
<Compile Include="Controller\Strategy\StatisticsStrategy.cs" />