Skip to content

Commit 2c15276

Browse files
committed
🔐 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
1 parent 444f1ba commit 2c15276

File tree

3 files changed

+91
-24
lines changed

3 files changed

+91
-24
lines changed

shadowsocks-csharp/Controller/Service/GeositeUpdater.cs

Lines changed: 89 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
using Newtonsoft.Json;
1010
using Shadowsocks.Model;
1111
using System.Net;
12+
using System.Net.Http;
13+
using System.Threading.Tasks;
14+
using System.Security.Cryptography;
1215

1316
namespace Shadowsocks.Controller
1417
{
@@ -32,23 +35,37 @@ public static class GeositeUpdater
3235

3336
private static readonly string DATABASE_PATH = Utils.GetTempPath("dlc.dat");
3437

38+
private static SocketsHttpHandler socketsHttpHandler;
39+
private static HttpClient httpClient;
3540
private static readonly string GEOSITE_URL = "https://github.com/v2ray/domain-list-community/raw/release/dlc.dat";
41+
private static readonly string GEOSITE_SHA256SUM_URL = "https://github.com/v2ray/domain-list-community/raw/release/dlc.dat.sha256sum";
42+
private static byte[] geositeDB;
3643

3744
public static readonly Dictionary<string, IList<DomainObject>> Geosites = new Dictionary<string, IList<DomainObject>>();
3845

3946
static GeositeUpdater()
4047
{
41-
if (!File.Exists(DATABASE_PATH))
48+
//socketsHttpHandler = new SocketsHttpHandler();
49+
//httpClient = new HttpClient(socketsHttpHandler);
50+
51+
if (File.Exists(DATABASE_PATH) && new FileInfo(DATABASE_PATH).Length > 0)
4252
{
53+
geositeDB = File.ReadAllBytes(DATABASE_PATH);
54+
}
55+
else
56+
{
57+
geositeDB = Resources.dlc_dat;
4358
File.WriteAllBytes(DATABASE_PATH, Resources.dlc_dat);
4459
}
4560
LoadGeositeList();
4661
}
4762

48-
static void LoadGeositeList(byte[] data = null)
63+
/// <summary>
64+
/// load new GeoSite data from geositeDB
65+
/// </summary>
66+
static void LoadGeositeList()
4967
{
50-
data = data ?? File.ReadAllBytes(DATABASE_PATH);
51-
var list = GeositeList.Parser.ParseFrom(data);
68+
var list = GeositeList.Parser.ParseFrom(geositeDB);
5269
foreach (var item in list.Entries)
5370
{
5471
Geosites[item.GroupName.ToLower()] = item.Domains;
@@ -61,9 +78,12 @@ public static void ResetEvent()
6178
Error = null;
6279
}
6380

64-
public static void UpdatePACFromGeosite(Configuration config)
81+
public static async Task UpdatePACFromGeosite()
6582
{
6683
string geositeUrl = GEOSITE_URL;
84+
string geositeSha256sumUrl = GEOSITE_SHA256SUM_URL;
85+
SHA256 mySHA256 = SHA256.Create();
86+
var config = Program.MainController.GetCurrentConfiguration();
6787
string group = config.geositeGroup;
6888
bool blacklist = config.geositeBlacklistMode;
6989

@@ -73,31 +93,83 @@ public static void UpdatePACFromGeosite(Configuration config)
7393
geositeUrl = config.geositeUrl;
7494
}
7595
logger.Info($"Checking Geosite from {geositeUrl}");
76-
WebClient http = new WebClient();
96+
97+
// use System.Net.Http.HttpClient to download GeoSite db.
98+
// NASTY workaround: new HttpClient every update
99+
// because we can't change proxy on existing socketsHttpHandler instance
100+
socketsHttpHandler = new SocketsHttpHandler();
101+
httpClient = new HttpClient(socketsHttpHandler);
77102
if (config.enabled)
78103
{
79-
http.Proxy = new WebProxy(
104+
socketsHttpHandler.UseProxy = true;
105+
socketsHttpHandler.Proxy = new WebProxy(
80106
config.isIPv6Enabled
81107
? $"[{IPAddress.IPv6Loopback}]"
82108
: IPAddress.Loopback.ToString(),
83109
config.localPort);
84110
}
85-
http.DownloadDataCompleted += (o, e) =>
111+
112+
try
86113
{
87-
try
114+
// download checksum first
115+
var geositeSha256sum = await httpClient.GetStringAsync(geositeSha256sumUrl);
116+
geositeSha256sum = geositeSha256sum.Substring(0, 64).ToUpper();
117+
logger.Info($"Got Sha256sum: {geositeSha256sum}");
118+
// compare downloaded checksum with local geositeDB
119+
byte[] localDBHashBytes = mySHA256.ComputeHash(geositeDB);
120+
string localDBHash = BitConverter.ToString(localDBHashBytes).Replace("-", String.Empty);
121+
logger.Info($"Local Sha256sum: {localDBHash}");
122+
// if already latest
123+
if (geositeSha256sum == localDBHash)
124+
{
125+
logger.Info("Local GeoSite DB is already the latest.");
126+
return;
127+
}
128+
129+
// not latest. download new DB
130+
var downloadedBytes = await httpClient.GetByteArrayAsync(geositeUrl);
131+
132+
// verify sha256sum
133+
byte[] downloadedDBHashBytes = mySHA256.ComputeHash(downloadedBytes);
134+
string downloadedDBHash = BitConverter.ToString(downloadedDBHashBytes).Replace("-", String.Empty);
135+
logger.Info($"Actual Sha256sum: {downloadedDBHash}");
136+
if (geositeSha256sum != downloadedDBHash)
88137
{
89-
File.WriteAllBytes(DATABASE_PATH, e.Result);
90-
LoadGeositeList();
138+
logger.Info("Sha256sum mismatch. Updating aborted.");
139+
throw new Exception("Sha256sum mismatch");
140+
}
141+
else
142+
{
143+
logger.Info("Sha256sum verification successful.");
144+
}
91145

92-
bool pacFileChanged = MergeAndWritePACFile(group, blacklist);
93-
UpdateCompleted?.Invoke(null, new GeositeResultEventArgs(pacFileChanged));
146+
// write to geosite file
147+
using (FileStream geositeFileStream = File.Create(DATABASE_PATH))
148+
await geositeFileStream.WriteAsync(downloadedBytes, 0, downloadedBytes.Length);
149+
150+
// update stuff
151+
geositeDB = downloadedBytes;
152+
LoadGeositeList();
153+
bool pacFileChanged = MergeAndWritePACFile(group, blacklist);
154+
UpdateCompleted?.Invoke(null, new GeositeResultEventArgs(pacFileChanged));
155+
}
156+
catch (Exception ex)
157+
{
158+
Error?.Invoke(null, new ErrorEventArgs(ex));
159+
}
160+
finally
161+
{
162+
if (socketsHttpHandler != null)
163+
{
164+
socketsHttpHandler.Dispose();
165+
socketsHttpHandler = null;
94166
}
95-
catch (Exception ex)
167+
if (httpClient != null)
96168
{
97-
Error?.Invoke(null, new ErrorEventArgs(ex));
169+
httpClient.Dispose();
170+
httpClient = null;
98171
}
99-
};
100-
http.DownloadDataAsync(new Uri(geositeUrl));
172+
}
101173
}
102174

103175
public static bool MergeAndWritePACFile(string group, bool blacklist)

shadowsocks-csharp/Controller/ShadowsocksController.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -411,11 +411,6 @@ public static string GetServerURL(Server server)
411411
return $"ss://{url}{tag}";
412412
}
413413

414-
public void UpdatePACFromGeosite()
415-
{
416-
GeositeUpdater.UpdatePACFromGeosite(_config);
417-
}
418-
419414
public void UpdateStatisticsConfiguration(bool enabled)
420415
{
421416
if (availabilityStatistics != null)

shadowsocks-csharp/View/MenuViewController.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -705,9 +705,9 @@ private void EditPACFileItem_Click(object sender, EventArgs e)
705705
controller.TouchPACFile();
706706
}
707707

708-
private void UpdatePACFromGeositeItem_Click(object sender, EventArgs e)
708+
private async void UpdatePACFromGeositeItem_Click(object sender, EventArgs e)
709709
{
710-
controller.UpdatePACFromGeosite();
710+
await GeositeUpdater.UpdatePACFromGeosite();
711711
}
712712

713713
private void EditUserRuleFileForGeositeItem_Click(object sender, EventArgs e)

0 commit comments

Comments
 (0)