Skip to content

Add hysteria2 port hopping and certificate pinning#110

Merged
PhoenixNil merged 4 commits into
mainfrom
feat/hy2-porthop-certpin
Jul 23, 2026
Merged

Add hysteria2 port hopping and certificate pinning#110
PhoenixNil merged 4 commits into
mainfrom
feat/hy2-porthop-certpin

Conversation

@PhoenixNil

@PhoenixNil PhoenixNil commented Jul 23, 2026

Copy link
Copy Markdown
Owner

Summary

  • Port hopping: mport= (share links) and Clash's ports: fold into finalmask.quicParams.udpHop, following the same fold-in/lift-out pattern already used for salamander obfs. Share links round-trip the range back out as mport=; a hand-tuned hop the writer wouldn't have produced itself is preserved verbatim in fm= instead of being silently rewritten (a Count == 2 && interval == "30" shape check, not a loose key-count guess — a looser check would have invented an interval value on re-import that the original author never wrote).
  • Certificate pinning: new ServerEntry.PinnedPeerCertSha256tlsSettings.pinnedPeerCertSha256, wired into the shared BuildTlsSettings helper so it applies to every TLS outbound (trojan/vless/vmess), not just hysteria2. Verified against the shipped xray core (xray.exe run -test) that the pin validates identically regardless of protocol — a short/malformed pin is rejected by the core's own length check on every protocol tested. Not applicable to REALITY (no tlsSettings); the edit dialog clears the field whenever the current config can't produce one, mirroring the existing ECH cleanup rule.
  • Fixed a bug where hysteria2's own URI form (host:35000-39000 port range in the address) threw inside new Uri() and silently dropped the whole node on import — no error, the node just never showed up.

Test plan

  • dotnet test XrayUI.Tests/XrayUI.Tests.csproj -c Release — 65/65 passing, including round-trip coverage for: mport + pin together, a non-canonical hand-tuned udpHop (interval != "30"), and a hand-written udpHop missing interval entirely (regression guard for the fold/lift shape check — confirmed this test fails against a looser Count-based check before the fix).
  • dotnet build (BuildAndRun.ps1) — clean, x64 Debug.
  • Manually validated generated config shapes against the shipped Assets/engine/xray.exe via run -test: hysteria2 with pin + udpHop + brutal congestion, vless+ws+tls with pin, and trojan with pin — all Configuration OK; negative controls (truncated pin, short pin) correctly rejected with incorrect pinnedPeerCertSha256 length.
  • Real-world hysteria2 node with port hopping + pin (reporter's own node) — not verified live, only via xray -test config validation.

🤖 Generated with Claude Code

Two hysteria2 features fall out of shared-link/Clash parameters that
were previously dropped on import: mport / Clash "ports" (port
hopping) and pinSHA256 / Clash "fingerprint" (certificate pin).

- Port hopping folds into finalmask.quicParams.udpHop, following the
  same fold-in/lift-out pattern as the existing salamander obfs
  support (FinalmaskJson.Add/TakeHysteria2UdpHop). Share links
  round-trip the port range back out as mport=; a hand-tuned hop the
  writer wouldn't have produced itself is preserved verbatim in fm=
  instead of being silently rewritten.
- Certificate pinning is a new ServerEntry.PinnedPeerCertSha256 field
  mapping to tlsSettings.pinnedPeerCertSha256 in the shared TLS
  builder (BuildTlsSettings), so it applies to every TLS outbound
  (trojan/vless/vmess), not just hysteria2 as first implemented -
  verified against the shipped xray core, which validates the pin
  identically regardless of protocol. Not applicable to REALITY,
  which has no tlsSettings. The edit dialog clears the field whenever
  the current config can't produce a tlsSettings block, mirroring the
  existing ECH cleanup rule.
- Fixed a bug where hysteria2's own URI form (host:port-range in the
  address, e.g. "host:35000-39000") threw inside `new Uri()` and
  caused the whole node to silently vanish on import.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 332d75c480

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Services/DialogService.cs Outdated
@PhoenixNil

Copy link
Copy Markdown
Owner Author

@codex address that feedback

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create an environment for this repo.

PhoenixNil and others added 3 commits July 23, 2026 22:09
Gate PinnedPeerCertSha256 cleanup on protocol AND security, not
Security alone. ss shares the same Security combobox as
vmess/vless/trojan for row-visibility purposes, but BuildSsOutbound
never reads Security — so a leftover "tls" selection from before
switching to ss went untouched by the previous check, and would
silently reapply the stale pin if the entry was later switched back
to a real TLS protocol pointing at a different server.

Addresses Codex review comment on PR #110.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@PhoenixNil
PhoenixNil merged commit 56674d9 into main Jul 23, 2026
3 checks passed
@PhoenixNil
PhoenixNil deleted the feat/hy2-porthop-certpin branch July 23, 2026 14:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant