Browse Source

Add proxy support

tags/3.2
noisyfox 8 years ago
parent
commit
95d4e3a0c0
5 changed files with 230 additions and 21 deletions
  1. +91
    -20
      shadowsocks-csharp/Controller/Service/TCPRelay.cs
  2. +1
    -1
      shadowsocks-csharp/Controller/ShadowsocksController.cs
  3. +98
    -0
      shadowsocks-csharp/Proxy/DirectConnect.cs
  4. +38
    -0
      shadowsocks-csharp/Proxy/IProxy.cs
  5. +2
    -0
      shadowsocks-csharp/shadowsocks-csharp.csproj

+ 91
- 20
shadowsocks-csharp/Controller/Service/TCPRelay.cs View File

@@ -7,6 +7,7 @@ using System.Timers;
using Shadowsocks.Controller.Strategy; using Shadowsocks.Controller.Strategy;
using Shadowsocks.Encryption; using Shadowsocks.Encryption;
using Shadowsocks.Model; using Shadowsocks.Model;
using Shadowsocks.Proxy;
namespace Shadowsocks.Controller namespace Shadowsocks.Controller
{ {
@@ -14,15 +15,17 @@ namespace Shadowsocks.Controller
{ {
private ShadowsocksController _controller; private ShadowsocksController _controller;
private DateTime _lastSweepTime; private DateTime _lastSweepTime;
private Configuration _config;
public ISet<TCPHandler> Handlers public ISet<TCPHandler> Handlers
{ {
get; set; get; set;
} }
public TCPRelay(ShadowsocksController controller)
public TCPRelay(ShadowsocksController controller, Configuration conf)
{ {
_controller = controller; _controller = controller;
_config = conf;
Handlers = new HashSet<TCPHandler>(); Handlers = new HashSet<TCPHandler>();
_lastSweepTime = DateTime.Now; _lastSweepTime = DateTime.Now;
} }
@@ -91,7 +94,7 @@ namespace Shadowsocks.Controller
public IEncryptor encryptor; public IEncryptor encryptor;
public Server server; public Server server;
// Client socket. // Client socket.
public Socket remote;
public IProxy remote;
public Socket connection; public Socket connection;
public ShadowsocksController controller; public ShadowsocksController controller;
public TCPRelay relay; public TCPRelay relay;
@@ -100,7 +103,8 @@ namespace Shadowsocks.Controller
private const int maxRetry = 4; private const int maxRetry = 4;
private int retryCount = 0; private int retryCount = 0;
private bool connected;
private bool proxyConnected;
private bool destConnected;
private byte command; private byte command;
private byte[] _firstPacket; private byte[] _firstPacket;
@@ -345,7 +349,7 @@ namespace Shadowsocks.Controller
if (ar.AsyncState != null) if (ar.AsyncState != null)
{ {
connection.EndSend(ar); 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); connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(ReadAll), null);
} }
else else
@@ -353,7 +357,7 @@ namespace Shadowsocks.Controller
int bytesRead = connection.EndReceive(ar); int bytesRead = connection.EndReceive(ar);
if (bytesRead > 0) 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); connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(ReadAll), null);
} }
else else
@@ -386,6 +390,16 @@ namespace Shadowsocks.Controller
} }
// inner class // inner class
private class ProxyTimer : Timer
{
public EndPoint DestEndPoint;
public Server Server;
public ProxyTimer(int p) : base(p)
{
}
}
private class ServerTimer : Timer private class ServerTimer : Timer
{ {
public Server Server; public Server Server;
@@ -411,31 +425,88 @@ namespace Shadowsocks.Controller
} }
IPEndPoint remoteEP = new IPEndPoint(ipAddress, server.server_port); 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();
ProxyTimer proxyTimer = new ProxyTimer(3000);
proxyTimer.AutoReset = false;
proxyTimer.Elapsed += proxyConnectTimer_Elapsed;
proxyTimer.Enabled = true;
proxyTimer.DestEndPoint = remoteEP;
proxyTimer.Server = server;
proxyConnected = false;
// Connect to the proxy server.
remote.BeginConnectProxy(remoteEP, new AsyncCallback(ProxyConnectCallback), proxyTimer);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
private void proxyConnectTimer_Elapsed(object sender, ElapsedEventArgs e)
{
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; _startConnectTime = DateTime.Now;
ServerTimer connectTimer = new ServerTimer(3000); ServerTimer connectTimer = new ServerTimer(3000);
connectTimer.AutoReset = false; connectTimer.AutoReset = false;
connectTimer.Elapsed += connectTimer_Elapsed;
connectTimer.Elapsed += destConnectTimer_Elapsed;
connectTimer.Enabled = true; connectTimer.Enabled = true;
connectTimer.Server = server; connectTimer.Server = server;
connected = false;
destConnected = false;
// Connect to the remote endpoint. // Connect to the remote endpoint.
remote.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), connectTimer);
remote.BeginConnectDest(destEP, new AsyncCallback(ConnectCallback), connectTimer);
}
catch (ArgumentException)
{
} }
catch (Exception e) catch (Exception e)
{ {
Logging.LogUsefulException(e); Logging.LogUsefulException(e);
Close();
RetryConnect();
} }
} }
private void connectTimer_Elapsed(object sender, ElapsedEventArgs e)
private void destConnectTimer_Elapsed(object sender, ElapsedEventArgs e)
{ {
if (connected)
if (destConnected)
{ {
return; return;
} }
@@ -475,16 +546,16 @@ namespace Shadowsocks.Controller
{ {
ServerTimer timer = (ServerTimer)ar.AsyncState; ServerTimer timer = (ServerTimer)ar.AsyncState;
server = timer.Server; server = timer.Server;
timer.Elapsed -= connectTimer_Elapsed;
timer.Elapsed -= destConnectTimer_Elapsed;
timer.Enabled = false; timer.Enabled = false;
timer.Dispose(); timer.Dispose();
// Complete the connection. // 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; var latency = DateTime.Now - _startConnectTime;
IStrategy strategy = controller.GetCurrentStrategy(); IStrategy strategy = controller.GetCurrentStrategy();
@@ -554,7 +625,7 @@ namespace Shadowsocks.Controller
} }
encryptor.Decrypt(remoteRecvBuffer, bytesRead, remoteSendBuffer, out bytesToSend); 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); connection.BeginSend(remoteSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeConnectionSendCallback), null);
IStrategy strategy = controller.GetCurrentStrategy(); IStrategy strategy = controller.GetCurrentStrategy();
@@ -606,7 +677,7 @@ namespace Shadowsocks.Controller
} }
encryptor.Encrypt(connetionRecvBuffer, bytesRead, connetionSendBuffer, out bytesToSend); 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); tcprelay.UpdateOutboundCounter(server, bytesToSend);
_startSendingTime = DateTime.Now; _startSendingTime = DateTime.Now;
_bytesToSend = bytesToSend; _bytesToSend = bytesToSend;


