Browse Source

reworked tcp relay process

pull/2897/head
Student Main database64128 3 years ago
parent
commit
429941148c
No known key found for this signature in database GPG Key ID: 1CA27546BEDB8B01
12 changed files with 324 additions and 1406 deletions
  1. +5
    -13
      Shadowsocks.Crypto/Crypto/AEAD/AEADCrypto.cs
  2. +2
    -2
      Shadowsocks.Crypto/Crypto/CryptoFactory.cs
  3. +0
    -2
      Shadowsocks.Crypto/Crypto/ICrypto.cs
  4. +2
    -2
      shadowsocks-csharp/Controller/Service/PortForwarder.cs
  5. +197
    -688
      shadowsocks-csharp/Controller/Service/TCPRelay.cs
  6. +15
    -41
      shadowsocks-csharp/Proxy/DirectConnect.cs
  7. +45
    -138
      shadowsocks-csharp/Proxy/HttpProxy.cs
  8. +6
    -14
      shadowsocks-csharp/Proxy/IProxy.cs
  9. +42
    -232
      shadowsocks-csharp/Proxy/Socks5Proxy.cs
  10. +9
    -2
      shadowsocks-csharp/Util/Sockets/LineReader.cs
  11. +0
    -268
      shadowsocks-csharp/Util/Sockets/WrappedSocket.cs
  12. +1
    -4
      test/CryptographyTest.cs

+ 5
- 13
Shadowsocks.Crypto/Crypto/AEAD/AEADCrypto.cs View File

@@ -43,6 +43,9 @@ namespace Shadowsocks.Crypto.AEAD
// Is first chunk(tcp request)
protected bool tcpRequestSent;

// [len(2)][lentag][data][datatag]
private int ChunkOverhead => tagLen * 2 + 2;

public AEADCrypto(string method, string password)
: base(method, password)
{
@@ -119,17 +122,6 @@ namespace Shadowsocks.Crypto.AEAD
outlength = saltLen;
}

if (!tcpRequestSent)
{
tcpRequestSent = true;

// read addr byte to encrypt
int encAddrBufLength = ChunkEncrypt(tmp.Slice(0, AddressBufferLength), cipher.Slice(outlength));
tmp = tmp.Slice(AddressBufferLength);
outlength += encAddrBufLength;
}

// handle other chunks
while (true)
{
// calculate next chunk size
@@ -147,7 +139,7 @@ namespace Shadowsocks.Crypto.AEAD

// check if we have enough space for outbuf
// if not, keep buf for next run, at this condition, buffer is not empty
if (outlength + TCPParameter.ChunkOverheadSize > TCPParameter.BufferSize)
if (outlength + ChunkOverhead > cipher.Length)
{
logger.Debug("enc outbuf almost full, giving up");

@@ -234,7 +226,7 @@ namespace Shadowsocks.Crypto.AEAD
outlength += len;

// logger.Debug("aead dec outlength " + outlength);
if (outlength + 100 > TCPParameter.BufferSize)
if (outlength + ChunkOverhead > cipher.Length)
{
logger.Trace($"{instanceId} output almost full, write {tmp.Length} byte back to buffer.");
tmp.CopyTo(buffer);


+ 2
- 2
Shadowsocks.Crypto/Crypto/CryptoFactory.cs View File

@@ -62,12 +62,12 @@ namespace Shadowsocks.Crypto
_registeredEncryptors.Add(method.Key, typeof(AEADAesGcmNativeCrypto));
}
}
foreach (var method in AEADNaClCrypto.SupportedCiphers())
foreach (var method in AEADBouncyCastleCrypto.SupportedCiphers())
{
if (!_registeredEncryptors.ContainsKey(method.Key))
{
ciphers.Add(method.Key, method.Value);
_registeredEncryptors.Add(method.Key, typeof(AEADNaClCrypto));
_registeredEncryptors.Add(method.Key, typeof(AEADBouncyCastleCrypto));
}
}
}


