How Shadowsocks‑Windows Parses ss:// URLs: Import, Export, and Legacy Support

Shadowsocks‑Windows uses a regex‑driven parser in Model/Server.cs to decode Base64‑encoded ss:// payloads into server configuration objects, exports canonical URLs via ToString(), and detects legacy plain‑text formats to emit deprecation warnings through ShadowsocksController.

The shadowsocks/shadowsocks-windows repository implements a robust URL handling system for the Shadowsocks protocol. Understanding how the client handles ss:// server URL parsing—including import, export, and backward‑compatible legacy support—is essential for developers integrating with the Windows client or troubleshooting configuration imports.

Core URL Parsing Infrastructure in Model/Server.cs

All ss:// processing logic resides in shadowsocks-csharp/Model/Server.cs. The file defines a static regex that acts as the entry point for every import operation.

The UrlFinder Regex Pattern

The parser first validates the URL structure using a case‑insensitive regular expression that captures the Base64 payload and an optional remark tag:

private static readonly Regex UrlFinder = 
    new Regex(
        @"ss://(?<base64>[A-Za-z0-9+-/=_]+)(?:#(?<tag>\S+))?", 
        RegexOptions.IgnoreCase
    );

This pattern ensures the string starts with ss://, extracts the Base64‑encoded payload (named group base64), and isolates the URL‑fragment tag (named group tag) when present. Query parameters such as ?plugin=obfs-local appear after the payload but are handled separately during string splitting.

Decoding the Base64 Payload

Once the regex matches, the client decodes the captured value using Convert.FromBase64String (or its URL‑safe variant) to reveal a UTF‑8 string formatted as method:password@host:port. If decoding fails, the parser rejects the URL as malformed and returns null.

Import Workflow: From ss:// String to Server Object

The Server.Parse method orchestrates the transformation from URL string to populated object:

  1. Trim whitespace from the input string.
  2. Execute UrlFinder.Match to locate the payload and tag.
  3. Base64‑decode the payload into method:password@host:port.
  4. Split at the first @ to separate credentials from the host portion.
  5. Parse the left side (method:password) by splitting at the first : into Method and Password.
  6. Parse the right side (host:port) by splitting at the last : into Host and Port (converting the port to an integer).
  7. Store the raw URL in _serverUrl for round‑trip fidelity.
  8. Return the fully populated Server instance.

Below is a practical example of importing a modern URL with a remark:

using Shadowsocks.Model;

string modernUrl = "ss://YWVzLTI1Ni1jZmI6c2VjcmV0QDE5Mi4xNjguMS4xOjgzODg=#Tokyo-Node";
Server srv = Server.Parse(modernUrl);

Console.WriteLine(srv.Method);   // aes-256-cfb
Console.WriteLine(srv.Password); // secret
Console.WriteLine(srv.Host);     // 192.168.1.1
Console.WriteLine(srv.Port);     // 8388
Console.WriteLine(srv.Tag);      // Tokyo-Node

The parser also preserves query strings. If the URL contains ?plugin=obfs-local;obfs=http, that substring remains attached to the internal _serverUrl field and is re‑appended during export.

Export Workflow: Generating Canonical ss:// URLs

When the UI or command line needs to share a server configuration, the Server class reconstructs the canonical URL through its overridden ToString method.

Server.ToString() Implementation

Located around line 118 in Server.cs, the export logic:

  1. Concatenates Method, Password, Host, and Port into method:password@host:port.
  2. Base64‑encodes the resulting UTF‑8 bytes.
  3. Prefixes the encoded string with ss://.
  4. Appends the #tag fragment if the Tag property is non‑empty.

The implementation returns a single interpolated string:

public override string ToString()
{
    // url = Base64Encode($"{Method}:{Password}@{Host}:{Port}")
    // tag = string.IsNullOrEmpty(Tag) ? "" : $"#{Tag}"
    return $"ss://{url}{tag}";
}