+ 1
- 1
shadowsocks-csharp/Controller/ShadowsocksController.cs View File

@@ -394,7 +394,7 @@ namespace Shadowsocks.Controller
polipoRunner.Start(_config); polipoRunner.Start(_config);
TCPRelay tcpRelay = new TCPRelay(this);
TCPRelay tcpRelay = new TCPRelay(this, _config);
UDPRelay udpRelay = new UDPRelay(this); UDPRelay udpRelay = new UDPRelay(this);
List<Listener.Service> services = new List<Listener.Service>(); List<Listener.Service> services = new List<Listener.Service>();
services.Add(tcpRelay); services.Add(tcpRelay);


+ 98
- 0
shadowsocks-csharp/Proxy/DirectConnect.cs 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
- 0
shadowsocks-csharp/Proxy/IProxy.cs 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();
}
}

+ 2
- 0
shadowsocks-csharp/shadowsocks-csharp.csproj View File

@@ -170,6 +170,8 @@
<Compile Include="3rd\zxing\ResultPoint.cs" /> <Compile Include="3rd\zxing\ResultPoint.cs" />
<Compile Include="3rd\zxing\ResultPointCallback.cs" /> <Compile Include="3rd\zxing\ResultPointCallback.cs" />
<Compile Include="3rd\zxing\WriterException.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\Service\AvailabilityStatistics.cs" />
<Compile Include="Controller\Strategy\HighAvailabilityStrategy.cs" /> <Compile Include="Controller\Strategy\HighAvailabilityStrategy.cs" />
<Compile Include="Controller\Strategy\StatisticsStrategy.cs" /> <Compile Include="Controller\Strategy\StatisticsStrategy.cs" />


Loading…
Cancel
Save