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:
parent
dd17e12e1b
commit
a3b4e1b994
@ -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
|
||||
|
92
client/internal/network/response.go
Normal file
92
client/internal/network/response.go
Normal 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
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user