Because the original _serverUrl is preserved during import, exporting a legacy URL that was imported will still produce a modern Base64‑encoded canonical form, effectively normalizing the configuration on every save.

Legacy URL Support and Deprecation Warnings

Versions prior to v4 occasionally used plain‑text ss:// URLs where the payload was not Base64‑encoded (e.g., ss://aes-256-cfb:password@1.2.3.4:8388). The current codebase maintains backward compatibility but flags these inputs as deprecated.

Detection and Warning Mechanism

In ShadowsocksController.cs around line 454, the controller checks whether the parsed URL adheres to the modern standard. If the string fails the UrlFinder regex validation or if internal logic detects a non‑Base64 payload structure, the application:

  1. Still attempts to parse the legacy format using fallback string splitting.
  2. Logs a user‑visible warning message stating that legacy URL support will be removed in v5.
  3. Proceeds with the import so the user’s configuration is not lost.

Developers should treat this as a transitional feature; automated tools generating ss:// links should always emit Base64‑encoded payloads to avoid triggering the deprecation warning.

Windows Protocol Association

End users can click ss:// links in browsers because the client registers itself as the protocol handler. The registration logic lives in View/MenuViewController.cs near line 289. When the user selects "Associate ss:// Links" from the tray menu, the code writes the necessary registry keys to map the ss:// scheme to the Shadowsocks executable path, enabling one‑click imports directly from web pages.

Unit Test Coverage for URL Edge Cases

The test/UrlTest.cs file contains comprehensive assertions covering:

  • Modern Base64 URLs with and without tags.
  • URLs containing plugin query parameters (?plugin=...).
  • Malformed Base64 strings (expecting null returns).
  • Legacy plain‑text formats (verifying correct parsing and warning emission).
  • Edge cases such as trailing slashes and empty passwords.

These tests ensure that changes to the parsing regex or decoding logic do not break existing user configurations.

Summary

  • Canonical parsing relies on the static UrlFinder regex in Model/Server.cs to isolate Base64 payloads and optional tags before decoding.
  • Field extraction splits the decoded method:password@host:port string to populate the Server object properties.
  • Export normalization occurs in Server.ToString(), which always generates modern Base64‑encoded URLs regardless of the original import format.
  • Legacy compatibility is handled as a fallback with explicit deprecation warnings in ShadowsocksController.cs, slated for removal in v5.
  • Protocol association is managed through MenuViewController.cs, allowing Windows to launch the client when users click ss:// links.
  • Test coverage in UrlTest.cs validates both contemporary and legacy URL formats, including plugin parameters and error conditions.

Frequently Asked Questions

How does shadowsocks‑windows handle invalid Base64 in ss:// URLs?

When the Base64 payload cannot be decoded (invalid characters, wrong padding, or corruption), Convert.FromBase64String throws an exception that the Server.Parse method catches. The parser returns null, and the calling code in the controller typically discards the malformed URL or presents an error to the user without crashing the application.

What is the difference between modern and legacy ss:// URL formats?

Modern formats encode the entire method:password@host:port segment as a Base64 string placed immediately after ss://, optionally followed by a #tag fragment. Legacy formats placed this information in plain text (e.g., ss://aes-256-cfb:pass@host:port). The modern client parses both but emits a deprecation warning for legacy plain‑text inputs because they will be unsupported in version 5.

Can shadowsocks‑windows export server configurations that include plugin parameters?

Yes. If the original imported URL contained a query string such as ?plugin=obfs-local;obfs=http;obfs-host=google.com, the raw _serverUrl field preserves that substring. When ToString() is called, it reconstructs the Base64 payload and appends the preserved query string, generating a fully functional URL that retains all plugin options.

Where is the ss:// protocol handler registered on Windows?

The registration occurs in View/MenuViewController.cs around line 289. Selecting the "Associate ss:// Links" menu item writes registry entries under HKEY_CLASSES_ROOT\ss that map the protocol to the Shadowsocks executable. This allows the operating system to route ss:// link clicks directly to the client for automatic import.

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 →