From 9493f93c0f8dea6e392d4542d51154904b2f2456 Mon Sep 17 00:00:00 2001 From: noisyfox Date: Thu, 30 Jun 2016 00:09:20 +0800 Subject: [PATCH] Add Socks5 Proxy --- shadowsocks-csharp/Proxy/DirectConnect.cs | 16 +- shadowsocks-csharp/Proxy/IProxy.cs | 8 +- shadowsocks-csharp/Proxy/Socks5Proxy.cs | 312 +++++++++++++++++++ shadowsocks-csharp/shadowsocks-csharp.csproj | 1 + 4 files changed, 324 insertions(+), 13 deletions(-) create mode 100644 shadowsocks-csharp/Proxy/Socks5Proxy.cs 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 @@ +