Files
Archive/shadowsocks-windows/shadowsocks-csharp/Proxy/Socks5Proxy.cs
2024-12-31 19:31:59 +01:00

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