From ad15c7e64b8ffc0e2dd0795b62d8a817b5608e0d Mon Sep 17 00:00:00 2001 From: noisyfox Date: Tue, 16 Aug 2016 17:28:26 +1000 Subject: [PATCH] Improve host address handling using DnsEndPoint Signed-off-by: noisyfox --- .../Controller/Service/TCPRelay.cs | 23 ++++----- shadowsocks-csharp/Proxy/DirectConnect.cs | 44 ++++++++++------ shadowsocks-csharp/Proxy/IProxy.cs | 22 ++++++-- shadowsocks-csharp/Proxy/Socks5Proxy.cs | 51 +++++++++---------- 4 files changed, 81 insertions(+), 59 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/TCPRelay.cs b/shadowsocks-csharp/Controller/Service/TCPRelay.cs index c97d4880..5bf73c34 100644 --- a/shadowsocks-csharp/Controller/Service/TCPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/TCPRelay.cs @@ -302,8 +302,7 @@ namespace Shadowsocks.Controller if (ar.AsyncState != null) { connection.EndSend(ar); - // TODO: need fix - //Logging.Debug(remote.LocalEndPoint, remote.DestEndPoint, RecvSize, "TCP Relay"); + Logging.Debug(remote.LocalEndPoint, remote.DestEndPoint, RecvSize, "TCP Relay"); connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null); } else @@ -311,8 +310,7 @@ namespace Shadowsocks.Controller int bytesRead = connection.EndReceive(ar); if (bytesRead > 0) { - // TODO: need fix - //Logging.Debug(remote.LocalEndPoint, remote.DestEndPoint, RecvSize, "TCP Relay"); + Logging.Debug(remote.LocalEndPoint, remote.DestEndPoint, RecvSize, "TCP Relay"); connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null); } else @@ -344,8 +342,7 @@ namespace Shadowsocks.Controller private class ProxyTimer : Timer { public IProxy Proxy; - public string DestHost; - public int DestPort; + public EndPoint DestEndPoint; public Server Server; public ProxyTimer(int p) : base(p) @@ -373,7 +370,11 @@ namespace Shadowsocks.Controller bool parsed = IPAddress.TryParse(_config.proxyServer, out ipAddress); if (!parsed) { - // TODO really necessary to resolve a proxy's address? Maybe from local hosts? + /* + * TODO really necessary to resolve a proxy's address? Maybe from local hosts? + * also we may simplify it by using dual-mode socket with + * the approach described in DirectConnect.BeginConnectDest + */ IPHostEntry ipHostInfo = Dns.GetHostEntry(_config.proxyServer); ipAddress = ipHostInfo.AddressList[0]; } @@ -394,8 +395,7 @@ namespace Shadowsocks.Controller proxyTimer.Enabled = true; proxyTimer.Proxy = remote; - proxyTimer.DestHost = server.server; - proxyTimer.DestPort = server.server_port; + proxyTimer.DestEndPoint = ProxyUtils.GetEndPoint(server.server, server.server_port); proxyTimer.Server = server; _proxyConnected = false; @@ -433,8 +433,7 @@ namespace Shadowsocks.Controller try { ProxyTimer timer = (ProxyTimer)ar.AsyncState; - var destHost = timer.DestHost; - var destPort = timer.DestPort; + var destEndPoint = timer.DestEndPoint; server = timer.Server; timer.Elapsed -= proxyConnectTimer_Elapsed; timer.Enabled = false; @@ -462,7 +461,7 @@ namespace Shadowsocks.Controller _destConnected = false; // Connect to the remote endpoint. - remote.BeginConnectDest(destHost, destPort, new AsyncCallback(ConnectCallback), connectTimer); + remote.BeginConnectDest(destEndPoint, new AsyncCallback(ConnectCallback), connectTimer); } catch (ArgumentException) { diff --git a/shadowsocks-csharp/Proxy/DirectConnect.cs b/shadowsocks-csharp/Proxy/DirectConnect.cs index a8a9be33..071110f1 100644 --- a/shadowsocks-csharp/Proxy/DirectConnect.cs +++ b/shadowsocks-csharp/Proxy/DirectConnect.cs @@ -35,9 +35,7 @@ namespace Shadowsocks.Proxy public EndPoint LocalEndPoint => _remote.LocalEndPoint; public EndPoint ProxyEndPoint { get; } = new FakeEndPoint(); - public string DestHost { get; private set; } - public int DestPort { get; private set; } - + public EndPoint DestEndPoint { get; private set; } public void BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state) { @@ -52,27 +50,41 @@ namespace Shadowsocks.Proxy // do nothing } - public void BeginConnectDest(string host, int port, AsyncCallback callback, object state) + public void BeginConnectDest(EndPoint destEndPoint, AsyncCallback callback, object state) { - // TODO async resolving - IPAddress ipAddress; - bool parsed = IPAddress.TryParse(host, out ipAddress); - if (!parsed) + EndPoint realEndPoint = DestEndPoint = destEndPoint; + + /* + * On windows vista or later, dual-mode socket is supported, so that + * we don't need to resolve a DnsEndPoint manually. + * We could just create a dual-mode socket and pass the DnsEndPoint + * directly to it's BeginConnect and the system will handle it correctlly + * so that we won't worry about async resolving any more. + * + * see: https://blogs.msdn.microsoft.com/webdev/2013/01/08/dual-mode-sockets-never-create-an-ipv4-socket-again/ + * + * But it seems that we can't use this feature because DnsEndPoint + * doesn't have a specific AddressFamily before it has been + * resolved (we don't know whether it's ipv4 or ipv6) and we don't have + * a dual-mode socket to use on windows xp :( + */ + var dep = realEndPoint as DnsEndPoint; + if (dep != null) { - IPHostEntry ipHostInfo = Dns.GetHostEntry(host); - ipAddress = ipHostInfo.AddressList[0]; - } - IPEndPoint remoteEP = new IPEndPoint(ipAddress, port); + // need to resolve manually + // TODO async resolving + IPHostEntry ipHostInfo = Dns.GetHostEntry(dep.Host); + IPAddress ipAddress = ipHostInfo.AddressList[0]; - DestHost = host; - DestPort = port; + realEndPoint = new IPEndPoint(ipAddress, dep.Port); + } if (_remote == null) { - _remote = new Socket(remoteEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + _remote = new Socket(realEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); _remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); } - _remote.BeginConnect(remoteEP, callback, state); + _remote.BeginConnect(realEndPoint, callback, state); } public void EndConnectDest(IAsyncResult asyncResult) diff --git a/shadowsocks-csharp/Proxy/IProxy.cs b/shadowsocks-csharp/Proxy/IProxy.cs index a3826b3b..81183110 100644 --- a/shadowsocks-csharp/Proxy/IProxy.cs +++ b/shadowsocks-csharp/Proxy/IProxy.cs @@ -11,15 +11,13 @@ namespace Shadowsocks.Proxy EndPoint ProxyEndPoint { get; } - string DestHost { get; } - - int DestPort { get; } + EndPoint DestEndPoint { get; } void BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state); void EndConnectProxy(IAsyncResult asyncResult); - void BeginConnectDest(string host, int port, AsyncCallback callback, object state); + void BeginConnectDest(EndPoint destEndPoint, AsyncCallback callback, object state); void EndConnectDest(IAsyncResult asyncResult); @@ -37,4 +35,20 @@ namespace Shadowsocks.Proxy void Close(); } + + public static class ProxyUtils + { + public static EndPoint GetEndPoint(string host, int port) + { + IPAddress ipAddress; + bool parsed = IPAddress.TryParse(host, out ipAddress); + if (parsed) + { + return new IPEndPoint(ipAddress, port); + } + + // maybe is a domain name + return new DnsEndPoint(host, port); + } + } } diff --git a/shadowsocks-csharp/Proxy/Socks5Proxy.cs b/shadowsocks-csharp/Proxy/Socks5Proxy.cs index 61a1707d..490a6275 100644 --- a/shadowsocks-csharp/Proxy/Socks5Proxy.cs +++ b/shadowsocks-csharp/Proxy/Socks5Proxy.cs @@ -47,8 +47,7 @@ namespace Shadowsocks.Proxy public EndPoint LocalEndPoint => _remote.LocalEndPoint; public EndPoint ProxyEndPoint { get; private set; } - public string DestHost { get; private set; } - public int DestPort { get; private set; } + public EndPoint DestEndPoint { get; private set; } public void BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state) { @@ -74,20 +73,32 @@ namespace Shadowsocks.Proxy } } - public void BeginConnectDest(string host, int port, AsyncCallback callback, object state) + public void BeginConnectDest(EndPoint destEndPoint, AsyncCallback callback, object state) { - DestHost = host; - DestPort = port; + DestEndPoint = destEndPoint; byte[] request = null; byte atyp = 0; - - IPAddress ipAddress; - bool parsed = IPAddress.TryParse(host, out ipAddress); - if (parsed) + int port; + + 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 { - IPEndPoint ep = new IPEndPoint(ipAddress, port); - switch (ep.AddressFamily) + switch (DestEndPoint.AddressFamily) { case AddressFamily.InterNetwork: request = new byte[4 + 4 + 2]; @@ -100,23 +111,10 @@ namespace Shadowsocks.Proxy default: throw new Exception(I18N.GetString("Proxy request failed")); } - - var addr = ep.Address.GetAddressBytes(); + port = ((IPEndPoint) DestEndPoint).Port; + var addr = ((IPEndPoint)DestEndPoint).Address.GetAddressBytes(); Array.Copy(addr, 0, request, 4, request.Length - 4 - 2); } - else - { - // maybe is a domain name, we will leave it to server - // need ValidateTcpPort? porttest > 1 && porttest < 65535? - - atyp = 3; // DOMAINNAME - var enc = Encoding.UTF8; - var hostByteCount = enc.GetByteCount(host); - - request = new byte[4 + 1/*length byte*/ + hostByteCount + 2]; - request[4] = (byte)hostByteCount; - enc.GetBytes(host, 0, host.Length, request, 5); - } // 构造request包剩余部分 request[0] = 5; @@ -131,7 +129,6 @@ namespace Shadowsocks.Proxy st.AsyncState = state; _remote?.BeginSend(request, 0, request.Length, 0, Socks5RequestSendCallback, st); - } public void EndConnectDest(IAsyncResult asyncResult)