mirror of
https://github.com/Mrs4s/MiraiGo.git
synced 2025-05-04 19:17:38 +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)
|
return utils.B2S(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Reader) ReadInt32Bytes() []byte {
|
||||||
|
return r.ReadBytes(int(r.ReadInt32() - 4))
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Reader) ReadStringShort() string {
|
func (r *Reader) ReadStringShort() string {
|
||||||
data := r.ReadBytes(int(r.ReadUInt16()))
|
data := r.ReadBytes(int(r.ReadUInt16()))
|
||||||
return utils.B2S(data)
|
return utils.B2S(data)
|
||||||
@ -116,6 +120,10 @@ func (r *Reader) Len() int {
|
|||||||
return r.buf.Len()
|
return r.buf.Len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Reader) Index() int64 {
|
||||||
|
return r.buf.Size()
|
||||||
|
}
|
||||||
|
|
||||||
func (tlv TlvMap) Exists(key uint16) bool {
|
func (tlv TlvMap) Exists(key uint16) bool {
|
||||||
_, ok := tlv[key]
|
_, ok := tlv[key]
|
||||||
return ok
|
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()))
|
w.WriteUInt32At(pos, uint32(w.Len()))
|
||||||
return append([]byte(nil), w.Bytes()...)
|
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/pkg/errors"
|
||||||
|
|
||||||
"github.com/Mrs4s/MiraiGo/client/internal/network"
|
"github.com/Mrs4s/MiraiGo/client/internal/network"
|
||||||
|
"github.com/Mrs4s/MiraiGo/client/internal/oicq"
|
||||||
"github.com/Mrs4s/MiraiGo/internal/packets"
|
"github.com/Mrs4s/MiraiGo/internal/packets"
|
||||||
"github.com/Mrs4s/MiraiGo/message"
|
"github.com/Mrs4s/MiraiGo/message"
|
||||||
"github.com/Mrs4s/MiraiGo/utils"
|
"github.com/Mrs4s/MiraiGo/utils"
|
||||||
@ -291,10 +292,11 @@ func (c *QQClient) netLoop() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
data, _ := c.TCP.ReadBytes(int(l) - 4)
|
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 {
|
if err != nil {
|
||||||
c.Error("parse incoming packet error: %v", err)
|
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()
|
c.Disconnect()
|
||||||
go c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: "session expired"})
|
go c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: "session expired"})
|
||||||
continue
|
continue
|
||||||
@ -305,20 +307,25 @@ func (c *QQClient) netLoop() {
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if pkt.Flag2 == 2 {
|
if resp.EncryptType == network.EncryptTypeEmptyKey {
|
||||||
m, err := c.oicq.Unmarshal(pkt.Payload)
|
m, err := c.oicq.Unmarshal(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Error("decrypt payload error: %v", err)
|
c.Error("decrypt payload error: %v", err)
|
||||||
if errors.Is(err, packets.ErrUnknownFlag) {
|
if errors.Is(err, oicq.ErrUnknownFlag) {
|
||||||
go c.quickReconnect()
|
go c.quickReconnect()
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
pkt.Payload = m.Body
|
resp.Body = m.Body
|
||||||
}
|
}
|
||||||
errCount = 0
|
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)
|
c.stat.PacketReceived.Add(1)
|
||||||
|
pkt := &packets.IncomingPacket{
|
||||||
|
SequenceId: uint16(resp.SequenceID),
|
||||||
|
CommandName: resp.CommandName,
|
||||||
|
Payload: resp.Body,
|
||||||
|
}
|
||||||
go func(pkt *packets.IncomingPacket) {
|
go func(pkt *packets.IncomingPacket) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if pan := recover(); pan != nil {
|
if pan := recover(); pan != nil {
|
||||||
|
@ -1,26 +1,9 @@
|
|||||||
package packets
|
package packets
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/Mrs4s/MiraiGo/binary"
|
"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 {
|
type IncomingPacket struct {
|
||||||
SequenceId uint16
|
SequenceId uint16
|
||||||
Flag2 byte
|
Flag2 byte
|
||||||
@ -45,125 +28,3 @@ func BuildCode2DRequestPacket(seq uint32, j uint64, cmd uint16, bodyFunc func(wr
|
|||||||
w.WriteUInt16At(pos, uint16(w.Len()))
|
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