From 0c9d5606e2f6d143fd4f8b6dd58ca814ff3ec7fa Mon Sep 17 00:00:00 2001 From: everyx Date: Tue, 12 May 2015 00:38:42 +0800 Subject: [PATCH 1/2] Add feature, something like gfwlist2pac --- shadowsocks-csharp/Controller/GfwListUpdater.cs | 164 ++++++++++++++++++--- .../Controller/ShadowsocksController.cs | 10 ++ shadowsocks-csharp/Data/builtin.txt.gz | Bin 0 -> 105 bytes shadowsocks-csharp/Data/cn.txt | 2 + shadowsocks-csharp/Data/proxy.pac.gz | Bin 0 -> 351 bytes shadowsocks-csharp/Model/Configuration.cs | 1 + .../Properties/Resources.Designer.cs | 72 +++++---- shadowsocks-csharp/Properties/Resources.resx | 12 +- shadowsocks-csharp/View/MenuViewController.cs | 31 ++++ 9 files changed, 243 insertions(+), 49 deletions(-) create mode 100644 shadowsocks-csharp/Data/builtin.txt.gz create mode 100644 shadowsocks-csharp/Data/proxy.pac.gz diff --git a/shadowsocks-csharp/Controller/GfwListUpdater.cs b/shadowsocks-csharp/Controller/GfwListUpdater.cs index 70720fb9..d6baf516 100644 --- a/shadowsocks-csharp/Controller/GfwListUpdater.cs +++ b/shadowsocks-csharp/Controller/GfwListUpdater.cs @@ -7,6 +7,8 @@ using Shadowsocks.Properties; using SimpleJson; using Shadowsocks.Util; using Shadowsocks.Model; +using System.Linq; +using System.Text.RegularExpressions; namespace Shadowsocks.Controller { @@ -32,34 +34,20 @@ namespace Shadowsocks.Controller } } - private void http_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) + private void savePacFile(string pacContent) { try { - List lines = ParseResult(e.Result); - if (File.Exists(USER_RULE_FILE)) - { - string local = File.ReadAllText(USER_RULE_FILE, Encoding.UTF8); - string[] rules = local.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); - foreach(string rule in rules) - { - if (rule.StartsWith("!") || rule.StartsWith("[")) - continue; - lines.Add(rule); - } - } - string abpContent = Utils.UnGzip(Resources.abp_js); - abpContent = abpContent.Replace("__RULES__", SimpleJson.SimpleJson.SerializeObject(lines)); if (File.Exists(PAC_FILE)) { string original = File.ReadAllText(PAC_FILE, Encoding.UTF8); - if (original == abpContent) + if (original == pacContent) { UpdateCompleted(this, new ResultEventArgs(false)); return; } } - File.WriteAllText(PAC_FILE, abpContent, Encoding.UTF8); + File.WriteAllText(PAC_FILE, pacContent, Encoding.UTF8); if (UpdateCompleted != null) { UpdateCompleted(this, new ResultEventArgs(true)); @@ -74,12 +62,152 @@ namespace Shadowsocks.Controller } } + private string getHostname(string domain) + { + string host = null; + + if (!(domain.StartsWith("http:") || domain.StartsWith("https:"))) + domain = "http://" + domain; + + Uri hostUri; + if (Uri.TryCreate(domain, UriKind.Absolute, out hostUri)) + host = hostUri.Host; + + return host; + } + + private void addDomainToList(List list, string domain) + { + string hostname = getHostname(domain); + if (hostname != null) + list.Add(hostname); + } + + private List parseGfwlist(List lines) + { + List domains = new List(); + lines.ForEach(delegate(string line) + { + if (line.StartsWith("@@") || line.StartsWith("/") || Regex.IsMatch(line, "^(\\d+\\.){3}\\d+$") || !line.Contains(".")) + return; + + if (line.Contains("//*")) + line = line.Replace("//*", "//"); + else if (line.Contains("*/") || line.Contains("*.")) + line = line.Replace("*", ""); + else if (line.Contains("*")) + line = line.Replace("*", "/"); + + if (line.StartsWith("|")) + line = line.TrimStart('|'); + else if (line.StartsWith(".")) + line = line.TrimStart('.'); + + addDomainToList(domains, line); + }); + return domains; + } + + private List reduceDomains(List domains) + { + HashSet uniDomains = new HashSet(); + foreach (var domain in domains) + { + var domainParts = domain.Split('.'); + bool isContainRootDomain = false; + + for (var i = 0; i <= domainParts.Length - 2; i++) + { + var domainPartsArray = new ArraySegment(domainParts, domainParts.Length - i - 2, i + 2); + var rootDomain = string.Join(".", domainPartsArray); + + if (domains.Contains(rootDomain)) + { + uniDomains.Add(rootDomain); + isContainRootDomain = true; + break; + } + } + if (!isContainRootDomain) + uniDomains.Add(domain); + } + return uniDomains.ToList(); + } + + private void generatePacFast(List lines) + { + List domains = parseGfwlist(lines); + domains = reduceDomains(domains).Distinct().ToList(); + var fastContent = Utils.UnGzip(Resources.proxy_pac); + var domainsJsonStr = "{\n " + + string.Join(",\n ", domains.Select(d=>string.Format("\"{0}\": 1", d))) + + "\n}"; + fastContent = fastContent.Replace("__DOMAINS__", domainsJsonStr); + savePacFile(fastContent); + } + + private void generatePacPrecise(List lines) + { + string abpContent = Utils.UnGzip(Resources.abp_js); + abpContent = abpContent.Replace("__RULES__", SimpleJson.SimpleJson.SerializeObject(lines)); + savePacFile(abpContent); + } + + private void parsePacStr(string pacStr, List lines) + { + string[] rules = pacStr.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); + foreach (string rule in rules) + { + if (rule.StartsWith("!") || rule.StartsWith("[")) + continue; + lines.Add(rule); + } + } + + private void http_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) + { + try + { + List lines = ParseResult(e.Result); + + string local = ""; + if (File.Exists(USER_RULE_FILE)) + { + local = File.ReadAllText(USER_RULE_FILE, Encoding.UTF8); + } + + bool usePreciseMode = (bool) e.UserState; + + if (!usePreciseMode) + { + local += Utils.UnGzip(Resources.builtin_txt); + } + parsePacStr(local, lines); + + if (usePreciseMode) + { + generatePacPrecise(lines); + } + else + { + generatePacFast(lines); + } + } + catch (Exception ex) + { + if (Error != null) + { + Error(this, new ErrorEventArgs(ex)); + } + } + } + public void UpdatePACFromGFWList(Configuration config) { WebClient http = new WebClient(); http.Proxy = new WebProxy(IPAddress.Loopback.ToString(), config.localPort); http.DownloadStringCompleted += http_DownloadStringCompleted; - http.DownloadStringAsync(new Uri(GFWLIST_URL)); + http.DownloadStringAsync(new Uri(GFWLIST_URL), config.usePreciseMode); } public List ParseResult(string response) diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs index 734f27d1..ab9335a8 100755 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -214,6 +214,16 @@ namespace Shadowsocks.Controller } } + public void UsePreciseMode(bool usePreciseMode) + { + _config.usePreciseMode = usePreciseMode; + SaveConfig(_config); + if (ConfigChanged != null) + { + ConfigChanged(this, new EventArgs()); + } + } + protected void Reload() { // some logic in configuration updated the config when saving, we need to read it again diff --git a/shadowsocks-csharp/Data/builtin.txt.gz b/shadowsocks-csharp/Data/builtin.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..015d9cf35ca921a032de580b03c94d31ece46bf7 GIT binary patch literal 105 zcmV-v0G9tBiwFoU6Gl}80Ah7%Y;>^w08MQi;ID zhl5IoABWR>APT!zmJTT`x%3nZ@&Ahshz}`VCI)fAs LeHtZ*g#Z8m^@J@> literal 0 KcmV+b0RR6000031 diff --git a/shadowsocks-csharp/Data/cn.txt b/shadowsocks-csharp/Data/cn.txt index e092575c..9cf197cb 100644 --- a/shadowsocks-csharp/Data/cn.txt +++ b/shadowsocks-csharp/Data/cn.txt @@ -14,6 +14,8 @@ Start on Boot=开机启动 Allow Clients from LAN=允许来自局域网的连接 Local PAC=使用本地 PAC Online PAC=使用在线 PAC +Fast Mode=快速模式 +Precise Mode=精确模式 Edit Local PAC File...=编辑本地 PAC 文件... Update Local PAC from GFWList=从 GFWList 更新本地 PAC Edit User Rule for GFWList...=编辑 GFWList 的用户规则... diff --git a/shadowsocks-csharp/Data/proxy.pac.gz b/shadowsocks-csharp/Data/proxy.pac.gz new file mode 100644 index 0000000000000000000000000000000000000000..97cfe6e8ee4096f0b5ff11a372fd9787d409aa18 GIT binary patch literal 351 zcmV-l0igaLiwFpW)lgLg0B~||czG^xVPgQ5kx@^=FcgK~N%#*pylh0=#78F&M#PY) zBdCc!HQm;+n$j+9=OD)aZnqBRNHlP>hqd3i?LDWy5BG!m%!Ph46Cr@3n_9Ub=4n6A4cnS_iK4t@K{s9VP+;= zRM*Xfd|f`cg=8bhgwjL!>i57@b+Rhblx~7{BNt}&TIVsW^qePT6Q-`?IYX=ZM=`3| zIgEGGGNX*RV|!y$GzGVkT~FX*4}Qb0DKEy1xlO8#0o!&^lITKmn{d#@-$0lme>XMJ xA9l6N_O}Ua7l0}vr6uL7+R~YIezX~TmbG()m1c?)-@tU4?+2_G))nUh000)%t=#|s literal 0 KcmV+b0RR6000031 diff --git a/shadowsocks-csharp/Model/Configuration.cs b/shadowsocks-csharp/Model/Configuration.cs index b78358e8..bf3ce611 100755 --- a/shadowsocks-csharp/Model/Configuration.cs +++ b/shadowsocks-csharp/Model/Configuration.cs @@ -19,6 +19,7 @@ namespace Shadowsocks.Model public int localPort; public string pacUrl; public bool useOnlinePac; + public bool usePreciseMode; private static string CONFIG_FILE = "gui-config.json"; diff --git a/shadowsocks-csharp/Properties/Resources.Designer.cs b/shadowsocks-csharp/Properties/Resources.Designer.cs index a5d9c107..746dd724 100755 --- a/shadowsocks-csharp/Properties/Resources.Designer.cs +++ b/shadowsocks-csharp/Properties/Resources.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.34209 +// Runtime Version:4.0.30319.34014 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -71,33 +71,39 @@ namespace Shadowsocks.Properties { } /// - /// Looks up a localized string similar to Shadowsocks=Shadowsocks - ///Enable System Proxy=启用系统代理 - ///Mode=系统代理模式 - ///PAC=PAC 模式 - ///Global=全局模式 - ///Servers=服务器 - ///Edit Servers...=编辑服务器... - ///Start on Boot=开机启动 - ///Allow Clients from LAN=允许来自局域网的连接 - ///Edit PAC File...=编辑 PAC 文件... - ///Edit User Rule for GFWList...=编辑 GFWList 的用户规则... - ///Show QRCode...=显示二维码... - ///Scan QRCode from Screen...=扫描屏幕上的二维码... - ///Show Logs...=显示日志... - ///About...=关于... - ///Quit=退出 - ///Edit Servers=编辑服务器 - ///&Add=添加(&A) - ///&Delete=删除(&D) - ///Server=服务器 - ///Server IP=服务器 IP - ///Server Port=服务器端口 - ///Password=密码 - ///Encryption=加密 - ///Proxy Port=代理端口 - ///Remarks=备注 - ///OK= [rest of string was truncated]";. + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] builtin_txt { + get { + object obj = ResourceManager.GetObject("builtin_txt", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized string similar to # translation for Simplified Chinese + /// + ///Shadowsocks=Shadowsocks + /// + ///# Menu items + /// + ///Enable System Proxy=启用系统代理 + ///Mode=系统代理模式 + ///PAC=PAC 模式 + ///Global=全局模式 + ///Servers=服务器 + ///Edit Servers...=编辑服务器... + ///Start on Boot=开机启动 + ///Allow Clients from LAN=允许来自局域网的连接 + ///Local PAC=使用本地 PAC + ///Online PAC=使用在线 PAC + ///Fast Mode=快速模式 + ///Precise Mode=精确模式 + ///Edit Local PAC File...=编辑本地 PAC 文件... + ///Update Local PAC from GFWList=从 GFWList 更新本地 PAC + ///Edit User Rule for GFWList...=编辑 GFWList 的用户规则... + ///Show QRCode...=显示二维码... + ///Scan QRCode from Screen...=扫 [rest of string was truncated]";. /// internal static string cn { get { @@ -146,6 +152,16 @@ namespace Shadowsocks.Properties { /// /// Looks up a localized resource of type System.Byte[]. /// + internal static byte[] proxy_pac { + get { + object obj = ResourceManager.GetObject("proxy_pac", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// internal static byte[] proxy_pac_txt { get { object obj = ResourceManager.GetObject("proxy_pac_txt", resourceCulture); diff --git a/shadowsocks-csharp/Properties/Resources.resx b/shadowsocks-csharp/Properties/Resources.resx index aa851e6b..7ad0cf05 100755 --- a/shadowsocks-csharp/Properties/Resources.resx +++ b/shadowsocks-csharp/Properties/Resources.resx @@ -112,15 +112,18 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + ..\Data\abp.js.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + ..\Data\builtin.txt.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + ..\data\cn.txt;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 @@ -133,6 +136,9 @@ ..\Data\polipo.exe.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + ..\Data\proxy.pac.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + ..\Data\proxy.pac.txt.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 diff --git a/shadowsocks-csharp/View/MenuViewController.cs b/shadowsocks-csharp/View/MenuViewController.cs index ff5a018c..ccf0c0ef 100755 --- a/shadowsocks-csharp/View/MenuViewController.cs +++ b/shadowsocks-csharp/View/MenuViewController.cs @@ -37,6 +37,8 @@ namespace Shadowsocks.View private MenuItem PACModeItem; private MenuItem localPACItem; private MenuItem onlinePACItem; + private MenuItem fastModeItem; + private MenuItem preciseModeItem; private MenuItem editLocalPACItem; private MenuItem updateFromGFWListItem; private MenuItem editGFWUserRuleItem; @@ -161,6 +163,9 @@ namespace Shadowsocks.View this.localPACItem = CreateMenuItem("Local PAC", new EventHandler(this.LocalPACItem_Click)), this.onlinePACItem = CreateMenuItem("Online PAC", new EventHandler(this.OnlinePACItem_Click)), new MenuItem("-"), + this.fastModeItem = CreateMenuItem("Fast Mode", new EventHandler(this.fastModeItem_Click)), + this.preciseModeItem = CreateMenuItem("Precise Mode", new EventHandler(this.preciseModeItem_Click)), + new MenuItem("-"), this.editLocalPACItem = CreateMenuItem("Edit Local PAC File...", new EventHandler(this.EditPACFileItem_Click)), this.updateFromGFWListItem = CreateMenuItem("Update Local PAC from GFWList", new EventHandler(this.UpdatePACFromGFWListItem_Click)), this.editGFWUserRuleItem = CreateMenuItem("Edit User Rule for GFWList...", new EventHandler(this.EditUserRuleFileForGFWListItem_Click)), @@ -253,6 +258,8 @@ namespace Shadowsocks.View AutoStartupItem.Checked = AutoStartup.Check(); onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac; localPACItem.Checked = !onlinePACItem.Checked; + fastModeItem.Checked = !config.usePreciseMode; + preciseModeItem.Checked = config.usePreciseMode; UpdatePACItemsEnabledStatus(); } @@ -525,6 +532,26 @@ namespace Shadowsocks.View } } + private void fastModeItem_Click(object sender, EventArgs e) + { + if (!fastModeItem.Checked) + { + fastModeItem.Checked = true; + preciseModeItem.Checked = false; + controller.UsePreciseMode(false); + } + } + + private void preciseModeItem_Click(object sender, EventArgs e) + { + if (!preciseModeItem.Checked) + { + fastModeItem.Checked = false; + preciseModeItem.Checked = true; + controller.UsePreciseMode(true); + } + } + private void UpdateOnlinePACURLItem_Click(object sender, EventArgs e) { string origPacUrl = controller.GetConfiguration().pacUrl; @@ -544,6 +571,8 @@ namespace Shadowsocks.View { this.editLocalPACItem.Enabled = true; this.updateFromGFWListItem.Enabled = true; + this.fastModeItem.Enabled = true; + this.preciseModeItem.Enabled = true; this.editGFWUserRuleItem.Enabled = true; this.editOnlinePACItem.Enabled = false; } @@ -551,6 +580,8 @@ namespace Shadowsocks.View { this.editLocalPACItem.Enabled = false; this.updateFromGFWListItem.Enabled = false; + this.fastModeItem.Enabled = false; + this.preciseModeItem.Enabled = false; this.editGFWUserRuleItem.Enabled = false; this.editOnlinePACItem.Enabled = true; } From 0c23e25ecb8f56e6c75a332f0cdfd772f3959575 Mon Sep 17 00:00:00 2001 From: everyx Date: Tue, 12 May 2015 00:41:29 +0800 Subject: [PATCH 2/2] Add missing file for feature gfwlist2pac --- .gitignore | 2 +- shadowsocks-csharp/shadowsocks-csharp.csproj | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 82c89bb5..3db95c12 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,4 @@ obj/ shadowsocks-csharp/shadowsocks-csharp.csproj.user TestResults *.suo - +*Thumbs.db diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index aa2c9000..54d60963 100755 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -10,7 +10,7 @@ Properties Shadowsocks Shadowsocks - v2.0 + v4.5 512 @@ -189,11 +189,15 @@ Designer + + + +