From b00e2d89558def8bf71c9f7e56e59d63d7627999 Mon Sep 17 00:00:00 2001 From: Igor Artamonov <1994.aik@gmail.com> Date: Fri, 1 May 2026 10:14:58 +0300 Subject: [PATCH] connector: hex-decode mtproxy secret dcs.MTProxy expects raw secret bytes. Carrying them verbatim through a YAML string field is impossible: real secrets contain bytes >= 0x80 (faketls starts with 0xee, secured with 0xdd) which cannot survive a unicode string round-trip, so the value reached the bridge corrupted or empty (gotd then logged "invalid secret"). Accept the standard hex form printed by mtg/MTProxy tooling (e.g. "ee" + 16-byte secret + cloak domain hex) and decode it before handing the bytes to gotd. Co-Authored-By: Claude Opus 4.7 (1M context) --- pkg/connector/example-config.yaml | 2 ++ pkg/connector/proxy.go | 24 +++++++++++++++++++- pkg/connector/proxy_test.go | 37 +++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 pkg/connector/proxy_test.go diff --git a/pkg/connector/example-config.yaml b/pkg/connector/example-config.yaml index d7f23a29..a8cc0649 100644 --- a/pkg/connector/example-config.yaml +++ b/pkg/connector/example-config.yaml @@ -61,6 +61,8 @@ proxy: # Proxy IP address/domain name and port. address: "127.0.0.1:1080" # Proxy authentication (optional). Put MTProxy secret in password field. + # For mtproxy, the secret must be hex-encoded (the same form mtg/MTProxy + # tools print, e.g. "ee" + 16-byte secret + cloak domain hex for faketls). username: password: diff --git a/pkg/connector/proxy.go b/pkg/connector/proxy.go index 2ee361c5..3f6fde76 100644 --- a/pkg/connector/proxy.go +++ b/pkg/connector/proxy.go @@ -17,13 +17,31 @@ package connector import ( + "encoding/hex" "fmt" + "strings" "golang.org/x/net/proxy" "go.mau.fi/mautrix-telegram/pkg/gotd/telegram/dcs" ) +// decodeMTProxySecret parses an MTProxy secret string into raw bytes. +// MTProxy secrets are binary (faketls secrets begin with 0xEE, secured with 0xDD) +// and cannot be carried verbatim in a YAML string field, so we accept the standard +// hex encoding (optionally prefixed with "ee"/"dd") used by mtg/MTProxy tooling. +func decodeMTProxySecret(s string) ([]byte, error) { + s = strings.TrimSpace(s) + if s == "" { + return nil, fmt.Errorf("mtproxy secret is empty") + } + b, err := hex.DecodeString(s) + if err != nil { + return nil, fmt.Errorf("mtproxy secret must be hex-encoded: %w", err) + } + return b, nil +} + func GetProxyDialFunc(cfg ProxyConfig) (dcs.DialFunc, error) { switch cfg.Type { // we can't proxy HTTP through mtproxy @@ -54,7 +72,11 @@ func GetProxyResolver(cfg ProxyConfig) (dcs.Resolver, error) { resolver := dcs.Plain(dcs.PlainOptions{Dial: dialer}) return resolver, nil case "mtproxy": - return dcs.MTProxy(cfg.Address, []byte(cfg.Password), dcs.MTProxyOptions{}) + secret, err := decodeMTProxySecret(cfg.Password) + if err != nil { + return nil, err + } + return dcs.MTProxy(cfg.Address, secret, dcs.MTProxyOptions{}) default: return nil, fmt.Errorf("unsupported proxy type %s", cfg.Type) } diff --git a/pkg/connector/proxy_test.go b/pkg/connector/proxy_test.go new file mode 100644 index 00000000..d6f3113c --- /dev/null +++ b/pkg/connector/proxy_test.go @@ -0,0 +1,37 @@ +package connector + +import ( + "bytes" + "testing" +) + +func TestDecodeMTProxySecret(t *testing.T) { + // faketls secret: 0xee + 16 bytes + cloak domain ("working-name.ru" = 15 bytes) + hexSecret := "ee971746d927f4c0138b18447bfe1269bc70312e776f726b696e672d6e616d652e7275" + want := []byte{ + 0xee, + 0x97, 0x17, 0x46, 0xd9, 0x27, 0xf4, 0xc0, 0x13, + 0x8b, 0x18, 0x44, 0x7b, 0xfe, 0x12, 0x69, 0xbc, + 0x70, 0x31, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x69, + 0x6e, 0x67, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x2e, + 0x72, 0x75, + } + got, err := decodeMTProxySecret(hexSecret) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !bytes.Equal(got, want) { + t.Fatalf("decoded bytes mismatch:\n got=%x\nwant=%x", got, want) + } + + if _, err := decodeMTProxySecret(" " + hexSecret + "\n"); err != nil { + t.Fatalf("whitespace should be tolerated: %v", err) + } + + if _, err := decodeMTProxySecret(""); err == nil { + t.Fatal("expected error for empty secret") + } + if _, err := decodeMTProxySecret("not-hex!!"); err == nil { + t.Fatal("expected error for non-hex secret") + } +}