+ 0
- 2
Shadowsocks.Crypto/Crypto/ICrypto.cs View File

@@ -4,8 +4,6 @@ namespace Shadowsocks.Crypto
{
public interface ICrypto
{
/* length == -1 means not used */
int AddressBufferLength { set; get; }
int Encrypt(ReadOnlySpan<byte> plain, Span<byte> cipher);
int Decrypt(Span<byte> plain, ReadOnlySpan<byte> cipher);
int EncryptUDP(ReadOnlySpan<byte> plain, Span<byte> cipher);


+ 2
- 2
shadowsocks-csharp/Controller/Service/PortForwarder.cs View File

@@ -46,7 +46,7 @@ namespace Shadowsocks.Controller
private byte[] _firstPacket;
private int _firstPacketLength;
private Socket _local;
private WrappedSocket _remote;
private Socket _remote;
private bool _closed = false;
private bool _localShutdown = false;
private bool _remoteShutdown = false;
@@ -70,7 +70,7 @@ namespace Shadowsocks.Controller
EndPoint remoteEP = SocketUtil.GetEndPoint(_local.AddressFamily == AddressFamily.InterNetworkV6 ? "[::1]" : "127.0.0.1", targetPort);
// Connect to the remote endpoint.
_remote = new WrappedSocket();
_remote = new Socket(SocketType.Stream, ProtocolType.Tcp);
_remote.BeginConnect(remoteEP, ConnectCallback, null);
}
catch (Exception e)


+ 197
- 688
shadowsocks-csharp/Controller/Service/TCPRelay.cs
File diff suppressed because it is too large
View File


+ 15
- 41
shadowsocks-csharp/Proxy/DirectConnect.cs View File

@@ -2,6 +2,7 @@
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using Shadowsocks.Util.Sockets;

namespace Shadowsocks.Proxy
@@ -31,69 +32,42 @@ namespace Shadowsocks.Proxy
}
}

private WrappedSocket _remote = new WrappedSocket();
private readonly Socket _remote = new Socket(SocketType.Stream, ProtocolType.Tcp);

public EndPoint LocalEndPoint => _remote.LocalEndPoint;

public EndPoint ProxyEndPoint { get; } = new FakeEndPoint();
public EndPoint DestEndPoint { get; private set; }

public void BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state)
{
// do nothing

var r = new FakeAsyncResult(state);
callback?.Invoke(r);
}

public void EndConnectProxy(IAsyncResult asyncResult)
{
// do nothing
}

public void BeginConnectDest(EndPoint destEndPoint, AsyncCallback callback, object state, NetworkCredential auth = null)
{
DestEndPoint = destEndPoint;

_remote.BeginConnect(destEndPoint, callback, state);
}

public void EndConnectDest(IAsyncResult asyncResult)
{
_remote.EndConnect(asyncResult);
_remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
}

public void BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
object state)
public void Shutdown(SocketShutdown how)
{
_remote.BeginSend(buffer, offset, size, socketFlags, callback, state);
_remote.Shutdown(how);
}

public int EndSend(IAsyncResult asyncResult)
public void Close()
{
return _remote.EndSend(asyncResult);
_remote.Dispose();
}

public void BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
object state)
public Task ConnectProxyAsync(EndPoint remoteEP, NetworkCredential auth = null, CancellationToken token = default)
{
_remote.BeginReceive(buffer, offset, size, socketFlags, callback, state);
return Task.CompletedTask;
}

public int EndReceive(IAsyncResult asyncResult)
public async Task ConnectRemoteAsync(EndPoint destEndPoint, CancellationToken token = default)
{
return _remote.EndReceive(asyncResult);
DestEndPoint = destEndPoint;
await _remote.ConnectAsync(destEndPoint);
}

