diff --git a/shadowsocks-csharp/Proxy/DirectConnect.cs b/shadowsocks-csharp/Proxy/DirectConnect.cs
index 48af6ac7..af8d70ae 100644
--- a/shadowsocks-csharp/Proxy/DirectConnect.cs
+++ b/shadowsocks-csharp/Proxy/DirectConnect.cs
@@ -29,15 +29,13 @@ namespace Shadowsocks.Proxy
public EndPoint DestEndPoint { get; private set; }
- public IAsyncResult BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state)
+ public void 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)
@@ -45,7 +43,7 @@ namespace Shadowsocks.Proxy
// do nothing
}
- public IAsyncResult BeginConnectDest(EndPoint remoteEP, AsyncCallback callback, object state)
+ public void BeginConnectDest(EndPoint remoteEP, AsyncCallback callback, object state)
{
if (_remote == null)
{
@@ -55,7 +53,7 @@ namespace Shadowsocks.Proxy
DestEndPoint = remoteEP;
- return _remote.BeginConnect(remoteEP, callback, state);
+ _remote.BeginConnect(remoteEP, callback, state);
}
public void EndConnectDest(IAsyncResult asyncResult)
@@ -63,10 +61,10 @@ namespace Shadowsocks.Proxy
_remote.EndConnect(asyncResult);
}
- public IAsyncResult BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
+ public void BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
object state)
{
- return _remote.BeginSend(buffer, offset, size, socketFlags, callback, state);
+ _remote.BeginSend(buffer, offset, size, socketFlags, callback, state);
}
public int EndSend(IAsyncResult asyncResult)
@@ -74,10 +72,10 @@ namespace Shadowsocks.Proxy
return _remote.EndSend(asyncResult);
}
- public IAsyncResult BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
+ public void BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
object state)
{
- return _remote.BeginReceive(buffer, offset, size, socketFlags, callback, state);
+ _remote.BeginReceive(buffer, offset, size, socketFlags, callback, state);
}
public int EndReceive(IAsyncResult asyncResult)
diff --git a/shadowsocks-csharp/Proxy/IProxy.cs b/shadowsocks-csharp/Proxy/IProxy.cs
index 39a7c3e7..4796c92f 100644
--- a/shadowsocks-csharp/Proxy/IProxy.cs
+++ b/shadowsocks-csharp/Proxy/IProxy.cs
@@ -13,20 +13,20 @@ namespace Shadowsocks.Proxy
EndPoint DestEndPoint { get; }
- IAsyncResult BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state);
+ void BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state);
void EndConnectProxy(IAsyncResult asyncResult);
- IAsyncResult BeginConnectDest(EndPoint remoteEP, AsyncCallback callback, object state);
+ void BeginConnectDest(EndPoint remoteEP, AsyncCallback callback, object state);
void EndConnectDest(IAsyncResult asyncResult);
- IAsyncResult BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
+ void 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,
+ void BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
object state);
int EndReceive(IAsyncResult asyncResult);
diff --git a/shadowsocks-csharp/Proxy/Socks5Proxy.cs b/shadowsocks-csharp/Proxy/Socks5Proxy.cs
new file mode 100644
index 00000000..b66a18c4
--- /dev/null
+++ b/shadowsocks-csharp/Proxy/Socks5Proxy.cs
@@ -0,0 +1,312 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading;
+using Shadowsocks.Controller;
+
+namespace Shadowsocks.Proxy
+{
+ public class Socks5Proxy : IProxy
+ {
+ private class FakeAsyncResult : IAsyncResult
+ {
+ public readonly Socks5State innerState;
+
+ private readonly IAsyncResult r;
+
+ public FakeAsyncResult(IAsyncResult orig, Socks5State state)
+ {
+ r = orig;
+ innerState = state;
+ }
+
+ public bool IsCompleted => r.IsCompleted;
+ public WaitHandle AsyncWaitHandle => r.AsyncWaitHandle;
+ public object AsyncState => innerState.AsyncState;
+ public bool CompletedSynchronously => r.CompletedSynchronously;
+ }
+
+ private class Socks5State
+ {
+ public AsyncCallback Callback { get; set; }
+
+ public object AsyncState { get; set; }
+
+ public int BytesToRead;
+
+ public Exception ex { get; set; }
+ }
+
+ private Socket _remote;
+
+ private const int Socks5PktMaxSize = 4 + 16 + 2;
+ private readonly byte[] _receiveBuffer = new byte[Socks5PktMaxSize];
+
+ public EndPoint LocalEndPoint => _remote.LocalEndPoint;
+ public EndPoint ProxyEndPoint { get; private set; }
+ public EndPoint DestEndPoint { get; private set; }
+
+ public void BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state)
+ {
+ _remote = new Socket(remoteEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
+ _remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
+
+ var st = new Socks5State();
+ st.Callback = callback;
+ st.AsyncState = state;
+
+ ProxyEndPoint = remoteEP;
+
+ _remote.BeginConnect(remoteEP, ConnectCallback, st);
+ }
+
+ public void EndConnectProxy(IAsyncResult asyncResult)
+ {
+ var state = ((FakeAsyncResult)asyncResult).innerState;
+
+ if (state.ex != null)
+ {
+ throw state.ex;
+ }
+ }
+
+ public void BeginConnectDest(EndPoint remoteEP, AsyncCallback callback, object state)
+ {
+ var ep = remoteEP as IPEndPoint;
+ if (ep == null)
+ {
+ throw new Exception(I18N.GetString("Proxy request faild"));
+ }
+
+ byte[] request = null;
+ byte atyp = 0;
+ switch (ep.AddressFamily)
+ {
+ case AddressFamily.InterNetwork:
+ request = new byte[4 + 4 + 2];
+ atyp = 1;
+ break;
+ case AddressFamily.InterNetworkV6:
+ request = new byte[4 + 16 + 2];
+ atyp = 4;
+ break;
+ }
+ if (request == null)
+ {
+ throw new Exception(I18N.GetString("Proxy request faild"));
+ }
+
+ // 构造request包
+ var addr = ep.Address.GetAddressBytes();
+ request[0] = 5;
+ request[1] = 1;
+ request[2] = 0;
+ request[3] = atyp;
+ Array.Copy(addr, 0, request, 4, request.Length - 4 - 2);
+ request[request.Length - 2] = (byte) ((ep.Port >> 8) & 0xff);
+ request[request.Length - 1] = (byte) (ep.Port & 0xff);
+
+ var st = new Socks5State();
+ st.Callback = callback;
+ st.AsyncState = state;
+
+ DestEndPoint = remoteEP;
+
+ _remote.BeginSend(request, 0, request.Length, 0, Socks5RequestSendCallback, st);
+
+ }
+
+ public void EndConnectDest(IAsyncResult asyncResult)
+ {
+ var state = ((FakeAsyncResult)asyncResult).innerState;
+
+ if (state.ex != null)
+ {
+ throw state.ex;
+ }
+ }
+
+ public void BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
+ object state)
+ {
+ _remote.BeginSend(buffer, offset, size, socketFlags, callback, state);
+ }
+
+ public int EndSend(IAsyncResult asyncResult)
+ {
+ return _remote.EndSend(asyncResult);
+ }
+
+ public void BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
+ object state)
+ {
+ _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();
+ }
+
+
+ private void ConnectCallback(IAsyncResult ar)
+ {
+ var state = (Socks5State) ar.AsyncState;
+ try
+ {
+ _remote.EndConnect(ar);
+
+ byte[] handshake = {5, 1, 0};
+ _remote.BeginSend(handshake, 0, handshake.Length, 0, Socks5HandshakeSendCallback, state);
+ }
+ catch (Exception ex)
+ {
+ state.ex = ex;
+ state.Callback?.Invoke(new FakeAsyncResult(ar, state));
+ }
+ }
+
+ private void Socks5HandshakeSendCallback(IAsyncResult ar)
+ {
+ var state = (Socks5State)ar.AsyncState;
+ try
+ {
+ _remote.EndSend(ar);
+
+ _remote.BeginReceive(_receiveBuffer, 0, 2, 0, Socks5HandshakeReceiveCallback, state);
+ }
+ catch (Exception ex)
+ {
+ state.ex = ex;
+ state.Callback?.Invoke(new FakeAsyncResult(ar, state));
+ }
+ }
+
+ private void Socks5HandshakeReceiveCallback(IAsyncResult ar)
+ {
+ Exception ex = null;
+ var state = (Socks5State)ar.AsyncState;
+ try
+ {
+ var bytesRead = _remote.EndReceive(ar);
+ if (bytesRead >= 2)
+ {
+ if (_receiveBuffer[0] != 5 || _receiveBuffer[1] != 0)
+ {
+ ex = new Exception(I18N.GetString("Proxy handshake faild"));
+ }
+ }
+ else
+ {
+ ex = new Exception(I18N.GetString("Proxy handshake faild"));
+ }
+ }
+ catch (Exception ex2)
+ {
+ ex = ex2;
+ }
+ state.ex = ex;
+ state.Callback?.Invoke(new FakeAsyncResult(ar, state));
+ }
+
+
+ private void Socks5RequestSendCallback(IAsyncResult ar)
+ {
+ var state = (Socks5State)ar.AsyncState;
+ try
+ {
+ _remote.EndSend(ar);
+
+ _remote.BeginReceive(_receiveBuffer, 0, 4, 0, Socks5ReplyReceiveCallback, state);
+ }
+ catch (Exception ex)
+ {
+ state.ex = ex;
+ state.Callback?.Invoke(new FakeAsyncResult(ar, state));
+ }
+ }
+
+ private void Socks5ReplyReceiveCallback(IAsyncResult ar)
+ {
+ var state = (Socks5State)ar.AsyncState;
+ try
+ {
+ var bytesRead = _remote.EndReceive(ar);
+ if (bytesRead >= 4)
+ {
+ if (_receiveBuffer[0] == 5 && _receiveBuffer[1] == 0)
+ {
+ // 跳过剩下的reply
+ switch (_receiveBuffer[3]) // atyp
+ {
+ case 1:
+ state.BytesToRead = 4 + 2;
+ _remote.BeginReceive(_receiveBuffer, 0, 4 + 2, 0, Socks5ReplyReceiveCallback2, state);
+ break;
+ case 4:
+ state.BytesToRead = 16 + 2;
+ _remote.BeginReceive(_receiveBuffer, 0, 16 + 2, 0, Socks5ReplyReceiveCallback2, state);
+ break;
+ default:
+ state.ex = new Exception(I18N.GetString("Proxy request faild"));
+ state.Callback?.Invoke(new FakeAsyncResult(ar, state));
+ break;
+ }
+ }
+ else
+ {
+ state.ex = new Exception(I18N.GetString("Proxy request faild"));
+ state.Callback?.Invoke(new FakeAsyncResult(ar, state));
+ }
+ }
+ else
+ {
+ state.ex = new Exception(I18N.GetString("Proxy request faild"));
+ state.Callback?.Invoke(new FakeAsyncResult(ar, state));
+ }
+ }
+ catch (Exception ex)
+ {
+ state.ex = ex;
+ state.Callback?.Invoke(new FakeAsyncResult(ar, state));
+ }
+ }
+
+
+ private void Socks5ReplyReceiveCallback2(IAsyncResult ar)
+ {
+ Exception ex = null;
+ var state = (Socks5State)ar.AsyncState;
+ try
+ {
+ var bytesRead = _remote.EndReceive(ar);
+ var bytesNeedSkip = state.BytesToRead;
+
+ if (bytesRead < bytesNeedSkip)
+ {
+ ex = new Exception(I18N.GetString("Proxy request faild"));
+ }
+ }
+ catch (Exception ex2)
+ {
+ ex = ex2;
+ }
+
+ state.ex = ex;
+ state.Callback?.Invoke(new FakeAsyncResult(ar, state));
+ }
+ }
+}
diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj
index 3b0adae2..8b7dabd0 100644
--- a/shadowsocks-csharp/shadowsocks-csharp.csproj
+++ b/shadowsocks-csharp/shadowsocks-csharp.csproj
@@ -207,6 +207,7 @@
+