Enhance MTProxy checker by introducing a new -probe mode deep-strict for stricter response validation. Update README and Docker documentation to clarify probing modes, exit codes, and the new MTPROXY_TDLIB_HELPER and MTPROXY_TDLIB_TIMEOUT environment variables for external helper integration. Refactor connection handling and error reporting to improve robustness and clarity in response verification.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// Package tgquick performs a minimal MTProto step (req_pq → resPQ) through an established MTProxy tunnel,
|
||||
// Package tgquick performs a minimal MTProto step (req_pq → ответ DC) through an established MTProxy tunnel,
|
||||
// matching Telethon's RandomizedIntermediate + MTProxyIO framing.
|
||||
package tgquick
|
||||
|
||||
@@ -24,9 +24,12 @@ const (
|
||||
maxRI = 1 << 20
|
||||
)
|
||||
|
||||
// ErrNoResPQ means no valid resPQ was received from Telegram through the tunnel.
|
||||
// ErrNoResPQ means strict deep check did not see resPQ#05162463 from DC.
|
||||
var ErrNoResPQ = errors.New("no resPQ from telegram through proxy (tunnel may be broken)")
|
||||
|
||||
// ErrNoDCReply means relaxed deep check got no parsable unencrypted MTProto reply from DC before deadline.
|
||||
var ErrNoDCReply = errors.New("no reply from telegram DC through proxy (timeout)")
|
||||
|
||||
// ErrPeerClosed is returned when the remote side closes TCP during the MTProto probe.
|
||||
var ErrPeerClosed = errors.New("connection closed by peer during mtproto probe")
|
||||
|
||||
@@ -109,6 +112,22 @@ func isResPQ(mtInner []byte) bool {
|
||||
return binary.LittleEndian.Uint32(body[0:4]) == tlResPQ
|
||||
}
|
||||
|
||||
// isUnencryptedDCEnvelope reports a valid MTProto unencrypted container (auth_key_id 0) with non-empty body.
|
||||
// After req_pq the DC normally sends resPQ, but any well-formed unencrypted reply proves the tunnel carried DC traffic.
|
||||
func isUnencryptedDCEnvelope(mtInner []byte) bool {
|
||||
if len(mtInner) < 24 {
|
||||
return false
|
||||
}
|
||||
if binary.LittleEndian.Uint64(mtInner[0:8]) != 0 {
|
||||
return false
|
||||
}
|
||||
ml := int(binary.LittleEndian.Uint32(mtInner[16:20]))
|
||||
if ml < 4 || ml > maxRI || 20+ml > len(mtInner) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// DrainPostInitEE reads inbound fake-TLS records after MTProxy init and before req_pq (Telethon TcpMTProxy waits for data ~2s).
|
||||
// Consumes 0x17 payloads with dec so the CTR stream stays aligned with the server; discards MTProto frames until idle.
|
||||
func DrainPostInitEE(ctx context.Context, br *bufio.Reader, conn net.Conn, dec cipher.Stream, maxWait time.Duration) error {
|
||||
@@ -172,9 +191,10 @@ func DrainPostInitEE(ctx context.Context, br *bufio.Reader, conn net.Conn, dec c
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifyResPQ sends req_pq and waits for resPQ on the MTProxy byte stream (decrypt with dec, encrypt with enc).
|
||||
// VerifyResPQ sends req_pq and waits for a DC reply on the MTProxy byte stream (decrypt with dec, encrypt with enc).
|
||||
// If strictResPQ is true, the first matching RI frame must be resPQ; otherwise any valid unencrypted MTProto message is enough.
|
||||
// For ee (fake-TLS), pass the same bufio.Reader used after init (and DrainPostInitEE); for dd pass tlsBR=nil.
|
||||
func VerifyResPQ(ctx context.Context, conn net.Conn, enc, dec cipher.Stream, tlsBR *bufio.Reader) error {
|
||||
func VerifyResPQ(ctx context.Context, conn net.Conn, enc, dec cipher.Stream, tlsBR *bufio.Reader, strictResPQ bool) error {
|
||||
plain, err := buildReqPQ()
|
||||
if err != nil {
|
||||
return fmt.Errorf("build req_pq: %w", err)
|
||||
@@ -263,14 +283,24 @@ func VerifyResPQ(ctx context.Context, conn net.Conn, enc, dec cipher.Stream, tls
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if isResPQ(frame) {
|
||||
if strictResPQ {
|
||||
if isResPQ(frame) {
|
||||
_ = conn.SetReadDeadline(time.Time{})
|
||||
return nil
|
||||
}
|
||||
continue
|
||||
}
|
||||
if isUnencryptedDCEnvelope(frame) {
|
||||
_ = conn.SetReadDeadline(time.Time{})
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = conn.SetReadDeadline(time.Time{})
|
||||
return ErrNoResPQ
|
||||
if strictResPQ {
|
||||
return ErrNoResPQ
|
||||
}
|
||||
return ErrNoDCReply
|
||||
}
|
||||
|
||||
// readNextTLS17Payload reads TLS 1.2-style records from the wire (plaintext record headers).
|
||||
|
||||
Reference in New Issue
Block a user