public void Shutdown(SocketShutdown how)
public async Task<int> SendAsync(ReadOnlyMemory<byte> buffer, CancellationToken token = default)
{
_remote.Shutdown(how);
return await _remote.SendAsync(buffer, SocketFlags.None, token);
}

public void Close()
public async Task<int> ReceiveAsync(Memory<byte> buffer, CancellationToken token = default)
{
_remote.Dispose();
return await _remote.ReceiveAsync(buffer, SocketFlags.None, token);
}
}
}

+ 45
- 138
shadowsocks-csharp/Proxy/HttpProxy.cs View File

@@ -4,6 +4,7 @@ using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using NLog;
using Shadowsocks.Controller;
using Shadowsocks.Util.Sockets;
@@ -12,60 +13,17 @@ namespace Shadowsocks.Proxy
{
public class HttpProxy : IProxy
{
private static Logger logger = LogManager.GetCurrentClassLogger();
private class FakeAsyncResult : IAsyncResult
{
public readonly HttpState innerState;
private readonly IAsyncResult r;
public FakeAsyncResult(IAsyncResult orig, HttpState 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 HttpState
{
public AsyncCallback Callback { get; set; }
public object AsyncState { get; set; }
public Exception ex { get; set; }
}
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public EndPoint LocalEndPoint => _remote.LocalEndPoint;
public EndPoint ProxyEndPoint { get; private set; }
public EndPoint DestEndPoint { get; private set; }
private readonly WrappedSocket _remote = new WrappedSocket();
public void BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state)
{
ProxyEndPoint = remoteEP;
_remote.BeginConnect(remoteEP, callback, state);
}
public void EndConnectProxy(IAsyncResult asyncResult)
{
_remote.EndConnect(asyncResult);
_remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
}
private readonly Socket _remote = new Socket(SocketType.Stream, ProtocolType.Tcp);
private const string HTTP_CRLF = "\r\n";
private const string HTTP_CONNECT_TEMPLATE =
"CONNECT {0} HTTP/1.1" + HTTP_CRLF +
private const string HTTP_CONNECT_TEMPLATE =
"CONNECT {0} HTTP/1.1" + HTTP_CRLF +
"Host: {0}" + HTTP_CRLF +
"Proxy-Connection: keep-alive" + HTTP_CRLF +
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36" + HTTP_CRLF +
@@ -73,58 +31,6 @@ namespace Shadowsocks.Proxy
"" + HTTP_CRLF; // End with an empty line
private const string PROXY_AUTH_TEMPLATE = "Proxy-Authorization: Basic {0}" + HTTP_CRLF;
public void BeginConnectDest(EndPoint destEndPoint, AsyncCallback callback, object state, NetworkCredential auth = null)
{
DestEndPoint = destEndPoint;
String authInfo = "";
if (auth != null)
{
string authKey = Convert.ToBase64String(Encoding.UTF8.GetBytes(auth.UserName + ":" + auth.Password));
authInfo = string.Format(PROXY_AUTH_TEMPLATE, authKey);
}
string request = string.Format(HTTP_CONNECT_TEMPLATE, destEndPoint, authInfo);
var b = Encoding.UTF8.GetBytes(request);
var st = new HttpState();
st.Callback = callback;
st.AsyncState = state;
_remote.BeginSend(b, 0, b.Length, 0, HttpRequestSendCallback, 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);
@@ -135,45 +41,6 @@ namespace Shadowsocks.Proxy
_remote.Dispose();
}
private void HttpRequestSendCallback(IAsyncResult ar)
{
var state = (HttpState) ar.AsyncState;
try
{
_remote.EndSend(ar);
// start line read
new LineReader(_remote, OnLineRead, OnException, OnFinish, Encoding.UTF8, HTTP_CRLF, 1024, new FakeAsyncResult(ar, state));
}
catch (Exception ex)
{
state.ex = ex;
state.Callback?.Invoke(new FakeAsyncResult(ar, state));
}
}
private void OnFinish(byte[] lastBytes, int index, int length, object state)
{
var st = (FakeAsyncResult)state;
if (st.innerState.ex == null)
{
if (!_established)
{
st.innerState.ex = new Exception(I18N.GetString("Proxy request failed"));
}
// TODO: save last bytes
}
st.innerState.Callback?.Invoke(st);
}
private void OnException(Exception ex, object state)
{
var st = (FakeAsyncResult) state;
st.innerState.ex = ex;
}
private static readonly Regex HttpRespondHeaderRegex = new Regex(@"^(HTTP/1\.\d) (\d{3}) (.+)$", RegexOptions.Compiled);
private int _respondLineCount = 0;
private bool _established = false;
@@ -206,5 +73,45 @@ namespace Shadowsocks.Proxy
return false;
}
private NetworkCredential auth;
public async Task ConnectProxyAsync(EndPoint remoteEP, NetworkCredential auth = null, CancellationToken token = default)
{
ProxyEndPoint = remoteEP;
this.auth = auth;
await _remote.ConnectAsync(remoteEP);
_remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
}
public async Task ConnectRemoteAsync(EndPoint destEndPoint, CancellationToken token = default)
{
DestEndPoint = destEndPoint;
String authInfo = "";
if (auth != null)
{
string authKey = Convert.ToBase64String(Encoding.UTF8.GetBytes(auth.UserName + ":" + auth.Password));
authInfo = string.Format(PROXY_AUTH_TEMPLATE, authKey);
}
string request = string.Format(HTTP_CONNECT_TEMPLATE, destEndPoint, authInfo);
var b = Encoding.UTF8.GetBytes(request);
await _remote.SendAsync(Encoding.UTF8.GetBytes(request), SocketFlags.None, token);
// start line read
LineReader reader = new LineReader(_remote, OnLineRead, (e, _) => throw e, (_1, _2, _3, _4) => { }, Encoding.UTF8, HTTP_CRLF, 1024, null);
await reader.Finished;
}
public async Task<int> SendAsync(ReadOnlyMemory<byte> buffer, CancellationToken token = default)
{
return await _remote.SendAsync(buffer, SocketFlags.None, token);
}
public async Task<int> ReceiveAsync(Memory<byte> buffer, CancellationToken token = default)
{
return await _remote.ReceiveAsync(buffer, SocketFlags.None, token);
}
}
}

