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