diff --git a/shadowsocks-csharp/Controller/Service/GeositeUpdater.cs b/shadowsocks-csharp/Controller/Service/GeositeUpdater.cs index 30e29666..c1305f3a 100644 --- a/shadowsocks-csharp/Controller/Service/GeositeUpdater.cs +++ b/shadowsocks-csharp/Controller/Service/GeositeUpdater.cs @@ -9,6 +9,9 @@ using System.Text; using Newtonsoft.Json; using Shadowsocks.Model; using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using System.Security.Cryptography; namespace Shadowsocks.Controller { @@ -32,23 +35,37 @@ namespace Shadowsocks.Controller private static readonly string DATABASE_PATH = Utils.GetTempPath("dlc.dat"); + private static HttpClientHandler httpClientHandler; + private static HttpClient httpClient; private static readonly string GEOSITE_URL = "https://github.com/v2ray/domain-list-community/raw/release/dlc.dat"; + private static readonly string GEOSITE_SHA256SUM_URL = "https://github.com/v2ray/domain-list-community/raw/release/dlc.dat.sha256sum"; + private static byte[] geositeDB; public static readonly Dictionary> Geosites = new Dictionary>(); static GeositeUpdater() { - if (!File.Exists(DATABASE_PATH)) + //socketsHttpHandler = new SocketsHttpHandler(); + //httpClient = new HttpClient(socketsHttpHandler); + + if (File.Exists(DATABASE_PATH) && new FileInfo(DATABASE_PATH).Length > 0) { + geositeDB = File.ReadAllBytes(DATABASE_PATH); + } + else + { + geositeDB = Resources.dlc_dat; File.WriteAllBytes(DATABASE_PATH, Resources.dlc_dat); } LoadGeositeList(); } - static void LoadGeositeList(byte[] data = null) + /// + /// load new GeoSite data from geositeDB + /// + static void LoadGeositeList() { - data = data ?? File.ReadAllBytes(DATABASE_PATH); - var list = GeositeList.Parser.ParseFrom(data); + var list = GeositeList.Parser.ParseFrom(geositeDB); foreach (var item in list.Entries) { Geosites[item.GroupName.ToLower()] = item.Domains; @@ -61,9 +78,12 @@ namespace Shadowsocks.Controller Error = null; } - public static void UpdatePACFromGeosite(Configuration config) + public static async Task UpdatePACFromGeosite() { string geositeUrl = GEOSITE_URL; + string geositeSha256sumUrl = GEOSITE_SHA256SUM_URL; + SHA256 mySHA256 = SHA256.Create(); + var config = Program.MainController.GetCurrentConfiguration(); string group = config.geositeGroup; bool blacklist = config.geositeBlacklistMode; @@ -73,31 +93,82 @@ namespace Shadowsocks.Controller geositeUrl = config.geositeUrl; } logger.Info($"Checking Geosite from {geositeUrl}"); - WebClient http = new WebClient(); + + // use System.Net.Http.HttpClient to download GeoSite db. + // NASTY workaround: new HttpClient every update + // because we can't change proxy on existing socketsHttpHandler instance + httpClientHandler = new HttpClientHandler(); + httpClient = new HttpClient(httpClientHandler); if (config.enabled) { - http.Proxy = new WebProxy( + httpClientHandler.Proxy = new WebProxy( config.isIPv6Enabled ? $"[{IPAddress.IPv6Loopback}]" : IPAddress.Loopback.ToString(), config.localPort); } - http.DownloadDataCompleted += (o, e) => + + try { - try + // download checksum first + var geositeSha256sum = await httpClient.GetStringAsync(geositeSha256sumUrl); + geositeSha256sum = geositeSha256sum.Substring(0, 64).ToUpper(); + logger.Info($"Got Sha256sum: {geositeSha256sum}"); + // compare downloaded checksum with local geositeDB + byte[] localDBHashBytes = mySHA256.ComputeHash(geositeDB); + string localDBHash = BitConverter.ToString(localDBHashBytes).Replace("-", String.Empty); + logger.Info($"Local Sha256sum: {localDBHash}"); + // if already latest + if (geositeSha256sum == localDBHash) + { + logger.Info("Local GeoSite DB is already the latest."); + return; + } + + // not latest. download new DB + var downloadedBytes = await httpClient.GetByteArrayAsync(geositeUrl); + + // verify sha256sum + byte[] downloadedDBHashBytes = mySHA256.ComputeHash(downloadedBytes); + string downloadedDBHash = BitConverter.ToString(downloadedDBHashBytes).Replace("-", String.Empty); + logger.Info($"Actual Sha256sum: {downloadedDBHash}"); + if (geositeSha256sum != downloadedDBHash) { - File.WriteAllBytes(DATABASE_PATH, e.Result); - LoadGeositeList(); + logger.Info("Sha256sum mismatch. Updating aborted."); + throw new Exception("Sha256sum mismatch"); + } + else + { + logger.Info("Sha256sum verification successful."); + } - bool pacFileChanged = MergeAndWritePACFile(group, blacklist); - UpdateCompleted?.Invoke(null, new GeositeResultEventArgs(pacFileChanged)); + // write to geosite file + using (FileStream geositeFileStream = File.Create(DATABASE_PATH)) + await geositeFileStream.WriteAsync(downloadedBytes, 0, downloadedBytes.Length); + + // update stuff + geositeDB = downloadedBytes; + LoadGeositeList(); + bool pacFileChanged = MergeAndWritePACFile(group, blacklist); + UpdateCompleted?.Invoke(null, new GeositeResultEventArgs(pacFileChanged)); + } + catch (Exception ex) + { + Error?.Invoke(null, new ErrorEventArgs(ex)); + } + finally + { + if (httpClientHandler != null) + { + httpClientHandler.Dispose(); + httpClientHandler = null; } - catch (Exception ex) + if (httpClient != null) { - Error?.Invoke(null, new ErrorEventArgs(ex)); + httpClient.Dispose(); + httpClient = null; } - }; - http.DownloadDataAsync(new Uri(geositeUrl)); + } } public static bool MergeAndWritePACFile(string group, bool blacklist) diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs index ea1382d9..0f67fe21 100644 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -415,11 +415,6 @@ namespace Shadowsocks.Controller return $"ss://{url}{tag}"; } - public void UpdatePACFromGeosite() - { - GeositeUpdater.UpdatePACFromGeosite(_config); - } - public void UpdateStatisticsConfiguration(bool enabled) { if (availabilityStatistics != null) diff --git a/shadowsocks-csharp/View/MenuViewController.cs b/shadowsocks-csharp/View/MenuViewController.cs index 89fde38a..385cbe7a 100644 --- a/shadowsocks-csharp/View/MenuViewController.cs +++ b/shadowsocks-csharp/View/MenuViewController.cs @@ -685,9 +685,9 @@ namespace Shadowsocks.View controller.TouchPACFile(); } - private void UpdatePACFromGeositeItem_Click(object sender, EventArgs e) + private async void UpdatePACFromGeositeItem_Click(object sender, EventArgs e) { - controller.UpdatePACFromGeosite(); + await GeositeUpdater.UpdatePACFromGeosite(); } private void EditUserRuleFileForGeositeItem_Click(object sender, EventArgs e) diff --git a/shadowsocks-csharp/packages.config b/shadowsocks-csharp/packages.config index 65e34eb9..fbabcf90 100644 --- a/shadowsocks-csharp/packages.config +++ b/shadowsocks-csharp/packages.config @@ -9,8 +9,15 @@ + + + + + + + \ No newline at end of file diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index 97a265bd..64a3fc77 100644 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -93,24 +93,60 @@ ..\packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll + + + ..\packages\System.IO.4.3.0\lib\net462\System.IO.dll + True + True + ..\packages\System.Memory.4.5.2\lib\netstandard2.0\System.Memory.dll + + ..\packages\System.Net.Http.4.3.4\lib\net46\System.Net.Http.dll + True + True + ..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll + + ..\packages\System.Runtime.4.3.0\lib\net462\System.Runtime.dll + True + True + ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll + + ..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net463\System.Security.Cryptography.Algorithms.dll + True + True + + + ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll + True + True + + + ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll + True + True + + + ..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll + True + True +