+ 6
- 14
shadowsocks-csharp/Proxy/IProxy.cs View File

@@ -1,6 +1,8 @@
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;

namespace Shadowsocks.Proxy
{
@@ -13,23 +15,13 @@ namespace Shadowsocks.Proxy

EndPoint DestEndPoint { get; }

void BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state);
Task ConnectProxyAsync(EndPoint remoteEP, NetworkCredential auth = null, CancellationToken token = default);

void EndConnectProxy(IAsyncResult asyncResult);
Task ConnectRemoteAsync(EndPoint destEndPoint, CancellationToken token = default);

void BeginConnectDest(EndPoint destEndPoint, AsyncCallback callback, object state, NetworkCredential auth = null);
Task<int> SendAsync(ReadOnlyMemory<byte> buffer, CancellationToken token = default);

void EndConnectDest(IAsyncResult asyncResult);

void BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
object state);

int EndSend(IAsyncResult asyncResult);

void BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
object state);

int EndReceive(IAsyncResult asyncResult);
Task<int> ReceiveAsync(Memory<byte> buffer, CancellationToken token = default);

void Shutdown(SocketShutdown how);



+ 42
- 232
shadowsocks-csharp/Proxy/Socks5Proxy.cs View File

@@ -3,6 +3,7 @@ using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Shadowsocks.Controller;
using Shadowsocks.Util.Sockets;

@@ -10,36 +11,7 @@ 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 readonly Socket _remote = new Socket(SocketType.Stream, ProtocolType.Tcp);

