From a2f3116ca805828e89258f22a67c478536c4965d Mon Sep 17 00:00:00 2001 From: Gang Zhuo Date: Thu, 8 Jan 2015 00:54:12 +0800 Subject: [PATCH] auto update gfwlist, see https://github.com/shadowsocks/shadowsocks-csharp/issues/115 --- .../Controller/GfwListUpdater.cs | 283 ++++++++++++++++++ shadowsocks-csharp/Controller/PACServer.cs | 77 +++++ shadowsocks-csharp/Data/tld.txt.gz | Bin 0 -> 26402 bytes .../Properties/Resources.Designer.cs | 58 ++-- shadowsocks-csharp/Properties/Resources.resx | 3 + shadowsocks-csharp/shadowsocks-csharp.csproj | 2 + 6 files changed, 399 insertions(+), 24 deletions(-) create mode 100644 shadowsocks-csharp/Controller/GfwListUpdater.cs create mode 100644 shadowsocks-csharp/Data/tld.txt.gz diff --git a/shadowsocks-csharp/Controller/GfwListUpdater.cs b/shadowsocks-csharp/Controller/GfwListUpdater.cs new file mode 100644 index 00000000..294422d4 --- /dev/null +++ b/shadowsocks-csharp/Controller/GfwListUpdater.cs @@ -0,0 +1,283 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Net; +using System.IO; +using System.IO.Compression; +using System.Security.Cryptography; +using Shadowsocks.Model; +using Shadowsocks.Properties; + +namespace Shadowsocks.Controller +{ + public class GfwListUpdater + { + private const string GFWLIST_URL = "https://autoproxy-gfwlist.googlecode.com/svn/trunk/gfwlist.txt"; + + private const int EXPIRE_HOURS = 6; + + public IWebProxy proxy = null; + + public bool useSystemProxy = true; + + public class GfwListChangedArgs : EventArgs + { + public string[] GfwList { get; set; } + } + + public event EventHandler GfwListChanged; + + private bool running = false; + private bool closed = false; + private int jobId = 0; + DateTime lastUpdateTimeUtc; + string lastUpdateMd5; + + private object locker = new object(); + + public GfwListUpdater() + { + } + + ~GfwListUpdater() + { + Stop(); + } + + public void Start() + { + lock (locker) + { + if (running) + return; + running = true; + closed = false; + jobId++; + new Thread(new ParameterizedThreadStart(UpdateJob)).Start(jobId); + } + } + + public void Stop() + { + lock(locker) + { + closed = true; + running = false; + jobId++; + } + } + + public void ScheduleUpdateTime(int delaySeconds) + { + lock(locker) + { + lastUpdateTimeUtc = DateTime.UtcNow.AddHours(-1 * EXPIRE_HOURS).AddSeconds(delaySeconds); + } + } + + private string DownloadGfwListFile() + { + try + { + WebClient http = new WebClient(); + http.Proxy = useSystemProxy ? WebRequest.GetSystemWebProxy() : proxy; + return http.DownloadString(new Uri(GFWLIST_URL)); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + return null; + } + + private bool IsExpire() + { + lock (locker) + { + TimeSpan ts = DateTime.UtcNow - lastUpdateTimeUtc; + bool expire = ((int)ts.TotalHours) >= EXPIRE_HOURS; + if (expire) + lastUpdateTimeUtc = DateTime.UtcNow; + return expire; + } + } + + private bool IsJobStop(int currentJobId) + { + lock (locker) + { + if (!running || closed || currentJobId != this.jobId) + return true; + } + return false; + } + + private bool IsGfwListChanged(string content) + { + byte[] inputBytes = Encoding.UTF8.GetBytes(content); + byte[] md5Bytes = MD5.Create().ComputeHash(inputBytes); + string md5 = ""; + for (int i = 0; i < md5Bytes.Length; i++) + md5 += md5Bytes[i].ToString("x").PadLeft(2, '0'); + if (md5 == lastUpdateMd5) + return false; + lastUpdateMd5 = md5; + return true; + } + + private void ParseGfwList(string response) + { + if (!IsGfwListChanged(response)) + return; + if (GfwListChanged != null) + { + Parser parser = new Parser(response); + GfwListChangedArgs args = new GfwListChangedArgs { + GfwList = parser.GetReducedDomains() + }; + GfwListChanged(this, args); + } + } + + private void UpdateJob(object state) + { + int currentJobId = (int)state; + int retryTimes = 3; + while (!IsJobStop(currentJobId)) + { + if (IsExpire()) + { + string response = DownloadGfwListFile(); + if (response != null) + { + ParseGfwList(response); + } + else if (retryTimes > 0) + { + ScheduleUpdateTime(30); /*Delay 30 seconds to retry*/ + retryTimes--; + } + else + { + retryTimes = 3; /* reset retry times, and wait next update time. */ + } + } + + Thread.Sleep(1000); + } + } + + class Parser + { + public string Content { get; private set; } + + public Parser(string response) + { + byte[] bytes = Convert.FromBase64String(response); + this.Content = Encoding.ASCII.GetString(bytes); + } + + public string[] GetLines() + { + return Content.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); + } + + /* refer https://github.com/clowwindy/gfwlist2pac/blob/master/gfwlist2pac/main.py */ + public string[] GetDomains() + { + string[] lines = GetLines(); + List domains = new List(lines.Length); + for(int i =0;i < lines.Length;i++) + { + string line = lines[i]; + if (line.IndexOf(".*") >= 0) + continue; + else if (line.IndexOf("*") >= 0) + line = line.Replace("*", "/"); + if (line.StartsWith("||")) + line = line.Substring(2); + else if (line.StartsWith("|")) + line = line.Substring(1); + else if (line.StartsWith(".")) + line = line.Substring(1); + if (line.StartsWith("!")) + continue; + else if (line.StartsWith("[")) + continue; + else if (line.StartsWith("@")) + continue; /*ignore white list*/ + domains.Add(line); + } + return domains.ToArray(); + } + + /* refer https://github.com/clowwindy/gfwlist2pac/blob/master/gfwlist2pac/main.py */ + public string[] GetReducedDomains() + { + string[] domains = GetDomains(); + List new_domains = new List(domains.Length); + IDictionary tld_dic = GetTldDictionary(); + + foreach(string domain in domains) + { + string last_root_domain = null; + int pos; + pos = domain.LastIndexOf('.'); + last_root_domain = domain.Substring(pos + 1); + if (!tld_dic.ContainsKey(last_root_domain)) + continue; + while(pos > 0) + { + pos = domain.LastIndexOf('.', pos - 1); + last_root_domain = domain.Substring(pos + 1); + if (tld_dic.ContainsKey(last_root_domain)) + continue; + else + break; + } + if (last_root_domain != null) + new_domains.Add(last_root_domain); + } + + return new_domains.ToArray(); + } + + private string[] GetTlds() + { + string[] tlds = null; + byte[] pacGZ = Resources.tld_txt; + byte[] buffer = new byte[1024]; + int n; + using(MemoryStream sb = new MemoryStream()) + { + using (GZipStream input = new GZipStream(new MemoryStream(pacGZ), + CompressionMode.Decompress, false)) + { + while ((n = input.Read(buffer, 0, buffer.Length)) > 0) + { + sb.Write(buffer, 0, n); + } + } + tlds = System.Text.Encoding.UTF8.GetString(sb.ToArray()) + .Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); + } + return tlds; + } + + private IDictionary GetTldDictionary() + { + string[] tlds = GetTlds(); + IDictionary dic = new Dictionary(tlds.Length); + foreach (string tld in tlds) + { + if (!dic.ContainsKey(tld)) + dic.Add(tld, tld); + } + return dic; + } + + } + + } +} diff --git a/shadowsocks-csharp/Controller/PACServer.cs b/shadowsocks-csharp/Controller/PACServer.cs index f02476ae..17351b19 100755 --- a/shadowsocks-csharp/Controller/PACServer.cs +++ b/shadowsocks-csharp/Controller/PACServer.cs @@ -8,6 +8,7 @@ using System.IO.Compression; using System.Net; using System.Net.Sockets; using System.Text; +using System.Text.RegularExpressions; namespace Shadowsocks.Controller { @@ -20,6 +21,8 @@ namespace Shadowsocks.Controller Socket _listener; FileSystemWatcher watcher; + GfwListUpdater gfwlistUpdater; + public event EventHandler PACFileChanged; public void Start(Configuration configuration) @@ -48,6 +51,7 @@ namespace Shadowsocks.Controller _listener); WatchPacFile(); + StartGfwListUpdater(); } catch (SocketException) { @@ -58,6 +62,11 @@ namespace Shadowsocks.Controller public void Stop() { + if (gfwlistUpdater != null) + { + gfwlistUpdater.Stop(); + gfwlistUpdater = null; + } if (_listener != null) { _listener.Close(); @@ -242,5 +251,73 @@ Connection: Close //} return proxy; } + + private void StartGfwListUpdater() + { + if (gfwlistUpdater != null) + { + gfwlistUpdater.Stop(); + gfwlistUpdater = null; + } + + gfwlistUpdater = new GfwListUpdater(); + gfwlistUpdater.GfwListChanged += gfwlistUpdater_GfwListChanged; + IPEndPoint localEndPoint = (IPEndPoint)_listener.LocalEndPoint; + gfwlistUpdater.proxy = new WebProxy(localEndPoint.Address.ToString(), 8123); + gfwlistUpdater.useSystemProxy = false; + /* Delay 30 seconds, wait proxy start up. */ + gfwlistUpdater.ScheduleUpdateTime(30); + gfwlistUpdater.Start(); + + } + + private void gfwlistUpdater_GfwListChanged(object sender, GfwListUpdater.GfwListChangedArgs e) + { + if (e.GfwList == null || e.GfwList.Length == 0) return; + string pacfile = TouchPACFile(); + string pacContent = File.ReadAllText(pacfile); + string oldDomains; + if (ClearPacContent(ref pacContent, out oldDomains)) + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine("{"); + for (int i = 0; i < e.GfwList.Length; i++) + { + if (i == e.GfwList.Length - 1) + sb.AppendFormat("\t\"{0}\": {1}\r\n", e.GfwList[i], 1); + else + sb.AppendFormat("\t\"{0}\": {1},\r\n", e.GfwList[i], 1); + } + sb.Append("}"); + string newDomains = sb.ToString(); + if (!string.Equals(oldDomains, newDomains)) + { + pacContent = pacContent.Replace("__LAST_MODIFIED__", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); + pacContent = pacContent.Replace("__DOMAINS__", newDomains); + File.WriteAllText(pacfile, pacContent); + Console.WriteLine("gfwlist updated - " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); + } + } + else + { + Console.WriteLine("Broken pac file."); + } + } + + private bool ClearPacContent(ref string pacContent, out string oldDomains) + { + Regex regex = new Regex("(/\\*.*?\\*/\\s*)?var\\s+domains\\s*=\\s*(\\{(\\s*\"[^\"]*\"\\s*:\\s*\\d+\\s*,)*\\s*(\\s*\"[^\"]*\"\\s*:\\s*\\d+\\s*)\\})", RegexOptions.Singleline); + Match m = regex.Match(pacContent); + if (m.Success) + { + oldDomains = m.Result("$2"); + pacContent = regex.Replace(pacContent, "/* Last Modified: __LAST_MODIFIED__ */\r\nvar domains = __DOMAINS__"); + return true; + } + oldDomains = null; + return false; + } + + } } diff --git a/shadowsocks-csharp/Data/tld.txt.gz b/shadowsocks-csharp/Data/tld.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..a502a241c14500b08eb05346fcaa8625cdfdd405 GIT binary patch literal 26402 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ν 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 z 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 BaYX -// This code was generated by a tool. -// Runtime Version:4.0.30319.18444 +// 此代码由工具生成。 +// 运行时版本:4.0.30319.34014 // -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 // //------------------------------------------------------------------------------ @@ -13,12 +13,12 @@ namespace Shadowsocks.Properties { /// - /// A strongly-typed resource class, for looking up localized strings, etc. + /// 一个强类型的资源类,用于查找本地化的字符串等。 /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] @@ -33,7 +33,7 @@ namespace Shadowsocks.Properties { } /// - /// Returns the cached ResourceManager instance used by this class. + /// 返回此类使用的缓存的 ResourceManager 实例。 /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { @@ -47,8 +47,8 @@ namespace Shadowsocks.Properties { } /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. + /// 使用此强类型资源类,为所有资源查找 + /// 重写当前线程的 CurrentUICulture 属性。 /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { @@ -61,14 +61,14 @@ namespace Shadowsocks.Properties { } /// - /// Looks up a localized string similar to Shadowsocks=Shadowsocks + /// 查找类似 Shadowsocks=Shadowsocks ///Enable=启用代理 ///Mode=代理模式 ///PAC=PAC 模式 ///Global=全局模式 ///Servers=服务器选择 ///Edit Servers...=编辑服务器... - ///Start on Boot=自动启动 + ///Start on Boot=开机启动 ///Share over LAN=在局域网共享代理 ///Edit PAC File...=编辑 PAC 文件... ///Show QRCode...=显示二维码... @@ -91,7 +91,7 @@ namespace Shadowsocks.Properties { ///QRCode=二维码 ///Shadowsocks Error: {0}=Shadowsocks 错误: {0} ///Port already in use=端口已被占用 - ///Il [rest of string was truncated]";. + ///Il [字符串的其余部分被截断]"; 的本地化字符串。 /// internal static string cn { get { @@ -100,7 +100,7 @@ namespace Shadowsocks.Properties { } /// - /// Looks up a localized resource of type System.Byte[]. + /// 查找 System.Byte[] 类型的本地化资源。 /// internal static byte[] libsscrypto_dll { get { @@ -110,7 +110,7 @@ namespace Shadowsocks.Properties { } /// - /// Looks up a localized string similar to proxyAddress = "__POLIPO_BIND_IP__" + /// 查找类似 proxyAddress = "__POLIPO_BIND_IP__" /// ///socksParentProxy = "127.0.0.1:__SOCKS_PORT__" ///socksProxyType = socks5 @@ -118,7 +118,7 @@ namespace Shadowsocks.Properties { ///localDocumentRoot = "" /// ///allowedPorts = 1-65535 - ///tunnelAllowedPorts = 1-65535. + ///tunnelAllowedPorts = 1-65535 的本地化字符串。 /// internal static string polipo_config { get { @@ -127,7 +127,7 @@ namespace Shadowsocks.Properties { } /// - /// Looks up a localized resource of type System.Byte[]. + /// 查找 System.Byte[] 类型的本地化资源。 /// internal static byte[] polipo_exe { get { @@ -137,7 +137,7 @@ namespace Shadowsocks.Properties { } /// - /// Looks up a localized resource of type System.Byte[]. + /// 查找 System.Byte[] 类型的本地化资源。 /// internal static byte[] proxy_pac_txt { get { @@ -147,7 +147,7 @@ namespace Shadowsocks.Properties { } /// - /// Looks up a localized resource of type System.Drawing.Bitmap. + /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// internal static System.Drawing.Bitmap ss16 { get { @@ -157,7 +157,7 @@ namespace Shadowsocks.Properties { } /// - /// Looks up a localized resource of type System.Drawing.Bitmap. + /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// internal static System.Drawing.Bitmap ss20 { get { @@ -167,7 +167,7 @@ namespace Shadowsocks.Properties { } /// - /// Looks up a localized resource of type System.Drawing.Bitmap. + /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// internal static System.Drawing.Bitmap ss24 { get { @@ -177,7 +177,7 @@ namespace Shadowsocks.Properties { } /// - /// Looks up a localized resource of type System.Drawing.Bitmap. + /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// internal static System.Drawing.Bitmap ssw128 { get { @@ -185,5 +185,15 @@ namespace Shadowsocks.Properties { return ((System.Drawing.Bitmap)(obj)); } } + + /// + /// 查找 System.Byte[] 类型的本地化资源。 + /// + internal static byte[] tld_txt { + get { + object obj = ResourceManager.GetObject("tld_txt", resourceCulture); + return ((byte[])(obj)); + } + } } } diff --git a/shadowsocks-csharp/Properties/Resources.resx b/shadowsocks-csharp/Properties/Resources.resx index d8fb470d..b5e75b3f 100755 --- a/shadowsocks-csharp/Properties/Resources.resx +++ b/shadowsocks-csharp/Properties/Resources.resx @@ -145,4 +145,7 @@ ..\Resources\ssw128.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Data\tld.txt.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + \ No newline at end of file diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index e044d4fb..b192d0fd 100755 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -86,6 +86,7 @@ + @@ -147,6 +148,7 @@ +