Support local IPv6 listener # Conflicts: # shadowsocks-csharp/Model/Configuration.cstags/4.1.7
@@ -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) | |||
@@ -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};"; | |||
} | |||
} | |||
} |
@@ -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(); | |||
@@ -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(); | |||
@@ -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]; | |||
@@ -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-(?<version>\d+(?:\.\d+)*)(?:|-(?<suffix>.+))\.\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 | |||
}; | |||
@@ -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 | |||
{ | |||
@@ -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 |
@@ -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) | |||