@@ -1,487 +0,0 @@ | |||||
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 | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,264 @@ | |||||
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 | |||||
} | |||||
} |
@@ -0,0 +1,293 @@ | |||||
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,17 +1,15 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Linq; | |||||
using System.Net; | using System.Net; | ||||
using System.Net.Sockets; | using System.Net.Sockets; | ||||
using System.Timers; | using System.Timers; | ||||
using Shadowsocks.Controller.Strategy; | using Shadowsocks.Controller.Strategy; | ||||
using Shadowsocks.Encryption; | using Shadowsocks.Encryption; | ||||
using Shadowsocks.ForwardProxy; | using Shadowsocks.ForwardProxy; | ||||
using Shadowsocks.Model; | using Shadowsocks.Model; | ||||
using Shadowsocks.Util.Sockets; | using Shadowsocks.Util.Sockets; | ||||
namespace Shadowsocks.Controller | |||||
namespace Shadowsocks.Controller.Service | |||||
{ | { | ||||
class TCPRelay : Listener.Service | class TCPRelay : Listener.Service | ||||
{ | { | ||||
@@ -19,23 +17,39 @@ namespace Shadowsocks.Controller | |||||
private DateTime _lastSweepTime; | private DateTime _lastSweepTime; | ||||
private Configuration _config; | private Configuration _config; | ||||
public ISet<TCPHandler> Handlers { get; set; } | |||||
private readonly List<ITCPHandlerFactory> _factories = new List<ITCPHandlerFactory>(); | |||||
public ISet<TCPHandler> Handlers { get; } = new HashSet<TCPHandler>(); | |||||
public TCPRelay(ShadowsocksController controller, Configuration conf) | public TCPRelay(ShadowsocksController controller, Configuration conf) | ||||
{ | { | ||||
_controller = controller; | _controller = controller; | ||||
_config = conf; | _config = conf; | ||||
Handlers = new HashSet<TCPHandler>(); | |||||
_lastSweepTime = DateTime.Now; | _lastSweepTime = DateTime.Now; | ||||
_factories.Add(new Socks5HandlerFactory()); | |||||
_factories.Add(new HttpHandlerHandlerFactory()); | |||||
} | } | ||||
public override bool Handle(byte[] firstPacket, int length, Socket socket, object state) | public override bool Handle(byte[] firstPacket, int length, Socket socket, object state) | ||||
{ | { | ||||
if (socket.ProtocolType != ProtocolType.Tcp | |||||
|| (length < 2 || firstPacket[0] != 5)) | |||||
TCPHandler handler = null; | |||||
foreach (var factory in _factories) | |||||
{ | |||||
if (factory.CanHandle(firstPacket, length)) | |||||
{ | |||||
handler = factory.NewHandler(_controller, _config, this, socket); | |||||
break; | |||||
} | |||||
} | |||||
if (handler == null) | |||||
{ | |||||
return false; | return false; | ||||
} | |||||
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); | socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); | ||||
TCPHandler handler = new TCPHandler(_controller, _config, this, socket); | |||||
IList<TCPHandler> handlersToClose = new List<TCPHandler>(); | IList<TCPHandler> handlersToClose = new List<TCPHandler>(); | ||||
lock (Handlers) | lock (Handlers) | ||||
@@ -62,7 +76,7 @@ namespace Shadowsocks.Controller | |||||
* Then the handler will never release until the next Handle call. Sometimes it will | * Then the handler will never release until the next Handle call. Sometimes it will | ||||
* cause odd problems (especially during memory profiling). | * cause odd problems (especially during memory profiling). | ||||
*/ | */ | ||||
handler.Start(firstPacket, length); | |||||
handler.StartHandshake(firstPacket, length); | |||||
return true; | return true; | ||||
} | } | ||||
@@ -74,7 +88,7 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
handlersToClose.AddRange(Handlers); | handlersToClose.AddRange(Handlers); | ||||
} | } | ||||
handlersToClose.ForEach(h=>h.Close()); | |||||
handlersToClose.ForEach(h => h.Close()); | |||||
} | } | ||||
public void UpdateInboundCounter(Server server, long n) | public void UpdateInboundCounter(Server server, long n) | ||||
@@ -93,9 +107,21 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
} | } | ||||
class TCPHandler | |||||
interface ITCPHandlerFactory | |||||
{ | |||||
bool CanHandle(byte[] firstPacket, int length); | |||||
TCPHandler NewHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket); | |||||
} | |||||
abstract class TCPHandler | |||||
{ | { | ||||
class AsyncSession | |||||
public abstract void StartHandshake(byte[] firstPacket, int length); | |||||
protected abstract void OnServerConnected(AsyncSession session); | |||||
protected class AsyncSession | |||||
{ | { | ||||
public IForwardProxy Remote { get; } | public IForwardProxy Remote { get; } | ||||
@@ -105,7 +131,7 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
} | } | ||||
class AsyncSession<T> : AsyncSession | |||||
protected class AsyncSession<T> : AsyncSession | |||||
{ | { | ||||
public T State { get; set; } | public T State { get; set; } | ||||
@@ -114,7 +140,7 @@ namespace Shadowsocks.Controller | |||||
State = state; | State = state; | ||||
} | } | ||||
public AsyncSession(AsyncSession session, T state): base(session.Remote) | |||||
public AsyncSession(AsyncSession session, T state) : base(session.Remote) | |||||
{ | { | ||||
State = state; | State = state; | ||||
} | } | ||||
@@ -130,39 +156,33 @@ namespace Shadowsocks.Controller | |||||
public DateTime lastActivity; | public DateTime lastActivity; | ||||
private ShadowsocksController _controller; | |||||
private Configuration _config; | |||||
private TCPRelay _tcprelay; | |||||
private Socket _connection; | |||||
private IEncryptor _encryptor; | |||||
private Server _server; | |||||
private ShadowsocksController _controller; | |||||
protected Configuration Config { get; } | |||||
private TCPRelay _tcprelay; | |||||
protected Socket Connection { get; } | |||||
private Server _server; | |||||
private AsyncSession _currentRemoteSession; | private AsyncSession _currentRemoteSession; | ||||
private bool _proxyConnected; | |||||
private bool _destConnected; | |||||
private bool _proxyConnected; | |||||
private bool _destConnected; | |||||
private byte _command; | |||||
private byte[] _firstPacket; | |||||
private int _firstPacketLength; | |||||
private int _totalRead = 0; | |||||
private int _totalWrite = 0; | |||||
private int _totalRead = 0; | |||||
private int _totalWrite = 0; | |||||
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 byte[] _remoteRecvBuffer = new byte[BufferSize]; | |||||
private byte[] _remoteSendBuffer = new byte[BufferSize]; | |||||
private byte[] _connetionRecvBuffer = new byte[BufferSize]; | |||||
private byte[] _connetionSendBuffer = new byte[BufferSize]; | |||||
private IEncryptor _encryptor; | |||||
private readonly object _encryptionLock = new object(); | |||||
private readonly object _decryptionLock = 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 bool _connectionShutdown = false; | |||||
private bool _remoteShutdown = false; | |||||
protected bool Closed { get; private set; }= false; | |||||
private readonly object _closeConnLock = new object(); | |||||
private DateTime _startConnectTime; | private DateTime _startConnectTime; | ||||
private DateTime _startReceivingTime; | private DateTime _startReceivingTime; | ||||
@@ -170,21 +190,24 @@ namespace Shadowsocks.Controller | |||||
private EndPoint _destEndPoint = null; | private EndPoint _destEndPoint = null; | ||||
public TCPHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket) | |||||
public TCPHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket, bool autoAppendHeader = true) | |||||
{ | { | ||||
_controller = controller; | _controller = controller; | ||||
_config = config; | |||||
Config = config; | |||||
_tcprelay = tcprelay; | _tcprelay = tcprelay; | ||||
_connection = socket; | |||||
Connection = socket; | |||||
_proxyTimeout = config.proxy.proxyTimeout * 1000; | _proxyTimeout = config.proxy.proxyTimeout * 1000; | ||||
_serverTimeout = config.GetCurrentServer().timeout * 1000; | _serverTimeout = config.GetCurrentServer().timeout * 1000; | ||||
lastActivity = DateTime.Now; | lastActivity = DateTime.Now; | ||||
_serverHeaderSent = !autoAppendHeader; | |||||
} | } | ||||
public void CreateRemote() | |||||
private 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 == "") | if (server == null || server.server == "") | ||||
throw new ArgumentException("No server configured"); | throw new ArgumentException("No server configured"); | ||||
lock (_encryptionLock) | lock (_encryptionLock) | ||||
@@ -197,13 +220,6 @@ namespace Shadowsocks.Controller | |||||
this._server = server; | this._server = server; | ||||
} | } | ||||
public void Start(byte[] firstPacket, int length) | |||||
{ | |||||
_firstPacket = firstPacket; | |||||
_firstPacketLength = length; | |||||
HandshakeReceive(); | |||||
} | |||||
private void CheckClose() | private void CheckClose() | ||||
{ | { | ||||
if (_connectionShutdown && _remoteShutdown) | if (_connectionShutdown && _remoteShutdown) | ||||
@@ -214,8 +230,8 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
lock (_closeConnLock) | lock (_closeConnLock) | ||||
{ | { | ||||
if (_closed) return; | |||||
_closed = true; | |||||
if (Closed) return; | |||||
Closed = true; | |||||
} | } | ||||
lock (_tcprelay.Handlers) | lock (_tcprelay.Handlers) | ||||
{ | { | ||||
@@ -223,8 +239,8 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
try | try | ||||
{ | { | ||||
_connection.Shutdown(SocketShutdown.Both); | |||||
_connection.Close(); | |||||
Connection.Shutdown(SocketShutdown.Both); | |||||
Connection.Close(); | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
@@ -249,245 +265,6 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
} | } | ||||
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 | // inner class | ||||
private class ProxyTimer : Timer | private class ProxyTimer : Timer | ||||
{ | { | ||||
@@ -509,18 +286,20 @@ namespace Shadowsocks.Controller | |||||
public ServerTimer(int p) : base(p) { } | public ServerTimer(int p) : base(p) { } | ||||
} | } | ||||
private void StartConnect() | |||||
protected void StartConnect(EndPoint target) | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
_destEndPoint = target; | |||||
CreateRemote(); | CreateRemote(); | ||||
// Setting up proxy | // Setting up proxy | ||||
IForwardProxy remote; | IForwardProxy remote; | ||||
EndPoint proxyEP; | EndPoint proxyEP; | ||||
if (_config.proxy.useProxy) | |||||
if (Config.proxy.useProxy) | |||||
{ | { | ||||
switch (_config.proxy.proxyType) | |||||
switch (Config.proxy.proxyType) | |||||
{ | { | ||||
case ProxyConfig.PROXY_SOCKS5: | case ProxyConfig.PROXY_SOCKS5: | ||||
remote = new Socks5Proxy(); | remote = new Socks5Proxy(); | ||||
@@ -531,7 +310,7 @@ namespace Shadowsocks.Controller | |||||
default: | default: | ||||
throw new NotSupportedException("Unknown forward proxy."); | 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 | else | ||||
{ | { | ||||
@@ -554,7 +333,7 @@ namespace Shadowsocks.Controller | |||||
_proxyConnected = false; | _proxyConnected = false; | ||||
// Connect to the proxy server. | // Connect to the proxy server. | ||||
remote.BeginConnectProxy(proxyEP, new AsyncCallback(ProxyConnectCallback), new AsyncSession<ProxyTimer>(remote, proxyTimer)); | |||||
remote.BeginConnectProxy(proxyEP, ProxyConnectCallback, new AsyncSession<ProxyTimer>(remote, proxyTimer)); | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
@@ -563,15 +342,16 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
} | } | ||||
private void proxyConnectTimer_Elapsed(object sender, ElapsedEventArgs e) | private void proxyConnectTimer_Elapsed(object sender, ElapsedEventArgs e) | ||||
{ | { | ||||
var timer = (ProxyTimer) sender; | |||||
var timer = (ProxyTimer)sender; | |||||
timer.Elapsed -= proxyConnectTimer_Elapsed; | timer.Elapsed -= proxyConnectTimer_Elapsed; | ||||
timer.Enabled = false; | timer.Enabled = false; | ||||
timer.Dispose(); | timer.Dispose(); | ||||
if (_proxyConnected || _destConnected || _closed) | |||||
if (_proxyConnected || _destConnected || Closed) | |||||
{ | { | ||||
return; | return; | ||||
} | } | ||||
@@ -585,13 +365,13 @@ namespace Shadowsocks.Controller | |||||
private void ProxyConnectCallback(IAsyncResult ar) | private void ProxyConnectCallback(IAsyncResult ar) | ||||
{ | { | ||||
Server server = null; | Server server = null; | ||||
if (_closed) | |||||
if (Closed) | |||||
{ | { | ||||
return; | return; | ||||
} | } | ||||
try | try | ||||
{ | { | ||||
var session = (AsyncSession<ProxyTimer>) ar.AsyncState; | |||||
var session = (AsyncSession<ProxyTimer>)ar.AsyncState; | |||||
ProxyTimer timer = session.State; | ProxyTimer timer = session.State; | ||||
var destEndPoint = timer.DestEndPoint; | var destEndPoint = timer.DestEndPoint; | ||||
server = timer.Server; | server = timer.Server; | ||||
@@ -606,7 +386,7 @@ namespace Shadowsocks.Controller | |||||
_proxyConnected = true; | _proxyConnected = true; | ||||
if (_config.isVerboseLogging) | |||||
if (Config.isVerboseLogging) | |||||
{ | { | ||||
if (!(remote is DirectConnect)) | if (!(remote is DirectConnect)) | ||||
{ | { | ||||
@@ -621,10 +401,10 @@ namespace Shadowsocks.Controller | |||||
connectTimer.Enabled = true; | connectTimer.Enabled = true; | ||||
connectTimer.Session = session; | connectTimer.Session = session; | ||||
connectTimer.Server = server; | connectTimer.Server = server; | ||||
_destConnected = false; | _destConnected = false; | ||||
// Connect to the remote endpoint. | // Connect to the remote endpoint. | ||||
remote.BeginConnectDest(destEndPoint, new AsyncCallback(ConnectCallback), new AsyncSession<ServerTimer>(session, connectTimer)); | |||||
remote.BeginConnectDest(destEndPoint, ConnectCallback, new AsyncSession<ServerTimer>(session, connectTimer)); | |||||
} | } | ||||
catch (ArgumentException) | catch (ArgumentException) | ||||
{ | { | ||||
@@ -643,7 +423,7 @@ namespace Shadowsocks.Controller | |||||
timer.Enabled = false; | timer.Enabled = false; | ||||
timer.Dispose(); | timer.Dispose(); | ||||
if (_destConnected || _closed) | |||||
if (_destConnected || Closed) | |||||
{ | { | ||||
return; | return; | ||||
} | } | ||||
@@ -659,10 +439,10 @@ namespace Shadowsocks.Controller | |||||
private void ConnectCallback(IAsyncResult ar) | private void ConnectCallback(IAsyncResult ar) | ||||
{ | { | ||||
if (_closed) return; | |||||
if (Closed) return; | |||||
try | try | ||||
{ | { | ||||
var session = (AsyncSession<ServerTimer>) ar.AsyncState; | |||||
var session = (AsyncSession<ServerTimer>)ar.AsyncState; | |||||
ServerTimer timer = session.State; | ServerTimer timer = session.State; | ||||
_server = timer.Server; | _server = timer.Server; | ||||
timer.Elapsed -= destConnectTimer_Elapsed; | timer.Elapsed -= destConnectTimer_Elapsed; | ||||
@@ -672,10 +452,10 @@ namespace Shadowsocks.Controller | |||||
var remote = session.Remote; | var remote = session.Remote; | ||||
// Complete the connection. | // Complete the connection. | ||||
remote.EndConnectDest(ar); | remote.EndConnectDest(ar); | ||||
_destConnected = true; | _destConnected = true; | ||||
if (_config.isVerboseLogging) | |||||
if (Config.isVerboseLogging) | |||||
{ | { | ||||
Logging.Info($"Socket connected to ss server: {_server.FriendlyName()}"); | Logging.Info($"Socket connected to ss server: {_server.FriendlyName()}"); | ||||
} | } | ||||
@@ -685,7 +465,7 @@ namespace Shadowsocks.Controller | |||||
strategy?.UpdateLatency(_server, latency); | strategy?.UpdateLatency(_server, latency); | ||||
_tcprelay.UpdateLatency(_server, latency); | _tcprelay.UpdateLatency(_server, latency); | ||||
StartPipe(session); | |||||
OnServerConnected(session); | |||||
} | } | ||||
catch (ArgumentException) | catch (ArgumentException) | ||||
{ | { | ||||
@@ -702,14 +482,14 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
} | } | ||||
private void StartPipe(AsyncSession session) | |||||
protected void StartPipe(AsyncSession session) | |||||
{ | { | ||||
if (_closed) return; | |||||
if (Closed) return; | |||||
try | try | ||||
{ | { | ||||
_startReceivingTime = DateTime.Now; | _startReceivingTime = DateTime.Now; | ||||
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session); | |||||
SendToServer(_firstPacketLength, session); | |||||
session.Remote.BeginReceive(RemoteRecvBuffer, 0, RecvSize, SocketFlags.None, PipeRemoteReceiveCallback, session); | |||||
Connection.BeginReceive(ConnetionRecvBuffer, 0, RecvSize, SocketFlags.None, PipeConnectionReceiveCallback, session); | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
@@ -720,10 +500,10 @@ namespace Shadowsocks.Controller | |||||
private void PipeRemoteReceiveCallback(IAsyncResult ar) | private void PipeRemoteReceiveCallback(IAsyncResult ar) | ||||
{ | { | ||||
if (_closed) return; | |||||
if (Closed) return; | |||||
try | try | ||||
{ | { | ||||
var session = (AsyncSession) ar.AsyncState; | |||||
var session = (AsyncSession)ar.AsyncState; | |||||
int bytesRead = session.Remote.EndReceive(ar); | int bytesRead = session.Remote.EndReceive(ar); | ||||
_totalRead += bytesRead; | _totalRead += bytesRead; | ||||
_tcprelay.UpdateInboundCounter(_server, bytesRead); | _tcprelay.UpdateInboundCounter(_server, bytesRead); | ||||
@@ -733,15 +513,15 @@ namespace Shadowsocks.Controller | |||||
int bytesToSend; | int bytesToSend; | ||||
lock (_decryptionLock) | lock (_decryptionLock) | ||||
{ | { | ||||
_encryptor.Decrypt(_remoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend); | |||||
_encryptor.Decrypt(RemoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend); | |||||
} | } | ||||
_connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeConnectionSendCallback), session); | |||||
Connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, PipeConnectionSendCallback, session); | |||||
IStrategy strategy = _controller.GetCurrentStrategy(); | IStrategy strategy = _controller.GetCurrentStrategy(); | ||||
strategy?.UpdateLastRead(_server); | strategy?.UpdateLastRead(_server); | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
_connection.Shutdown(SocketShutdown.Send); | |||||
Connection.Shutdown(SocketShutdown.Send); | |||||
_connectionShutdown = true; | _connectionShutdown = true; | ||||
CheckClose(); | CheckClose(); | ||||
} | } | ||||
@@ -755,12 +535,12 @@ namespace Shadowsocks.Controller | |||||
private void PipeConnectionReceiveCallback(IAsyncResult ar) | private void PipeConnectionReceiveCallback(IAsyncResult ar) | ||||
{ | { | ||||
if (_closed) return; | |||||
if (Closed) return; | |||||
try | 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; | var remote = session.Remote; | ||||
if (bytesRead > 0) | if (bytesRead > 0) | ||||
@@ -783,27 +563,53 @@ namespace Shadowsocks.Controller | |||||
private void SendToServer(int length, AsyncSession session) | 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; | _totalWrite += length; | ||||
int bytesToSend; | int bytesToSend; | ||||
lock (_encryptionLock) | lock (_encryptionLock) | ||||
{ | { | ||||
_encryptor.Encrypt(_connetionRecvBuffer, length, _connetionSendBuffer, out bytesToSend); | |||||
_encryptor.Encrypt(ConnetionRecvBuffer, length, _connetionSendBuffer, out bytesToSend); | |||||
} | } | ||||
_tcprelay.UpdateOutboundCounter(_server, bytesToSend); | _tcprelay.UpdateOutboundCounter(_server, bytesToSend); | ||||
_startSendingTime = DateTime.Now; | _startSendingTime = DateTime.Now; | ||||
session.Remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeRemoteSendCallback), session); | |||||
session.Remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, callback, session); | |||||
IStrategy strategy = _controller.GetCurrentStrategy(); | IStrategy strategy = _controller.GetCurrentStrategy(); | ||||
strategy?.UpdateLastWrite(_server); | strategy?.UpdateLastWrite(_server); | ||||
} | } | ||||
protected AsyncSession EndSendToServer(IAsyncResult ar) | |||||
{ | |||||
var session = (AsyncSession)ar.AsyncState; | |||||
session.Remote.EndSend(ar); | |||||
return session; | |||||
} | |||||
private void PipeRemoteSendCallback(IAsyncResult ar) | private void PipeRemoteSendCallback(IAsyncResult ar) | ||||
{ | { | ||||
if (_closed) return; | |||||
if (Closed) return; | |||||
try | try | ||||
{ | { | ||||
var session = (AsyncSession)ar.AsyncState; | |||||
session.Remote.EndSend(ar); | |||||
_connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback), session); | |||||
var session = EndSendToServer(ar); | |||||
Connection.BeginReceive(ConnetionRecvBuffer, 0, RecvSize, SocketFlags.None, PipeConnectionReceiveCallback, session); | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
@@ -817,8 +623,8 @@ namespace Shadowsocks.Controller | |||||
try | try | ||||
{ | { | ||||
var session = (AsyncSession)ar.AsyncState; | var session = (AsyncSession)ar.AsyncState; | ||||
_connection.EndSend(ar); | |||||
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session); | |||||
Connection.EndSend(ar); | |||||
session.Remote.BeginReceive(RemoteRecvBuffer, 0, RecvSize, SocketFlags.None, PipeRemoteReceiveCallback, session); | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
@@ -459,10 +459,10 @@ namespace Shadowsocks.Controller | |||||
TCPRelay tcpRelay = new TCPRelay(this, _config); | TCPRelay tcpRelay = new TCPRelay(this, _config); | ||||
UDPRelay udpRelay = new UDPRelay(this); | UDPRelay udpRelay = new UDPRelay(this); | ||||
List<Listener.IService> services = new List<Listener.IService>(); | List<Listener.IService> services = new List<Listener.IService>(); | ||||
services.Add(_pacServer); | |||||
services.Add(tcpRelay); | services.Add(tcpRelay); | ||||
services.Add(udpRelay); | services.Add(udpRelay); | ||||
services.Add(_pacServer); | |||||
services.Add(new Http2Socks5(_config.localPort)); | |||||
//services.Add(new Http2Socks5(_config.localPort)); | |||||
_listener = new Listener(services); | _listener = new Listener(services); | ||||
_listener.Start(_config); | _listener.Start(_config); | ||||
} | } | ||||
@@ -73,52 +73,15 @@ namespace Shadowsocks.ForwardProxy | |||||
{ | { | ||||
DestEndPoint = destEndPoint; | DestEndPoint = destEndPoint; | ||||
byte[] request = null; | |||||
byte atyp = 0; | |||||
int port; | |||||
int len = Socks5Util.HeaderAddrLength(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); | |||||
} | |||||
byte[] request = new byte[len + 3]; | |||||
Socks5Util.FillHeaderAddr(request, 3, destEndPoint); | |||||
// 构造request包剩余部分 | // 构造request包剩余部分 | ||||
request[0] = 5; | request[0] = 5; | ||||
request[1] = 1; | request[1] = 1; | ||||
request[2] = 0; | 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(); | var st = new Socks5State(); | ||||
st.Callback = callback; | st.Callback = callback; | ||||
@@ -0,0 +1,85 @@ | |||||
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,7 +140,9 @@ | |||||
<Compile Include="3rd\zxing\ResultPoint.cs" /> | <Compile Include="3rd\zxing\ResultPoint.cs" /> | ||||
<Compile Include="3rd\zxing\ResultPointCallback.cs" /> | <Compile Include="3rd\zxing\ResultPointCallback.cs" /> | ||||
<Compile Include="3rd\zxing\WriterException.cs" /> | <Compile Include="3rd\zxing\WriterException.cs" /> | ||||
<Compile Include="Controller\Service\Http2Socks5.cs" /> | |||||
<Compile Include="Controller\Service\HttpHandler.cs" /> | |||||
<Compile Include="Controller\Service\Socks5Handler.cs" /> | |||||
<Compile Include="Controller\Service\TCPRelay.cs" /> | |||||
<Compile Include="Controller\System\Hotkeys\HotkeyCallbacks.cs" /> | <Compile Include="Controller\System\Hotkeys\HotkeyCallbacks.cs" /> | ||||
<Compile Include="Model\HotKeyConfig.cs" /> | <Compile Include="Model\HotKeyConfig.cs" /> | ||||
<Compile Include="Model\ProxyConfig.cs" /> | <Compile Include="Model\ProxyConfig.cs" /> | ||||
@@ -193,6 +195,7 @@ | |||||
<Compile Include="Util\Sockets\ByteSearch.cs" /> | <Compile Include="Util\Sockets\ByteSearch.cs" /> | ||||
<Compile Include="Util\Sockets\LineReader.cs" /> | <Compile Include="Util\Sockets\LineReader.cs" /> | ||||
<Compile Include="Util\Sockets\SocketUtil.cs" /> | <Compile Include="Util\Sockets\SocketUtil.cs" /> | ||||
<Compile Include="Util\Sockets\Socks5Util.cs" /> | |||||
<Compile Include="Util\Sockets\WrappedSocket.cs" /> | <Compile Include="Util\Sockets\WrappedSocket.cs" /> | ||||
<Compile Include="Util\SystemProxy\INTERNET_OPTION.cs" /> | <Compile Include="Util\SystemProxy\INTERNET_OPTION.cs" /> | ||||
<Compile Include="Util\SystemProxy\INTERNET_PER_CONN_OPTION.cs" /> | <Compile Include="Util\SystemProxy\INTERNET_PER_CONN_OPTION.cs" /> | ||||
@@ -209,7 +212,6 @@ | |||||
<Compile Include="View\ConfigForm.Designer.cs"> | <Compile Include="View\ConfigForm.Designer.cs"> | ||||
<DependentUpon>ConfigForm.cs</DependentUpon> | <DependentUpon>ConfigForm.cs</DependentUpon> | ||||
</Compile> | </Compile> | ||||
<Compile Include="Controller\Service\TCPRelay.cs" /> | |||||
<Compile Include="Program.cs" /> | <Compile Include="Program.cs" /> | ||||
<Compile Include="Properties\AssemblyInfo.cs" /> | <Compile Include="Properties\AssemblyInfo.cs" /> | ||||
<Compile Include="Controller\ShadowsocksController.cs" /> | <Compile Include="Controller\ShadowsocksController.cs" /> | ||||