doctor: deepen DC verification with MTProto handshake probe#496
Draft
dolonet wants to merge 3 commits into9seconds:masterfrom
Draft
doctor: deepen DC verification with MTProto handshake probe#496dolonet wants to merge 3 commits into9seconds:masterfrom
dolonet wants to merge 3 commits into9seconds:masterfrom
Conversation
Per discussion on 9seconds#494, this allows external packages (e.g. the upcoming mtglib/dcprobe for the doctor RPC probe) to reuse the obfuscated2 transport without an internal wrapper. No public-API change beyond the import path. The only exported names (Obfuscator, its two methods, and the Secret field) were already exported within the package.
New leaf package that performs the first step of the MTProto handshake (req_pq_multi -> resPQ) over the existing obfuscated2 transport. No auth_key is generated; no long-lived state is introduced. Two TL messages, one round-trip, no new dependencies. A generic listener cannot fake the reply because it must echo back our random nonce in resPQ. Used by the doctor command in a follow-up commit to distinguish a real Telegram DC from a generic TCP listener bound to port 443.
Closes 9seconds#494. After a successful TCP connect, run an unauthenticated req_pq_multi -> resPQ exchange via mtglib/dcprobe. This rejects generic listeners that happen to bind 443 but cannot speak MTProto. Output now shows "(rpc <rtt>)" on success; on failure the wrapped error distinguishes "tcp connect to ...: ..." from "rpc handshake to ...: ...". The probe runs by default — an opt-in flag would defeat the purpose, since the existing TCP-only check is what motivated the issue.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #494.
Stacked on top of #495 — keeping this as a draft until #495 merges, then I'll rebase to drop the obfuscation rename commit and leave only the two commits unique to this work. The diff currently shows 3 commits because of that.
What this does
After a successful TCP connect,
mtg doctorperforms an unauthenticatedreq_pq_multi→resPQexchange against each DC. This proves the peer can speak MTProto, not just bind on port 443.mtglib/dcprobe— new leaf package, ~170 LOC, no new dependencies. Reuses the now-publicmtglib/obfuscation(mtglib: promote obfuscation out of internal #495). Single round-trip, two TL messages, hand-rolled (de)serialization to avoid pulling ingotd/tdor similar.Doctor.checkNetworkAddresses— the dial loop now runs the probe and surfacesrpc <rtt>next to the connect line.tcp connect to <addr>: …fromrpc handshake to <addr>: …via wrapped errors.errors.Is(err, dcprobe.ErrNotTelegram)lets callers tell the cases apart.The probe is enabled by default — an opt-in
--deepflag would defeat the purpose, since the existing TCP-only check is what motivated the issue. Open to a flag if you'd rather have a soft rollout.Output sample
Verified locally on a Hetzner Helsinki host with a real config:
Tests
mtglib/dcprobe/probe_test.goships two tests:TestProbeRejectsMisbehavingPeer— deterministic, in-process, no network. Spins up a localhost listener that accepts the obfs2 handshake then writes garbage; assertserrors.Is(err, dcprobe.ErrNotTelegram).TestProbeAgainstTelegramDCs— opt-in viaMTG_PROBE_NETWORK=1, hits all 5 main DCs + 2 IPv6 DCs. Skipped by default to keep CI hermetic; matches the existing pattern for the obfuscation fuzz tests.go build ./...,go vet ./...,go test ./...all clean.Notes
checkNetworkAddressesso the probe knows which DC to bake into the obfs2 handshake frame; previously the function only needed the address list.