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