diff --git a/mtproto/protocol.go b/mtproto/protocol.go new file mode 100644 index 0000000..bca7366 --- /dev/null +++ b/mtproto/protocol.go @@ -0,0 +1,96 @@ +package mtproto + +import ( + "fmt" + + "github.com/9seconds/mtg/mtproto/rpc" + "github.com/9seconds/mtg/protocol" + "github.com/9seconds/mtg/telegram" + "github.com/9seconds/mtg/wrappers" +) + +func TelegramProtocol(req *protocol.TelegramRequest) (wrappers.Wrap, error) { + conn, err := telegram.Middle.Dial(req.Ctx, + req.Cancel, + req.ClientProtocol.DC(), + req.ClientProtocol.ConnectionProtocol()) + if err != nil { + return nil, fmt.Errorf("cannot connect to telegram: %w", err) + } + + rpcNonceConn := wrappers.NewMtprotoFrame(conn, rpc.SeqNoNonce) + rpcNonceReq, err := doRPCNonceRequest(rpcNonceConn) + if err != nil { + return nil, fmt.Errorf("cannot do nonce request: %w", err) + } + + rpcNonceResp, err := getRPCNonceResponse(rpcNonceConn, rpcNonceReq) + if err != nil { + return nil, fmt.Errorf("cannot get nonce response: %w", err) + } + + secureConn := wrappers.NewMiddleProxyCipher(conn, rpcNonceReq, rpcNonceResp, telegram.Middle.Secret()) + frameConn := wrappers.NewMtprotoFrame(secureConn, rpc.SeqNoHandshake) + + if err := doRPCHandshakeRequest(frameConn); err != nil { + return nil, fmt.Errorf("cannot do handshake request: %w", err) + } + if err := getRPCHandshakeResponse(frameConn); err != nil { + return nil, fmt.Errorf("cannot get handshake response: %w", err) + } + + return frameConn, nil +} + +func doRPCNonceRequest(conn wrappers.PacketWriter) (*rpc.NonceRequest, error) { + rpcNonceReq, err := rpc.NewNonceRequest(telegram.Middle.Secret()) + if err != nil { + panic(err) + } + if err := conn.Write(rpcNonceReq.Bytes()); err != nil { + return nil, err + } + + return rpcNonceReq, nil +} + +func getRPCNonceResponse(conn wrappers.PacketReader, req *rpc.NonceRequest) (*rpc.NonceResponse, error) { + packet, err := conn.Read() + if err != nil { + return nil, fmt.Errorf("cannot read from connection: %w", err) + } + + resp, err := rpc.NewNonceResponse(packet) + if err != nil { + return nil, fmt.Errorf("cannot build rpc nonce responce: %w", err) + } + if err = resp.Valid(req); err != nil { + return nil, fmt.Errorf("invalid nonce response: %w", err) + } + + return resp, nil +} + +func doRPCHandshakeRequest(conn wrappers.PacketWriter) error { + if err := conn.Write(rpc.HandshakeRequest); err != nil { + return fmt.Errorf("cannot make a request: %w", err) + } + return nil +} + +func getRPCHandshakeResponse(conn wrappers.PacketReader) error { + packet, err := conn.Read() + if err != nil { + return fmt.Errorf("cannot read a response: %w", err) + } + + resp, err := rpc.NewHandshakeResponse(packet) + if err != nil { + return fmt.Errorf("cannot build a handshake response: %w", err) + } + if err := resp.Valid(); err != nil { + return fmt.Errorf("invalid handshake response: %w", err) + } + + return nil +} diff --git a/telegram/base.go b/telegram/base.go index b4d7717..bc3319e 100644 --- a/telegram/base.go +++ b/telegram/base.go @@ -17,12 +17,17 @@ const telegramDialTimeout = 10 * time.Second type baseTelegram struct { dialer net.Dialer + secret []byte v4DefaultDC conntypes.DC V6DefaultDC conntypes.DC v4Addresses map[conntypes.DC][]string v6Addresses map[conntypes.DC][]string } +func (b *baseTelegram) Secret() []byte { + return b.secret +} + func (b *baseTelegram) dialToAddress(ctx context.Context, cancel context.CancelFunc, addr string) (wrappers.StreamReadWriteCloser, error) { diff --git a/telegram/interfaces.go b/telegram/interfaces.go index b75f057..9dc1bb8 100644 --- a/telegram/interfaces.go +++ b/telegram/interfaces.go @@ -12,4 +12,5 @@ type Telegram interface { context.CancelFunc, conntypes.DC, conntypes.ConnectionProtocol) (wrappers.StreamReadWriteCloser, error) + Secret() []byte } diff --git a/telegram/middle.go b/telegram/middle.go index 817b199..c465556 100644 --- a/telegram/middle.go +++ b/telegram/middle.go @@ -21,7 +21,6 @@ var Middle = NewMiddleTelegram() type middleTelegram struct { baseTelegram - secret []byte mutex sync.RWMutex }