Skip to content

Commit

Permalink
🔐 Add Sha256sum verification for GeositeUpdater
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
database64128 committed Jun 11, 2020
1 parent 444f1ba commit 2c15276
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 24 deletions.
106 changes: 89 additions & 17 deletions shadowsocks-csharp/Controller/Service/GeositeUpdater.cs
Expand Up @@ -9,6 +9,9 @@
using Newtonsoft.Json;
using Shadowsocks.Model;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Security.Cryptography;

namespace Shadowsocks.Controller
{
Expand All @@ -32,23 +35,37 @@ public static class GeositeUpdater

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<string, IList<DomainObject>> Geosites = new Dictionary<string, IList<DomainObject>>();

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)
/// <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)
{
Geosites[item.GroupName.ToLower()] = item.Domains;
Expand All @@ -61,9 +78,12 @@ public static void ResetEvent()
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;

Expand All @@ -73,31 +93,83 @@ public static void UpdatePACFromGeosite(Configuration config)
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)
Expand Down
5 changes: 0 additions & 5 deletions shadowsocks-csharp/Controller/ShadowsocksController.cs
Expand Up @@ -411,11 +411,6 @@ public static string GetServerURL(Server server)
return $"ss://{url}{tag}";
}

public void UpdatePACFromGeosite()
{
GeositeUpdater.UpdatePACFromGeosite(_config);
}

public void UpdateStatisticsConfiguration(bool enabled)
{
if (availabilityStatistics != null)
Expand Down
4 changes: 2 additions & 2 deletions shadowsocks-csharp/View/MenuViewController.cs
Expand Up @@ -705,9 +705,9 @@ private void EditPACFileItem_Click(object sender, EventArgs e)
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)
Expand Down

0 comments on commit 2c15276

Please sign in to comment.