diff --git a/shadowsocks-csharp/Controller/Service/Listener.cs b/shadowsocks-csharp/Controller/Service/Listener.cs index 8b7a48d8..70819163 100644 --- a/shadowsocks-csharp/Controller/Service/Listener.cs +++ b/shadowsocks-csharp/Controller/Service/Listener.cs @@ -27,9 +27,14 @@ namespace Shadowsocks.Controller public class UDPState { + public UDPState(Socket s) + { + socket = s; + remoteEndPoint = new IPEndPoint(s.AddressFamily == AddressFamily.InterNetworkV6 ? IPAddress.IPv6Any : IPAddress.Any, 0); + } public Socket socket; public byte[] buffer = new byte[4096]; - public EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); + public EndPoint remoteEndPoint; } Configuration _config; @@ -60,14 +65,14 @@ namespace Shadowsocks.Controller try { // Create a TCP/IP socket. - _tcpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - _udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + _tcpSocket = new Socket(config.isIPv6Enabled ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + _udpSocket = new Socket(config.isIPv6Enabled ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); _tcpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); _udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); IPEndPoint localEndPoint = null; localEndPoint = _shareOverLAN - ? new IPEndPoint(IPAddress.Any, _config.localPort) - : new IPEndPoint(IPAddress.Loopback, _config.localPort); + ? new IPEndPoint(config.isIPv6Enabled ? IPAddress.IPv6Any : IPAddress.Any, _config.localPort) + : new IPEndPoint(config.isIPv6Enabled ? IPAddress.IPv6Loopback : IPAddress.Loopback, _config.localPort); // Bind the socket to the local endpoint and listen for incoming connections. _tcpSocket.Bind(localEndPoint); @@ -81,8 +86,7 @@ namespace Shadowsocks.Controller Logging.Info(Encryption.EncryptorFactory.DumpRegisteredEncryptor()); } _tcpSocket.BeginAccept(new AsyncCallback(AcceptCallback), _tcpSocket); - UDPState udpState = new UDPState(); - udpState.socket = _udpSocket; + UDPState udpState = new UDPState(_udpSocket); _udpSocket.BeginReceiveFrom(udpState.buffer, 0, udpState.buffer.Length, 0, ref udpState.remoteEndPoint, new AsyncCallback(RecvFromCallback), udpState); } catch (SocketException) @@ -105,7 +109,7 @@ namespace Shadowsocks.Controller _udpSocket = null; } - _services.ForEach(s=>s.Stop()); + _services.ForEach(s => s.Stop()); } public void RecvFromCallback(IAsyncResult ar) diff --git a/shadowsocks-csharp/Controller/Service/PACServer.cs b/shadowsocks-csharp/Controller/Service/PACServer.cs index 63d0b5bd..f1ee3218 100644 --- a/shadowsocks-csharp/Controller/Service/PACServer.cs +++ b/shadowsocks-csharp/Controller/Service/PACServer.cs @@ -51,7 +51,7 @@ namespace Shadowsocks.Controller PacSecret = ""; } - PacUrl = $"http://127.0.0.1:{config.localPort}/pac?t={GetTimestamp(DateTime.Now)}{PacSecret}"; + PacUrl = $"http://{config.localHost}:{config.localPort}/pac?t={GetTimestamp(DateTime.Now)}{PacSecret}"; } @@ -101,7 +101,7 @@ namespace Shadowsocks.Controller } if (!secretMatch) { - if(line.IndexOf(PacSecret, StringComparison.Ordinal) >= 0) + if (line.IndexOf(PacSecret, StringComparison.Ordinal) >= 0) { secretMatch = true; } @@ -255,7 +255,7 @@ Connection: Close if (UserRuleFileChanged != null) { Logging.Info($"Detected: User Rule file '{e.Name}' was {e.ChangeType.ToString().ToLower()}."); - Task.Factory.StartNew(()=> + Task.Factory.StartNew(() => { ((FileSystemWatcher)sender).EnableRaisingEvents = false; System.Threading.Thread.Sleep(10); @@ -268,7 +268,9 @@ Connection: Close private string GetPACAddress(IPEndPoint localEndPoint, bool useSocks) { - return $"{(useSocks ? "SOCKS5" : "PROXY")} {localEndPoint.Address}:{_config.localPort};"; + return localEndPoint.AddressFamily == AddressFamily.InterNetworkV6 + ? $"{(useSocks ? "SOCKS5" : "PROXY")} [{localEndPoint.Address}]:{_config.localPort};" + : $"{(useSocks ? "SOCKS5" : "PROXY")} {localEndPoint.Address}:{_config.localPort};"; } } } diff --git a/shadowsocks-csharp/Controller/Service/PortForwarder.cs b/shadowsocks-csharp/Controller/Service/PortForwarder.cs index 46ec9733..1acfa5bd 100644 --- a/shadowsocks-csharp/Controller/Service/PortForwarder.cs +++ b/shadowsocks-csharp/Controller/Service/PortForwarder.cs @@ -49,7 +49,8 @@ namespace Shadowsocks.Controller _local = socket; try { - EndPoint remoteEP = SocketUtil.GetEndPoint("127.0.0.1", targetPort); + // Local Port Forward use IP as is + EndPoint remoteEP = SocketUtil.GetEndPoint(_local.AddressFamily == AddressFamily.InterNetworkV6 ? "[::1]" : "127.0.0.1", targetPort); // Connect to the remote endpoint. _remote = new WrappedSocket(); diff --git a/shadowsocks-csharp/Controller/Service/PrivoxyRunner.cs b/shadowsocks-csharp/Controller/Service/PrivoxyRunner.cs index 0258087c..187e7a09 100644 --- a/shadowsocks-csharp/Controller/Service/PrivoxyRunner.cs +++ b/shadowsocks-csharp/Controller/Service/PrivoxyRunner.cs @@ -49,10 +49,14 @@ namespace Shadowsocks.Controller KillProcess(p); } string privoxyConfig = Resources.privoxy_conf; - _runningPort = GetFreePort(); + _runningPort = GetFreePort(configuration.isIPv6Enabled); privoxyConfig = privoxyConfig.Replace("__SOCKS_PORT__", configuration.localPort.ToString()); privoxyConfig = privoxyConfig.Replace("__PRIVOXY_BIND_PORT__", _runningPort.ToString()); - privoxyConfig = privoxyConfig.Replace("__PRIVOXY_BIND_IP__", configuration.shareOverLan ? "0.0.0.0" : "127.0.0.1"); + privoxyConfig = configuration.isIPv6Enabled + ? privoxyConfig.Replace("__PRIVOXY_BIND_IP__", configuration.shareOverLan ? "[::]" : "[::1]") + .Replace("__SOCKS_HOST__", "[::1]") + : privoxyConfig.Replace("__PRIVOXY_BIND_IP__", configuration.shareOverLan ? "0.0.0.0" : "127.0.0.1") + .Replace("__SOCKS_HOST__", "127.0.0.1"); FileManager.ByteArrayToFile(Utils.GetTempPath(_uniqueConfigFile), Encoding.UTF8.GetBytes(privoxyConfig)); _process = new Process @@ -140,13 +144,13 @@ namespace Shadowsocks.Controller } } - private int GetFreePort() + private int GetFreePort(bool isIPv6 = false) { int defaultPort = 8123; try { // TCP stack please do me a favor - TcpListener l = new TcpListener(IPAddress.Loopback, 0); + TcpListener l = new TcpListener(isIPv6 ? IPAddress.IPv6Loopback : IPAddress.Loopback, 0); l.Start(); var port = ((IPEndPoint)l.LocalEndpoint).Port; l.Stop(); diff --git a/shadowsocks-csharp/Controller/Service/UDPRelay.cs b/shadowsocks-csharp/Controller/Service/UDPRelay.cs index 5367e29a..5ac03839 100644 --- a/shadowsocks-csharp/Controller/Service/UDPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/UDPRelay.cs @@ -58,6 +58,19 @@ namespace Shadowsocks.Controller private IPEndPoint _localEndPoint; private IPEndPoint _remoteEndPoint; + private IPAddress GetIPAddress() + { + switch (_remote.AddressFamily) + { + case AddressFamily.InterNetwork: + return IPAddress.Any; + case AddressFamily.InterNetworkV6: + return IPAddress.IPv6Any; + default: + return IPAddress.Any; + } + } + public UDPHandler(Socket local, Server server, IPEndPoint localEndPoint) { _local = local; @@ -74,7 +87,7 @@ namespace Shadowsocks.Controller } _remoteEndPoint = new IPEndPoint(ipAddress, server.server_port); _remote = new Socket(_remoteEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp); - _remote.Bind(new IPEndPoint(IPAddress.Any, 0)); + _remote.Bind(new IPEndPoint(GetIPAddress(), 0)); } public void Send(byte[] data, int length) @@ -91,7 +104,7 @@ namespace Shadowsocks.Controller public void Receive() { - EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); + EndPoint remoteEndPoint = new IPEndPoint(GetIPAddress(), 0); Logging.Debug($"++++++Receive Server Port, size:" + _buffer.Length); _remote?.BeginReceiveFrom(_buffer, 0, _buffer.Length, 0, ref remoteEndPoint, new AsyncCallback(RecvFromCallback), null); } @@ -101,7 +114,7 @@ namespace Shadowsocks.Controller try { if (_remote == null) return; - EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); + EndPoint remoteEndPoint = new IPEndPoint(GetIPAddress(), 0); int bytesRead = _remote.EndReceiveFrom(ar, ref remoteEndPoint); byte[] dataOut = new byte[bytesRead]; diff --git a/shadowsocks-csharp/Controller/Service/UpdateChecker.cs b/shadowsocks-csharp/Controller/Service/UpdateChecker.cs index b65ac3e3..6cf562a3 100644 --- a/shadowsocks-csharp/Controller/Service/UpdateChecker.cs +++ b/shadowsocks-csharp/Controller/Service/UpdateChecker.cs @@ -84,7 +84,7 @@ namespace Shadowsocks.Controller { foreach (JObject release in result) { - var isPreRelease = (bool) release["prerelease"]; + var isPreRelease = (bool)release["prerelease"]; if (isPreRelease && !config.checkPreRelease) { continue; @@ -170,7 +170,7 @@ namespace Shadowsocks.Controller { WebClient http = new WebClient(); http.Headers.Add("User-Agent", UserAgent); - http.Proxy = new WebProxy(IPAddress.Loopback.ToString(), config.localPort); + http.Proxy = new WebProxy(config.localHost, config.localPort); return http; } @@ -189,7 +189,7 @@ namespace Shadowsocks.Controller public static Asset ParseAsset(JObject assertJObject) { - var name = (string) assertJObject["name"]; + var name = (string)assertJObject["name"]; Match match = Regex.Match(name, @"^Shadowsocks-(?\d+(?:\.\d+)*)(?:|-(?.+))\.\w+$", RegexOptions.IgnoreCase); if (match.Success) @@ -198,7 +198,7 @@ namespace Shadowsocks.Controller var asset = new Asset { - browser_download_url = (string) assertJObject["browser_download_url"], + browser_download_url = (string)assertJObject["browser_download_url"], name = name, version = version }; diff --git a/shadowsocks-csharp/Controller/System/SystemProxy.cs b/shadowsocks-csharp/Controller/System/SystemProxy.cs index dbb6548f..f02d3d11 100644 --- a/shadowsocks-csharp/Controller/System/SystemProxy.cs +++ b/shadowsocks-csharp/Controller/System/SystemProxy.cs @@ -28,7 +28,7 @@ namespace Shadowsocks.Controller { if (global) { - Sysproxy.SetIEProxy(true, true, "127.0.0.1:" + config.localPort.ToString(), null); + Sysproxy.SetIEProxy(true, true, "localhost:" + config.localPort.ToString(), null); } else { diff --git a/shadowsocks-csharp/Data/privoxy_conf.txt b/shadowsocks-csharp/Data/privoxy_conf.txt index a75aa56c..bef1c726 100644 --- a/shadowsocks-csharp/Data/privoxy_conf.txt +++ b/shadowsocks-csharp/Data/privoxy_conf.txt @@ -3,6 +3,6 @@ toggle 0 logfile ss_privoxy.log show-on-task-bar 0 activity-animation 0 -forward-socks5 / 127.0.0.1:__SOCKS_PORT__ . +forward-socks5 / __SOCKS_HOST__:__SOCKS_PORT__ . max-client-connections 2048 hide-console diff --git a/shadowsocks-csharp/Model/Configuration.cs b/shadowsocks-csharp/Model/Configuration.cs index 3bb1af3b..98c2a7ff 100644 --- a/shadowsocks-csharp/Model/Configuration.cs +++ b/shadowsocks-csharp/Model/Configuration.cs @@ -20,6 +20,7 @@ namespace Shadowsocks.Model public bool enabled; public bool shareOverLan; public bool isDefault; + public bool isIPv6Enabled = false; public int localPort; public bool portableMode = true; public string pacUrl; @@ -34,7 +35,11 @@ namespace Shadowsocks.Model public HotkeyConfig hotkey; private static readonly string CONFIG_FILE = "gui-config.json"; - + [JsonIgnore] + public string localHost => GetLocalHost(); + private string GetLocalHost() { + return isIPv6Enabled ? "[::1]" : "127.0.0.1"; + } public Server GetCurrentServer() { if (index >= 0 && index < configs.Count) @@ -86,6 +91,10 @@ namespace Shadowsocks.Model config.proxy = new ProxyConfig(); if (config.hotkey == null) config.hotkey = new HotkeyConfig(); + if (!System.Net.Sockets.Socket.OSSupportsIPv6) { + config.isIPv6Enabled = false; // disable IPv6 if os not support + } + //TODO if remote host(server) do not support IPv6 (or DNS resolve AAAA TYPE record) disable IPv6? config.proxy.CheckConfig(); @@ -192,7 +201,8 @@ namespace Shadowsocks.Model public static void CheckTimeout(int timeout, int maxTimeout) { if (timeout <= 0 || timeout > maxTimeout) - throw new ArgumentException(I18N.GetString("Timeout is invalid, it should not exceed {0}", maxTimeout)); + throw new ArgumentException( + I18N.GetString("Timeout is invalid, it should not exceed {0}", maxTimeout)); } public static void CheckProxyAuthUser(string user)