Refactor TLS record reading logic in probe.go to replace readOneTLSApplicationRecord with readNextTLS17Payload. The new function handles TLS 1.2-style records, improving error handling and skipping non-application data records. This change enhances the robustness of MTProxy decryption.
Publish mtproxy_checker Docker image / test (push) Successful in 7s
Publish mtproxy_checker Docker image / build-and-push (push) Successful in 54s

This commit is contained in:
Denozordec
2026-04-11 01:51:56 +07:00
parent 7d13c599c4
commit bb1f71a6b2
+35 -17
View File
@@ -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) {