Browse Source

🔐 Add Sha256sum verification for GeositeUpdater

- Use System.Net.Http.HttpClient for GeositeUpdater
- New update check mechanism: first download checksum and compare, only download GeoSite DB on different checksum
- Verifiy downloaded GeoSite DB by comparing sha256sum before committing the change
pull/2909/head
database64128 4 years ago
parent
commit
2c15276af1
No known key found for this signature in database GPG Key ID: 1CA27546BEDB8B01
3 changed files with 91 additions and 24 deletions
  1. +89
    -17
      shadowsocks-csharp/Controller/Service/GeositeUpdater.cs
  2. +0
    -5
      shadowsocks-csharp/Controller/ShadowsocksController.cs
  3. +2
    -2
      shadowsocks-csharp/View/MenuViewController.cs

+ 89
- 17
shadowsocks-csharp/Controller/Service/GeositeUpdater.cs View File

@@ -9,6 +9,9 @@ using System.Text;
using Newtonsoft.Json; using Newtonsoft.Json;
using Shadowsocks.Model; using Shadowsocks.Model;
using System.Net; using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Security.Cryptography;


namespace Shadowsocks.Controller namespace Shadowsocks.Controller
{ {
@@ -32,23 +35,37 @@ namespace Shadowsocks.Controller


private static readonly string DATABASE_PATH = Utils.GetTempPath("dlc.dat"); 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_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<string, IList<DomainObject>> Geosites = new Dictionary<string, IList<DomainObject>>(); public static readonly Dictionary<string, IList<DomainObject>> Geosites = new Dictionary<string, IList<DomainObject>>();


static GeositeUpdater() 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); File.WriteAllBytes(DATABASE_PATH, Resources.dlc_dat);
} }
LoadGeositeList(); LoadGeositeList();
} }


static void LoadGeositeList(byte[] data = null)
/// <summary>
/// load new GeoSite data from geositeDB
/// </summary>
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) foreach (var item in list.Entries)
{ {
Geosites[item.GroupName.ToLower()] = item.Domains; Geosites[item.GroupName.ToLower()] = item.Domains;
@@ -61,9 +78,12 @@ namespace Shadowsocks.Controller
Error = null; Error = null;
} }


public static void UpdatePACFromGeosite(Configuration config)
public static async Task UpdatePACFromGeosite()
{ {
string geositeUrl = GEOSITE_URL; string geositeUrl = GEOSITE_URL;
string geositeSha256sumUrl = GEOSITE_SHA256SUM_URL;
SHA256 mySHA256 = SHA256.Create();
var config = Program.MainController.GetCurrentConfiguration();
string group = config.geositeGroup; string group = config.geositeGroup;
bool blacklist = config.geositeBlacklistMode; bool blacklist = config.geositeBlacklistMode;
@@ -73,31 +93,83 @@ namespace Shadowsocks.Controller
geositeUrl = config.geositeUrl; geositeUrl = config.geositeUrl;
} }
logger.Info($"Checking Geosite from {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) if (config.enabled)
{ {
http.Proxy = new WebProxy(
socketsHttpHandler.UseProxy = true;
socketsHttpHandler.Proxy = new WebProxy(
config.isIPv6Enabled config.isIPv6Enabled
? $"[{IPAddress.IPv6Loopback}]" ? $"[{IPAddress.IPv6Loopback}]"
: IPAddress.Loopback.ToString(), : IPAddress.Loopback.ToString(),
config.localPort); 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) public static bool MergeAndWritePACFile(string group, bool blacklist)


+ 0
- 5
shadowsocks-csharp/Controller/ShadowsocksController.cs View File

@@ -411,11 +411,6 @@ namespace Shadowsocks.Controller
return $"ss://{url}{tag}"; return $"ss://{url}{tag}";
} }
public void UpdatePACFromGeosite()
{
GeositeUpdater.UpdatePACFromGeosite(_config);
}
public void UpdateStatisticsConfiguration(bool enabled) public void UpdateStatisticsConfiguration(bool enabled)
{ {
if (availabilityStatistics != null) if (availabilityStatistics != null)


+ 2
- 2
shadowsocks-csharp/View/MenuViewController.cs View File

@@ -705,9 +705,9 @@ namespace Shadowsocks.View
controller.TouchPACFile(); 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) private void EditUserRuleFileForGeositeItem_Click(object sender, EventArgs e)


Loading…
Cancel
Save