diff --git a/shadowsocks-csharp/Controller/Listener.cs b/shadowsocks-csharp/Controller/Listener.cs index 48c9ea81..7ce066aa 100755 --- a/shadowsocks-csharp/Controller/Listener.cs +++ b/shadowsocks-csharp/Controller/Listener.cs @@ -260,7 +260,11 @@ public void AcceptCallback(IAsyncResult ar) conn.Close(); } - if ((_authUser ?? "").Length == 0 && !Util.Utils.isLAN(conn)) + int local_port = ((IPEndPoint)conn.LocalEndPoint).Port; + + if ((_authUser ?? "").Length == 0 && !Util.Utils.isLAN(conn) + && !(_config.GetPortMapCache().ContainsKey(local_port) + || _config.GetPortMapCache()[local_port].type == PortMapType.Forward)) { conn.Shutdown(SocketShutdown.Both); conn.Close(); @@ -273,7 +277,6 @@ public void AcceptCallback(IAsyncResult ar) buf }; - int local_port = ((IPEndPoint)conn.LocalEndPoint).Port; if (!_config.GetPortMapCache().ContainsKey(local_port) || _config.GetPortMapCache()[local_port].type != PortMapType.Forward) { conn.BeginReceive(buf, 0, buf.Length, 0, diff --git a/shadowsocks-csharp/Controller/Local.cs b/shadowsocks-csharp/Controller/Local.cs index 890c25b0..4e1b7103 100644 --- a/shadowsocks-csharp/Controller/Local.cs +++ b/shadowsocks-csharp/Controller/Local.cs @@ -103,7 +103,7 @@ public bool Handle(byte[] firstPacket, int length, Socket socket) } } - class HandlerConfig + class HandlerConfig : ICloneable { public string targetHost; public int targetPort; @@ -111,6 +111,7 @@ class HandlerConfig public Double TTL = 0; // Second public Double connect_timeout = 0; public int try_keep_alive = 0; + public string local_dns_servers; public string dns_servers; public bool fouce_local_dns_query = false; // Server proxy @@ -127,6 +128,31 @@ class HandlerConfig public int reconnectTimes = 0; public bool random = false; public bool forceRandom = false; + + public object Clone() + { + HandlerConfig obj = new HandlerConfig(); + obj.targetHost = targetHost; + obj.targetPort = targetPort; + obj.TTL = TTL; + obj.connect_timeout = connect_timeout; + obj.try_keep_alive = try_keep_alive; + obj.local_dns_servers = local_dns_servers; + obj.dns_servers = dns_servers; + obj.fouce_local_dns_query = fouce_local_dns_query; + obj.proxyType = proxyType; + obj.socks5RemoteHost = socks5RemoteHost; + obj.socks5RemotePort = socks5RemotePort; + obj.socks5RemoteUsername = socks5RemoteUsername; + obj.socks5RemotePassword = socks5RemotePassword; + obj.proxyUserAgent = proxyUserAgent; + obj.autoSwitchOff = autoSwitchOff; + obj.reconnectTimesRemain = reconnectTimesRemain; + obj.reconnectTimes = reconnectTimes; + obj.random = random; + obj.forceRandom = forceRandom; + return obj; + } } class Handler @@ -230,7 +256,7 @@ private void ResetTimeout(double time, bool reset_keep_alive = true) } } } - else + else if (!closed) { if (lastTimerSetTime != null && (DateTime.Now - lastTimerSetTime).TotalMilliseconds > 500) { @@ -459,7 +485,7 @@ public bool ReConnect() handler.select_server = select_server; handler.connection = connection; handler.connectionUDP = connectionUDP; - handler.cfg = cfg; + handler.cfg = cfg.Clone() as HandlerConfig; handler.cfg.reconnectTimesRemain = cfg.reconnectTimesRemain - 1; handler.cfg.reconnectTimes = cfg.reconnectTimes + 1; @@ -505,7 +531,15 @@ public void Start(byte[] firstPacket, int length, string rsp_protocol) connectionSendBufferList.Add(data); remoteHeaderSendBuffer = data; - Connect(); + if (cfg.reconnectTimes > 0) + { + InvokeHandler handler = () => Connect(); + handler.BeginInvoke(null, null); + } + else + { + Connect(); + } } else { @@ -745,8 +779,6 @@ public void Close() } this.State = ConnectState.END; } - getCurrentServer = null; - keepCurrentServer = null; } if (!reconnect) @@ -761,19 +793,24 @@ public void Close() connection = null; connectionUDP = null; } - - detector = null; - speedTester = null; - random = null; - remoteUDPRecvBuffer = null; - - server = null; - select_server = null; } catch (Exception e) { Logging.LogUsefulException(e); } + + getCurrentServer = null; + keepCurrentServer = null; + + detector = null; + speedTester = null; + random = null; + remoteUDPRecvBuffer = null; + + server = null; + select_server = null; + + cfg = null; } private bool ConnectProxyServer(string strRemoteHost, int iRemotePort) @@ -835,10 +872,10 @@ private void Connect() } } speedTester.server = server.server; - Logging.Info($"Connect {cfg.targetHost}:{cfg.targetPort.ToString()}"); + Logging.Info($"Connect {cfg.targetHost}:{cfg.targetPort.ToString()} via {server.server}:{server.server_port}"); ResetTimeout(cfg.TTL); - if (cfg.fouce_local_dns_query && cfg.targetHost != null) + if (cfg.targetHost != null) { IPAddress ipAddress; @@ -858,6 +895,7 @@ private void Connect() ipAddress = Utils.QueryDns(host, null); } } + Logging.Info($"DNS nolock query {host} answer {ipAddress.ToString()}"); if (ipAddress != null) { Utils.DnsBuffer.Set(host, new IPAddress(ipAddress.GetAddressBytes())); @@ -899,18 +937,20 @@ private void Connect() bool parsed = IPAddress.TryParse(serverHost, out ipAddress); if (!parsed) { + if (server.ServerSpeedLog().ErrorContinurousTimes > 10) + server.DnsBuffer().force_expired = true; if (server.DnsBuffer().isExpired(serverHost)) { bool dns_ok = false; { DnsBuffer buf = server.DnsBuffer(); - lock (buf) + if (Monitor.TryEnter(buf, buf.ip != null ? 100 : 1000000)) { if (buf.isExpired(serverHost)) { if (serverHost.IndexOf('.') >= 0) { - ipAddress = Util.Utils.QueryDns(serverHost, cfg.dns_servers); + ipAddress = Util.Utils.QueryDns(serverHost, cfg.local_dns_servers); } else { @@ -927,6 +967,15 @@ private void Connect() ipAddress = buf.ip; dns_ok = true; } + Monitor.Exit(buf); + } + else + { + if (buf.ip != null) + { + ipAddress = buf.ip; + dns_ok = true; + } } } if (!dns_ok) diff --git a/shadowsocks-csharp/Controller/ProxyAuth.cs b/shadowsocks-csharp/Controller/ProxyAuth.cs index d41e8ca3..6c5341b1 100644 --- a/shadowsocks-csharp/Controller/ProxyAuth.cs +++ b/shadowsocks-csharp/Controller/ProxyAuth.cs @@ -548,6 +548,10 @@ private void Connect() handler.cfg.TTL = _config.TTL; handler.cfg.connect_timeout = _config.connectTimeout; handler.cfg.autoSwitchOff = _config.autoBan; + if (!string.IsNullOrEmpty(_config.localDnsServer)) + { + handler.cfg.local_dns_servers = _config.localDnsServer; + } if (!string.IsNullOrEmpty(_config.dnsServer)) { handler.cfg.dns_servers = _config.dnsServer; diff --git a/shadowsocks-csharp/Controller/Socks5Forwarder.cs b/shadowsocks-csharp/Controller/Socks5Forwarder.cs index 9cc0e9f6..a4b472ec 100644 --- a/shadowsocks-csharp/Controller/Socks5Forwarder.cs +++ b/shadowsocks-csharp/Controller/Socks5Forwarder.cs @@ -302,14 +302,14 @@ private void Connect() } if (ipAddress == null) { - ipAddress = Utils.DnsBuffer.Get(_remote_host); + ipAddress = Utils.LocalDnsBuffer.Get(_remote_host); } } if (ipAddress == null) { if (_remote_host.IndexOf('.') >= 0) { - ipAddress = Util.Utils.QueryDns(_remote_host, _config.dnsServer); + ipAddress = Util.Utils.QueryDns(_remote_host, _config.localDnsServer); } else { @@ -318,8 +318,8 @@ private void Connect() } if (ipAddress != null) { - Utils.DnsBuffer.Set(_remote_host, new IPAddress(ipAddress.GetAddressBytes())); - Utils.DnsBuffer.Sweep(); + Utils.LocalDnsBuffer.Set(_remote_host, new IPAddress(ipAddress.GetAddressBytes())); + Utils.LocalDnsBuffer.Sweep(); } else { diff --git a/shadowsocks-csharp/Model/Configuration.cs b/shadowsocks-csharp/Model/Configuration.cs index 5962ffd5..46b167b8 100755 --- a/shadowsocks-csharp/Model/Configuration.cs +++ b/shadowsocks-csharp/Model/Configuration.cs @@ -111,6 +111,7 @@ public class Configuration public int localPort; public string localAuthPassword; + public string localDnsServer; public string dnsServer; public int reconnectTimes; public string balanceAlgorithm; @@ -146,9 +147,8 @@ public class Configuration public Dictionary portMap = new Dictionary(); private Dictionary serverStrategyMap = new Dictionary(); - private Dictionary uri2time = new Dictionary(); - private SortedDictionary time2uri = new SortedDictionary(); private Dictionary portMapCache = new Dictionary(); + private LRUCache uricache = new LRUCache(180); private static string CONFIG_FILE = "gui-config.json"; @@ -174,9 +174,9 @@ public bool KeepCurrentServer(int localPort, string targetAddr, string id) serverStrategyMap[localPort] = new ServerSelectStrategy(); ServerSelectStrategy serverStrategy = serverStrategyMap[localPort]; - if (uri2time.ContainsKey(targetAddr)) + if (uricache.ContainsKey(targetAddr)) { - UriVisitTime visit = uri2time[targetAddr]; + UriVisitTime visit = uricache.Get(targetAddr); int index = -1; for (int i = 0; i < configs.Count; ++i) { @@ -188,11 +188,7 @@ public bool KeepCurrentServer(int localPort, string targetAddr, string id) } if (index >= 0 && visit.index == index && configs[index].enable) { - time2uri.Remove(visit); - visit.index = index; - visit.visitTime = DateTime.Now; - uri2time[targetAddr] = visit; - time2uri[visit] = targetAddr; + uricache.Del(targetAddr); return true; } } @@ -209,25 +205,14 @@ public Server GetCurrentServer(int localPort, ServerSelectStrategy.FilterFunc fi serverStrategyMap[localPort] = new ServerSelectStrategy(); ServerSelectStrategy serverStrategy = serverStrategyMap[localPort]; - foreach (KeyValuePair p in time2uri) + uricache.SetTimeout(keepVisitTime); + uricache.Sweep(); + if (sameHostForSameTarget && !forceRandom && targetAddr != null && uricache.ContainsKey(targetAddr)) { - if ((DateTime.Now - p.Key.visitTime).TotalSeconds < keepVisitTime) - break; - - uri2time.Remove(p.Value); - time2uri.Remove(p.Key); - break; - } - if (sameHostForSameTarget && !forceRandom && targetAddr != null && uri2time.ContainsKey(targetAddr)) - { - UriVisitTime visit = uri2time[targetAddr]; + UriVisitTime visit = uricache.Get(targetAddr); if (visit.index < configs.Count && configs[visit.index].enable && configs[visit.index].ServerSpeedLog().ErrorContinurousTimes == 0) { - //uri2time.Remove(targetURI); - time2uri.Remove(visit); - visit.visitTime = DateTime.Now; - uri2time[targetAddr] = visit; - time2uri[visit] = targetAddr; + uricache.Del(targetAddr); return configs[visit.index]; } } @@ -273,12 +258,7 @@ public Server GetCurrentServer(int localPort, ServerSelectStrategy.FilterFunc fi visit.uri = targetAddr; visit.index = index; visit.visitTime = DateTime.Now; - if (uri2time.ContainsKey(targetAddr)) - { - time2uri.Remove(uri2time[targetAddr]); - } - uri2time[targetAddr] = visit; - time2uri[visit] = targetAddr; + uricache.Set(targetAddr, visit); } return configs[index]; } @@ -308,12 +288,7 @@ public Server GetCurrentServer(int localPort, ServerSelectStrategy.FilterFunc fi visit.uri = targetAddr; visit.index = selIndex; visit.visitTime = DateTime.Now; - if (uri2time.ContainsKey(targetAddr)) - { - time2uri.Remove(uri2time[targetAddr]); - } - uri2time[targetAddr] = visit; - time2uri[visit] = targetAddr; + uricache.Set(targetAddr, visit); } return configs[selIndex]; } @@ -380,6 +355,8 @@ public void FlushPortMapCache() if (!portMapCache.ContainsKey(localPort)) serverStrategyMap.Remove(localPort); } + + uricache.Clear(); } public Dictionary GetPortMapCache() @@ -413,6 +390,7 @@ public Configuration() keepVisitTime = 180; connectTimeout = 5; dnsServer = ""; + localDnsServer = ""; balanceAlgorithm = "LowException"; random = true; @@ -445,6 +423,7 @@ public void CopyFrom(Configuration config) TTL = config.TTL; connectTimeout = config.connectTimeout; dnsServer = config.dnsServer; + localDnsServer = config.localDnsServer; proxyEnable = config.proxyEnable; pacDirectGoProxy = config.pacDirectGoProxy; proxyType = config.proxyType; diff --git a/shadowsocks-csharp/Model/LRUCache.cs b/shadowsocks-csharp/Model/LRUCache.cs index afb0a035..21a6d375 100644 --- a/shadowsocks-csharp/Model/LRUCache.cs +++ b/shadowsocks-csharp/Model/LRUCache.cs @@ -23,6 +23,16 @@ public void SetTimeout(int time) _sweep_time = time; } + public void Clear() + { + lock (_lock) + { + _store.Clear(); + _key_2_time.Clear(); + _time_2_key.Clear(); + } + } + public bool isTimeout(K key) { lock (_lock) diff --git a/shadowsocks-csharp/Model/Server.cs b/shadowsocks-csharp/Model/Server.cs index 702f32e5..d1e353df 100755 --- a/shadowsocks-csharp/Model/Server.cs +++ b/shadowsocks-csharp/Model/Server.cs @@ -19,10 +19,12 @@ public class DnsBuffer public IPAddress ip; public DateTime updateTime; public string host; - public bool isExpired(string host) + public bool force_expired; + public bool isExpired(string host) { if (updateTime == null) return true; if (this.host != host) return true; + if (force_expired && (DateTime.Now - updateTime).TotalMinutes > 1) return true; return (DateTime.Now - updateTime).TotalMinutes > 30; } public void UpdateDns(string host, IPAddress ip) @@ -30,6 +32,7 @@ public void UpdateDns(string host, IPAddress ip) updateTime = DateTime.Now; this.ip = new IPAddress(ip.GetAddressBytes()); this.host = host; + force_expired = false; } } @@ -104,8 +107,8 @@ public class Server { public string id; public string server; - public int server_port; - public int server_udp_port; + public ushort server_port; + public ushort server_udp_port; public string password; public string method; public string protocol; @@ -379,7 +382,7 @@ public void ServerFromSSR(string ssrURL, string force_group) throw new FormatException(); server = match.Groups[1].Value; - server_port = int.Parse(match.Groups[2].Value); + server_port = ushort.Parse(match.Groups[2].Value); protocol = match.Groups[3].Value.Length == 0 ? "origin" : match.Groups[3].Value; protocol = protocol.Replace("_compatible", ""); method = match.Groups[4].Value; @@ -411,7 +414,7 @@ public void ServerFromSSR(string ssrURL, string force_group) } if (params_dict.ContainsKey("udpport")) { - server_udp_port = int.Parse(params_dict["udpport"]); + server_udp_port = ushort.Parse(params_dict["udpport"]); } if (!String.IsNullOrEmpty(force_group)) group = force_group; @@ -434,7 +437,7 @@ public void ServerFromSS(string ssURL, string force_group) method = match.Groups["method"].Value; password = match.Groups["password"].Value; server = match.Groups["hostname"].Value; - server_port = int.Parse(match.Groups["port"].Value); + server_port = ushort.Parse(match.Groups["port"].Value); if (!String.IsNullOrEmpty(force_group)) group = force_group; else diff --git a/shadowsocks-csharp/Obfs/AuthAkarin.cs b/shadowsocks-csharp/Obfs/AuthAkarin.cs new file mode 100644 index 00000000..11386fa5 --- /dev/null +++ b/shadowsocks-csharp/Obfs/AuthAkarin.cs @@ -0,0 +1,718 @@ +using System; +using System.Collections.Generic; +using System.Security.Cryptography; +using System.Text; +using Shadowsocks.Controller; +using Shadowsocks.Encryption; + +namespace Shadowsocks.Obfs +{ + class AuthAkarin : VerifySimpleBase + { + protected class AuthDataAesChain : AuthData + { + } + + public AuthAkarin(string method) + : base(method) + { + has_sent_header = false; + has_recv_header = false; + pack_id = 1; + recv_id = 1; + SALT = method; + byte[] bytes = new byte[4]; + g_random.GetBytes(bytes); + random = new Random(BitConverter.ToInt32(bytes, 0)); + } + + private static Dictionary _obfs = new Dictionary { + {"auth_akarin_rand", new int[]{1, 0, 1}}, + }; + + protected bool has_sent_header; + protected bool has_recv_header; + protected static RNGCryptoServiceProvider g_random = new RNGCryptoServiceProvider(); + protected string SALT; + + protected uint pack_id; + protected uint recv_id; + protected byte[] user_key; + protected byte[] user_id; + protected byte[] send_buffer; + protected int last_datalength; + protected byte[] last_client_hash; + protected byte[] last_server_hash; + protected xorshift128plus random_client = new xorshift128plus(0); + protected xorshift128plus random_server = new xorshift128plus(0); + protected IEncryptor encryptor; + protected int send_tcp_mss = 2000; + protected int recv_tcp_mss = 2000; + protected List send_back_cmd = new List(); + + protected const int overhead = 4; + + public static List SupportedObfs() + { + return new List(_obfs.Keys); + } + + public override Dictionary GetObfs() + { + return _obfs; + } + + public override object InitData() + { + return new AuthDataAesChain(); + } + + public override bool isKeepAlive() + { + return true; + } + + public override bool isAlwaysSendback() + { + return true; + } + + public override int GetOverhead() + { + return overhead; + } + + protected MbedTLS.HMAC CreateHMAC(byte[] key) + { + return new MbedTLS.HMAC_MD5(key); + } + + protected virtual int GetSendRandLen(int datalength, xorshift128plus random, byte[] last_hash) + { + if (datalength + Server.overhead > send_tcp_mss) + { + random.init_from_bin(last_hash, datalength); + return (int)(random.next() % 521); + } + if (datalength >= 1440 || datalength + Server.overhead == recv_tcp_mss) + return 0; + random.init_from_bin(last_hash, datalength); + if (datalength > 1300) + return (int)(random.next() % 31); + if (datalength > 900) + return (int)(random.next() % 127); + if (datalength > 400) + return (int)(random.next() % 521); + return (int)(random.next() % (ulong)(send_tcp_mss - datalength - Server.overhead)); + //return (int)(random.next() % 1021); + } + + protected virtual int GetRecvRandLen(int datalength, xorshift128plus random, byte[] last_hash) + { + if (datalength + Server.overhead > recv_tcp_mss) + { + random.init_from_bin(last_hash, datalength); + return (int)(random.next() % 521); + } + if (datalength >= 1440 || datalength + Server.overhead == recv_tcp_mss) + return 0; + random.init_from_bin(last_hash, datalength); + if (datalength > 1300) + return (int)(random.next() % 31); + if (datalength > 900) + return (int)(random.next() % 127); + if (datalength > 400) + return (int)(random.next() % 521); + return (int)(random.next() % (ulong)(recv_tcp_mss - datalength - Server.overhead)); + //return (int)(random.next() % 1021); + } + + protected int UdpGetRandLen(xorshift128plus random, byte[] last_hash) + { + random.init_from_bin(last_hash); + return (int)(random.next() % 127); + } + + protected int GetSendRandLen(int datalength) + { + return GetSendRandLen(datalength, random_client, last_client_hash); + } + + public void PackData(byte[] data, int datalength, byte[] outdata, out int outlength) + { + int cmdlen = 0; + int rand_len; + int start_pos = 2; + if (send_back_cmd.Count > 0) + { + cmdlen += 2; + //TODO + send_tcp_mss = recv_tcp_mss; + rand_len = GetSendRandLen(datalength + cmdlen); + outlength = rand_len + datalength + cmdlen + 2; + start_pos += cmdlen; + outdata[0] = (byte)(send_back_cmd[0] ^ last_client_hash[14]); + outdata[1] = (byte)((send_back_cmd[0] >> 8) ^ last_client_hash[15]); + outdata[2] = (byte)(datalength ^ last_client_hash[12]); + outdata[3] = (byte)((datalength >> 8) ^ last_client_hash[13]); + send_back_cmd.Clear(); + } + else + { + rand_len = GetSendRandLen(datalength); + outlength = rand_len + datalength + 2; + outdata[0] = (byte)(datalength ^ last_client_hash[14]); + outdata[1] = (byte)((datalength >> 8) ^ last_client_hash[15]); + } + { + byte[] rnd_data = new byte[rand_len]; + random.NextBytes(rnd_data); + encryptor.Encrypt(data, datalength, data, out datalength); + if (datalength > 0) + { + if (rand_len > 0) + { + Array.Copy(data, 0, outdata, start_pos, datalength); + Array.Copy(rnd_data, 0, outdata, start_pos + datalength, rand_len); + } + else + { + Array.Copy(data, 0, outdata, start_pos, datalength); + } + } + else + { + rnd_data.CopyTo(outdata, start_pos); + } + } + + byte[] key = new byte[user_key.Length + 4]; + user_key.CopyTo(key, 0); + BitConverter.GetBytes(pack_id).CopyTo(key, key.Length - 4); + + MbedTLS.HMAC md5 = CreateHMAC(key); + ++pack_id; + { + byte[] md5data = md5.ComputeHash(outdata, 0, outlength); + last_client_hash = md5data; + Array.Copy(md5data, 0, outdata, outlength, 2); + outlength += 2; + } + } + + public void PackAuthData(byte[] data, int datalength, byte[] outdata, out int outlength) + { + const int authhead_len = 4 + 8 + 4 + 16 + 4; + byte[] encrypt = new byte[24]; + AuthDataAesChain authData = this.Server.data as AuthDataAesChain; + + lock (authData) + { + if (authData.connectionID > 0xFF000000) + { + authData.clientID = null; + } + if (authData.clientID == null) + { + authData.clientID = new byte[4]; + g_random.GetBytes(authData.clientID); + authData.connectionID = (UInt32)BitConverter.ToInt32(authData.clientID, 0) % 0xFFFFFD; + } + authData.connectionID += 1; + Array.Copy(authData.clientID, 0, encrypt, 4, 4); + Array.Copy(BitConverter.GetBytes(authData.connectionID), 0, encrypt, 8, 4); + } + + outlength = authhead_len; + byte[] encrypt_data = new byte[32]; + byte[] key = new byte[Server.iv.Length + Server.key.Length]; + Server.iv.CopyTo(key, 0); + Server.key.CopyTo(key, Server.iv.Length); + + UInt64 utc_time_second = (UInt64)Math.Floor(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds); + UInt32 utc_time = (UInt32)(utc_time_second); + Array.Copy(BitConverter.GetBytes(utc_time), 0, encrypt, 0, 4); + + encrypt[12] = (byte)(Server.overhead); + encrypt[13] = (byte)(Server.overhead >> 8); + send_tcp_mss = 1024; //random.Next(1024) + 400; + recv_tcp_mss = send_tcp_mss; + encrypt[14] = (byte)(send_tcp_mss); + encrypt[15] = (byte)(send_tcp_mss >> 8); + + // first 12 bytes + { + byte[] rnd = new byte[4]; + random.NextBytes(rnd); + rnd.CopyTo(outdata, 0); + MbedTLS.HMAC md5 = CreateHMAC(key); + byte[] md5data = md5.ComputeHash(rnd, 0, rnd.Length); + last_client_hash = md5data; + Array.Copy(md5data, 0, outdata, rnd.Length, 8); + } + // uid & 16 bytes auth data + { + byte[] uid = new byte[4]; + int index_of_split = Server.param.IndexOf(':'); + if (index_of_split > 0) + { + try + { + uint user = uint.Parse(Server.param.Substring(0, index_of_split)); + user_key = System.Text.Encoding.UTF8.GetBytes(Server.param.Substring(index_of_split + 1)); + BitConverter.GetBytes(user).CopyTo(uid, 0); + } + catch (Exception ex) + { + Logging.Log(LogLevel.Warn, $"Faild to parse auth param, fallback to basic mode. {ex}"); + } + } + if (user_key == null) + { + random.NextBytes(uid); + user_key = Server.key; + } + for (int i = 0; i < 4; ++i) + { + uid[i] ^= last_client_hash[8 + i]; + } + + byte[] encrypt_key = user_key; + + Encryption.IEncryptor encryptor = Encryption.EncryptorFactory.GetEncryptor("aes-128-cbc", System.Convert.ToBase64String(encrypt_key) + SALT, false); + int enc_outlen; + + encryptor.SetIV(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }); + encryptor.Encrypt(encrypt, 16, encrypt_data, out enc_outlen); + encryptor.Dispose(); + Array.Copy(encrypt_data, 0, encrypt, 4, 16); + uid.CopyTo(encrypt, 0); + } + // final HMAC + { + MbedTLS.HMAC md5 = CreateHMAC(user_key); + byte[] md5data = md5.ComputeHash(encrypt, 0, 20); + last_server_hash = md5data; + Array.Copy(md5data, 0, encrypt, 20, 4); + } + encrypt.CopyTo(outdata, 12); + encryptor = EncryptorFactory.GetEncryptor("chacha20", System.Convert.ToBase64String(user_key) + System.Convert.ToBase64String(last_client_hash, 0, 16), false); + { + byte[] iv = new byte[8]; + Array.Copy(last_client_hash, iv, 8); + encryptor.SetIV(iv); + } + { + int pack_outlength; + encryptor.Decrypt(last_server_hash, 8, outdata, out pack_outlength); + } + + // combine first chunk + { + byte[] pack_outdata = new byte[outdata.Length]; + int pack_outlength; + PackData(data, datalength, pack_outdata, out pack_outlength); + Array.Copy(pack_outdata, 0, outdata, outlength, pack_outlength); + outlength += pack_outlength; + } + } + + // plaindata == null try send buffer data, return null if empty buffer + // datalength == 0 sendback, return 0 + // datalength == -1 keepalive + public override byte[] ClientPreEncrypt(byte[] plaindata, int datalength, out int outlength) + { + byte[] outdata = new byte[datalength + datalength / 10 + 32]; + byte[] packdata = new byte[9000]; + byte[] data = plaindata == null ? send_buffer : plaindata; + outlength = 0; + if (data == null) + { + return null; + } + else if (data == send_buffer) + { + datalength = send_buffer.Length; + send_buffer = null; + } + else if (send_buffer != null) + { + if (datalength <= 0) + { + return outdata; + } + else + { + Array.Resize(ref send_buffer, send_buffer.Length + datalength); + Array.Copy(data, 0, send_buffer, send_buffer.Length - datalength, datalength); + data = send_buffer; + datalength = send_buffer.Length; + send_buffer = null; + } + } + int unit_len = Server.tcp_mss - Server.overhead; + int ogn_datalength = datalength; + if (!has_sent_header) + { + int _datalength = Math.Min(1200, datalength); + int outlen; + PackAuthData(data, _datalength, packdata, out outlen); + has_sent_header = true; + Util.Utils.SetArrayMinSize2(ref outdata, outlength + outlen); + Array.Copy(packdata, 0, outdata, outlength, outlen); + outlength += outlen; + datalength -= _datalength; + byte[] newdata = new byte[datalength]; + Array.Copy(data, _datalength, newdata, 0, newdata.Length); + data = newdata; + + send_buffer = data.Length > 0 ? data : null; + + return outdata; + } + + if (datalength > 120 * 4 && pack_id < 32) + { + int send_len = LinearRandomInt(120 * 16); + if (send_len < datalength) + { + send_len = TrapezoidRandomInt(Math.Min(datalength - 1, Server.tcp_mss - overhead) - 1, -0.3) + 1; // must less than datalength + + send_len = datalength - send_len; + + if (send_len > 0) + { + send_buffer = new byte[send_len]; + Array.Copy(data, datalength - send_len, send_buffer, 0, send_len); + datalength -= send_len; + } + } + } + while (datalength > unit_len) + { + int outlen; + PackData(data, unit_len, packdata, out outlen); + Util.Utils.SetArrayMinSize2(ref outdata, outlength + outlen); + Array.Copy(packdata, 0, outdata, outlength, outlen); + outlength += outlen; + datalength -= unit_len; + byte[] newdata = new byte[datalength]; + Array.Copy(data, unit_len, newdata, 0, newdata.Length); + data = newdata; + } + if (datalength > 0 || ogn_datalength == -1) + { + int outlen; + if (ogn_datalength == -1) + datalength = 0; + PackData(data, datalength, packdata, out outlen); + Util.Utils.SetArrayMinSize2(ref outdata, outlength + outlen); + Array.Copy(packdata, 0, outdata, outlength, outlen); + outlength += outlen; + } + last_datalength = ogn_datalength; + return outdata; + } + + public override byte[] ClientPostDecrypt(byte[] plaindata, int datalength, out int outlength) + { + byte[] outdata = new byte[recv_buf_len + datalength]; + Array.Copy(plaindata, 0, recv_buf, recv_buf_len, datalength); + recv_buf_len += datalength; + outlength = 0; + byte[] key = new byte[user_key.Length + 4]; + user_key.CopyTo(key, 0); + while (recv_buf_len > 4) + { + BitConverter.GetBytes(recv_id).CopyTo(key, key.Length - 4); + MbedTLS.HMAC md5 = CreateHMAC(key); + + int data_len = ((recv_buf[1] ^ last_server_hash[15]) << 8) + (recv_buf[0] ^ last_server_hash[14]); + int rand_len = GetRecvRandLen(data_len, random_server, last_server_hash); + int len = rand_len + data_len; + if (len >= 4096) + { + throw new ObfsException("ClientPostDecrypt data error"); + } + if (len + 4 > recv_buf_len) + break; + + byte[] md5data = md5.ComputeHash(recv_buf, 0, len + 2); + if (md5data[0] != recv_buf[len + 2] + || md5data[1] != recv_buf[len + 3] + ) + { + throw new ObfsException("ClientPostDecrypt data uncorrect checksum"); + } + + { + int pos = 2; + int outlen = data_len; + Util.Utils.SetArrayMinSize2(ref outdata, outlength + outlen); + byte[] data = new byte[outlen]; + Array.Copy(recv_buf, pos, data, 0, outlen); + encryptor.Decrypt(data, outlen, data, out outlen); + last_server_hash = md5data; + if (recv_id == 1) + { + Server.tcp_mss = recv_tcp_mss = data[0] | (data[1] << 8); + pos = 2; + outlen -= 2; + send_back_cmd.Add(0xff00); + } + else + { + pos = 0; + } + Array.Copy(data, pos, outdata, outlength, outlen); + outlength += outlen; + recv_buf_len -= len + 4; + Array.Copy(recv_buf, len + 4, recv_buf, 0, recv_buf_len); + ++recv_id; + } + } + return outdata; + } + + public override byte[] ClientUdpPreEncrypt(byte[] plaindata, int datalength, out int outlength) + { + byte[] outdata = new byte[datalength + 1024]; + if (user_key == null) + { + user_id = new byte[4]; + int index_of_split = Server.param.IndexOf(':'); + if (index_of_split > 0) + { + try + { + uint user = uint.Parse(Server.param.Substring(0, index_of_split)); + user_key = System.Text.Encoding.UTF8.GetBytes(Server.param.Substring(index_of_split + 1)); + BitConverter.GetBytes(user).CopyTo(user_id, 0); + } + catch (Exception ex) + { + Logging.Log(LogLevel.Warn, $"Faild to parse auth param, fallback to basic mode. {ex}"); + } + } + if (user_key == null) + { + random.NextBytes(user_id); + user_key = Server.key; + } + } + byte[] auth_data = new byte[3]; + random.NextBytes(auth_data); + + MbedTLS.HMAC md5 = CreateHMAC(Server.key); + byte[] md5data = md5.ComputeHash(auth_data, 0, auth_data.Length); + int rand_len = UdpGetRandLen(random_client, md5data); + byte[] rand_data = new byte[rand_len]; + random.NextBytes(rand_data); + outlength = datalength + rand_len + 8; + encryptor = EncryptorFactory.GetEncryptor("chacha20", System.Convert.ToBase64String(user_key) + System.Convert.ToBase64String(md5data, 0, 16), false); + { + byte[] iv = new byte[8]; + Array.Copy(Server.key, iv, 8); + encryptor.SetIV(iv); + } + encryptor.Encrypt(plaindata, datalength, outdata, out datalength); + rand_data.CopyTo(outdata, datalength); + auth_data.CopyTo(outdata, outlength - 8); + byte[] uid = new byte[4]; + for (int i = 0; i < 4; ++i) + { + uid[i] = (byte)(user_id[i] ^ md5data[i]); + } + uid.CopyTo(outdata, outlength - 5); + { + md5 = CreateHMAC(user_key); + md5data = md5.ComputeHash(outdata, 0, outlength - 1); + Array.Copy(md5data, 0, outdata, outlength - 1, 1); + } + return outdata; + } + + public override byte[] ClientUdpPostDecrypt(byte[] plaindata, int datalength, out int outlength) + { + if (datalength <= 8) + { + outlength = 0; + return plaindata; + } + MbedTLS.HMAC md5 = CreateHMAC(user_key); + byte[] md5data = md5.ComputeHash(plaindata, 0, datalength - 1); + if (md5data[0] != plaindata[datalength - 1]) + { + outlength = 0; + return plaindata; + } + md5 = CreateHMAC(Server.key); + md5data = md5.ComputeHash(plaindata, datalength - 8, 7); + int rand_len = UdpGetRandLen(random_server, md5data); + outlength = datalength - rand_len - 8; + encryptor = EncryptorFactory.GetEncryptor("chacha20", System.Convert.ToBase64String(user_key) + System.Convert.ToBase64String(md5data, 0, 16), false); + { + int temp; + byte[] iv = new byte[8]; + Array.Copy(Server.key, iv, 8); + encryptor.Decrypt(iv, 8, plaindata, out temp); + } + encryptor.Decrypt(plaindata, outlength, plaindata, out outlength); + return plaindata; + } + } + + class AuthAkarin_spec_a : AuthAkarin + { + public AuthAkarin_spec_a(string method) + : base(method) + { + + } + + private static Dictionary _obfs = new Dictionary { + {"auth_akarin_spec_a", new int[]{1, 0, 1}}, + }; + + protected int[] data_size_list = null; + protected int[] data_size_list2 = null; + + public static new List SupportedObfs() + { + return new List(_obfs.Keys); + } + + public override Dictionary GetObfs() + { + return _obfs; + } + + protected void InitDataSizeList() + { + xorshift128plus random = new xorshift128plus(0); + random.init_from_bin(Server.key); + int len = (int)(random.next() % 8 + 4); + List data_list = new List(); + for (int i = 0; i < len; ++i) + { + data_list.Add((int)(random.next() % 2340 % 2040 % 1440)); + } + data_list.Sort(); + data_size_list = data_list.ToArray(); + + len = (int)(random.next() % 16 + 8); + data_list.Clear(); + for (int i = 0; i < len; ++i) + { + data_list.Add((int)(random.next() % 2340 % 2040 % 1440)); + } + data_list.Sort(); + data_size_list2 = data_list.ToArray(); + } + + public override void SetServerInfo(ServerInfo serverInfo) + { + Server = serverInfo; + InitDataSizeList(); + } + + protected int FindPos(int[] arr, int key) + { + int low = 0; + int high = arr.Length - 1; + int middle = -1; + + if (key > arr[high]) + return arr.Length; + + while (low < high) + { + middle = (low + high) / 2; + if (key > arr[middle]) + { + low = middle + 1; + } + else if (key <= arr[middle]) + { + high = middle; + } + } + return low; + } + + protected override int GetSendRandLen(int datalength, xorshift128plus random, byte[] last_hash) + { + if (datalength + Server.overhead > send_tcp_mss) + { + random.init_from_bin(last_hash, datalength); + return (int)(random.next() % 521); + } + if (datalength >= 1440 || datalength + Server.overhead == recv_tcp_mss) + return 0; + random.init_from_bin(last_hash, datalength); + + int pos = FindPos(data_size_list, datalength + Server.overhead); + int final_pos = pos + (int)(random.next() % (ulong)(data_size_list.Length)); + if (final_pos < data_size_list.Length) + { + return data_size_list[final_pos] - datalength - Server.overhead; + } + + pos = FindPos(data_size_list2, datalength + Server.overhead); + final_pos = pos + (int)(random.next() % (ulong)(data_size_list2.Length)); + if (final_pos < data_size_list2.Length) + { + return data_size_list2[final_pos] - datalength - Server.overhead; + } + if (final_pos < pos + data_size_list2.Length - 1) + { + return 0; + } + if (datalength > 1300) + return (int)(random.next() % 31); + if (datalength > 900) + return (int)(random.next() % 127); + if (datalength > 400) + return (int)(random.next() % 521); + return (int)(random.next() % 1021); + } + + protected override int GetRecvRandLen(int datalength, xorshift128plus random, byte[] last_hash) + { + if (datalength + Server.overhead > recv_tcp_mss) + { + random.init_from_bin(last_hash, datalength); + return (int)(random.next() % 521); + } + if (datalength >= 1440 || datalength + Server.overhead == recv_tcp_mss) + return 0; + random.init_from_bin(last_hash, datalength); + + int pos = FindPos(data_size_list, datalength + Server.overhead); + int final_pos = pos + (int)(random.next() % (ulong)(data_size_list.Length)); + if (final_pos < data_size_list.Length) + { + return data_size_list[final_pos] - datalength - Server.overhead; + } + + pos = FindPos(data_size_list2, datalength + Server.overhead); + final_pos = pos + (int)(random.next() % (ulong)(data_size_list2.Length)); + if (final_pos < data_size_list2.Length) + { + return data_size_list2[final_pos] - datalength - Server.overhead; + } + if (final_pos < pos + data_size_list2.Length - 1) + { + return 0; + } + if (datalength > 1300) + return (int)(random.next() % 31); + if (datalength > 900) + return (int)(random.next() % 127); + if (datalength > 400) + return (int)(random.next() % 521); + return (int)(random.next() % 1021); + } + + } +} diff --git a/shadowsocks-csharp/Obfs/ObfsFactory.cs b/shadowsocks-csharp/Obfs/ObfsFactory.cs index b255a18c..05252dd2 100644 --- a/shadowsocks-csharp/Obfs/ObfsFactory.cs +++ b/shadowsocks-csharp/Obfs/ObfsFactory.cs @@ -71,6 +71,14 @@ static ObfsFactory() { _registeredObfs.Add(method, typeof(AuthChain_f)); } + foreach (string method in AuthAkarin.SupportedObfs()) + { + _registeredObfs.Add(method, typeof(AuthAkarin)); + } + foreach (string method in AuthAkarin_spec_a.SupportedObfs()) + { + _registeredObfs.Add(method, typeof(AuthAkarin_spec_a)); + } } public static IObfs GetObfs(string method) diff --git a/shadowsocks-csharp/Program.cs b/shadowsocks-csharp/Program.cs index e153123b..ac991cc0 100755 --- a/shadowsocks-csharp/Program.cs +++ b/shadowsocks-csharp/Program.cs @@ -58,8 +58,8 @@ static void Main(string[] args) #endif Directory.SetCurrentDirectory(Application.StartupPath); - int try_times = 0; #if !_CONSOLE + int try_times = 0; while (Configuration.Load() == null) { if (try_times >= 5) @@ -73,21 +73,20 @@ static void Main(string[] args) } try_times += 1; } -#endif - //#if !DEBUG if (try_times > 0) Logging.save_to_file = false; +#endif + //#if !DEBUG Logging.OpenLogFile(); //#endif _controller = new ShadowsocksController(); HostMap.Instance().LoadHostFile(); -#if _DOTNET_CURRENT +#if _DOTNET_4_0 // Enable Modern TLS when .NET 4.5+ installed. if (Util.EnvCheck.CheckDotNet45()) ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072; #endif - #if !_CONSOLE _viewController = new MenuViewController(_controller); #endif diff --git a/shadowsocks-csharp/Util/Util.cs b/shadowsocks-csharp/Util/Util.cs index e27ba2bf..22c32406 100755 --- a/shadowsocks-csharp/Util/Util.cs +++ b/shadowsocks-csharp/Util/Util.cs @@ -32,6 +32,14 @@ public static LRUCache DnsBuffer } } + public static LRUCache LocalDnsBuffer + { + get + { + return dnsBuffer; + } + } + static Process current_process = Process.GetCurrentProcess(); public static void ReleaseMemory() diff --git a/shadowsocks-csharp/View/ConfigForm.Designer.cs b/shadowsocks-csharp/View/ConfigForm.Designer.cs index 07d84564..c21ee437 100755 --- a/shadowsocks-csharp/View/ConfigForm.Designer.cs +++ b/shadowsocks-csharp/View/ConfigForm.Designer.cs @@ -481,7 +481,10 @@ private void InitializeComponent() "auth_chain_c", "auth_chain_d", "auth_chain_e", - "auth_chain_f"}); + "auth_chain_f", + "auth_akarin_rand", + "auth_akarin_spec_a" + }); this.TCPProtocolComboBox.Location = new System.Drawing.Point(108, 117); this.TCPProtocolComboBox.Margin = new System.Windows.Forms.Padding(3, 3, 3, 7); this.TCPProtocolComboBox.Name = "TCPProtocolComboBox"; diff --git a/shadowsocks-csharp/View/ConfigForm.cs b/shadowsocks-csharp/View/ConfigForm.cs index 060317ae..5072d34f 100755 --- a/shadowsocks-csharp/View/ConfigForm.cs +++ b/shadowsocks-csharp/View/ConfigForm.cs @@ -208,8 +208,8 @@ private int SaveOldSelectedServer() Server server = new Server { server = IPTextBox.Text.Trim(), - server_port = Convert.ToInt32(NumServerPort.Value), - server_udp_port = Convert.ToInt32(NumUDPPort.Value), + server_port = Convert.ToUInt16(NumServerPort.Value), + server_udp_port = Convert.ToUInt16(NumUDPPort.Value), password = PasswordTextBox.Text, method = EncryptionSelect.Text, protocol = TCPProtocolComboBox.Text, diff --git a/shadowsocks-csharp/View/MenuViewController.cs b/shadowsocks-csharp/View/MenuViewController.cs index 5d64eda5..6bdc4b32 100755 --- a/shadowsocks-csharp/View/MenuViewController.cs +++ b/shadowsocks-csharp/View/MenuViewController.cs @@ -18,6 +18,18 @@ namespace Shadowsocks.View { + public class EventParams + { + public object sender; + public EventArgs e; + + public EventParams(object sender, EventArgs e) + { + this.sender = sender; + this.e = e; + } + } + public class MenuViewController { // yes this is just a menu view controller @@ -58,6 +70,9 @@ public class MenuViewController private string _urlToOpen; private System.Timers.Timer timerDelayCheckUpdate; + private bool configfrom_open = false; + private List eventList = new List(); + public MenuViewController(ShadowsocksController controller) { this.controller = controller; @@ -335,6 +350,11 @@ void controller_UpdatePACFromGFWListCompleted(object sender, GFWListUpdater.Resu void updateFreeNodeChecker_NewFreeNodeFound(object sender, EventArgs e) { + if (configfrom_open) + { + eventList.Add(new EventParams(sender, e)); + return; + } string lastGroup = null; int count = 0; if (!String.IsNullOrEmpty(updateFreeNodeChecker.FreeNodeResult)) @@ -419,7 +439,7 @@ void updateFreeNodeChecker_NewFreeNodeFound(object sender, EventArgs e) break; } } - if (lastGroup == null) + if (String.IsNullOrEmpty(lastGroup)) { lastGroup = curGroup; } @@ -569,10 +589,6 @@ void updateFreeNodeChecker_NewFreeNodeFound(object sender, EventArgs e) controller.SaveServersConfig(config); } } - else - { - lastGroup = updateFreeNodeChecker.subscribeTask.Group; - } if (count > 0) { @@ -582,6 +598,11 @@ void updateFreeNodeChecker_NewFreeNodeFound(object sender, EventArgs e) } else { + if (lastGroup == null) + { + lastGroup = updateFreeNodeChecker.subscribeTask.Group; + //lastGroup = updateSubscribeManager.LastGroup; + } ShowBalloonTip(I18N.GetString("Error"), String.Format(I18N.GetString("Update subscribe {0} failure"), lastGroup), ToolTipIcon.Info, 10000); } @@ -728,6 +749,7 @@ private void ShowConfigForm(bool addNode) } else { + configfrom_open = true; configForm = new ConfigForm(controller, updateChecker, addNode ? -1 : -2); configForm.Show(); configForm.Activate(); @@ -744,6 +766,7 @@ private void ShowConfigForm(int index) } else { + configfrom_open = true; configForm = new ConfigForm(controller, updateChecker, index); configForm.Show(); configForm.Activate(); @@ -855,7 +878,16 @@ private void ShowSubscribeSettingForm() void configForm_FormClosed(object sender, FormClosedEventArgs e) { configForm = null; + configfrom_open = false; Util.Utils.ReleaseMemory(); + if (eventList.Count > 0) + { + foreach (EventParams p in eventList) + { + updateFreeNodeChecker_NewFreeNodeFound(p.sender, p.e); + } + eventList.Clear(); + } } void settingsForm_FormClosed(object sender, FormClosedEventArgs e) diff --git a/shadowsocks-csharp/View/SettingsForm.Designer.cs b/shadowsocks-csharp/View/SettingsForm.Designer.cs index 9acd5a05..98b1bf50 100644 --- a/shadowsocks-csharp/View/SettingsForm.Designer.cs +++ b/shadowsocks-csharp/View/SettingsForm.Designer.cs @@ -74,6 +74,8 @@ private void InitializeComponent() this.DNSText = new System.Windows.Forms.TextBox(); this.buttonDefault = new System.Windows.Forms.Button(); this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.LocalDNSText = new System.Windows.Forms.TextBox(); this.tableLayoutPanel1.SuspendLayout(); this.tableLayoutPanel2.SuspendLayout(); this.Socks5ProxyGroup.SuspendLayout(); @@ -107,7 +109,7 @@ private void InitializeComponent() this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); - this.tableLayoutPanel1.Size = new System.Drawing.Size(581, 452); + this.tableLayoutPanel1.Size = new System.Drawing.Size(581, 479); this.tableLayoutPanel1.TabIndex = 0; // // tableLayoutPanel2 @@ -495,7 +497,7 @@ private void InitializeComponent() this.tableLayoutPanel10.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel10.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel10.RowStyles.Add(new System.Windows.Forms.RowStyle()); - this.tableLayoutPanel10.Size = new System.Drawing.Size(186, 191); + this.tableLayoutPanel10.Size = new System.Drawing.Size(186, 218); this.tableLayoutPanel10.TabIndex = 3; // // tableLayoutPanel3 @@ -509,7 +511,7 @@ private void InitializeComponent() this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 23F)); this.tableLayoutPanel3.Controls.Add(this.MyCancelButton, 1, 0); this.tableLayoutPanel3.Controls.Add(this.OKButton, 0, 0); - this.tableLayoutPanel3.Location = new System.Drawing.Point(3, 146); + this.tableLayoutPanel3.Location = new System.Drawing.Point(3, 173); this.tableLayoutPanel3.Margin = new System.Windows.Forms.Padding(3, 3, 0, 3); this.tableLayoutPanel3.Name = "tableLayoutPanel3"; this.tableLayoutPanel3.RowCount = 1; @@ -551,34 +553,37 @@ private void InitializeComponent() this.tableLayoutPanel5.ColumnCount = 2; this.tableLayoutPanel5.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); this.tableLayoutPanel5.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); - this.tableLayoutPanel5.Controls.Add(this.ReconnectLabel, 0, 3); - this.tableLayoutPanel5.Controls.Add(this.NumReconnect, 1, 3); - this.tableLayoutPanel5.Controls.Add(this.TTLLabel, 0, 5); - this.tableLayoutPanel5.Controls.Add(this.NumTTL, 1, 5); - this.tableLayoutPanel5.Controls.Add(this.labelTimeout, 0, 4); - this.tableLayoutPanel5.Controls.Add(this.NumTimeout, 1, 4); + this.tableLayoutPanel5.Controls.Add(this.ReconnectLabel, 0, 4); + this.tableLayoutPanel5.Controls.Add(this.NumReconnect, 1, 4); + this.tableLayoutPanel5.Controls.Add(this.TTLLabel, 0, 6); + this.tableLayoutPanel5.Controls.Add(this.NumTTL, 1, 6); + this.tableLayoutPanel5.Controls.Add(this.labelTimeout, 0, 5); + this.tableLayoutPanel5.Controls.Add(this.NumTimeout, 1, 5); this.tableLayoutPanel5.Controls.Add(this.DNSText, 1, 1); this.tableLayoutPanel5.Controls.Add(this.buttonDefault, 1, 0); this.tableLayoutPanel5.Controls.Add(this.label2, 0, 1); + this.tableLayoutPanel5.Controls.Add(this.label3, 0, 2); + this.tableLayoutPanel5.Controls.Add(this.LocalDNSText, 1, 2); this.tableLayoutPanel5.Location = new System.Drawing.Point(0, 0); this.tableLayoutPanel5.Margin = new System.Windows.Forms.Padding(0); this.tableLayoutPanel5.Name = "tableLayoutPanel5"; this.tableLayoutPanel5.Padding = new System.Windows.Forms.Padding(3); - this.tableLayoutPanel5.RowCount = 6; + this.tableLayoutPanel5.RowCount = 7; this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle()); - this.tableLayoutPanel5.Size = new System.Drawing.Size(186, 143); + this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel5.Size = new System.Drawing.Size(186, 170); this.tableLayoutPanel5.TabIndex = 3; // // ReconnectLabel // this.ReconnectLabel.Anchor = System.Windows.Forms.AnchorStyles.Right; this.ReconnectLabel.AutoSize = true; - this.ReconnectLabel.Location = new System.Drawing.Point(6, 66); + this.ReconnectLabel.Location = new System.Drawing.Point(6, 93); this.ReconnectLabel.Name = "ReconnectLabel"; this.ReconnectLabel.Size = new System.Drawing.Size(59, 12); this.ReconnectLabel.TabIndex = 3; @@ -587,7 +592,7 @@ private void InitializeComponent() // NumReconnect // this.NumReconnect.ImeMode = System.Windows.Forms.ImeMode.Off; - this.NumReconnect.Location = new System.Drawing.Point(71, 62); + this.NumReconnect.Location = new System.Drawing.Point(71, 89); this.NumReconnect.Maximum = new decimal(new int[] { 20, 0, @@ -601,7 +606,7 @@ private void InitializeComponent() // this.TTLLabel.Anchor = System.Windows.Forms.AnchorStyles.Right; this.TTLLabel.AutoSize = true; - this.TTLLabel.Location = new System.Drawing.Point(42, 120); + this.TTLLabel.Location = new System.Drawing.Point(42, 147); this.TTLLabel.Name = "TTLLabel"; this.TTLLabel.Size = new System.Drawing.Size(23, 12); this.TTLLabel.TabIndex = 3; @@ -610,7 +615,7 @@ private void InitializeComponent() // NumTTL // this.NumTTL.ImeMode = System.Windows.Forms.ImeMode.Off; - this.NumTTL.Location = new System.Drawing.Point(71, 116); + this.NumTTL.Location = new System.Drawing.Point(71, 143); this.NumTTL.Maximum = new decimal(new int[] { 600, 0, @@ -624,7 +629,7 @@ private void InitializeComponent() // this.labelTimeout.Anchor = System.Windows.Forms.AnchorStyles.Right; this.labelTimeout.AutoSize = true; - this.labelTimeout.Location = new System.Drawing.Point(12, 93); + this.labelTimeout.Location = new System.Drawing.Point(12, 120); this.labelTimeout.Name = "labelTimeout"; this.labelTimeout.Size = new System.Drawing.Size(53, 12); this.labelTimeout.TabIndex = 3; @@ -633,7 +638,7 @@ private void InitializeComponent() // NumTimeout // this.NumTimeout.ImeMode = System.Windows.Forms.ImeMode.Off; - this.NumTimeout.Location = new System.Drawing.Point(71, 89); + this.NumTimeout.Location = new System.Drawing.Point(71, 116); this.NumTimeout.Maximum = new decimal(new int[] { 60, 0, @@ -673,6 +678,26 @@ private void InitializeComponent() this.label2.TabIndex = 3; this.label2.Text = "DNS"; // + // label3 + // + this.label3.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(6, 66); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(59, 12); + this.label3.TabIndex = 3; + this.label3.Text = "Local DNS"; + // + // LocalDNSText + // + this.LocalDNSText.ImeMode = System.Windows.Forms.ImeMode.Off; + this.LocalDNSText.Location = new System.Drawing.Point(71, 62); + this.LocalDNSText.MaxLength = 0; + this.LocalDNSText.Name = "LocalDNSText"; + this.LocalDNSText.Size = new System.Drawing.Size(109, 21); + this.LocalDNSText.TabIndex = 17; + this.LocalDNSText.WordWrap = false; + // // SettingsForm // this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; @@ -763,5 +788,7 @@ private void InitializeComponent() private System.Windows.Forms.NumericUpDown NumTimeout; private System.Windows.Forms.Button buttonDefault; private System.Windows.Forms.CheckBox checkBalanceInGroup; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.TextBox LocalDNSText; } } \ No newline at end of file diff --git a/shadowsocks-csharp/View/SettingsForm.cs b/shadowsocks-csharp/View/SettingsForm.cs index 02ac4a59..a17da84b 100644 --- a/shadowsocks-csharp/View/SettingsForm.cs +++ b/shadowsocks-csharp/View/SettingsForm.cs @@ -52,6 +52,7 @@ public SettingsForm(ShadowsocksController controller) buttonDefault.Height = buttonDefault.Height * dpi_mul / 4; buttonDefault.Width = buttonDefault.Width * dpi_mul / 4; DNSText.Width = DNSText.Width * dpi_mul / 4; + LocalDNSText.Width = LocalDNSText.Width * dpi_mul / 4; NumReconnect.Width = NumReconnect.Width * dpi_mul / 4; NumTimeout.Width = NumTimeout.Width * dpi_mul / 4; NumTTL.Width = NumTTL.Width * dpi_mul / 4; @@ -141,6 +142,7 @@ private int SaveOldSelectedServer() _modifiedConfiguration.TTL = Convert.ToInt32(NumTTL.Value); _modifiedConfiguration.connectTimeout = Convert.ToInt32(NumTimeout.Value); _modifiedConfiguration.dnsServer = DNSText.Text; + _modifiedConfiguration.localDnsServer = LocalDNSText.Text; _modifiedConfiguration.proxyEnable = CheckSockProxy.Checked; _modifiedConfiguration.pacDirectGoProxy = checkBoxPacProxy.Checked; _modifiedConfiguration.proxyType = comboProxyType.SelectedIndex; @@ -185,6 +187,7 @@ private void LoadSelectedServer() NumTTL.Value = _modifiedConfiguration.TTL; NumTimeout.Value = _modifiedConfiguration.connectTimeout; DNSText.Text = _modifiedConfiguration.dnsServer; + LocalDNSText.Text = _modifiedConfiguration.localDnsServer; CheckSockProxy.Checked = _modifiedConfiguration.proxyEnable; checkBoxPacProxy.Checked = _modifiedConfiguration.pacDirectGoProxy; diff --git a/shadowsocks-csharp/shadowsocks-csharp-console.csproj b/shadowsocks-csharp/shadowsocks-csharp-console.csproj index 3b1effec..b5468b1e 100644 --- a/shadowsocks-csharp/shadowsocks-csharp-console.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp-console.csproj @@ -111,6 +111,7 @@ + diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index f03a66d6..f22b30c6 100755 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -168,6 +168,7 @@ + diff --git a/shadowsocks-csharp/shadowsocks-csharp4.0.csproj b/shadowsocks-csharp/shadowsocks-csharp4.0.csproj index 3f22fbf0..00d20043 100644 --- a/shadowsocks-csharp/shadowsocks-csharp4.0.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp4.0.csproj @@ -43,7 +43,7 @@ true bin\4.0\Debug\ - TRACE;DEBUG;_DOTNET_4_0, _DOTNET_CURRENT, PROTOCOL_STATISTICS + TRACE;DEBUG;_DOTNET_4_0, PROTOCOL_STATISTICS full AnyCPU prompt @@ -52,7 +52,7 @@ true bin\4.0\Release\ - TRACE;PROTOCOL_STATISTICS + TRACE;_DOTNET_4_0, PROTOCOL_STATISTICS true pdbonly AnyCPU @@ -167,6 +167,7 @@ + diff --git a/test/ServerTest.cs b/test/ServerTest.cs index 33440282..99da177a 100644 --- a/test/ServerTest.cs +++ b/test/ServerTest.cs @@ -18,7 +18,7 @@ public void TestServerFromSSR() server.ServerFromSSR(nornameCase, ""); Assert.AreEqual(server.server, "127.0.0.1"); - Assert.AreEqual(server.server_port, 1234); + Assert.AreEqual(server.server_port, 1234); Assert.AreEqual(server.protocol, "auth_aes128_md5"); Assert.AreEqual(server.method, "aes-128-cfb"); Assert.AreEqual(server.obfs, "tls1.2_ticket_auth"); @@ -31,7 +31,7 @@ public void TestServerFromSSR() server.ServerFromSSR(normalCaseWithRemark, "firewallAirport"); Assert.AreEqual(server.server, "127.0.0.1"); - Assert.AreEqual(server.server_port, 1234); + Assert.AreEqual(server.server_port, 1234); Assert.AreEqual(server.protocol, "auth_aes128_md5"); Assert.AreEqual(server.method, "aes-128-cfb"); Assert.AreEqual(server.obfs, "tls1.2_ticket_auth"); @@ -57,5 +57,20 @@ public void TestHideServerName() Assert.AreEqual(addrs[key], val); } } + + [TestMethod] + public void TestBadPortNumber() + { + Server server = new Server(); + + string link = "ssr://MTI3LjAuMC4xOjgwOmF1dGhfc2hhMV92NDpjaGFjaGEyMDpodHRwX3NpbXBsZTplaWZnYmVpd3ViZ3IvP29iZnNwYXJhbT0mcHJvdG9wYXJhbT0mcmVtYXJrcz0mZ3JvdXA9JnVkcHBvcnQ9NDY0MzgxMzYmdW90PTQ2MDA3MTI4"; + try { + server.ServerFromSSR(link, ""); + } catch (System.OverflowException e) + { + Console.Write(e.ToString()); + } + + } } }