diff --git a/shadowsocks-csharp/Controller/Service/Http2Socks5.cs b/shadowsocks-csharp/Controller/Service/Http2Socks5.cs deleted file mode 100644 index adaa3896..00000000 --- a/shadowsocks-csharp/Controller/Service/Http2Socks5.cs +++ /dev/null @@ -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 _headers = new Queue(); - - 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 - - } - } -} diff --git a/shadowsocks-csharp/Controller/Service/HttpHandler.cs b/shadowsocks-csharp/Controller/Service/HttpHandler.cs new file mode 100644 index 00000000..94d94da8 --- /dev/null +++ b/shadowsocks-csharp/Controller/Service/HttpHandler.cs @@ -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 _headers = new Queue(); + + 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 + + } +} diff --git a/shadowsocks-csharp/Controller/Service/Socks5Handler.cs b/shadowsocks-csharp/Controller/Service/Socks5Handler.cs new file mode 100644 index 00000000..1d7efb22 --- /dev/null +++ b/shadowsocks-csharp/Controller/Service/Socks5Handler.cs @@ -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(); + } + } + } +} diff --git a/shadowsocks-csharp/Controller/Service/TCPRelay.cs b/shadowsocks-csharp/Controller/Service/TCPRelay.cs index c34ade81..f261d345 100644 --- a/shadowsocks-csharp/Controller/Service/TCPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/TCPRelay.cs @@ -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 Handlers { get; set; } + + private readonly List _factories = new List(); + + public ISet Handlers { get; } = new HashSet(); public TCPRelay(ShadowsocksController controller, Configuration conf) { _controller = controller; _config = conf; - Handlers = new HashSet(); _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 handlersToClose = new List(); 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 + { + 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; } @@ -105,7 +131,7 @@ namespace Shadowsocks.Controller } } - class AsyncSession : AsyncSession + protected class AsyncSession : 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(remote, proxyTimer)); + remote.BeginConnectProxy(proxyEP, ProxyConnectCallback, new AsyncSession(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) ar.AsyncState; + var session = (AsyncSession)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(session, connectTimer)); + remote.BeginConnectDest(destEndPoint, ConnectCallback, new AsyncSession(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) ar.AsyncState; + var session = (AsyncSession)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) { diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs index 0b35cfe2..3e4f1c7f 100644 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -459,10 +459,10 @@ namespace Shadowsocks.Controller TCPRelay tcpRelay = new TCPRelay(this, _config); UDPRelay udpRelay = new UDPRelay(this); List services = new List(); + 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); } diff --git a/shadowsocks-csharp/ForwardProxy/Socks5Proxy.cs b/shadowsocks-csharp/ForwardProxy/Socks5Proxy.cs index 8c9dd409..99366fee 100644 --- a/shadowsocks-csharp/ForwardProxy/Socks5Proxy.cs +++ b/shadowsocks-csharp/ForwardProxy/Socks5Proxy.cs @@ -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; diff --git a/shadowsocks-csharp/Util/Sockets/Socks5Util.cs b/shadowsocks-csharp/Util/Sockets/Socks5Util.cs new file mode 100644 index 00000000..3bdf2e83 --- /dev/null +++ b/shadowsocks-csharp/Util/Sockets/Socks5Util.cs @@ -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; + } + } +} diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index f663e8cd..f679f74b 100644 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -140,7 +140,9 @@ - + + + @@ -193,6 +195,7 @@ + @@ -209,7 +212,6 @@ ConfigForm.cs -