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)); } } }