diff --git a/internal/tgquick/probe.go b/internal/tgquick/probe.go index ab56c1c..e2c7289 100644 --- a/internal/tgquick/probe.go +++ b/internal/tgquick/probe.go @@ -154,7 +154,7 @@ func VerifyResPQ(ctx context.Context, conn net.Conn, enc, dec cipher.Stream, eeT var chunk []byte var rerr error if eeTLS { - chunk, rerr = readOneTLSApplicationRecord(br) + chunk, rerr = readNextTLS17Payload(br) } else { chunk, rerr = readAtMost(conn, 65536) } @@ -193,23 +193,41 @@ func VerifyResPQ(ctx context.Context, conn net.Conn, enc, dec cipher.Stream, eeT return ErrNoResPQ } -func readOneTLSApplicationRecord(br *bufio.Reader) ([]byte, error) { - h := make([]byte, 5) - if _, err := io.ReadFull(br, h); err != nil { - return nil, err +// readNextTLS17Payload reads TLS 1.2-style records from the wire (plaintext record headers). +// Handshake (0x16) and ChangeCipherSpec (0x15) payloads are not MTProxy-CTR ciphertext — skip them. +// Only application data (0x17) inner bytes are passed through the MTProxy decrypt stream (caller XORs). +// Some proxies send extra handshake records after the fake ServerHello; clients skip them before MTProto. +func readNextTLS17Payload(br *bufio.Reader) ([]byte, error) { + const maxSkips = 128 + for range maxSkips { + h := make([]byte, 5) + if _, err := io.ReadFull(br, h); err != nil { + return nil, err + } + ver := binary.BigEndian.Uint16(h[1:3]) + if ver != 0x0303 && ver != 0x0301 { + return nil, fmt.Errorf("unexpected tls record version 0x%04x (type 0x%02x); if type is 0x48 ('H') the peer may be speaking HTTP on this socket", ver, h[0]) + } + n := int(binary.BigEndian.Uint16(h[3:5])) + if n <= 0 || n > 1<<20 { + return nil, fmt.Errorf("invalid tls record length %d", n) + } + payload := make([]byte, n) + if _, err := io.ReadFull(br, payload); err != nil { + return nil, err + } + switch h[0] { + case 0x17: // application data — inner bytes are MTProxy-obfuscated + return payload, nil + case 0x16, 0x15: // more handshake / CCS after ServerHello — plaintext, do not advance CTR interpretation here + continue + case 0x14: // alert + return nil, fmt.Errorf("tls alert record from proxy (len=%d)", n) + default: + return nil, fmt.Errorf("unexpected tls record type 0x%02x (len=%d); 0x48 often means cleartext HTTP if the stream is misaligned", h[0], n) + } } - if h[0] != 0x17 { - return nil, fmt.Errorf("unexpected tls record type 0x%02x", h[0]) - } - n := int(binary.BigEndian.Uint16(h[3:5])) - if n <= 0 || n > 1<<20 { - return nil, fmt.Errorf("invalid tls app length %d", n) - } - p := make([]byte, n) - if _, err := io.ReadFull(br, p); err != nil { - return nil, err - } - return p, nil + return nil, fmt.Errorf("too many non-application tls records before 0x17") } func readAtMost(conn net.Conn, max int) ([]byte, error) {