private const int Socks5PktMaxSize = 4 + 16 + 2;
private readonly byte[] _receiveBuffer = new byte[Socks5PktMaxSize];
@@ -48,38 +20,40 @@ namespace Shadowsocks.Proxy
public EndPoint ProxyEndPoint { get; private set; }
public EndPoint DestEndPoint { get; private set; }

public void BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state)
public void Shutdown(SocketShutdown how)
{
var st = new Socks5State();
st.Callback = callback;
st.AsyncState = state;

ProxyEndPoint = remoteEP;

_remote.BeginConnect(remoteEP, ConnectCallback, st);
_remote.Shutdown(how);
}

public void EndConnectProxy(IAsyncResult asyncResult)
public void Close()
{
var state = ((FakeAsyncResult)asyncResult).innerState;
_remote.Dispose();
}

if (state.ex != null)
public async Task ConnectProxyAsync(EndPoint remoteEP, NetworkCredential auth = null, CancellationToken token = default)
{
ProxyEndPoint = remoteEP;
await _remote.ConnectAsync(remoteEP);
await _remote.SendAsync(new byte[] { 5, 1, 0 }, SocketFlags.None);
if (await _remote.ReceiveAsync(_receiveBuffer.AsMemory(0, 2), SocketFlags.None) != 2)
{
throw new Exception(I18N.GetString("Proxy handshake failed"));
}
if (_receiveBuffer[0] != 5 || _receiveBuffer[1] != 0)
{
throw state.ex;
throw new Exception(I18N.GetString("Proxy handshake failed"));
}
}

public void BeginConnectDest(EndPoint destEndPoint, AsyncCallback callback, object state, NetworkCredential auth = null)
public async Task ConnectRemoteAsync(EndPoint destEndPoint, CancellationToken token = default)
{
// TODO: support SOCKS5 auth
DestEndPoint = destEndPoint;

byte[] request = null;
byte atyp = 0;
byte[] request;
byte atyp;
int port;

var dep = destEndPoint as DnsEndPoint;
if (dep != null)
if (destEndPoint is DnsEndPoint dep)
{
// is a domain name, we will leave it to server

@@ -108,7 +82,7 @@ namespace Shadowsocks.Proxy
default:
throw new Exception(I18N.GetString("Proxy request failed"));
}
port = ((IPEndPoint) DestEndPoint).Port;
port = ((IPEndPoint)DestEndPoint).Port;
var addr = ((IPEndPoint)DestEndPoint).Address.GetAddressBytes();
Array.Copy(addr, 0, request, 4, request.Length - 4 - 2);
}
@@ -118,206 +92,42 @@ namespace Shadowsocks.Proxy
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);
}
request[^2] = (byte)((port >> 8) & 0xff);
request[^1] = (byte)(port & 0xff);

public void Close()
{
_remote.Dispose();
}
await _remote.SendAsync(request, SocketFlags.None, token);

private void ConnectCallback(IAsyncResult ar)
{
var state = (Socks5State) ar.AsyncState;
try
if (await _remote.ReceiveAsync(_receiveBuffer.AsMemory(0, 4), SocketFlags.None, token) != 4)
{
_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)
throw new Exception(I18N.GetString("Proxy request failed"));
};
if (_receiveBuffer[0] != 5 || _receiveBuffer[1] != 0)
{
state.ex = ex;
state.Callback?.Invoke(new FakeAsyncResult(ar, state));
throw new Exception(I18N.GetString("Proxy request failed"));
}
}

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 addrLen = _receiveBuffer[3] switch
{
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)
1 => 6,
4 => 18,
_ => throw new NotImplementedException(),
};
if (await _remote.ReceiveAsync(_receiveBuffer.AsMemory(0, addrLen), SocketFlags.None, token) != addrLen)
{
ex = ex2;
throw new Exception(I18N.GetString("Proxy request failed"));
}
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)
public async Task<int> SendAsync(ReadOnlyMemory<byte> buffer, CancellationToken token = default)
{
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));
}
return await _remote.SendAsync(buffer, SocketFlags.None, token);
}


