mtproto: add compat transport quick ack support

(cherry picked from commit d051bc37bd14076fdd0a83ad41cd507929b20ece)
This commit is contained in:
A 2026-06-09 02:44:15 +08:00
parent 47cab2c9b0
commit 091d8f084b
11 changed files with 915 additions and 21 deletions

View file

@ -2,6 +2,8 @@ package mtprotoedge
import (
"context"
"crypto/sha256"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
@ -125,6 +127,9 @@ func (s *Server) handleEncrypted(ctx context.Context, tc transport.Conn, cs *con
)
return current, s.sendBadMsg(ctx, current, data.MessageID, data.SeqNo, code)
}
if err := sendQuickAckIfRequested(ctx, tc, key, data); err != nil {
return current, err
}
content := clientMessageNeedsAck(typeID)
if record, ok := cs.seenRecord(data.MessageID); ok {
@ -170,6 +175,30 @@ func (s *Server) handleEncrypted(ctx context.Context, tc transport.Conn, cs *con
return current, nil
}
func sendQuickAckIfRequested(ctx context.Context, tc transport.Conn, key crypto.AuthKey, data *crypto.EncryptedMessageData) error {
q, ok := tc.(quickAckTransport)
if !ok || !q.ConsumeQuickAckRequested() {
return nil
}
token, err := clientQuickAckToken(key, data)
if err != nil {
return err
}
return q.SendQuickAck(ctx, token)
}
func clientQuickAckToken(key crypto.AuthKey, data *crypto.EncryptedMessageData) (uint32, error) {
var plain bin.Buffer
if err := data.Encode(&plain); err != nil {
return 0, err
}
h := sha256.New()
_, _ = h.Write(key.Value[88:120])
_, _ = h.Write(plain.Raw())
sum := h.Sum(nil)
return binary.LittleEndian.Uint32(sum[:4]) &^ quickAckResponseFlag, nil
}
// dispatch 处理一条明文消息:解包 container/gzip,处理服务消息,其余转 RPC 路由。
// content-related 消息(ping、RPC)的 msg_id 会收集到 acks 以便统一确认。
func (s *Server) dispatch(ctx context.Context, cs *connState, c *Conn, msgID int64, seqNo int32, b *bin.Buffer, acks *[]int64) error {
@ -560,6 +589,9 @@ func validateClientEnvelope(now time.Time, msgID int64, seqNo int32, typeID uint
if msgTime.After(now.Add(30 * time.Second)) {
return badMsgIDTooHigh
}
if clientMessageAllowsEitherSeqParity(typeID) {
return 0
}
if clientMessageNeedsAck(typeID) {
if seqNo%2 == 0 {
return badMsgSeqNotOdd
@ -593,6 +625,9 @@ func validateClientContainerEnvelope(msgID int64, seqNo int32, typeID uint32) in
if msgID == 0 || proto.MessageID(msgID).Type() != proto.MessageFromClient {
return badMsgIDInvalidBits
}
if clientMessageAllowsEitherSeqParity(typeID) {
return 0
}
if clientMessageNeedsAck(typeID) {
if seqNo%2 == 0 {
return badMsgSeqNotOdd
@ -603,6 +638,15 @@ func validateClientContainerEnvelope(msgID int64, seqNo int32, typeID uint32) in
return 0
}
func clientMessageAllowsEitherSeqParity(typeID uint32) bool {
switch typeID {
case mt.PingDelayDisconnectRequestTypeID:
return true
default:
return false
}
}
func clientMessageNeedsAck(typeID uint32) bool {
switch typeID {
case proto.MessageContainerTypeID,