package mtprotoedge import ( "encoding/binary" "errors" "fmt" "io" "net" "github.com/iamxvbaba/td/mtproxy/obfuscated2" "github.com/iamxvbaba/td/proto/codec" "github.com/iamxvbaba/td/transport" ) var errInvalidTCPTransportPrefix = errors.New("invalid MTProto TCP transport prefix") var errInvalidObfuscatedProtocol = errors.New("invalid obfuscated MTProto protocol tag") type tcpWireMode uint8 const ( tcpWireModeUnknown tcpWireMode = iota tcpWireModePlain tcpWireModeObfuscated ) type detectedTCPCodec uint8 const ( detectedTCPCodecUnknown detectedTCPCodec = iota detectedTCPCodecAbridged detectedTCPCodecIntermediate detectedTCPCodecPaddedIntermediate detectedTCPCodecFull ) type tcpTransportProbe struct { raw net.Conn prefix [8]byte prefixLen int mode tcpWireMode codec detectedTCPCodec } // detectTCPTransport reads at most the first eight bytes once per physical TCP // connection. Telegram's obfuscated2 nonce generation explicitly excludes all // plaintext codec tags and requires bytes 4..8 to be non-zero, while the first // Full frame has transport sequence number zero. Those disjoint invariants let // one public port admit both wire modes without probabilistic guessing. func detectTCPTransport(raw net.Conn) (tcpTransportProbe, error) { probe := tcpTransportProbe{raw: raw} if _, err := io.ReadFull(raw, probe.prefix[:1]); err != nil { return probe, fmt.Errorf("read first transport byte: %w", err) } probe.prefixLen = 1 if probe.prefix[0] == codec.AbridgedClientStart[0] { probe.mode = tcpWireModePlain probe.codec = detectedTCPCodecAbridged return probe, nil } if _, err := io.ReadFull(raw, probe.prefix[1:4]); err != nil { return probe, fmt.Errorf("read transport prefix: %w", err) } probe.prefixLen = 4 var firstFour [4]byte copy(firstFour[:], probe.prefix[:4]) switch firstFour { case codec.IntermediateClientStart: probe.mode = tcpWireModePlain probe.codec = detectedTCPCodecIntermediate return probe, nil case codec.PaddedIntermediateClientStart: probe.mode = tcpWireModePlain probe.codec = detectedTCPCodecPaddedIntermediate return probe, nil } first := binary.LittleEndian.Uint32(firstFour[:]) if isHTTPHeaderPrefix(firstFour) || first == 0x02010316 { return probe, fmt.Errorf("%w: reserved prefix %x", errInvalidTCPTransportPrefix, firstFour) } if _, err := io.ReadFull(raw, probe.prefix[4:8]); err != nil { return probe, fmt.Errorf("read transport discriminator: %w", err) } probe.prefixLen = 8 if binary.LittleEndian.Uint32(probe.prefix[4:8]) == 0 { length := binary.LittleEndian.Uint32(probe.prefix[:4]) if length < 3*4 || length > maxTransportMessageSize || length%4 != 0 { return probe, fmt.Errorf("%w: invalid full header length %d", errInvalidTCPTransportPrefix, length) } probe.mode = tcpWireModePlain probe.codec = detectedTCPCodecFull return probe, nil } probe.mode = tcpWireModeObfuscated return probe, nil } func (p tcpTransportProbe) replayConn() net.Conn { return newReplayNetConn(p.raw, p.prefix[:p.prefixLen]) } func (p tcpTransportProbe) plainFrameConn() net.Conn { if p.codec == detectedTCPCodecFull { // Full has no standalone codec tag: the first eight bytes are already the // length and sequence number of its first frame and must be replayed. return p.replayConn() } // Abridged/intermediate/padded-intermediate tags are client-only connection // headers. They were consumed by detection and are not part of the first frame. return p.raw } type replayNetConn struct { net.Conn prefix [8]byte n uint8 offset uint8 } func newReplayNetConn(conn net.Conn, prefix []byte) *replayNetConn { if len(prefix) > 8 { panic("mtprotoedge: replay prefix exceeds fixed transport discriminator") } replayed := &replayNetConn{Conn: conn, n: uint8(len(prefix))} copy(replayed.prefix[:], prefix) return replayed } func (c *replayNetConn) Read(p []byte) (int, error) { if c.offset < c.n { n := copy(p, c.prefix[c.offset:c.n]) c.offset += uint8(n) return n, nil } return c.Conn.Read(p) } type serverObfuscatedConn struct { net.Conn rw io.ReadWriter } func (c *serverObfuscatedConn) Read(p []byte) (int, error) { return c.rw.Read(p) } func (c *serverObfuscatedConn) Write(p []byte) (int, error) { return c.rw.Write(p) } func detectedCompatCodec(kind detectedTCPCodec) (transport.Codec, error) { switch kind { case detectedTCPCodecAbridged: return &quickAckAbridgedCodec{}, nil case detectedTCPCodecIntermediate: return &quickAckIntermediateCodec{}, nil case detectedTCPCodecPaddedIntermediate: return &quickAckPaddedIntermediateCodec{}, nil case detectedTCPCodecFull: return transport.Full.Codec(), nil default: return nil, fmt.Errorf("%w: unknown detected codec %d", errInvalidTCPTransportPrefix, kind) } } func detectObfuscatedProtocol(tag [4]byte) (detectedTCPCodec, int, error) { switch tag { case (codec.Abridged{}).ObfuscatedTag(): return detectedTCPCodecAbridged, 1, nil case codec.IntermediateClientStart: return detectedTCPCodecIntermediate, 4, nil case codec.PaddedIntermediateClientStart: return detectedTCPCodecPaddedIntermediate, 4, nil default: return detectedTCPCodecUnknown, 0, fmt.Errorf("%w: %x", errInvalidObfuscatedProtocol, tag) } } func (s *Server) promoteMixedTCP(raw net.Conn) (transport.Conn, string, error) { probe, err := detectTCPTransport(raw) if err != nil { return nil, "tcp_auto", err } if probe.mode == tcpWireModePlain { if s.codec != nil { conn, err := newCompatTransportConn(s.codec, probe.replayConn(), s.frameBudget) return conn, "tcp", err } connCodec, err := detectedCompatCodec(probe.codec) if err != nil { return nil, "tcp", err } conn, err := newCompatTransportConnWithCodec( probe.plainFrameConn(), connCodec, s.frameBudget, false, ) return conn, "tcp", err } replayed := probe.replayConn() rw, metadata, err := obfuscated2.Accept(replayed, nil) if err != nil { return nil, "obfuscated_tcp", fmt.Errorf("accept obfuscated2: %w", err) } obfuscated := &serverObfuscatedConn{Conn: replayed, rw: rw} detected, tagLen, err := detectObfuscatedProtocol(metadata.Protocol) if err != nil { return nil, "obfuscated_tcp", err } if s.codec != nil { conn, err := newCompatTransportConn( s.codec, newReplayNetConn(obfuscated, metadata.Protocol[:tagLen]), s.frameBudget, ) return conn, "obfuscated_tcp", err } connCodec, err := detectedCompatCodec(detected) if err != nil { return nil, "obfuscated_tcp", err } conn, err := newCompatTransportConnWithCodec( obfuscated, connCodec, s.frameBudget, false, ) return conn, "obfuscated_tcp", err }