private void Socks5ReplyReceiveCallback2(IAsyncResult ar)
public async Task<int> ReceiveAsync(Memory<byte> buffer, CancellationToken token = default)
{
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));
return await _remote.ReceiveAsync(buffer, SocketFlags.None, token);
}
}
}

+ 9
- 2
shadowsocks-csharp/Util/Sockets/LineReader.cs View File

@@ -1,11 +1,13 @@
using System;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace Shadowsocks.Util.Sockets
{
public class LineReader
{
private readonly WrappedSocket _socket;
private readonly Socket _socket;
private readonly Func<string, object, bool> _onLineRead;
private readonly Action<Exception, object> _onException;
private readonly Action<byte[], int, int, object> _onFinish;
@@ -20,7 +22,10 @@ namespace Shadowsocks.Util.Sockets

private int _bufferIndex;

public LineReader(WrappedSocket socket, Func<string, object, bool> onLineRead, Action<Exception, object> onException,
private readonly TaskCompletionSource<int> finishPromise = new TaskCompletionSource<int>();
public Task Finished => finishPromise.Task;

public LineReader(Socket socket, Func<string, object, bool> onLineRead, Action<Exception, object> onException,
Action<byte[], int, int, object> onFinish, Encoding encoding, string delimiter, int maxLineBytes, object state)
{
if (socket == null)
@@ -80,6 +85,7 @@ namespace Shadowsocks.Util.Sockets
if (bytesRead == 0)
{
OnFinish(length);
finishPromise.TrySetResult(0);
return;
}

@@ -128,6 +134,7 @@ namespace Shadowsocks.Util.Sockets

private void OnException(Exception ex)
{
finishPromise.TrySetException(ex);
_onException?.Invoke(ex, _state);
}



+ 0
- 268
shadowsocks-csharp/Util/Sockets/WrappedSocket.cs View File

@@ -1,268 +0,0 @@
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace Shadowsocks.Util.Sockets
{
/*
* A wrapped socket class which support both ipv4 and ipv6 based on the
* connected remote endpoint.
*
* If the server address is host name, then it may have both ipv4 and ipv6 address
* after resolving. The main idea is we don't want to resolve and choose the address
* by ourself. Instead, Socket.ConnectAsync() do handle this thing internally by trying
* each address and returning an established socket connection.
*/
public class WrappedSocket
{
public EndPoint LocalEndPoint => _activeSocket?.LocalEndPoint;
// Only used during connection and close, so it won't cost too much.
private SpinLock _socketSyncLock = new SpinLock();
private bool _disposed;
private bool Connected => _activeSocket != null;
private Socket _activeSocket;
public void BeginConnect(EndPoint remoteEP, AsyncCallback callback, object state)
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
if (Connected)
{
throw new SocketException((int) SocketError.IsConnected);
}
var arg = new SocketAsyncEventArgs();
arg.RemoteEndPoint = remoteEP;
arg.Completed += OnTcpConnectCompleted;
arg.UserToken = new TcpUserToken(callback, state);
if(!Socket.ConnectAsync(SocketType.Stream, ProtocolType.Tcp, arg))
{
OnTcpConnectCompleted(this, arg);
}
}
private class FakeAsyncResult : IAsyncResult
{
public bool IsCompleted { get; } = true;
public WaitHandle AsyncWaitHandle { get; } = null;
public object AsyncState { get; set; }
public bool CompletedSynchronously { get; } = true;
public Exception InternalException { get; set; } = null;
}
private class TcpUserToken
{
public AsyncCallback Callback { get; }
public object AsyncState { get; }
public TcpUserToken(AsyncCallback callback, object state)
{
Callback = callback;
AsyncState = state;
}
}
private void OnTcpConnectCompleted(object sender, SocketAsyncEventArgs args)
{
using (args)
{
args.Completed -= OnTcpConnectCompleted;
var token = (TcpUserToken) args.UserToken;
if (args.SocketError != SocketError.Success)
{
var ex = args.ConnectByNameError ?? new SocketException((int) args.SocketError);
var r = new FakeAsyncResult()
{
AsyncState = token.AsyncState,
InternalException = ex
};
token.Callback(r);
}
else
{
var lockTaken = false;
if (!_socketSyncLock.IsHeldByCurrentThread)
{
_socketSyncLock.TryEnter(ref lockTaken);
}
try
{
if (Connected)
{
args.ConnectSocket.FullClose();
}
else
{
_activeSocket = args.ConnectSocket;
if (_disposed)
{
_activeSocket.FullClose();
}
var r = new FakeAsyncResult()
{
AsyncState = token.AsyncState
};
token.Callback(r);
}
}
finally
{
if (lockTaken)
{
_socketSyncLock.Exit();
}
}
}
}
}
public void EndConnect(IAsyncResult asyncResult)
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
var r = asyncResult as FakeAsyncResult;
if (r == null)
{
throw new ArgumentException("Invalid asyncResult.", nameof(asyncResult));
}
if (r.InternalException != null)
{
throw r.InternalException;
}
}
public void Dispose()
{
if (_disposed)
{
return;
}
var lockTaken = false;
if (!_socketSyncLock.IsHeldByCurrentThread)
{
_socketSyncLock.TryEnter(ref lockTaken);
}
try
{
_disposed = true;
_activeSocket?.FullClose();
}
finally
{
if (lockTaken)
{
_socketSyncLock.Exit();
}
}
}
public IAsyncResult BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags,
AsyncCallback callback,
object state)
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
if (!Connected)
{
throw new SocketException((int) SocketError.NotConnected);
}
return _activeSocket.BeginSend(buffer, offset, size, socketFlags, callback, state);
}
public int EndSend(IAsyncResult asyncResult)
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
if (!Connected)
{
throw new SocketException((int) SocketError.NotConnected);
}
return _activeSocket.EndSend(asyncResult);
}
public IAsyncResult BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags,
AsyncCallback callback,
object state)
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
if (!Connected)
{
throw new SocketException((int) SocketError.NotConnected);
}
return _activeSocket.BeginReceive(buffer, offset, size, socketFlags, callback, state);
}
public int EndReceive(IAsyncResult asyncResult)
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
if (!Connected)
{
throw new SocketException((int) SocketError.NotConnected);
}
return _activeSocket.EndReceive(asyncResult);
}
public void Shutdown(SocketShutdown how)
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
if (!Connected)
{
return;
}
_activeSocket.Shutdown(how);
}
public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, bool optionValue)
{
SetSocketOption(optionLevel, optionName, optionValue ? 1 : 0);
}
public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, int optionValue)
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
if (!Connected)
{
throw new SocketException((int)SocketError.NotConnected);
}
_activeSocket.SetSocketOption(optionLevel, optionName, optionValue);
}
}
}

+ 1
- 4
test/CryptographyTest.cs View File

@@ -55,10 +55,7 @@ namespace Shadowsocks.Test
{
IEncryptor encryptor = (IEncryptor)ector.Invoke(new object[] { method, password });
IEncryptor decryptor = (IEncryptor)dctor.Invoke(new object[] { method, password });
encryptor.AddressBufferLength = 1 + 4 + 2;// ADDR_ATYP_LEN + 4 + ADDR_PORT_LEN;
decryptor.AddressBufferLength = 1 + 4 + 2;// ADDR_ATYP_LEN + 4 + ADDR_PORT_LEN;
for (int i = 0; i < 16; i++)
{
RunEncryptionRound(encryptor, decryptor);


Loading…
Cancel
Save