From a02cd11dc97fc1ec4a19bab5d23b540ee84f082d Mon Sep 17 00:00:00 2001 From: noisyfox Date: Tue, 16 Aug 2016 01:09:06 +1000 Subject: [PATCH 1/8] Let proxy module handle name resolving itself. Fix wrong output in proxyConnectTimer_Elapsed. Signed-off-by: noisyfox --- .../Controller/Service/TCPRelay.cs | 40 +++++++++---------- shadowsocks-csharp/Proxy/DirectConnect.cs | 35 ++++++++++++---- shadowsocks-csharp/Proxy/IProxy.cs | 6 ++- shadowsocks-csharp/Proxy/Socks5Proxy.cs | 20 ++++++---- 4 files changed, 64 insertions(+), 37 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/TCPRelay.cs b/shadowsocks-csharp/Controller/Service/TCPRelay.cs index 4bce227d..c97d4880 100644 --- a/shadowsocks-csharp/Controller/Service/TCPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/TCPRelay.cs @@ -302,7 +302,8 @@ namespace Shadowsocks.Controller if (ar.AsyncState != null) { connection.EndSend(ar); - Logging.Debug(remote.LocalEndPoint, remote.DestEndPoint, RecvSize, "TCP Relay"); + // TODO: need fix + //Logging.Debug(remote.LocalEndPoint, remote.DestEndPoint, RecvSize, "TCP Relay"); connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null); } else @@ -310,7 +311,8 @@ namespace Shadowsocks.Controller int bytesRead = connection.EndReceive(ar); if (bytesRead > 0) { - Logging.Debug(remote.LocalEndPoint, remote.DestEndPoint, RecvSize, "TCP Relay"); + // TODO: need fix + //Logging.Debug(remote.LocalEndPoint, remote.DestEndPoint, RecvSize, "TCP Relay"); connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null); } else @@ -341,7 +343,9 @@ namespace Shadowsocks.Controller // inner class private class ProxyTimer : Timer { - public EndPoint DestEndPoint; + public IProxy Proxy; + public string DestHost; + public int DestPort; public Server Server; public ProxyTimer(int p) : base(p) @@ -361,23 +365,15 @@ namespace Shadowsocks.Controller { CreateRemote(); - // TODO async resolving - IPAddress ipAddress; - bool parsed = IPAddress.TryParse(server.server, out ipAddress); - if (!parsed) - { - IPHostEntry ipHostInfo = Dns.GetHostEntry(server.server); - ipAddress = ipHostInfo.AddressList[0]; - } - IPEndPoint destEP = new IPEndPoint(ipAddress, server.server_port); - // Setting up proxy IPEndPoint proxyEP; if (_config.useProxy) { - parsed = IPAddress.TryParse(_config.proxyServer, out ipAddress); + IPAddress ipAddress; + bool parsed = IPAddress.TryParse(_config.proxyServer, out ipAddress); if (!parsed) { + // TODO really necessary to resolve a proxy's address? Maybe from local hosts? IPHostEntry ipHostInfo = Dns.GetHostEntry(_config.proxyServer); ipAddress = ipHostInfo.AddressList[0]; } @@ -388,7 +384,7 @@ namespace Shadowsocks.Controller else { remote = new DirectConnect(); - proxyEP = destEP; + proxyEP = null; } @@ -396,7 +392,10 @@ namespace Shadowsocks.Controller proxyTimer.AutoReset = false; proxyTimer.Elapsed += proxyConnectTimer_Elapsed; proxyTimer.Enabled = true; - proxyTimer.DestEndPoint = destEP; + + proxyTimer.Proxy = remote; + proxyTimer.DestHost = server.server; + proxyTimer.DestPort = server.server_port; proxyTimer.Server = server; _proxyConnected = false; @@ -417,9 +416,9 @@ namespace Shadowsocks.Controller { return; } - var ep = ((ProxyTimer)sender).DestEndPoint; + var proxy = ((ProxyTimer)sender).Proxy; - Logging.Info($"Proxy {ep} timed out"); + Logging.Info($"Proxy {proxy.ProxyEndPoint} timed out"); remote?.Close(); RetryConnect(); } @@ -434,7 +433,8 @@ namespace Shadowsocks.Controller try { ProxyTimer timer = (ProxyTimer)ar.AsyncState; - var destEP = timer.DestEndPoint; + var destHost = timer.DestHost; + var destPort = timer.DestPort; server = timer.Server; timer.Elapsed -= proxyConnectTimer_Elapsed; timer.Enabled = false; @@ -462,7 +462,7 @@ namespace Shadowsocks.Controller _destConnected = false; // Connect to the remote endpoint. - remote.BeginConnectDest(destEP, new AsyncCallback(ConnectCallback), connectTimer); + remote.BeginConnectDest(destHost, destPort, new AsyncCallback(ConnectCallback), connectTimer); } catch (ArgumentException) { diff --git a/shadowsocks-csharp/Proxy/DirectConnect.cs b/shadowsocks-csharp/Proxy/DirectConnect.cs index 7de4e59f..a8a9be33 100644 --- a/shadowsocks-csharp/Proxy/DirectConnect.cs +++ b/shadowsocks-csharp/Proxy/DirectConnect.cs @@ -20,19 +20,28 @@ namespace Shadowsocks.Proxy public bool CompletedSynchronously { get; } = true; } + private class FakeEndPoint : EndPoint + { + public override AddressFamily AddressFamily { get; } = AddressFamily.Unspecified; + + public override string ToString() + { + return "null proxy"; + } + } + private Socket _remote; public EndPoint LocalEndPoint => _remote.LocalEndPoint; - public EndPoint ProxyEndPoint { get; private set; } - - public EndPoint DestEndPoint { get; private set; } + public EndPoint ProxyEndPoint { get; } = new FakeEndPoint(); + public string DestHost { get; private set; } + public int DestPort { get; private set; } public void BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state) { // do nothing - ProxyEndPoint = remoteEP; var r = new FakeAsyncResult(state); callback?.Invoke(r); @@ -43,16 +52,26 @@ namespace Shadowsocks.Proxy // do nothing } - public void BeginConnectDest(EndPoint remoteEP, AsyncCallback callback, object state) + public void BeginConnectDest(string host, int port, AsyncCallback callback, object state) { + // TODO async resolving + IPAddress ipAddress; + bool parsed = IPAddress.TryParse(host, out ipAddress); + if (!parsed) + { + IPHostEntry ipHostInfo = Dns.GetHostEntry(host); + ipAddress = ipHostInfo.AddressList[0]; + } + IPEndPoint remoteEP = new IPEndPoint(ipAddress, port); + + DestHost = host; + DestPort = port; + if (_remote == null) { _remote = new Socket(remoteEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp); _remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); } - - DestEndPoint = remoteEP; - _remote.BeginConnect(remoteEP, callback, state); } diff --git a/shadowsocks-csharp/Proxy/IProxy.cs b/shadowsocks-csharp/Proxy/IProxy.cs index 4796c92f..a3826b3b 100644 --- a/shadowsocks-csharp/Proxy/IProxy.cs +++ b/shadowsocks-csharp/Proxy/IProxy.cs @@ -11,13 +11,15 @@ namespace Shadowsocks.Proxy EndPoint ProxyEndPoint { get; } - EndPoint DestEndPoint { get; } + string DestHost { get; } + + int DestPort { get; } void BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state); void EndConnectProxy(IAsyncResult asyncResult); - void BeginConnectDest(EndPoint remoteEP, AsyncCallback callback, object state); + void BeginConnectDest(string host, int port, AsyncCallback callback, object state); void EndConnectDest(IAsyncResult asyncResult); diff --git a/shadowsocks-csharp/Proxy/Socks5Proxy.cs b/shadowsocks-csharp/Proxy/Socks5Proxy.cs index 6e6c04a9..c400e52b 100644 --- a/shadowsocks-csharp/Proxy/Socks5Proxy.cs +++ b/shadowsocks-csharp/Proxy/Socks5Proxy.cs @@ -47,7 +47,8 @@ namespace Shadowsocks.Proxy public EndPoint LocalEndPoint => _remote.LocalEndPoint; public EndPoint ProxyEndPoint { get; private set; } - public EndPoint DestEndPoint { get; private set; } + public string DestHost { get; private set; } + public int DestPort { get; private set; } public void BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state) { @@ -73,13 +74,20 @@ namespace Shadowsocks.Proxy } } - public void BeginConnectDest(EndPoint remoteEP, AsyncCallback callback, object state) + public void BeginConnectDest(string host, int port, AsyncCallback callback, object state) { - var ep = remoteEP as IPEndPoint; - if (ep == null) + // TODO resolving by proxy + IPAddress ipAddress; + bool parsed = IPAddress.TryParse(host, out ipAddress); + if (!parsed) { - throw new Exception(I18N.GetString("Proxy request faild")); + IPHostEntry ipHostInfo = Dns.GetHostEntry(host); + ipAddress = ipHostInfo.AddressList[0]; } + IPEndPoint ep = new IPEndPoint(ipAddress, port); + + DestHost = host; + DestPort = port; byte[] request = null; byte atyp = 0; @@ -113,8 +121,6 @@ namespace Shadowsocks.Proxy st.Callback = callback; st.AsyncState = state; - DestEndPoint = remoteEP; - _remote?.BeginSend(request, 0, request.Length, 0, Socks5RequestSendCallback, st); } From 22740c5102cc906704387eaa401631e29dba0422 Mon Sep 17 00:00:00 2001 From: noisyfox Date: Tue, 16 Aug 2016 02:08:32 +1000 Subject: [PATCH 2/8] Let Socks5 proxy handle server host's domain name. Don't resolve it locally because socks5 support domain name connection. Signed-off-by: noisyfox --- shadowsocks-csharp/Proxy/Socks5Proxy.cs | 61 ++++++++++++++----------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/shadowsocks-csharp/Proxy/Socks5Proxy.cs b/shadowsocks-csharp/Proxy/Socks5Proxy.cs index c400e52b..cac20cfa 100644 --- a/shadowsocks-csharp/Proxy/Socks5Proxy.cs +++ b/shadowsocks-csharp/Proxy/Socks5Proxy.cs @@ -76,46 +76,55 @@ namespace Shadowsocks.Proxy public void BeginConnectDest(string host, int port, AsyncCallback callback, object state) { - // TODO resolving by proxy - IPAddress ipAddress; - bool parsed = IPAddress.TryParse(host, out ipAddress); - if (!parsed) - { - IPHostEntry ipHostInfo = Dns.GetHostEntry(host); - ipAddress = ipHostInfo.AddressList[0]; - } - IPEndPoint ep = new IPEndPoint(ipAddress, port); - DestHost = host; DestPort = port; byte[] request = null; byte atyp = 0; - switch (ep.AddressFamily) + + IPAddress ipAddress; + bool parsed = IPAddress.TryParse(host, out ipAddress); + if (parsed) { - case AddressFamily.InterNetwork: - request = new byte[4 + 4 + 2]; - atyp = 1; - break; - case AddressFamily.InterNetworkV6: - request = new byte[4 + 16 + 2]; - atyp = 4; - break; + IPEndPoint ep = new IPEndPoint(ipAddress, port); + switch (ep.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 faild")); + } + + var addr = ep.Address.GetAddressBytes(); + Array.Copy(addr, 0, request, 4, request.Length - 4 - 2); } - if (request == null) + else { - throw new Exception(I18N.GetString("Proxy request faild")); + // 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包 - var addr = ep.Address.GetAddressBytes(); + // 构造request包剩余部分 request[0] = 5; request[1] = 1; request[2] = 0; request[3] = atyp; - Array.Copy(addr, 0, request, 4, request.Length - 4 - 2); - request[request.Length - 2] = (byte) ((ep.Port >> 8) & 0xff); - request[request.Length - 1] = (byte) (ep.Port & 0xff); + request[request.Length - 2] = (byte) ((port >> 8) & 0xff); + request[request.Length - 1] = (byte) (port & 0xff); var st = new Socks5State(); st.Callback = callback; From aef88543d22dedd17d40b04dbded20a484816758 Mon Sep 17 00:00:00 2001 From: noisyfox Date: Tue, 16 Aug 2016 15:47:55 +1000 Subject: [PATCH 3/8] Fix typo Update translate Signed-off-by: noisyfox --- shadowsocks-csharp/Data/cn.txt | 3 ++- shadowsocks-csharp/Proxy/Socks5Proxy.cs | 14 +++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/shadowsocks-csharp/Data/cn.txt b/shadowsocks-csharp/Data/cn.txt index 5d48ef7d..d471863a 100644 --- a/shadowsocks-csharp/Data/cn.txt +++ b/shadowsocks-csharp/Data/cn.txt @@ -113,4 +113,5 @@ Failed to update registry=无法修改注册表 System Proxy On: =系统代理已启用: Running: Port {0}=正在运行:端口 {0} Unexpected error, shadowsocks will exit. Please report to=非预期错误,Shadowsocks将退出。请提交此错误到 - \ No newline at end of file +Proxy request failed=代理请求失败 +Proxy handshake failed=代理握手失败 diff --git a/shadowsocks-csharp/Proxy/Socks5Proxy.cs b/shadowsocks-csharp/Proxy/Socks5Proxy.cs index cac20cfa..61a1707d 100644 --- a/shadowsocks-csharp/Proxy/Socks5Proxy.cs +++ b/shadowsocks-csharp/Proxy/Socks5Proxy.cs @@ -98,7 +98,7 @@ namespace Shadowsocks.Proxy atyp = 4; // IP V6 address break; default: - throw new Exception(I18N.GetString("Proxy request faild")); + throw new Exception(I18N.GetString("Proxy request failed")); } var addr = ep.Address.GetAddressBytes(); @@ -221,12 +221,12 @@ namespace Shadowsocks.Proxy { if (_receiveBuffer[0] != 5 || _receiveBuffer[1] != 0) { - ex = new Exception(I18N.GetString("Proxy handshake faild")); + ex = new Exception(I18N.GetString("Proxy handshake failed")); } } else { - ex = new Exception(I18N.GetString("Proxy handshake faild")); + ex = new Exception(I18N.GetString("Proxy handshake failed")); } } catch (Exception ex2) @@ -276,20 +276,20 @@ namespace Shadowsocks.Proxy _remote.BeginReceive(_receiveBuffer, 0, 16 + 2, 0, Socks5ReplyReceiveCallback2, state); break; default: - state.ex = new Exception(I18N.GetString("Proxy request faild")); + state.ex = new Exception(I18N.GetString("Proxy request failed")); state.Callback?.Invoke(new FakeAsyncResult(ar, state)); break; } } else { - state.ex = new Exception(I18N.GetString("Proxy request faild")); + state.ex = new Exception(I18N.GetString("Proxy request failed")); state.Callback?.Invoke(new FakeAsyncResult(ar, state)); } } else { - state.ex = new Exception(I18N.GetString("Proxy request faild")); + state.ex = new Exception(I18N.GetString("Proxy request failed")); state.Callback?.Invoke(new FakeAsyncResult(ar, state)); } } @@ -312,7 +312,7 @@ namespace Shadowsocks.Proxy if (bytesRead < bytesNeedSkip) { - ex = new Exception(I18N.GetString("Proxy request faild")); + ex = new Exception(I18N.GetString("Proxy request failed")); } } catch (Exception ex2) From ad15c7e64b8ffc0e2dd0795b62d8a817b5608e0d Mon Sep 17 00:00:00 2001 From: noisyfox Date: Tue, 16 Aug 2016 17:28:26 +1000 Subject: [PATCH 4/8] 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) From 53ce1948e460fc5cf83fb468eefe5c7556158c47 Mon Sep 17 00:00:00 2001 From: noisyfox Date: Tue, 16 Aug 2016 20:40:09 +1000 Subject: [PATCH 5/8] Unified ipv4, ipv6 and domain connections. -Use dual-mode socket & DnsEndPoint to handle all domain name connection. -Move all socket creation into one single function. So that we could treat domain name just like ipv4 & ipv6 address. This also solves the async domain solving problem because the os will hanlde it for us. --- .../Controller/Service/PortForwarder.cs | 10 +-- .../Controller/Service/TCPRelay.cs | 20 ++---- .../Controller/Service/UDPRelay.cs | 15 ++--- shadowsocks-csharp/Proxy/DirectConnect.cs | 32 ++------- shadowsocks-csharp/Proxy/IProxy.cs | 16 ----- shadowsocks-csharp/Proxy/Socks5Proxy.cs | 3 +- shadowsocks-csharp/Util/SocketUtil.cs | 66 +++++++++++++++++++ shadowsocks-csharp/shadowsocks-csharp.csproj | 1 + 8 files changed, 84 insertions(+), 79 deletions(-) create mode 100644 shadowsocks-csharp/Util/SocketUtil.cs diff --git a/shadowsocks-csharp/Controller/Service/PortForwarder.cs b/shadowsocks-csharp/Controller/Service/PortForwarder.cs index 4f78a24c..f76a1284 100644 --- a/shadowsocks-csharp/Controller/Service/PortForwarder.cs +++ b/shadowsocks-csharp/Controller/Service/PortForwarder.cs @@ -1,6 +1,7 @@ using System; using System.Net; using System.Net.Sockets; +using Shadowsocks.Util; namespace Shadowsocks.Controller { @@ -45,14 +46,9 @@ namespace Shadowsocks.Controller this._local = socket; try { - // TODO async resolving - IPAddress ipAddress; - bool parsed = IPAddress.TryParse("127.0.0.1", out ipAddress); - IPEndPoint remoteEP = new IPEndPoint(ipAddress, targetPort); + EndPoint remoteEP = SocketUtil.GetEndPoint("localhost", targetPort); - - _remote = new Socket(ipAddress.AddressFamily, - SocketType.Stream, ProtocolType.Tcp); + _remote = SocketUtil.CreateSocket(remoteEP); _remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); // Connect to the remote endpoint. diff --git a/shadowsocks-csharp/Controller/Service/TCPRelay.cs b/shadowsocks-csharp/Controller/Service/TCPRelay.cs index 5bf73c34..b7638864 100644 --- a/shadowsocks-csharp/Controller/Service/TCPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/TCPRelay.cs @@ -9,6 +9,7 @@ using Shadowsocks.Controller.Strategy; using Shadowsocks.Encryption; using Shadowsocks.Model; using Shadowsocks.Proxy; +using Shadowsocks.Util; namespace Shadowsocks.Controller { @@ -363,24 +364,11 @@ namespace Shadowsocks.Controller CreateRemote(); // Setting up proxy - IPEndPoint proxyEP; + EndPoint proxyEP; if (_config.useProxy) { - IPAddress ipAddress; - bool parsed = IPAddress.TryParse(_config.proxyServer, out ipAddress); - if (!parsed) - { - /* - * 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]; - } - remote = new Socks5Proxy(); - proxyEP = new IPEndPoint(ipAddress, _config.proxyPort); + proxyEP = SocketUtil.GetEndPoint(_config.proxyServer, _config.proxyPort); } else { @@ -395,7 +383,7 @@ namespace Shadowsocks.Controller proxyTimer.Enabled = true; proxyTimer.Proxy = remote; - proxyTimer.DestEndPoint = ProxyUtils.GetEndPoint(server.server, server.server_port); + proxyTimer.DestEndPoint = SocketUtil.GetEndPoint(server.server, server.server_port); proxyTimer.Server = server; _proxyConnected = false; diff --git a/shadowsocks-csharp/Controller/Service/UDPRelay.cs b/shadowsocks-csharp/Controller/Service/UDPRelay.cs index 5f0d2363..0a5a18ac 100644 --- a/shadowsocks-csharp/Controller/Service/UDPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/UDPRelay.cs @@ -7,6 +7,7 @@ using System.Runtime.CompilerServices; using Shadowsocks.Controller.Strategy; using Shadowsocks.Encryption; using Shadowsocks.Model; +using Shadowsocks.Util; namespace Shadowsocks.Controller { @@ -56,7 +57,7 @@ namespace Shadowsocks.Controller private byte[] _buffer = new byte[1500]; private IPEndPoint _localEndPoint; - private IPEndPoint _remoteEndPoint; + private EndPoint _remoteEndPoint; public UDPHandler(Socket local, Server server, IPEndPoint localEndPoint) { @@ -64,16 +65,8 @@ namespace Shadowsocks.Controller _server = server; _localEndPoint = localEndPoint; - // TODO async resolving - IPAddress ipAddress; - bool parsed = IPAddress.TryParse(server.server, out ipAddress); - if (!parsed) - { - IPHostEntry ipHostInfo = Dns.GetHostEntry(server.server); - ipAddress = ipHostInfo.AddressList[0]; - } - _remoteEndPoint = new IPEndPoint(ipAddress, server.server_port); - _remote = new Socket(_remoteEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp); + _remoteEndPoint = SocketUtil.GetEndPoint(server.server, server.server_port); + _remote = SocketUtil.CreateSocket(_remoteEndPoint, ProtocolType.Udp); } public void Send(byte[] data, int length) diff --git a/shadowsocks-csharp/Proxy/DirectConnect.cs b/shadowsocks-csharp/Proxy/DirectConnect.cs index 071110f1..487adc41 100644 --- a/shadowsocks-csharp/Proxy/DirectConnect.cs +++ b/shadowsocks-csharp/Proxy/DirectConnect.cs @@ -2,6 +2,7 @@ using System.Net; using System.Net.Sockets; using System.Threading; +using Shadowsocks.Util; namespace Shadowsocks.Proxy { @@ -52,39 +53,14 @@ namespace Shadowsocks.Proxy public void BeginConnectDest(EndPoint destEndPoint, AsyncCallback callback, object state) { - 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) - { - // need to resolve manually - // TODO async resolving - IPHostEntry ipHostInfo = Dns.GetHostEntry(dep.Host); - IPAddress ipAddress = ipHostInfo.AddressList[0]; - - realEndPoint = new IPEndPoint(ipAddress, dep.Port); - } + DestEndPoint = destEndPoint; if (_remote == null) { - _remote = new Socket(realEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + _remote = SocketUtil.CreateSocket(destEndPoint); _remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); } - _remote.BeginConnect(realEndPoint, callback, state); + _remote.BeginConnect(destEndPoint, callback, state); } public void EndConnectDest(IAsyncResult asyncResult) diff --git a/shadowsocks-csharp/Proxy/IProxy.cs b/shadowsocks-csharp/Proxy/IProxy.cs index 81183110..cc76b543 100644 --- a/shadowsocks-csharp/Proxy/IProxy.cs +++ b/shadowsocks-csharp/Proxy/IProxy.cs @@ -35,20 +35,4 @@ 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 490a6275..adb94402 100644 --- a/shadowsocks-csharp/Proxy/Socks5Proxy.cs +++ b/shadowsocks-csharp/Proxy/Socks5Proxy.cs @@ -6,6 +6,7 @@ using System.Net.Sockets; using System.Text; using System.Threading; using Shadowsocks.Controller; +using Shadowsocks.Util; namespace Shadowsocks.Proxy { @@ -51,7 +52,7 @@ namespace Shadowsocks.Proxy public void BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state) { - _remote = new Socket(remoteEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + _remote = SocketUtil.CreateSocket(remoteEP); _remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); var st = new Socks5State(); diff --git a/shadowsocks-csharp/Util/SocketUtil.cs b/shadowsocks-csharp/Util/SocketUtil.cs new file mode 100644 index 00000000..ad413d49 --- /dev/null +++ b/shadowsocks-csharp/Util/SocketUtil.cs @@ -0,0 +1,66 @@ +using System; +using System.Net; +using System.Net.Sockets; + +namespace Shadowsocks.Util +{ + public static class SocketUtil + { + private class DnsEndPoint2 : DnsEndPoint + { + public DnsEndPoint2(string host, int port) : base(host, port) + { + } + + public DnsEndPoint2(string host, int port, AddressFamily addressFamily) : base(host, port, addressFamily) + { + } + + public override string ToString() + { + return this.Host + ":" + this.Port; + } + } + + 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 DnsEndPoint2(host, port); + } + + public static Socket CreateSocket(EndPoint endPoint, ProtocolType protocolType = ProtocolType.Tcp) + { + SocketType socketType; + switch (protocolType) + { + case ProtocolType.Tcp: + socketType = SocketType.Stream; + break; + case ProtocolType.Udp: + socketType = SocketType.Dgram; + break; + default: + throw new NotSupportedException("Protocol " + protocolType + " doesn't supported!"); + } + + if (endPoint is DnsEndPoint) + { + var socket = new Socket(AddressFamily.InterNetworkV6, socketType, protocolType); + socket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, false); + + return socket; + } + else + { + return new Socket(endPoint.AddressFamily, socketType, protocolType); + } + } + } +} diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index c4c87271..10e2c7d2 100644 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -176,6 +176,7 @@ + Form From 6cb58b229d9774755896086ead00ae362fa545b6 Mon Sep 17 00:00:00 2001 From: noisyfox Date: Tue, 16 Aug 2016 20:51:32 +1000 Subject: [PATCH 6/8] Fix a null ref exception. At that time the remote even doesn't exist. Also this debug output doesn't make any sense because when handling udp request, this tcp connection won't have any meaningful data. So I simply removed these two lines. Signed-off-by: noisyfox --- shadowsocks-csharp/Controller/Service/TCPRelay.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/TCPRelay.cs b/shadowsocks-csharp/Controller/Service/TCPRelay.cs index b7638864..35f9f9a7 100644 --- a/shadowsocks-csharp/Controller/Service/TCPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/TCPRelay.cs @@ -303,7 +303,6 @@ namespace Shadowsocks.Controller if (ar.AsyncState != null) { connection.EndSend(ar); - Logging.Debug(remote.LocalEndPoint, remote.DestEndPoint, RecvSize, "TCP Relay"); connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null); } else @@ -311,7 +310,6 @@ namespace Shadowsocks.Controller int bytesRead = connection.EndReceive(ar); if (bytesRead > 0) { - Logging.Debug(remote.LocalEndPoint, remote.DestEndPoint, RecvSize, "TCP Relay"); connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null); } else From 3417a984fa81dc89d984aa3fd3728e90a92d79fb Mon Sep 17 00:00:00 2001 From: Syrone Wong Date: Wed, 17 Aug 2016 10:28:27 +0800 Subject: [PATCH 7/8] Change requirements - Running on Windows Vista or above Signed-off-by: Syrone Wong --- shadowsocks-csharp/Data/cn.txt | 1 + shadowsocks-csharp/Program.cs | 8 ++++++++ shadowsocks-csharp/Util/Util.cs | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/shadowsocks-csharp/Data/cn.txt b/shadowsocks-csharp/Data/cn.txt index d471863a..c7ef11a5 100644 --- a/shadowsocks-csharp/Data/cn.txt +++ b/shadowsocks-csharp/Data/cn.txt @@ -113,5 +113,6 @@ Failed to update registry=无法修改注册表 System Proxy On: =系统代理已启用: Running: Port {0}=正在运行:端口 {0} Unexpected error, shadowsocks will exit. Please report to=非预期错误,Shadowsocks将退出。请提交此错误到 +Unsupported operating system, use Windows Vista at least.=不支持的操作系统版本,最低需求为Windows Vista。 Proxy request failed=代理请求失败 Proxy handshake failed=代理握手失败 diff --git a/shadowsocks-csharp/Program.cs b/shadowsocks-csharp/Program.cs index be7e2afe..ee5465dc 100755 --- a/shadowsocks-csharp/Program.cs +++ b/shadowsocks-csharp/Program.cs @@ -18,6 +18,14 @@ namespace Shadowsocks [STAThread] static void Main() { + // Check OS since we are using dual-mode socket + if (!Utils.IsWinVistaOrHigher()) + { + MessageBox.Show(I18N.GetString("Unsupported operating system, use Windows Vista at least."), + "Shadowsocks Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + Utils.ReleaseMemory(true); using (Mutex mutex = new Mutex(false, "Global\\Shadowsocks_" + Application.StartupPath.GetHashCode())) { diff --git a/shadowsocks-csharp/Util/Util.cs b/shadowsocks-csharp/Util/Util.cs index 53077c87..9170c348 100755 --- a/shadowsocks-csharp/Util/Util.cs +++ b/shadowsocks-csharp/Util/Util.cs @@ -157,6 +157,10 @@ namespace Shadowsocks.Util return userKey; } + public static bool IsWinVistaOrHigher() { + return Environment.OSVersion.Version.Major > 5; + } + [DllImport("kernel32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool SetProcessWorkingSetSize(IntPtr process, From ddb73744321b586f5666f07853f861a32b5751c7 Mon Sep 17 00:00:00 2001 From: Syrone Wong Date: Wed, 17 Aug 2016 10:31:21 +0800 Subject: [PATCH 8/8] Refine Windows registry again(#512) Used a method added in .NET 4, user may not have this version installed, reverting to an older method seems to be a better solution. Signed-off-by: Syrone Wong --- shadowsocks-csharp/Util/Util.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shadowsocks-csharp/Util/Util.cs b/shadowsocks-csharp/Util/Util.cs index 9170c348..fdb8482e 100755 --- a/shadowsocks-csharp/Util/Util.cs +++ b/shadowsocks-csharp/Util/Util.cs @@ -151,9 +151,9 @@ namespace Shadowsocks.Util // we are building x86 binary for both x86 and x64, which will // cause problem when opening registry key // detect operating system instead of CPU - RegistryKey userKey = RegistryKey.OpenBaseKey( RegistryHive.CurrentUser, - Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32 ); - userKey = userKey.OpenSubKey( name, writable ); + RegistryKey userKey = RegistryKey.OpenRemoteBaseKey( RegistryHive.CurrentUser, "", + Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32 ) + .OpenSubKey( name, writable ); return userKey; }