This reverts commit b04c59d8e6
.
tags/3.4.2
@@ -0,0 +1,487 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Net.Sockets; | |||
using System.Text; | |||
using System.Text.RegularExpressions; | |||
using Shadowsocks.ForwardProxy; | |||
using Shadowsocks.Util.Sockets; | |||
namespace Shadowsocks.Controller.Service | |||
{ | |||
class Http2Socks5 : Listener.Service | |||
{ | |||
private readonly ByteSearch.SearchTarget _connectSearch = | |||
new ByteSearch.SearchTarget(Encoding.UTF8.GetBytes("HTTP")); | |||
private readonly int _socks5Port; | |||
public Http2Socks5(int socks5Port) | |||
{ | |||
_socks5Port = socks5Port; | |||
} | |||
public override bool Handle(byte[] firstPacket, int length, Socket socket, object state) | |||
{ | |||
if (socket.ProtocolType != ProtocolType.Tcp) | |||
{ | |||
return false; | |||
} | |||
if (_connectSearch.SearchIn(firstPacket, 0, length) != -1) | |||
{ | |||
new HttpHandler(_socks5Port, firstPacket, length, socket); | |||
return true; | |||
} | |||
return false; | |||
} | |||
private class HttpHandler | |||
{ | |||
private const string HTTP_CRLF = "\r\n"; | |||
private const string HTTP_CONNECT_200 = | |||
"HTTP/1.1 200 Connection established" + HTTP_CRLF + | |||
"Proxy-Connection: close" + HTTP_CRLF + | |||
"Proxy-Agent: Shadowsocks" + HTTP_CRLF + | |||
"" + HTTP_CRLF; // End with an empty line | |||
private readonly WrappedSocket _localSocket; | |||
private readonly int _socks5Port; | |||
private Socks5Proxy _socks5; | |||
private bool _closed = false; | |||
private bool _localShutdown = false; | |||
private bool _remoteShutdown = false; | |||
private readonly object _Lock = new object(); | |||
private const int RecvSize = 16384; | |||
// remote receive buffer | |||
private readonly byte[] _remoteRecvBuffer = new byte[RecvSize]; | |||
// connection receive buffer | |||
private readonly byte[] _connetionRecvBuffer = new byte[RecvSize]; | |||
public HttpHandler(int socks5Port, byte[] firstPacket, int length, Socket socket) | |||
{ | |||
_socks5Port = socks5Port; | |||
_localSocket = new WrappedSocket(socket); | |||
_localSocket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); | |||
new LineReader(firstPacket, _localSocket, firstPacket, 0, length, OnLineRead, OnException, OnFinish, | |||
Encoding.UTF8, HTTP_CRLF, null); | |||
} | |||
private void CheckClose() | |||
{ | |||
if (_localShutdown && _remoteShutdown) | |||
{ | |||
Close(); | |||
} | |||
} | |||
private void Close() | |||
{ | |||
lock (_Lock) | |||
{ | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
_closed = true; | |||
} | |||
_localSocket.Dispose(); | |||
_socks5?.Close(); | |||
} | |||
private byte[] _lastBytes; | |||
private int _lastBytesIndex; | |||
private int _lastBytesLength; | |||
#region Socks5 Process | |||
private void ProxyConnectCallback(IAsyncResult ar) | |||
{ | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
_socks5.EndConnectProxy(ar); | |||
_socks5.BeginConnectDest(SocketUtil.GetEndPoint(_targetHost, _targetPort), ConnectCallback, null); | |||
} | |||
catch (ArgumentException) | |||
{ | |||
} | |||
catch (Exception e) | |||
{ | |||
Logging.LogUsefulException(e); | |||
Close(); | |||
} | |||
} | |||
private void ConnectCallback(IAsyncResult ar) | |||
{ | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
_socks5.EndConnectDest(ar); | |||
if (_isConnect) | |||
{ | |||
// http connect response | |||
SendConnectResponse(); | |||
} | |||
else | |||
{ | |||
// send header | |||
SendHeader(); | |||
} | |||
} | |||
catch (ArgumentException) | |||
{ | |||
} | |||
catch (Exception e) | |||
{ | |||
Logging.LogUsefulException(e); | |||
Close(); | |||
} | |||
} | |||
#endregion | |||
#region CONNECT | |||
private void SendConnectResponse() | |||
{ | |||
var len = Encoding.UTF8.GetBytes(HTTP_CONNECT_200, 0, HTTP_CONNECT_200.Length, _remoteRecvBuffer, 0); | |||
_localSocket.BeginSend(_remoteRecvBuffer, 0, len, SocketFlags.None, Http200SendCallback, null); | |||
} | |||
private void Http200SendCallback(IAsyncResult ar) | |||
{ | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
_localSocket.EndSend(ar); | |||
StartPipe(); | |||
} | |||
catch (ArgumentException) | |||
{ | |||
} | |||
catch (Exception e) | |||
{ | |||
Logging.LogUsefulException(e); | |||
Close(); | |||
} | |||
} | |||
#endregion | |||
#region Other http method except CONNECT | |||
private void SendHeader() | |||
{ | |||
var h = _headers.Dequeue() + HTTP_CRLF; | |||
var len = Encoding.UTF8.GetBytes(h, 0, h.Length, _connetionRecvBuffer, 0); | |||
_socks5.BeginSend(_connetionRecvBuffer, 0, len, SocketFlags.None, HeaderSendCallback, null); | |||
} | |||
private void HeaderSendCallback(IAsyncResult ar) | |||
{ | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
_socks5.EndSend(ar); | |||
if (_headers.Count > 0) | |||
{ | |||
SendHeader(); | |||
} | |||
else | |||
{ | |||
StartPipe(); | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
Logging.LogUsefulException(e); | |||
Close(); | |||
} | |||
} | |||
#endregion | |||
#region Pipe | |||
private void StartPipe() | |||
{ | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
_socks5.BeginReceive(_remoteRecvBuffer, 0, RecvSize, 0, | |||
PipeRemoteReceiveCallback, null); | |||
if (_lastBytesLength > 0) | |||
{ | |||
_socks5.BeginSend(_lastBytes, _lastBytesIndex, _lastBytesLength, SocketFlags.None, | |||
PipeRemoteSendCallback, null); | |||
} | |||
else | |||
{ | |||
_localSocket.BeginReceive(_connetionRecvBuffer, 0, RecvSize, 0, | |||
PipeConnectionReceiveCallback, null); | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
Logging.LogUsefulException(e); | |||
Close(); | |||
} | |||
} | |||
private void PipeRemoteReceiveCallback(IAsyncResult ar) | |||
{ | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
int bytesRead = _socks5.EndReceive(ar); | |||
if (bytesRead > 0) | |||
{ | |||
_localSocket.BeginSend(_remoteRecvBuffer, 0, bytesRead, 0, PipeConnectionSendCallback, null); | |||
} | |||
else | |||
{ | |||
_localSocket.Shutdown(SocketShutdown.Send); | |||
_localShutdown = true; | |||
CheckClose(); | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
Logging.LogUsefulException(e); | |||
Close(); | |||
} | |||
} | |||
private void PipeConnectionReceiveCallback(IAsyncResult ar) | |||
{ | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
int bytesRead = _localSocket.EndReceive(ar); | |||
if (bytesRead > 0) | |||
{ | |||
_socks5.BeginSend(_connetionRecvBuffer, 0, bytesRead, 0, PipeRemoteSendCallback, null); | |||
} | |||
else | |||
{ | |||
_socks5.Shutdown(SocketShutdown.Send); | |||
_remoteShutdown = true; | |||
CheckClose(); | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
Logging.LogUsefulException(e); | |||
Close(); | |||
} | |||
} | |||
private void PipeRemoteSendCallback(IAsyncResult ar) | |||
{ | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
_socks5.EndSend(ar); | |||
_localSocket.BeginReceive(_connetionRecvBuffer, 0, RecvSize, 0, | |||
PipeConnectionReceiveCallback, null); | |||
} | |||
catch (Exception e) | |||
{ | |||
Logging.LogUsefulException(e); | |||
Close(); | |||
} | |||
} | |||
private void PipeConnectionSendCallback(IAsyncResult ar) | |||
{ | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
_localSocket.EndSend(ar); | |||
_socks5.BeginReceive(_remoteRecvBuffer, 0, RecvSize, 0, | |||
PipeRemoteReceiveCallback, null); | |||
} | |||
catch (Exception e) | |||
{ | |||
Logging.LogUsefulException(e); | |||
Close(); | |||
} | |||
} | |||
#endregion | |||
#region Header Parse | |||
private void OnException(Exception ex, object state) | |||
{ | |||
throw ex; | |||
} | |||
private static readonly Regex HttpRequestHeaderRegex = new Regex(@"^([A-Z]+?) ([^\s]+) HTTP/1\.\d$"); | |||
private int _requestLineCount = 0; | |||
private volatile bool _isConnect = false; | |||
private string _targetHost; | |||
private int _targetPort; | |||
private readonly Queue<string> _headers = new Queue<string>(); | |||
private bool ParseHost(string host) | |||
{ | |||
var locs = host.Split(':'); | |||
_targetHost = locs[0]; | |||
if (locs.Length > 1) | |||
{ | |||
if (!int.TryParse(locs[1], out _targetPort)) | |||
{ | |||
return false; | |||
} | |||
} | |||
else | |||
{ | |||
_targetPort = 80; | |||
} | |||
return true; | |||
} | |||
private bool OnLineRead(string line, object state) | |||
{ | |||
if (_closed) | |||
{ | |||
return true; | |||
} | |||
Logging.Debug(line); | |||
if (!line.StartsWith("Proxy-")) | |||
{ | |||
_headers.Enqueue(line); | |||
} | |||
if (_requestLineCount == 0) | |||
{ | |||
var m = HttpRequestHeaderRegex.Match(line); | |||
if (m.Success) | |||
{ | |||
var method = m.Groups[1].Value; | |||
if (method == "CONNECT") | |||
{ | |||
_isConnect = true; | |||
if (!ParseHost(m.Groups[2].Value)) | |||
{ | |||
throw new Exception("Bad http header: " + line); | |||
} | |||
} | |||
} | |||
} | |||
else | |||
{ | |||
if (line.IsNullOrEmpty()) | |||
{ | |||
return true; | |||
} | |||
if (!_isConnect) | |||
{ | |||
if (line.StartsWith("Host: ")) | |||
{ | |||
if (!ParseHost(line.Substring(6).Trim())) | |||
{ | |||
throw new Exception("Bad http header: " + line); | |||
} | |||
} | |||
} | |||
} | |||
_requestLineCount++; | |||
return false; | |||
} | |||
private void OnFinish(byte[] lastBytes, int index, int length, object state) | |||
{ | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
if (_targetHost == null) | |||
{ | |||
Logging.Error("Unkonwn host"); | |||
Close(); | |||
} | |||
else | |||
{ | |||
if (length > 0) | |||
{ | |||
_lastBytes = lastBytes; | |||
_lastBytesIndex = index; | |||
_lastBytesLength = length; | |||
} | |||
// Start socks5 conn | |||
_socks5 = new Socks5Proxy(); | |||
_socks5.BeginConnectProxy(SocketUtil.GetEndPoint("127.0.0.1", _socks5Port), ProxyConnectCallback, | |||
null); | |||
} | |||
} | |||
#endregion | |||
} | |||
} | |||
} |
@@ -1,264 +0,0 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Net.Sockets; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using Shadowsocks.Model; | |||
using Shadowsocks.Util.Sockets; | |||
using System.Text.RegularExpressions; | |||
namespace Shadowsocks.Controller.Service | |||
{ | |||
class HttpHandlerHandlerFactory : ITCPHandlerFactory | |||
{ | |||
private static readonly ByteSearch.SearchTarget HttpSearch = | |||
new ByteSearch.SearchTarget(Encoding.UTF8.GetBytes("HTTP")); | |||
public bool CanHandle(byte[] firstPacket, int length) | |||
{ | |||
return HttpSearch.SearchIn(firstPacket, 0, length) != -1; | |||
} | |||
public TCPHandler NewHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket) | |||
{ | |||
return new HttpHandler(controller, config, tcprelay, socket); | |||
} | |||
} | |||
class HttpHandler : TCPHandler | |||
{ | |||
private const string HTTP_CRLF = "\r\n"; | |||
private const string HTTP_CONNECT_200 = | |||
"HTTP/1.1 200 Connection established" + HTTP_CRLF + | |||
"Proxy-Connection: close" + HTTP_CRLF + | |||
"Proxy-Agent: Shadowsocks" + HTTP_CRLF + | |||
"" + HTTP_CRLF; // End with an empty line | |||
private readonly WrappedSocket _localSocket; | |||
private byte[] _lastBytes; | |||
private int _lastBytesIndex; | |||
private int _lastBytesLength; | |||
public HttpHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket) : base(controller, config, tcprelay, socket) | |||
{ | |||
_localSocket = new WrappedSocket(socket); | |||
} | |||
public override void StartHandshake(byte[] firstPacket, int length) | |||
{ | |||
new LineReader(firstPacket, _localSocket, firstPacket, 0, length, OnLineRead, OnException, OnFinish, | |||
Encoding.UTF8, HTTP_CRLF, null); | |||
} | |||
#region Header Parse | |||
private void OnException(Exception ex, object state) | |||
{ | |||
throw ex; | |||
} | |||
private static readonly Regex HttpRequestHeaderRegex = new Regex(@"^([A-Z]+?) ([^\s]+) HTTP/1\.\d$"); | |||
private int _requestLineCount = 0; | |||
private volatile bool _isConnect = false; | |||
private string _targetHost; | |||
private int _targetPort; | |||
private readonly Queue<string> _headers = new Queue<string>(); | |||
private bool ParseHost(string host) | |||
{ | |||
var locs = host.Split(':'); | |||
_targetHost = locs[0]; | |||
if (locs.Length > 1) | |||
{ | |||
if (!int.TryParse(locs[1], out _targetPort)) | |||
{ | |||
return false; | |||
} | |||
} | |||
else | |||
{ | |||
_targetPort = 80; | |||
} | |||
return true; | |||
} | |||
private bool OnLineRead(string line, object state) | |||
{ | |||
if (Closed) | |||
{ | |||
return true; | |||
} | |||
Logging.Debug(line); | |||
if (!line.StartsWith("Proxy-")) | |||
{ | |||
_headers.Enqueue(line); | |||
} | |||
if (_requestLineCount == 0) | |||
{ | |||
var m = HttpRequestHeaderRegex.Match(line); | |||
if (m.Success) | |||
{ | |||
var method = m.Groups[1].Value; | |||
if (method == "CONNECT") | |||
{ | |||
_isConnect = true; | |||
if (!ParseHost(m.Groups[2].Value)) | |||
{ | |||
throw new Exception("Bad http header: " + line); | |||
} | |||
} | |||
} | |||
} | |||
else | |||
{ | |||
if (line.IsNullOrEmpty()) | |||
{ | |||
return true; | |||
} | |||
if (!_isConnect) | |||
{ | |||
if (line.StartsWith("Host: ")) | |||
{ | |||
if (!ParseHost(line.Substring(6).Trim())) | |||
{ | |||
throw new Exception("Bad http header: " + line); | |||
} | |||
} | |||
} | |||
} | |||
_requestLineCount++; | |||
return false; | |||
} | |||
private void OnFinish(byte[] lastBytes, int index, int length, object state) | |||
{ | |||
if (Closed) | |||
{ | |||
return; | |||
} | |||
if (_targetHost == null) | |||
{ | |||
Logging.Error("Unkonwn host"); | |||
Close(); | |||
} | |||
else | |||
{ | |||
if (length > 0) | |||
{ | |||
_lastBytes = lastBytes; | |||
_lastBytesIndex = index; | |||
_lastBytesLength = length; | |||
} | |||
StartConnect(SocketUtil.GetEndPoint(_targetHost, _targetPort)); | |||
} | |||
} | |||
#endregion | |||
protected override void OnServerConnected(AsyncSession session) | |||
{ | |||
if (_isConnect) | |||
{ | |||
// http connect response | |||
SendConnectResponse(session); | |||
} | |||
else | |||
{ | |||
// send header | |||
SendHeader(session); | |||
} | |||
} | |||
#region CONNECT | |||
private void SendConnectResponse(AsyncSession session) | |||
{ | |||
var len = Encoding.UTF8.GetBytes(HTTP_CONNECT_200, 0, HTTP_CONNECT_200.Length, RemoteRecvBuffer, 0); | |||
_localSocket.BeginSend(RemoteRecvBuffer, 0, len, SocketFlags.None, Http200SendCallback, session); | |||
} | |||
private void Http200SendCallback(IAsyncResult ar) | |||
{ | |||
if (Closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
_localSocket.EndSend(ar); | |||
StartPipe((AsyncSession) ar.AsyncState); | |||
} | |||
catch (ArgumentException) | |||
{ | |||
} | |||
catch (Exception e) | |||
{ | |||
Logging.LogUsefulException(e); | |||
Close(); | |||
} | |||
} | |||
#endregion | |||
#region Other http method except CONNECT | |||
private void SendHeader(AsyncSession session) | |||
{ | |||
var h = _headers.Dequeue() + HTTP_CRLF; | |||
var len = Encoding.UTF8.GetBytes(h, 0, h.Length, ConnetionRecvBuffer, 0); | |||
BeginSendToServer(len, session, HeaderSendCallback); | |||
} | |||
private void HeaderSendCallback(IAsyncResult ar) | |||
{ | |||
if (Closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
var session = EndSendToServer(ar); | |||
if (_headers.Count > 0) | |||
{ | |||
SendHeader(session); | |||
} | |||
else | |||
{ | |||
StartPipe(session); | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
Logging.LogUsefulException(e); | |||
Close(); | |||
} | |||
} | |||
#endregion | |||
} | |||
} |
@@ -1,293 +0,0 @@ | |||
using System; | |||
using System.Linq; | |||
using System.Net; | |||
using System.Net.Sockets; | |||
using Shadowsocks.Model; | |||
using Shadowsocks.Util.Sockets; | |||
namespace Shadowsocks.Controller.Service | |||
{ | |||
class Socks5HandlerFactory : ITCPHandlerFactory | |||
{ | |||
public bool CanHandle(byte[] firstPacket, int length) | |||
{ | |||
return length >= 2 && firstPacket[0] == 5; | |||
} | |||
public TCPHandler NewHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket) | |||
{ | |||
return new Socks5Handler(controller, config, tcprelay, socket); | |||
} | |||
} | |||
class Socks5Handler : TCPHandler | |||
{ | |||
private byte _command; | |||
private int _firstPacketLength; | |||
public Socks5Handler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket) : base(controller, config, tcprelay, socket, false) | |||
{ | |||
} | |||
public override void StartHandshake(byte[] firstPacket, int length) | |||
{ | |||
if (Closed) return; | |||
try | |||
{ | |||
int bytesRead = length; | |||
if (bytesRead > 1) | |||
{ | |||
byte[] response = { 5, 0 }; | |||
if (firstPacket[0] != 5) | |||
{ | |||
// reject socks 4 | |||
response = new byte[] { 0, 91 }; | |||
Logging.Error("socks 5 protocol error"); | |||
} | |||
Connection.BeginSend(response, 0, response.Length, SocketFlags.None, HandshakeSendCallback, null); | |||
} | |||
else | |||
Close(); | |||
} | |||
catch (Exception e) | |||
{ | |||
Logging.LogUsefulException(e); | |||
Close(); | |||
} | |||
} | |||
private void HandshakeSendCallback(IAsyncResult ar) | |||
{ | |||
if (Closed) return; | |||
try | |||
{ | |||
Connection.EndSend(ar); | |||
// +-----+-----+-------+------+----------+----------+ | |||
// | VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | | |||
// +-----+-----+-------+------+----------+----------+ | |||
// | 1 | 1 | X'00' | 1 | Variable | 2 | | |||
// +-----+-----+-------+------+----------+----------+ | |||
// Skip first 3 bytes, and read 2 more bytes to analysis the address. | |||
// 2 more bytes is designed if address is domain then we don't need to read once more to get the addr length. | |||
// TODO validate | |||
Connection.BeginReceive(ConnetionRecvBuffer, 0, 3 + 2, SocketFlags.None, | |||
handshakeReceive2Callback, null); | |||
} | |||
catch (Exception e) | |||
{ | |||
Logging.LogUsefulException(e); | |||
Close(); | |||
} | |||
} | |||
private void handshakeReceive2Callback(IAsyncResult ar) | |||
{ | |||
if (Closed) return; | |||
try | |||
{ | |||
int bytesRead = Connection.EndReceive(ar); | |||
if (bytesRead >= 5) | |||
{ | |||
_command = ConnetionRecvBuffer[1]; | |||
if (_command != 1 && _command != 3) | |||
{ | |||
Logging.Debug("Unsupported CMD=" + _command); | |||
Close(); | |||
} | |||
else | |||
{ | |||
int atyp = ConnetionRecvBuffer[3]; | |||
switch (atyp) | |||
{ | |||
case 1: // IPv4 address, 4 bytes | |||
ReadAddress(4 + 2 - 1); | |||
break; | |||
case 3: // domain name, length + str | |||
int len = ConnetionRecvBuffer[4]; | |||
ReadAddress(len + 2); | |||
break; | |||
case 4: // IPv6 address, 16 bytes | |||
ReadAddress(16 + 2 - 1); | |||
break; | |||
default: | |||
Logging.Debug("Unsupported ATYP=" + atyp); | |||
Close(); | |||
break; | |||
} | |||
} | |||
} | |||
else | |||
{ | |||
Logging.Debug("failed to recv data in Shadowsocks.Controller.TCPHandler.handshakeReceive2Callback()"); | |||
Close(); | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
Logging.LogUsefulException(e); | |||
Close(); | |||
} | |||
} | |||
private void ReadAddress(int bytesRemain) | |||
{ | |||
Array.Copy(ConnetionRecvBuffer, 3, ConnetionRecvBuffer, 0, 2); | |||
// Read the remain address bytes | |||
Connection.BeginReceive(ConnetionRecvBuffer, 2, RecvSize - 2, SocketFlags.None, OnAddressFullyRead, bytesRemain); | |||
} | |||
private void OnAddressFullyRead(IAsyncResult ar) | |||
{ | |||
if (Closed) return; | |||
try | |||
{ | |||
int bytesRead = Connection.EndReceive(ar); | |||
int bytesRemain = (int)ar.AsyncState; | |||
if (bytesRead >= bytesRemain) | |||
{ | |||
_firstPacketLength = bytesRead + 2; | |||
int atyp = ConnetionRecvBuffer[0]; | |||
string dst_addr = "Unknown"; | |||
int dst_port = -1; | |||
switch (atyp) | |||
{ | |||
case 1: // IPv4 address, 4 bytes | |||
dst_addr = new IPAddress(ConnetionRecvBuffer.Skip(1).Take(4).ToArray()).ToString(); | |||
dst_port = (ConnetionRecvBuffer[5] << 8) + ConnetionRecvBuffer[6]; | |||
break; | |||
case 3: // domain name, length + str | |||
int len = ConnetionRecvBuffer[1]; | |||
dst_addr = System.Text.Encoding.UTF8.GetString(ConnetionRecvBuffer, 2, len); | |||
dst_port = (ConnetionRecvBuffer[len + 2] << 8) + ConnetionRecvBuffer[len + 3]; | |||
break; | |||
case 4: // IPv6 address, 16 bytes | |||
dst_addr = $"[{new IPAddress(ConnetionRecvBuffer.Skip(1).Take(16).ToArray())}]"; | |||
dst_port = (ConnetionRecvBuffer[17] << 8) + ConnetionRecvBuffer[18]; | |||
break; | |||
} | |||
if (Config.isVerboseLogging) | |||
{ | |||
Logging.Info($"connect to {dst_addr}:{dst_port}"); | |||
} | |||
var destEndPoint = SocketUtil.GetEndPoint(dst_addr, dst_port); | |||
if (_command == 1) | |||
{ | |||
byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 }; | |||
Connection.BeginSend(response, 0, response.Length, SocketFlags.None, | |||
ResponseCallback, destEndPoint); | |||
} | |||
else if (_command == 3) | |||
{ | |||
HandleUDPAssociate(); | |||
} | |||
} | |||
else | |||
{ | |||
Logging.Debug("failed to recv data in Shadowsocks.Controller.TCPHandler.OnAddressFullyRead()"); | |||
Close(); | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
Logging.LogUsefulException(e); | |||
Close(); | |||
} | |||
} | |||
private void HandleUDPAssociate() | |||
{ | |||
IPEndPoint endPoint = (IPEndPoint)Connection.LocalEndPoint; | |||
byte[] address = endPoint.Address.GetAddressBytes(); | |||
int port = endPoint.Port; | |||
byte[] response = new byte[4 + address.Length + 2]; | |||
response[0] = 5; | |||
switch (endPoint.AddressFamily) | |||
{ | |||
case AddressFamily.InterNetwork: | |||
response[3] = 1; | |||
break; | |||
case AddressFamily.InterNetworkV6: | |||
response[3] = 4; | |||
break; | |||
} | |||
address.CopyTo(response, 4); | |||
response[response.Length - 1] = (byte)(port & 0xFF); | |||
response[response.Length - 2] = (byte)((port >> 8) & 0xFF); | |||
Connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(ReadAll), true); | |||
} | |||
private void ReadAll(IAsyncResult ar) | |||
{ | |||
if (Closed) return; | |||
try | |||
{ | |||
if (ar.AsyncState != null) | |||
{ | |||
Connection.EndSend(ar); | |||
Connection.BeginReceive(ConnetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null); | |||
} | |||
else | |||
{ | |||
int bytesRead = Connection.EndReceive(ar); | |||
if (bytesRead > 0) | |||
{ | |||
Connection.BeginReceive(ConnetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null); | |||
} | |||
else | |||
Close(); | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
Logging.LogUsefulException(e); | |||
Close(); | |||
} | |||
} | |||
private void ResponseCallback(IAsyncResult ar) | |||
{ | |||
try | |||
{ | |||
Connection.EndSend(ar); | |||
StartConnect((EndPoint) ar.AsyncState); | |||
} | |||
catch (Exception e) | |||
{ | |||
Logging.LogUsefulException(e); | |||
Close(); | |||
} | |||
} | |||
protected override void OnServerConnected(AsyncSession session) | |||
{ | |||
BeginSendToServer(_firstPacketLength, session, FirstPackageSendCallback); | |||
} | |||
private void FirstPackageSendCallback(IAsyncResult ar) | |||
{ | |||
if (Closed) return; | |||
try | |||
{ | |||
var session = EndSendToServer(ar); | |||
StartPipe(session); | |||
} | |||
catch (Exception e) | |||
{ | |||
Logging.LogUsefulException(e); | |||
Close(); | |||
} | |||
} | |||
} | |||
} |
@@ -1,15 +1,17 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Net; | |||
using System.Net.Sockets; | |||
using System.Timers; | |||
using Shadowsocks.Controller.Strategy; | |||
using Shadowsocks.Encryption; | |||
using Shadowsocks.ForwardProxy; | |||
using Shadowsocks.Model; | |||
using Shadowsocks.Util.Sockets; | |||
namespace Shadowsocks.Controller.Service | |||
namespace Shadowsocks.Controller | |||
{ | |||
class TCPRelay : Listener.Service | |||
{ | |||
@@ -17,39 +19,23 @@ namespace Shadowsocks.Controller.Service | |||
private DateTime _lastSweepTime; | |||
private Configuration _config; | |||
private readonly List<ITCPHandlerFactory> _factories = new List<ITCPHandlerFactory>(); | |||
public ISet<TCPHandler> Handlers { get; } = new HashSet<TCPHandler>(); | |||
public ISet<TCPHandler> Handlers { get; set; } | |||
public TCPRelay(ShadowsocksController controller, Configuration conf) | |||
{ | |||
_controller = controller; | |||
_config = conf; | |||
Handlers = new HashSet<TCPHandler>(); | |||
_lastSweepTime = DateTime.Now; | |||
_factories.Add(new Socks5HandlerFactory()); | |||
_factories.Add(new HttpHandlerHandlerFactory()); | |||
} | |||
public override bool Handle(byte[] firstPacket, int length, Socket socket, object state) | |||
{ | |||
TCPHandler handler = null; | |||
foreach (var factory in _factories) | |||
{ | |||
if (factory.CanHandle(firstPacket, length)) | |||
{ | |||
handler = factory.NewHandler(_controller, _config, this, socket); | |||
break; | |||
} | |||
} | |||
if (handler == null) | |||
{ | |||
if (socket.ProtocolType != ProtocolType.Tcp | |||
|| (length < 2 || firstPacket[0] != 5)) | |||
return false; | |||
} | |||
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); | |||
TCPHandler handler = new TCPHandler(_controller, _config, this, socket); | |||
IList<TCPHandler> handlersToClose = new List<TCPHandler>(); | |||
lock (Handlers) | |||
@@ -76,7 +62,7 @@ namespace Shadowsocks.Controller.Service | |||
* Then the handler will never release until the next Handle call. Sometimes it will | |||
* cause odd problems (especially during memory profiling). | |||
*/ | |||
handler.StartHandshake(firstPacket, length); | |||
handler.Start(firstPacket, length); | |||
return true; | |||
} | |||
@@ -88,7 +74,7 @@ namespace Shadowsocks.Controller.Service | |||
{ | |||
handlersToClose.AddRange(Handlers); | |||
} | |||
handlersToClose.ForEach(h => h.Close()); | |||
handlersToClose.ForEach(h=>h.Close()); | |||
} | |||
public void UpdateInboundCounter(Server server, long n) | |||
@@ -107,21 +93,9 @@ namespace Shadowsocks.Controller.Service | |||
} | |||
} | |||
interface ITCPHandlerFactory | |||
{ | |||
bool CanHandle(byte[] firstPacket, int length); | |||
TCPHandler NewHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket); | |||
} | |||
abstract class TCPHandler | |||
class TCPHandler | |||
{ | |||
public abstract void StartHandshake(byte[] firstPacket, int length); | |||
protected abstract void OnServerConnected(AsyncSession session); | |||
protected class AsyncSession | |||
class AsyncSession | |||
{ | |||
public IForwardProxy Remote { get; } | |||
@@ -131,7 +105,7 @@ namespace Shadowsocks.Controller.Service | |||
} | |||
} | |||
protected class AsyncSession<T> : AsyncSession | |||
class AsyncSession<T> : AsyncSession | |||
{ | |||
public T State { get; set; } | |||
@@ -140,7 +114,7 @@ namespace Shadowsocks.Controller.Service | |||
State = state; | |||
} | |||
public AsyncSession(AsyncSession session, T state) : base(session.Remote) | |||
public AsyncSession(AsyncSession session, T state): base(session.Remote) | |||
{ | |||
State = state; | |||
} | |||
@@ -156,33 +130,39 @@ namespace Shadowsocks.Controller.Service | |||
public DateTime lastActivity; | |||
private ShadowsocksController _controller; | |||
protected Configuration Config { get; } | |||
private TCPRelay _tcprelay; | |||
protected Socket Connection { get; } | |||
private ShadowsocksController _controller; | |||
private Configuration _config; | |||
private TCPRelay _tcprelay; | |||
private Socket _connection; | |||
private IEncryptor _encryptor; | |||
private Server _server; | |||
private Server _server; | |||
private AsyncSession _currentRemoteSession; | |||
private bool _proxyConnected; | |||
private bool _destConnected; | |||
private bool _proxyConnected; | |||
private bool _destConnected; | |||
private int _totalRead = 0; | |||
private int _totalWrite = 0; | |||
private byte _command; | |||
private byte[] _firstPacket; | |||
private int _firstPacketLength; | |||
protected byte[] RemoteRecvBuffer { get; } = new byte[BufferSize]; | |||
private readonly byte[] _remoteSendBuffer = new byte[BufferSize]; | |||
protected byte[] ConnetionRecvBuffer { get; } = new byte[BufferSize]; | |||
private readonly byte[] _connetionSendBuffer = new byte[BufferSize]; | |||
private int _totalRead = 0; | |||
private int _totalWrite = 0; | |||
private IEncryptor _encryptor; | |||
private readonly object _encryptionLock = new object(); | |||
private readonly object _decryptionLock = new object(); | |||
private byte[] _remoteRecvBuffer = new byte[BufferSize]; | |||
private byte[] _remoteSendBuffer = new byte[BufferSize]; | |||
private byte[] _connetionRecvBuffer = new byte[BufferSize]; | |||
private byte[] _connetionSendBuffer = new byte[BufferSize]; | |||
private bool _connectionShutdown = false; | |||
private bool _remoteShutdown = false; | |||
protected bool Closed { get; private set; }= false; | |||
private readonly object _closeConnLock = new object(); | |||
private bool _connectionShutdown = false; | |||
private bool _remoteShutdown = false; | |||
private bool _closed = false; | |||
// instance-based lock without static | |||
private readonly object _encryptionLock = new object(); | |||
private readonly object _decryptionLock = new object(); | |||
private readonly object _closeConnLock = new object(); | |||
private DateTime _startConnectTime; | |||
private DateTime _startReceivingTime; | |||
@@ -190,24 +170,21 @@ namespace Shadowsocks.Controller.Service | |||
private EndPoint _destEndPoint = null; | |||
public TCPHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket, bool autoAppendHeader = true) | |||
public TCPHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket) | |||
{ | |||
_controller = controller; | |||
Config = config; | |||
_config = config; | |||
_tcprelay = tcprelay; | |||
Connection = socket; | |||
_connection = socket; | |||
_proxyTimeout = config.proxy.proxyTimeout * 1000; | |||
_serverTimeout = config.GetCurrentServer().timeout * 1000; | |||
lastActivity = DateTime.Now; | |||
_serverHeaderSent = !autoAppendHeader; | |||
} | |||
private void CreateRemote() | |||
public void CreateRemote() | |||
{ | |||
Server server = _controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)Connection.RemoteEndPoint, _destEndPoint); | |||
Server server = _controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)_connection.RemoteEndPoint, _destEndPoint); | |||
if (server == null || server.server == "") | |||
throw new ArgumentException("No server configured"); | |||
lock (_encryptionLock) | |||
@@ -220,6 +197,13 @@ namespace Shadowsocks.Controller.Service | |||
this._server = server; | |||
} | |||
public void Start(byte[] firstPacket, int length) | |||
{ | |||
_firstPacket = firstPacket; | |||
_firstPacketLength = length; | |||
HandshakeReceive(); | |||
} | |||
private void CheckClose() | |||
{ | |||
if (_connectionShutdown && _remoteShutdown) | |||
@@ -230,8 +214,8 @@ namespace Shadowsocks.Controller.Service | |||
{ | |||
lock (_closeConnLock) | |||
{ | |||
if (Closed) return; | |||
Closed = true; | |||
if (_closed) return; | |||
_closed = true; | |||
} | |||
lock (_tcprelay.Handlers) | |||
{ | |||
@@ -239,8 +223,8 @@ namespace Shadowsocks.Controller.Service | |||
} | |||
try | |||
{ | |||
Connection.Shutdown(SocketShutdown.Both); | |||
Connection.Close(); | |||
_connection.Shutdown(SocketShutdown.Both); | |||
_connection.Close(); | |||
} | |||
catch (Exception e) | |||
{ | |||
@@ -270,6 +254,245 @@ namespace Shadowsocks.Controller.Service | |||
} | |||
} | |||
private void HandshakeReceive() | |||
{ | |||
if (_closed) return; | |||
try | |||
{ | |||
int bytesRead = _firstPacketLength; | |||
if (bytesRead > 1) | |||
{ | |||
byte[] response = { 5, 0 }; | |||
if (_firstPacket[0] != 5) | |||
{ | |||
// reject socks 4 | |||
response = new byte[] { 0, 91 }; | |||
Logging.Error("socks 5 protocol error"); | |||
} | |||
_connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(HandshakeSendCallback), null); | |||
} | |||
else | |||
Close(); | |||
} | |||
catch (Exception e) | |||
{ | |||
Logging.LogUsefulException(e); | |||
Close(); | |||
} | |||
} | |||
private void HandshakeSendCallback(IAsyncResult ar) | |||
{ | |||
if (_closed) return; | |||
try | |||
{ | |||
_connection.EndSend(ar); | |||
// +-----+-----+-------+------+----------+----------+ | |||
// | VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | | |||
// +-----+-----+-------+------+----------+----------+ | |||
// | 1 | 1 | X'00' | 1 | Variable | 2 | | |||
// +-----+-----+-------+------+----------+----------+ | |||
// Skip first 3 bytes, and read 2 more bytes to analysis the address. | |||
// 2 more bytes is designed if address is domain then we don't need to read once more to get the addr length. | |||
// TODO validate | |||
_connection.BeginReceive(_connetionRecvBuffer, 0, 3 + 2, SocketFlags.None, | |||
new AsyncCallback(handshakeReceive2Callback), null); | |||
} | |||
catch (Exception e) | |||
{ | |||
Logging.LogUsefulException(e); | |||
Close(); | |||
} | |||
} | |||
private void handshakeReceive2Callback(IAsyncResult ar) | |||
{ | |||
if (_closed) return; | |||
try | |||
{ | |||
int bytesRead = _connection.EndReceive(ar); | |||
if (bytesRead >= 5) | |||
{ | |||
_command = _connetionRecvBuffer[1]; | |||
if (_command != 1 && _command != 3) | |||
{ | |||
Logging.Debug("Unsupported CMD=" + _command); | |||
Close(); | |||
} | |||
else | |||
{ | |||
int atyp = _connetionRecvBuffer[3]; | |||
switch (atyp) | |||
{ | |||
case 1: // IPv4 address, 4 bytes | |||
ReadAddress(4 + 2 - 1); | |||
break; | |||
case 3: // domain name, length + str | |||
int len = _connetionRecvBuffer[4]; | |||
ReadAddress(len + 2); | |||
break; | |||
case 4: // IPv6 address, 16 bytes | |||
ReadAddress(16 + 2 - 1); | |||
break; | |||
default: | |||
Logging.Debug("Unsupported ATYP=" + atyp); | |||
Close(); | |||
break; | |||
} | |||
} | |||
} | |||
else | |||
{ | |||
Logging.Debug("failed to recv data in Shadowsocks.Controller.TCPHandler.handshakeReceive2Callback()"); | |||
Close(); | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
Logging.LogUsefulException(e); | |||
Close(); | |||
} | |||
} | |||
private void ReadAddress(int bytesRemain) | |||
{ | |||
Array.Copy(_connetionRecvBuffer, 3, _connetionRecvBuffer, 0, 2); | |||
// Read the remain address bytes | |||
_connection.BeginReceive(_connetionRecvBuffer, 2, RecvSize - 2, SocketFlags.None, OnAddressFullyRead, bytesRemain); | |||
} | |||
private void OnAddressFullyRead(IAsyncResult ar) | |||
{ | |||
if (_closed) return; | |||
try | |||
{ | |||
int bytesRead = _connection.EndReceive(ar); | |||
int bytesRemain = (int) ar.AsyncState; | |||
if (bytesRead >= bytesRemain) | |||
{ | |||
_firstPacketLength = bytesRead + 2; | |||
int atyp = _connetionRecvBuffer[0]; | |||
string dst_addr = "Unknown"; | |||
int dst_port = -1; | |||
switch (atyp) | |||
{ | |||
case 1: // IPv4 address, 4 bytes | |||
dst_addr = new IPAddress(_connetionRecvBuffer.Skip(1).Take(4).ToArray()).ToString(); | |||
dst_port = (_connetionRecvBuffer[5] << 8) + _connetionRecvBuffer[6]; | |||
break; | |||
case 3: // domain name, length + str | |||
int len = _connetionRecvBuffer[1]; | |||
dst_addr = System.Text.Encoding.UTF8.GetString(_connetionRecvBuffer, 2, len); | |||
dst_port = (_connetionRecvBuffer[len + 2] << 8) + _connetionRecvBuffer[len + 3]; | |||
break; | |||
case 4: // IPv6 address, 16 bytes | |||
dst_addr = $"[{new IPAddress(_connetionRecvBuffer.Skip(1).Take(16).ToArray())}]"; | |||
dst_port = (_connetionRecvBuffer[17] << 8) + _connetionRecvBuffer[18]; | |||
break; | |||
} | |||
if (_config.isVerboseLogging) | |||
{ | |||
Logging.Info($"connect to {dst_addr}:{dst_port}"); | |||
} | |||
_destEndPoint = SocketUtil.GetEndPoint(dst_addr, dst_port); | |||
if (_command == 1) | |||
{ | |||
byte[] response = {5, 0, 0, 1, 0, 0, 0, 0, 0, 0}; | |||
_connection.BeginSend(response, 0, response.Length, SocketFlags.None, | |||
new AsyncCallback(ResponseCallback), null); | |||
} | |||
else if (_command == 3) | |||
{ | |||
HandleUDPAssociate(); | |||
} | |||
} | |||
else | |||
{ | |||
Logging.Debug("failed to recv data in Shadowsocks.Controller.TCPHandler.OnAddressFullyRead()"); | |||
Close(); | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
Logging.LogUsefulException(e); | |||
Close(); | |||
} | |||
} | |||
private void HandleUDPAssociate() | |||
{ | |||
IPEndPoint endPoint = (IPEndPoint)_connection.LocalEndPoint; | |||
byte[] address = endPoint.Address.GetAddressBytes(); | |||
int port = endPoint.Port; | |||
byte[] response = new byte[4 + address.Length + 2]; | |||
response[0] = 5; | |||
switch (endPoint.AddressFamily) | |||
{ | |||
case AddressFamily.InterNetwork: | |||
response[3] = 1; | |||
break; | |||
case AddressFamily.InterNetworkV6: | |||
response[3] = 4; | |||
break; | |||
} | |||
address.CopyTo(response, 4); | |||
response[response.Length - 1] = (byte)(port & 0xFF); | |||
response[response.Length - 2] = (byte)((port >> 8) & 0xFF); | |||
_connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(ReadAll), true); | |||
} | |||
private void ReadAll(IAsyncResult ar) | |||
{ | |||
if (_closed) return; | |||
try | |||
{ | |||
if (ar.AsyncState != null) | |||
{ | |||
_connection.EndSend(ar); | |||
_connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null); | |||
} | |||
else | |||
{ | |||
int bytesRead = _connection.EndReceive(ar); | |||
if (bytesRead > 0) | |||
{ | |||
_connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null); | |||
} | |||
else | |||
Close(); | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
Logging.LogUsefulException(e); | |||
Close(); | |||
} | |||
} | |||
private void ResponseCallback(IAsyncResult ar) | |||
{ | |||
try | |||
{ | |||
_connection.EndSend(ar); | |||
StartConnect(); | |||
} | |||
catch (Exception e) | |||
{ | |||
Logging.LogUsefulException(e); | |||
Close(); | |||
} | |||
} | |||
// inner class | |||
private class ProxyTimer : Timer | |||
{ | |||
@@ -291,20 +514,18 @@ namespace Shadowsocks.Controller.Service | |||
public ServerTimer(int p) : base(p) { } | |||
} | |||
protected void StartConnect(EndPoint target) | |||
private void StartConnect() | |||
{ | |||
try | |||
{ | |||
_destEndPoint = target; | |||
CreateRemote(); | |||
// Setting up proxy | |||
IForwardProxy remote; | |||
EndPoint proxyEP; | |||
if (Config.proxy.useProxy) | |||
if (_config.proxy.useProxy) | |||
{ | |||
switch (Config.proxy.proxyType) | |||
switch (_config.proxy.proxyType) | |||
{ | |||
case ProxyConfig.PROXY_SOCKS5: | |||
remote = new Socks5Proxy(); | |||
@@ -315,7 +536,7 @@ namespace Shadowsocks.Controller.Service | |||
default: | |||
throw new NotSupportedException("Unknown forward proxy."); | |||
} | |||
proxyEP = SocketUtil.GetEndPoint(Config.proxy.proxyServer, Config.proxy.proxyPort); | |||
proxyEP = SocketUtil.GetEndPoint(_config.proxy.proxyServer, _config.proxy.proxyPort); | |||
} | |||
else | |||
{ | |||
@@ -347,7 +568,7 @@ namespace Shadowsocks.Controller.Service | |||
_proxyConnected = false; | |||
// Connect to the proxy server. | |||
remote.BeginConnectProxy(proxyEP, ProxyConnectCallback, new AsyncSession<ProxyTimer>(remote, proxyTimer)); | |||
remote.BeginConnectProxy(proxyEP, new AsyncCallback(ProxyConnectCallback), new AsyncSession<ProxyTimer>(remote, proxyTimer)); | |||
} | |||
catch (Exception e) | |||
{ | |||
@@ -356,16 +577,15 @@ namespace Shadowsocks.Controller.Service | |||
} | |||
} | |||
private void proxyConnectTimer_Elapsed(object sender, ElapsedEventArgs e) | |||
{ | |||
var timer = (ProxyTimer)sender; | |||
var timer = (ProxyTimer) sender; | |||
timer.Elapsed -= proxyConnectTimer_Elapsed; | |||
timer.Enabled = false; | |||
timer.Dispose(); | |||
if (_proxyConnected || _destConnected || Closed) | |||
if (_proxyConnected || _destConnected || _closed) | |||
{ | |||
return; | |||
} | |||
@@ -379,13 +599,13 @@ namespace Shadowsocks.Controller.Service | |||
private void ProxyConnectCallback(IAsyncResult ar) | |||
{ | |||
Server server = null; | |||
if (Closed) | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
var session = (AsyncSession<ProxyTimer>)ar.AsyncState; | |||
var session = (AsyncSession<ProxyTimer>) ar.AsyncState; | |||
ProxyTimer timer = session.State; | |||
var destEndPoint = timer.DestEndPoint; | |||
server = timer.Server; | |||
@@ -400,7 +620,7 @@ namespace Shadowsocks.Controller.Service | |||
_proxyConnected = true; | |||
if (Config.isVerboseLogging) | |||
if (_config.isVerboseLogging) | |||
{ | |||
if (!(remote is DirectConnect)) | |||
{ | |||
@@ -415,10 +635,10 @@ namespace Shadowsocks.Controller.Service | |||
connectTimer.Enabled = true; | |||
connectTimer.Session = session; | |||
connectTimer.Server = server; | |||
_destConnected = false; | |||
// Connect to the remote endpoint. | |||
remote.BeginConnectDest(destEndPoint, ConnectCallback, new AsyncSession<ServerTimer>(session, connectTimer)); | |||
remote.BeginConnectDest(destEndPoint, new AsyncCallback(ConnectCallback), new AsyncSession<ServerTimer>(session, connectTimer)); | |||
} | |||
catch (ArgumentException) | |||
{ | |||
@@ -437,7 +657,7 @@ namespace Shadowsocks.Controller.Service | |||
timer.Enabled = false; | |||
timer.Dispose(); | |||
if (_destConnected || Closed) | |||
if (_destConnected || _closed) | |||
{ | |||
return; | |||
} | |||
@@ -453,10 +673,10 @@ namespace Shadowsocks.Controller.Service | |||
private void ConnectCallback(IAsyncResult ar) | |||
{ | |||
if (Closed) return; | |||
if (_closed) return; | |||
try | |||
{ | |||
var session = (AsyncSession<ServerTimer>)ar.AsyncState; | |||
var session = (AsyncSession<ServerTimer>) ar.AsyncState; | |||
ServerTimer timer = session.State; | |||
_server = timer.Server; | |||
timer.Elapsed -= destConnectTimer_Elapsed; | |||
@@ -466,10 +686,10 @@ namespace Shadowsocks.Controller.Service | |||
var remote = session.Remote; | |||
// Complete the connection. | |||
remote.EndConnectDest(ar); | |||
_destConnected = true; | |||
if (Config.isVerboseLogging) | |||
if (_config.isVerboseLogging) | |||
{ | |||
Logging.Info($"Socket connected to ss server: {_server.FriendlyName()}"); | |||
} | |||
@@ -479,7 +699,7 @@ namespace Shadowsocks.Controller.Service | |||
strategy?.UpdateLatency(_server, latency); | |||
_tcprelay.UpdateLatency(_server, latency); | |||
OnServerConnected(session); | |||
StartPipe(session); | |||
} | |||
catch (ArgumentException) | |||
{ | |||
@@ -496,14 +716,14 @@ namespace Shadowsocks.Controller.Service | |||
} | |||
} | |||
protected void StartPipe(AsyncSession session) | |||
private void StartPipe(AsyncSession session) | |||
{ | |||
if (Closed) return; | |||
if (_closed) return; | |||
try | |||
{ | |||
_startReceivingTime = DateTime.Now; | |||
session.Remote.BeginReceive(RemoteRecvBuffer, 0, RecvSize, SocketFlags.None, PipeRemoteReceiveCallback, session); | |||
Connection.BeginReceive(ConnetionRecvBuffer, 0, RecvSize, SocketFlags.None, PipeConnectionReceiveCallback, session); | |||
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session); | |||
SendToServer(_firstPacketLength, session); | |||
} | |||
catch (Exception e) | |||
{ | |||
@@ -514,10 +734,10 @@ namespace Shadowsocks.Controller.Service | |||
private void PipeRemoteReceiveCallback(IAsyncResult ar) | |||
{ | |||
if (Closed) return; | |||
if (_closed) return; | |||
try | |||
{ | |||
var session = (AsyncSession)ar.AsyncState; | |||
var session = (AsyncSession) ar.AsyncState; | |||
int bytesRead = session.Remote.EndReceive(ar); | |||
_totalRead += bytesRead; | |||
_tcprelay.UpdateInboundCounter(_server, bytesRead); | |||
@@ -527,15 +747,15 @@ namespace Shadowsocks.Controller.Service | |||
int bytesToSend; | |||
lock (_decryptionLock) | |||
{ | |||
_encryptor.Decrypt(RemoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend); | |||
_encryptor.Decrypt(_remoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend); | |||
} | |||
Connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, PipeConnectionSendCallback, session); | |||
_connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeConnectionSendCallback), session); | |||
IStrategy strategy = _controller.GetCurrentStrategy(); | |||
strategy?.UpdateLastRead(_server); | |||
} | |||
else | |||
{ | |||
Connection.Shutdown(SocketShutdown.Send); | |||
_connection.Shutdown(SocketShutdown.Send); | |||
_connectionShutdown = true; | |||
CheckClose(); | |||
} | |||
@@ -549,12 +769,12 @@ namespace Shadowsocks.Controller.Service | |||
private void PipeConnectionReceiveCallback(IAsyncResult ar) | |||
{ | |||
if (Closed) return; | |||
if (_closed) return; | |||
try | |||
{ | |||
int bytesRead = Connection.EndReceive(ar); | |||
int bytesRead = _connection.EndReceive(ar); | |||
var session = (AsyncSession)ar.AsyncState; | |||
var session = (AsyncSession) ar.AsyncState; | |||
var remote = session.Remote; | |||
if (bytesRead > 0) | |||
@@ -577,53 +797,27 @@ namespace Shadowsocks.Controller.Service | |||
private void SendToServer(int length, AsyncSession session) | |||
{ | |||
BeginSendToServer(length, session, PipeRemoteSendCallback); | |||
} | |||
private bool _serverHeaderSent; | |||
protected void BeginSendToServer(int length, AsyncSession session, AsyncCallback callback) | |||
{ | |||
if (!_serverHeaderSent) | |||
{ | |||
_serverHeaderSent = true; | |||
// Append socks5 header | |||
int len = Socks5Util.HeaderAddrLength(_destEndPoint); | |||
Array.Copy(ConnetionRecvBuffer, 0, ConnetionRecvBuffer, len, length); | |||
Socks5Util.FillHeaderAddr(ConnetionRecvBuffer, 0, _destEndPoint); | |||
length += len; | |||
} | |||
_totalWrite += length; | |||
int bytesToSend; | |||
lock (_encryptionLock) | |||
{ | |||
_encryptor.Encrypt(ConnetionRecvBuffer, length, _connetionSendBuffer, out bytesToSend); | |||
_encryptor.Encrypt(_connetionRecvBuffer, length, _connetionSendBuffer, out bytesToSend); | |||
} | |||
_tcprelay.UpdateOutboundCounter(_server, bytesToSend); | |||
_startSendingTime = DateTime.Now; | |||
session.Remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, callback, session); | |||
session.Remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeRemoteSendCallback), session); | |||
IStrategy strategy = _controller.GetCurrentStrategy(); | |||
strategy?.UpdateLastWrite(_server); | |||
} | |||
protected AsyncSession EndSendToServer(IAsyncResult ar) | |||
{ | |||
var session = (AsyncSession)ar.AsyncState; | |||
session.Remote.EndSend(ar); | |||
return session; | |||
} | |||
private void PipeRemoteSendCallback(IAsyncResult ar) | |||
{ | |||
if (Closed) return; | |||
if (_closed) return; | |||
try | |||
{ | |||
var session = EndSendToServer(ar); | |||
Connection.BeginReceive(ConnetionRecvBuffer, 0, RecvSize, SocketFlags.None, PipeConnectionReceiveCallback, session); | |||
var session = (AsyncSession)ar.AsyncState; | |||
session.Remote.EndSend(ar); | |||
_connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback), session); | |||
} | |||
catch (Exception e) | |||
{ | |||
@@ -637,8 +831,8 @@ namespace Shadowsocks.Controller.Service | |||
try | |||
{ | |||
var session = (AsyncSession)ar.AsyncState; | |||
Connection.EndSend(ar); | |||
session.Remote.BeginReceive(RemoteRecvBuffer, 0, RecvSize, SocketFlags.None, PipeRemoteReceiveCallback, session); | |||
_connection.EndSend(ar); | |||
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session); | |||
} | |||
catch (Exception e) | |||
{ | |||
@@ -460,10 +460,10 @@ namespace Shadowsocks.Controller | |||
TCPRelay tcpRelay = new TCPRelay(this, _config); | |||
UDPRelay udpRelay = new UDPRelay(this); | |||
List<Listener.IService> services = new List<Listener.IService>(); | |||
services.Add(_pacServer); | |||
services.Add(tcpRelay); | |||
services.Add(udpRelay); | |||
//services.Add(new Http2Socks5(_config.localPort)); | |||
services.Add(_pacServer); | |||
services.Add(new Http2Socks5(_config.localPort)); | |||
_listener = new Listener(services); | |||
_listener.Start(_config); | |||
} | |||
@@ -73,15 +73,52 @@ namespace Shadowsocks.ForwardProxy | |||
{ | |||
DestEndPoint = destEndPoint; | |||
int len = Socks5Util.HeaderAddrLength(destEndPoint); | |||
byte[] request = null; | |||
byte atyp = 0; | |||
int port; | |||
byte[] request = new byte[len + 3]; | |||
Socks5Util.FillHeaderAddr(request, 3, destEndPoint); | |||
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; | |||
@@ -1,85 +0,0 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Net; | |||
using System.Net.Sockets; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using Shadowsocks.Controller; | |||
namespace Shadowsocks.Util.Sockets | |||
{ | |||
public static class Socks5Util | |||
{ | |||
public static int HeaderAddrLength(EndPoint addrEp) | |||
{ | |||
var dep = addrEp as DnsEndPoint; | |||
if (dep != null) | |||
{ | |||
var enc = Encoding.UTF8; | |||
var hostByteCount = enc.GetByteCount(dep.Host); | |||
return 1 + 1 /*length byte*/+ hostByteCount + 2; | |||
} | |||
switch (addrEp.AddressFamily) | |||
{ | |||
case AddressFamily.InterNetwork: | |||
return 1 + 4 + 2; | |||
case AddressFamily.InterNetworkV6: | |||
return 1 + 16 + 2; | |||
default: | |||
throw new Exception(I18N.GetString("Proxy request failed")); | |||
} | |||
} | |||
public static int FillHeaderAddr(byte[] buffer, int offset, EndPoint addrEp) | |||
{ | |||
byte atyp; | |||
int port; | |||
int len; | |||
var dep = addrEp 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); | |||
len = 1 + 1 /*length byte*/+ hostByteCount + 2; | |||
buffer[offset + 1] = (byte)hostByteCount; | |||
enc.GetBytes(dep.Host, 0, dep.Host.Length, buffer, offset + 2); | |||
port = dep.Port; | |||
} | |||
else | |||
{ | |||
switch (addrEp.AddressFamily) | |||
{ | |||
case AddressFamily.InterNetwork: | |||
len = 1 + 4 + 2; | |||
atyp = 1; // IP V4 address | |||
break; | |||
case AddressFamily.InterNetworkV6: | |||
len = 1 + 16 + 2; | |||
atyp = 4; // IP V6 address | |||
break; | |||
default: | |||
throw new Exception(I18N.GetString("Proxy request failed")); | |||
} | |||
port = ((IPEndPoint)addrEp).Port; | |||
var addr = ((IPEndPoint)addrEp).Address.GetAddressBytes(); | |||
Array.Copy(addr, 0, buffer, offset + 1, len - 1 - 2); | |||
} | |||
buffer[offset] = atyp; | |||
buffer[offset + len - 2] = (byte)((port >> 8) & 0xff); | |||
buffer[offset + len - 1] = (byte)(port & 0xff); | |||
return len; | |||
} | |||
} | |||
} |
@@ -140,9 +140,7 @@ | |||
<Compile Include="3rd\zxing\ResultPoint.cs" /> | |||
<Compile Include="3rd\zxing\ResultPointCallback.cs" /> | |||
<Compile Include="3rd\zxing\WriterException.cs" /> | |||
<Compile Include="Controller\Service\HttpHandler.cs" /> | |||
<Compile Include="Controller\Service\Socks5Handler.cs" /> | |||
<Compile Include="Controller\Service\TCPRelay.cs" /> | |||
<Compile Include="Controller\Service\Http2Socks5.cs" /> | |||
<Compile Include="Controller\System\Hotkeys\HotkeyCallbacks.cs" /> | |||
<Compile Include="Model\HotKeyConfig.cs" /> | |||
<Compile Include="Model\ProxyConfig.cs" /> | |||
@@ -195,7 +193,6 @@ | |||
<Compile Include="Util\Sockets\ByteSearch.cs" /> | |||
<Compile Include="Util\Sockets\LineReader.cs" /> | |||
<Compile Include="Util\Sockets\SocketUtil.cs" /> | |||
<Compile Include="Util\Sockets\Socks5Util.cs" /> | |||
<Compile Include="Util\Sockets\WrappedSocket.cs" /> | |||
<Compile Include="Util\SystemProxy\ProxyException.cs" /> | |||
<Compile Include="Util\SystemProxy\Sysproxy.cs" /> | |||
@@ -207,6 +204,7 @@ | |||
<Compile Include="View\ConfigForm.Designer.cs"> | |||
<DependentUpon>ConfigForm.cs</DependentUpon> | |||
</Compile> | |||
<Compile Include="Controller\Service\TCPRelay.cs" /> | |||
<Compile Include="Program.cs" /> | |||
<Compile Include="Properties\AssemblyInfo.cs" /> | |||
<Compile Include="Controller\ShadowsocksController.cs" /> | |||