http -> shadowsocks protocol directly
No more http->socks5 transform.
This commit is contained in:
@@ -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.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
|
||||
namespace Shadowsocks.Controller.Service
|
||||
{
|
||||
class TCPRelay : Listener.Service
|
||||
{
|
||||
@@ -19,23 +17,39 @@ namespace Shadowsocks.Controller
|
||||
private DateTime _lastSweepTime;
|
||||
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)
|
||||
{
|
||||
_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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
|
||||
TCPHandler handler = new TCPHandler(_controller, _config, this, socket);
|
||||
|
||||
IList<TCPHandler> handlersToClose = new List<TCPHandler>();
|
||||
lock (Handlers)
|
||||
@@ -62,7 +76,7 @@ namespace Shadowsocks.Controller
|
||||
* Then the handler will never release until the next Handle call. Sometimes it will
|
||||
* cause odd problems (especially during memory profiling).
|
||||
*/
|
||||
handler.Start(firstPacket, length);
|
||||
handler.StartHandshake(firstPacket, length);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -74,7 +88,7 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
handlersToClose.AddRange(Handlers);
|
||||
}
|
||||
handlersToClose.ForEach(h=>h.Close());
|
||||
handlersToClose.ForEach(h => h.Close());
|
||||
}
|
||||
|
||||
public void UpdateInboundCounter(Server server, long n)
|
||||
@@ -93,9 +107,21 @@ namespace Shadowsocks.Controller
|
||||
}
|
||||
}
|
||||
|
||||
class TCPHandler
|
||||
interface ITCPHandlerFactory
|
||||
{
|
||||
class AsyncSession
|
||||
bool CanHandle(byte[] firstPacket, int length);
|
||||
|
||||
TCPHandler NewHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket);
|
||||
}
|
||||
|
||||
abstract class TCPHandler
|
||||
{
|
||||
public abstract void StartHandshake(byte[] firstPacket, int length);
|
||||
|
||||
protected abstract void OnServerConnected(AsyncSession session);
|
||||
|
||||
|
||||
protected class AsyncSession
|
||||
{
|
||||
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; }
|
||||
|
||||
@@ -114,7 +140,7 @@ namespace Shadowsocks.Controller
|
||||
State = state;
|
||||
}
|
||||
|
||||
public AsyncSession(AsyncSession session, T state): base(session.Remote)
|
||||
public AsyncSession(AsyncSession session, T state) : base(session.Remote)
|
||||
{
|
||||
State = state;
|
||||
}
|
||||
@@ -130,39 +156,33 @@ namespace Shadowsocks.Controller
|
||||
|
||||
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 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 _startReceivingTime;
|
||||
@@ -170,21 +190,24 @@ namespace Shadowsocks.Controller
|
||||
|
||||
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;
|
||||
_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;
|
||||
}
|
||||
|
||||
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 == "")
|
||||
throw new ArgumentException("No server configured");
|
||||
lock (_encryptionLock)
|
||||
@@ -197,13 +220,6 @@ namespace Shadowsocks.Controller
|
||||
this._server = server;
|
||||
}
|
||||
|
||||
public void Start(byte[] firstPacket, int length)
|
||||
{
|
||||
_firstPacket = firstPacket;
|
||||
_firstPacketLength = length;
|
||||
HandshakeReceive();
|
||||
}
|
||||
|
||||
private void CheckClose()
|
||||
{
|
||||
if (_connectionShutdown && _remoteShutdown)
|
||||
@@ -214,8 +230,8 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
lock (_closeConnLock)
|
||||
{
|
||||
if (_closed) return;
|
||||
_closed = true;
|
||||
if (Closed) return;
|
||||
Closed = true;
|
||||
}
|
||||
lock (_tcprelay.Handlers)
|
||||
{
|
||||
@@ -223,8 +239,8 @@ namespace Shadowsocks.Controller
|
||||
}
|
||||
try
|
||||
{
|
||||
_connection.Shutdown(SocketShutdown.Both);
|
||||
_connection.Close();
|
||||
Connection.Shutdown(SocketShutdown.Both);
|
||||
Connection.Close();
|
||||
}
|
||||
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
|
||||
private class ProxyTimer : Timer
|
||||
{
|
||||
@@ -509,18 +286,20 @@ namespace Shadowsocks.Controller
|
||||
public ServerTimer(int p) : base(p) { }
|
||||
}
|
||||
|
||||
private void StartConnect()
|
||||
protected void StartConnect(EndPoint target)
|
||||
{
|
||||
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();
|
||||
@@ -531,7 +310,7 @@ namespace Shadowsocks.Controller
|
||||
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
|
||||
{
|
||||
@@ -554,7 +333,7 @@ namespace Shadowsocks.Controller
|
||||
_proxyConnected = false;
|
||||
|
||||
// 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)
|
||||
{
|
||||
@@ -563,15 +342,16 @@ namespace Shadowsocks.Controller
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -585,13 +365,13 @@ namespace Shadowsocks.Controller
|
||||
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;
|
||||
@@ -606,7 +386,7 @@ namespace Shadowsocks.Controller
|
||||
|
||||
_proxyConnected = true;
|
||||
|
||||
if (_config.isVerboseLogging)
|
||||
if (Config.isVerboseLogging)
|
||||
{
|
||||
if (!(remote is DirectConnect))
|
||||
{
|
||||
@@ -621,10 +401,10 @@ namespace Shadowsocks.Controller
|
||||
connectTimer.Enabled = true;
|
||||
connectTimer.Session = session;
|
||||
connectTimer.Server = server;
|
||||
|
||||
|
||||
_destConnected = false;
|
||||
// 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)
|
||||
{
|
||||
@@ -643,7 +423,7 @@ namespace Shadowsocks.Controller
|
||||
timer.Enabled = false;
|
||||
timer.Dispose();
|
||||
|
||||
if (_destConnected || _closed)
|
||||
if (_destConnected || Closed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -659,10 +439,10 @@ namespace Shadowsocks.Controller
|
||||
|
||||
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;
|
||||
@@ -672,10 +452,10 @@ namespace Shadowsocks.Controller
|
||||
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()}");
|
||||
}
|
||||
@@ -685,7 +465,7 @@ namespace Shadowsocks.Controller
|
||||
strategy?.UpdateLatency(_server, latency);
|
||||
_tcprelay.UpdateLatency(_server, latency);
|
||||
|
||||
StartPipe(session);
|
||||
OnServerConnected(session);
|
||||
}
|
||||
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
|
||||
{
|
||||
_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)
|
||||
{
|
||||
@@ -720,10 +500,10 @@ namespace Shadowsocks.Controller
|
||||
|
||||
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);
|
||||
@@ -733,15 +513,15 @@ namespace Shadowsocks.Controller
|
||||
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, new AsyncCallback(PipeConnectionSendCallback), session);
|
||||
Connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, PipeConnectionSendCallback, session);
|
||||
IStrategy strategy = _controller.GetCurrentStrategy();
|
||||
strategy?.UpdateLastRead(_server);
|
||||
}
|
||||
else
|
||||
{
|
||||
_connection.Shutdown(SocketShutdown.Send);
|
||||
Connection.Shutdown(SocketShutdown.Send);
|
||||
_connectionShutdown = true;
|
||||
CheckClose();
|
||||
}
|
||||
@@ -755,12 +535,12 @@ namespace Shadowsocks.Controller
|
||||
|
||||
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)
|
||||
@@ -783,27 +563,53 @@ namespace Shadowsocks.Controller
|
||||
|
||||
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, new AsyncCallback(PipeRemoteSendCallback), session);
|
||||
session.Remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, callback, 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 = (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)
|
||||
{
|
||||
@@ -817,8 +623,8 @@ namespace Shadowsocks.Controller
|
||||
try
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -459,10 +459,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(_pacServer);
|
||||
services.Add(new Http2Socks5(_config.localPort));
|
||||
//services.Add(new Http2Socks5(_config.localPort));
|
||||
_listener = new Listener(services);
|
||||
_listener.Start(_config);
|
||||
}
|
||||
|
||||
@@ -73,52 +73,15 @@ namespace Shadowsocks.ForwardProxy
|
||||
{
|
||||
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[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;
|
||||
|
||||
@@ -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\ResultPointCallback.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="Model\HotKeyConfig.cs" />
|
||||
<Compile Include="Model\ProxyConfig.cs" />
|
||||
@@ -193,6 +195,7 @@
|
||||
<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\INTERNET_OPTION.cs" />
|
||||
<Compile Include="Util\SystemProxy\INTERNET_PER_CONN_OPTION.cs" />
|
||||
@@ -209,7 +212,6 @@
|
||||
<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" />
|
||||
|
||||
Reference in New Issue
Block a user