diff --git a/shadowsocks-csharp/Controller/Service/GeositeUpdater.cs b/shadowsocks-csharp/Controller/Service/GeositeUpdater.cs index 30e29666..46814b10 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 SocketsHttpHandler socketsHttpHandler; + 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,83 @@ 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 + socketsHttpHandler = new SocketsHttpHandler(); + httpClient = new HttpClient(socketsHttpHandler); if (config.enabled) { - http.Proxy = new WebProxy( + socketsHttpHandler.UseProxy = true; + socketsHttpHandler.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 (socketsHttpHandler != null) + { + socketsHttpHandler.Dispose(); + socketsHttpHandler = 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 796c8eb0..1d0ecce5 100644 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -411,11 +411,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 83eb3384..5083cd1d 100644 --- a/shadowsocks-csharp/View/MenuViewController.cs +++ b/shadowsocks-csharp/View/MenuViewController.cs @@ -705,9 +705,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)