1
0
mirror of https://github.com/Mrs4s/MiraiGo.git synced 2025-05-04 11:07:40 +08:00

client: move parse packet logic to transport

This commit is contained in:
wdvxdr 2021-12-24 16:36:18 +08:00
parent dd17e12e1b
commit a3b4e1b994
No known key found for this signature in database
GPG Key ID: 703F8C071DE7A1B6
5 changed files with 114 additions and 170 deletions

View File

@ -71,6 +71,10 @@ func (r *Reader) ReadString() string {
return utils.B2S(data)
}
func (r *Reader) ReadInt32Bytes() []byte {
return r.ReadBytes(int(r.ReadInt32() - 4))
}
func (r *Reader) ReadStringShort() string {
data := r.ReadBytes(int(r.ReadUInt16()))
return utils.B2S(data)
@ -116,6 +120,10 @@ func (r *Reader) Len() int {
return r.buf.Len()
}
func (r *Reader) Index() int64 {
return r.buf.Size()
}
func (tlv TlvMap) Exists(key uint16) bool {
_, ok := tlv[key]
return ok

View File

@ -0,0 +1,92 @@
package network
import (
"strconv"
"github.com/pkg/errors"
"github.com/Mrs4s/MiraiGo/binary"
)
type Response struct {
Type RequestType
EncryptType EncryptType
SequenceID int32
Uin int64
CommandName string
Body []byte
Message string
// Request is the original request that obtained this response.
// Request *Request
}
var (
ErrSessionExpired = errors.New("session expired")
ErrPacketDropped = errors.New("packet dropped")
ErrInvalidPacketType = errors.New("invalid packet type")
)
func (t *Transport) ReadResponse(head []byte) (*Response, error) {
resp := new(Response)
r := binary.NewReader(head)
resp.Type = RequestType(r.ReadInt32())
if resp.Type != RequestTypeLogin && resp.Type != RequestTypeSimple {
return resp, ErrInvalidPacketType
}
resp.EncryptType = EncryptType(r.ReadByte())
_ = r.ReadByte() // 0x00?
resp.Uin, _ = strconv.ParseInt(r.ReadString(), 10, 64)
body := r.ReadAvailable()
switch resp.EncryptType {
case EncryptTypeNoEncrypt:
// nothing to do
case EncryptTypeD2Key:
body = binary.NewTeaCipher(t.Sig.D2Key).Decrypt(body)
case EncryptTypeEmptyKey:
body = binary.NewTeaCipher(emptyKey).Decrypt(body)
}
err := t.readSSOFrame(resp, body)
return resp, err
}
func (t *Transport) readSSOFrame(resp *Response, payload []byte) error {
reader := binary.NewReader(payload)
headLen := reader.ReadInt32()
if headLen-4 > int32(reader.Len()) {
return errors.WithStack(ErrPacketDropped)
}
head := binary.NewReader(reader.ReadBytes(int(headLen) - 4))
resp.SequenceID = head.ReadInt32()
switch retCode := head.ReadInt32(); retCode {
case 0:
// ok
case -10008:
return errors.WithStack(ErrSessionExpired)
default:
return errors.Errorf("return code unsuccessful: %d", retCode)
}
resp.Message = head.ReadString()
resp.CommandName = head.ReadString()
if resp.CommandName == "Heartbeat.Alive" {
return nil
}
_ = head.ReadInt32Bytes() // session id
compressedFlag := head.ReadInt32()
bodyLen := reader.ReadInt32() - 4
body := reader.ReadAvailable()
if bodyLen > 0 && bodyLen < int32(len(body)) {
body = body[:bodyLen]
}
switch compressedFlag {
case 0, 8:
case 1:
body = binary.ZlibUncompress(body)
}
resp.Body = body
return nil
}

View File

@ -97,27 +97,3 @@ func (t *Transport) PackPacket(req *Request) []byte {
w.WriteUInt32At(pos, uint32(w.Len()))
return append([]byte(nil), w.Bytes()...)
}
func (t *Transport) parse(head []byte) *Request {
req := new(Request)
r := binary.NewReader(head)
req.Type = RequestType(r.ReadInt32())
encryptType := EncryptType(r.ReadInt32())
switch req.Type {
case RequestTypeLogin:
// req.Key = r.ReadBytes(int(encryptType))
case RequestTypeSimple:
req.SequenceID = r.ReadInt32()
}
_ = r.ReadString() // req.Uin
body := r.ReadAvailable()
switch encryptType {
case EncryptTypeNoEncrypt:
req.Body = body
case EncryptTypeD2Key:
req.Body = binary.NewTeaCipher(t.Sig.D2Key).Decrypt(body)
case EncryptTypeEmptyKey:
req.Body = binary.NewTeaCipher(emptyKey).Decrypt(body)
}
return req
}

View File

@ -9,6 +9,7 @@ import (
"github.com/pkg/errors"
"github.com/Mrs4s/MiraiGo/client/internal/network"
"github.com/Mrs4s/MiraiGo/client/internal/oicq"
"github.com/Mrs4s/MiraiGo/internal/packets"
"github.com/Mrs4s/MiraiGo/message"
"github.com/Mrs4s/MiraiGo/utils"
@ -291,10 +292,11 @@ func (c *QQClient) netLoop() {
continue
}
data, _ := c.TCP.ReadBytes(int(l) - 4)
pkt, err := packets.ParseIncomingPacket(data, c.sig.D2Key)
resp, err := c.transport.ReadResponse(data)
// pkt, err := packets.ParseIncomingPacket(data, c.sig.D2Key)
if err != nil {
c.Error("parse incoming packet error: %v", err)
if errors.Is(err, packets.ErrSessionExpired) || errors.Is(err, packets.ErrPacketDropped) {
if errors.Is(err, network.ErrSessionExpired) || errors.Is(err, network.ErrPacketDropped) {
c.Disconnect()
go c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: "session expired"})
continue
@ -305,20 +307,25 @@ func (c *QQClient) netLoop() {
}
continue
}
if pkt.Flag2 == 2 {
m, err := c.oicq.Unmarshal(pkt.Payload)
if resp.EncryptType == network.EncryptTypeEmptyKey {
m, err := c.oicq.Unmarshal(resp.Body)
if err != nil {
c.Error("decrypt payload error: %v", err)
if errors.Is(err, packets.ErrUnknownFlag) {
if errors.Is(err, oicq.ErrUnknownFlag) {
go c.quickReconnect()
}
continue
}
pkt.Payload = m.Body
resp.Body = m.Body
}
errCount = 0
c.Debug("rev pkt: %v seq: %v", pkt.CommandName, pkt.SequenceId)
c.Debug("rev pkt: %v seq: %v", resp.CommandName, resp.SequenceID)
c.stat.PacketReceived.Add(1)
pkt := &packets.IncomingPacket{
SequenceId: uint16(resp.SequenceID),
CommandName: resp.CommandName,
Payload: resp.Body,
}
go func(pkt *packets.IncomingPacket) {
defer func() {
if pan := recover(); pan != nil {

View File

@ -1,26 +1,9 @@
package packets
import (
"strconv"
"github.com/pkg/errors"
"github.com/Mrs4s/MiraiGo/binary"
)
var (
ErrUnknownFlag = errors.New("unknown flag")
ErrInvalidPayload = errors.New("invalid payload")
ErrDecryptFailed = errors.New("decrypt failed")
ErrSessionExpired = errors.New("session expired")
ErrPacketDropped = errors.New("packet dropped")
)
type ISendingPacket interface {
CommandId() uint16
Writer() *binary.Writer
}
type IncomingPacket struct {
SequenceId uint16
Flag2 byte
@ -45,125 +28,3 @@ func BuildCode2DRequestPacket(seq uint32, j uint64, cmd uint16, bodyFunc func(wr
w.WriteUInt16At(pos, uint16(w.Len()))
})
}
func ParseIncomingPacket(payload, d2key []byte) (*IncomingPacket, error) {
if len(payload) < 6 {
return nil, errors.WithStack(ErrInvalidPayload)
}
reader := binary.NewReader(payload)
flag1 := reader.ReadInt32()
flag2 := reader.ReadByte()
if reader.ReadByte() != 0 { // flag3
// return nil, errors.WithStack(ErrUnknownFlag)
}
reader.ReadString() // uin string
decrypted := func() (data []byte) {
switch flag2 {
case 0:
return reader.ReadAvailable()
case 1:
d2 := binary.NewTeaCipher(d2key)
return d2.Decrypt(reader.ReadAvailable())
case 2:
z16 := binary.NewTeaCipher(make([]byte, 16))
return z16.Decrypt(reader.ReadAvailable())
}
return nil
}()
if len(decrypted) == 0 {
return nil, errors.WithStack(ErrDecryptFailed)
}
if flag1 != 0x0A && flag1 != 0x0B {
return nil, errors.WithStack(ErrDecryptFailed)
}
return parseSsoFrame(decrypted, flag2)
}
func parseSsoFrame(payload []byte, flag2 byte) (*IncomingPacket, error) {
reader := binary.NewReader(payload)
headLen := reader.ReadInt32()
if headLen-4 > int32(reader.Len()) {
return nil, errors.WithStack(ErrPacketDropped)
}
head := binary.NewReader(reader.ReadBytes(int(headLen) - 4))
seqID := head.ReadInt32()
retCode := head.ReadInt32()
if retCode != 0 {
if retCode == -10008 {
return nil, errors.WithStack(ErrSessionExpired)
}
return nil, errors.New("return code unsuccessful: " + strconv.FormatInt(int64(retCode), 10))
}
head.ReadBytes(int(head.ReadInt32()) - 4) // extra data
commandName := head.ReadString()
sessionID := head.ReadBytes(int(head.ReadInt32()) - 4)
if commandName == "Heartbeat.Alive" {
return &IncomingPacket{
SequenceId: uint16(seqID),
Flag2: flag2,
CommandName: commandName,
SessionId: sessionID,
Payload: []byte{},
}, nil
}
compressedFlag := head.ReadInt32()
reader.ReadInt32()
packet := func() []byte {
if compressedFlag == 0 {
return reader.ReadAvailable()
}
if compressedFlag == 1 {
return binary.ZlibUncompress(reader.ReadAvailable())
}
if compressedFlag == 8 {
return reader.ReadAvailable()
}
return nil
}()
return &IncomingPacket{
SequenceId: uint16(seqID),
Flag2: flag2,
CommandName: commandName,
SessionId: sessionID,
Payload: packet,
}, nil
}
func (pkt *IncomingPacket) DecryptPayload(ecdhShareKey, random, sessionKey []byte) ([]byte, error) {
reader := binary.NewReader(pkt.Payload)
if flag := reader.ReadByte(); flag != 2 {
return nil, ErrUnknownFlag
}
reader.ReadBytes(2)
reader.ReadBytes(2)
reader.ReadUInt16()
reader.ReadUInt16()
reader.ReadInt32()
encryptType := reader.ReadUInt16()
reader.ReadByte()
if encryptType == 0 {
data := func() (decrypted []byte) {
d := reader.ReadBytes(reader.Len() - 1)
defer func() {
if pan := recover(); pan != nil {
tea := binary.NewTeaCipher(random)
decrypted = tea.Decrypt(d)
}
}()
tea := binary.NewTeaCipher(ecdhShareKey)
decrypted = tea.Decrypt(d)
return
}()
return data, nil
}
if encryptType == 3 {
return func() []byte {
d := reader.ReadBytes(reader.Len() - 1)
return binary.NewTeaCipher(sessionKey).Decrypt(d)
}(), nil
}
if encryptType == 4 {
panic("todo")
}
return nil, errors.WithStack(ErrUnknownFlag)
}