mirror of
				https://github.com/bolucat/Archive.git
				synced 2025-10-31 11:57:05 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			324 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			324 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System;
 | |
| using System.Net;
 | |
| using System.Net.Sockets;
 | |
| using System.Text;
 | |
| using System.Threading;
 | |
| using Shadowsocks.Controller;
 | |
| using Shadowsocks.Util.Sockets;
 | |
| 
 | |
| 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 readonly WrappedSocket _remote = new WrappedSocket();
 | |
| 
 | |
|         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)
 | |
|         {
 | |
|             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 destEndPoint, AsyncCallback callback, object state, NetworkCredential auth = null)
 | |
|         {
 | |
|             // TODO: support SOCKS5 auth
 | |
|             DestEndPoint = destEndPoint;
 | |
| 
 | |
|             byte[] request = null;
 | |
|             byte atyp = 0;
 | |
|             int port;
 | |
| 
 | |
|             var dep = destEndPoint as DnsEndPoint;
 | |
|             if (dep != null)
 | |
|             {
 | |
|                 // is a domain name, we will leave it to server
 | |
| 
 | |
|                 atyp = 3; // DOMAINNAME
 | |
|                 var enc = Encoding.UTF8;
 | |
|                 var hostByteCount = enc.GetByteCount(dep.Host);
 | |
| 
 | |
|                 request = new byte[4 + 1/*length byte*/ + hostByteCount + 2];
 | |
|                 request[4] = (byte)hostByteCount;
 | |
|                 enc.GetBytes(dep.Host, 0, dep.Host.Length, request, 5);
 | |
| 
 | |
|                 port = dep.Port;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 switch (DestEndPoint.AddressFamily)
 | |
|                 {
 | |
|                     case AddressFamily.InterNetwork:
 | |
|                         request = new byte[4 + 4 + 2];
 | |
|                         atyp = 1; // IP V4 address
 | |
|                         break;
 | |
|                     case AddressFamily.InterNetworkV6:
 | |
|                         request = new byte[4 + 16 + 2];
 | |
|                         atyp = 4; // IP V6 address
 | |
|                         break;
 | |
|                     default:
 | |
|                         throw new Exception(I18N.GetString("Proxy request failed"));
 | |
|                 }
 | |
|                 port = ((IPEndPoint) DestEndPoint).Port;
 | |
|                 var addr = ((IPEndPoint)DestEndPoint).Address.GetAddressBytes();
 | |
|                 Array.Copy(addr, 0, request, 4, request.Length - 4 - 2);
 | |
|             }
 | |
| 
 | |
|             // 构造request包剩余部分
 | |
|             request[0] = 5;
 | |
|             request[1] = 1;
 | |
|             request[2] = 0;
 | |
|             request[3] = atyp;
 | |
|             request[request.Length - 2] = (byte) ((port >> 8) & 0xff);
 | |
|             request[request.Length - 1] = (byte) (port & 0xff);
 | |
| 
 | |
|             var st = new Socks5State();
 | |
|             st.Callback = callback;
 | |
|             st.AsyncState = state;
 | |
| 
 | |
|             _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.Dispose();
 | |
|         }
 | |
|         
 | |
| 
 | |
|         private void ConnectCallback(IAsyncResult ar)
 | |
|         {
 | |
|             var state = (Socks5State) ar.AsyncState;
 | |
|             try
 | |
|             {
 | |
|                 _remote.EndConnect(ar);
 | |
| 
 | |
|                 _remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
 | |
| 
 | |
|                 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 failed"));
 | |
|                     }
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     ex = new Exception(I18N.GetString("Proxy handshake failed"));
 | |
|                 }
 | |
|             }
 | |
|             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 failed"));
 | |
|                                 state.Callback?.Invoke(new FakeAsyncResult(ar, state));
 | |
|                                 break;
 | |
|                         }
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         state.ex = new Exception(I18N.GetString("Proxy request failed"));
 | |
|                         state.Callback?.Invoke(new FakeAsyncResult(ar, state));
 | |
|                     }
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     state.ex = new Exception(I18N.GetString("Proxy request failed"));
 | |
|                     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 failed"));
 | |
|                 }
 | |
|             }
 | |
|             catch (Exception ex2)
 | |
|             {
 | |
|                 ex = ex2;
 | |
|             }
 | |
| 
 | |
|             state.ex = ex;
 | |
|             state.Callback?.Invoke(new FakeAsyncResult(ar, state));
 | |
|         }
 | |
|     }
 | |
| }
 | 
![github-action[bot]](/assets/img/avatar_default.png)