How GeositeUpdater Fetches and Updates the Geosite Database in Shadowsocks-Windows

The GeositeUpdater service downloads the latest dlc.dat binary and its SHA-256 checksum from the v2fly/domain-list-community repository, verifies file integrity against the hash, caches the validated data locally, and reloads the routing tables to regenerate the PAC file.

The GeositeUpdater class in the shadowsocks/shadowsocks-windows client automates the retrieval and maintenance of domain routing data from the community-maintained v2fly/domain-list-community project. This component ensures your local PAC (Proxy Auto-Config) file always contains the latest domain classifications without manual intervention. By implementing cryptographic verification and local caching, the updater balances freshness with performance and security.

Configuration and Remote Source URLs

The updater relies on two hard-coded constants defined in shadowsocks-csharp/Controller/Service/GeositeUpdater.cs at lines 38-39:

  • GEOSITE_URL: Points to the binary dlc.dat file containing the GeoSite protobuf database
  • GEOSITE_SHA256SUM_URL: Points to the SHA-256 checksum file used for integrity verification

If you provide custom URLs through the configuration file, the updater substitutes these defaults at runtime during the URL resolution phase (lines 87-94). This allows users to mirror the database internally or use alternative distribution endpoints.

Local Cache Initialization

When the GeositeUpdater class initializes, it attempts to load an existing cache before fetching remote data. The system checks for dlc.dat in the temporary folder returned by Utils.GetTempPath("dlc.dat").

If the cached file exists and contains data, the updater loads it into the geositeDB byte array (lines 46-49). When no cache exists, the system falls back to the embedded resource Resources.dlc_dat and writes it to the temporary location to bootstrap the database (lines 52-54).

After loading the raw bytes, the LoadGeositeList() method parses the protobuf using GeositeList.Parser.ParseFrom and populates the static dictionary Geosites for runtime lookups (lines 63-68).

The Update Workflow in UpdatePACFromGeosite()

The core logic resides in UpdatePACFromGeosite(), which executes the following sequence when triggered by the UI or startup routine:

  1. Resolve URLs: The method first checks for user-provided override URLs and determines whether SHA-256 verification is enabled (lines 78-97).

  2. Download Checksum: If verification is enabled, the system fetches the remote SHA-256 hash using await httpClient.GetStringAsync(geositeSha256sumUrl) (line 106).

  3. Compare Local Hash: The updater computes the SHA-256 hash of the cached geositeDB using mySHA256.ComputeHash() and compares it against the remote checksum. If they match, the process skips downloading to conserve bandwidth (lines 110-118).

  4. Download New Database: When the hashes differ, the system downloads the fresh database via await httpClient.GetByteArrayAsync(geositeUrl) (line 123).

  5. Verify Download: The updater recomputes the SHA-256 hash of the downloaded bytes and validates against the remote checksum, aborting the operation if the verification fails (lines 126-138).

  6. Persist to Cache: Valid data overwrites the local dlc.dat file in the temporary directory (lines 143-145).

  7. Reload Memory Structures: The geositeDB variable updates with the new bytes, and LoadGeositeList() repopulates the in-memory dictionary (lines 147-148).

  8. Regenerate PAC: The MergeAndWritePACFile method rebuilds pac.txt using the refreshed domain groups (line 149).

  9. Fire Events: The system raises UpdateCompleted on success (line 150) or Error if any exception occurs (lines 152-155).

Parsing the Protobuf Database

The binary database conforms to the schema defined in shadowsocks-csharp/Model/Geosite/geosite.proto. The key protobuf definitions include:

  • GeositeList: A container holding repeated Geosite entries
  • Geosite: Contains a group_name field and a repeated list of DomainObject entries
  • DomainObject: Specifies the domain type, value, and optional attributes

The GeositeList.Parser.ParseFrom method (line 63) deserializes the byte array into these C# objects, enabling the PAC generator to iterate over domain groups like "google", "cn", or "geolocation-!cn".

Integration with PAC Generation

After a successful update, the MergeAndWritePACFile method performs three critical operations:

  1. Reads the user-provided ABP script template from Resources.abp_js or a custom file path
  2. Merges user-defined rules with the generated rules from the GeoSite groups using GenerateRules, GenerateBlockingRules, and GenerateExceptionRules
  3. Writes the final output to PACDaemon.PAC_FILE (typically pac.txt)

This integration ensures that new domain classifications immediately affect routing decisions without requiring a client restart.

Event-Driven Notifications

Client code can subscribe to update notifications through two static events declared at lines 32-34:

GeositeUpdater.UpdateCompleted += (sender, args) => {
    Console.WriteLine($"GeoSite update finished. PAC changed: {args.Success}");
};

GeositeUpdater.Error += (sender, err) => {
    Console.WriteLine($"Update failed: {err.GetException().Message}");
};

The UpdateCompleted event includes a Success property indicating whether the PAC file actually changed, while the Error event provides exception details for logging or UI alerts.

Triggering Updates Programmatically

You can manually initiate an update cycle or check for specific domain groups using the following patterns.

Manual update trigger:

using Shadowsocks.Controller;

// Typically called from UI threads or background timers
await GeositeUpdater.UpdatePACFromGeosite();

Checking specific groups:

bool hasGoogle = GeositeUpdater.CheckGeositeGroup("google");
if (hasGoogle)
{
    var domains = GeositeUpdater.Geosites["google"]; // keys are lower-cased
    // Process domains for custom rule generation
}

Summary

  • Hard-coded URLs: GEOSITE_URL and GEOSITE_SHA256SUM_URL in GeositeUpdater.cs (lines 38-39) define the default v2fly/domain-list-community endpoints
  • Smart caching: The system loads from Utils.GetTempPath("dlc.dat") or falls back to the embedded Resources.dlc_dat resource
  • Cryptographic verification: SHA-256 checksums validate both existing caches and fresh downloads to prevent corruption
  • Atomic updates: The UpdatePACFromGeosite() method downloads, verifies, persists, and reloads data in a single workflow
  • Protobuf parsing: GeositeList.Parser.ParseFrom converts the binary dlc.dat into searchable domain group dictionaries
  • PAC integration: Successful updates immediately trigger MergeAndWritePACFile to rebuild the proxy auto-configuration script

Frequently Asked Questions

How does GeositeUpdater handle network failures during download?

If the HTTP request to fetch either the checksum or the database fails, the method catches the exception and fires the Error event (lines 152-155). The existing cached database remains untouched and continues to serve requests, ensuring the proxy remains functional even when updates fail.

Can I use a custom mirror for the geosite database instead of GitHub?

Yes. The updater checks the configuration file during the URL resolution phase (lines 87-94). If you specify custom values for the geosite URL and SHA-256 URL in your settings, the system uses those endpoints instead of the hard-coded defaults pointing to v2fly/domain-list-community.

What format is the geosite database and how is it parsed?

The database is a binary protobuf file defined by geosite.proto in the Model/Geosite directory. The updater uses GeositeList.Parser.ParseFrom to deserialize the byte array into C# objects, creating a dictionary where keys are lowercase group names (like "google" or "cn") and values are lists of domain objects with type information.

Why does the updater skip downloading sometimes?

The updater computes the SHA-256 hash of the local cache and compares it with the remote checksum before initiating a download (lines 110-118). If the hashes match, the database is already current, so the system skips the download to save bandwidth and immediately fires UpdateCompleted with Success set to indicate no changes were necessary.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →