1
0
mirror of https://github.com/Mrs4s/MiraiGo.git synced 2025-05-04 19:17:38 +08:00
MiraiGo/client/qidian.go
wdvxdr ec4cd4a6f3
style: internal/proto
use this package for conveniently replacing proto package
in the future.
2021-11-22 22:05:19 +08:00

196 lines
6.4 KiB
Go

// 企点协议相关特殊逻辑
package client
import (
"bytes"
"fmt"
"io"
"net/http"
"github.com/pkg/errors"
"github.com/Mrs4s/MiraiGo/binary"
"github.com/Mrs4s/MiraiGo/client/pb/cmd0x3f6"
"github.com/Mrs4s/MiraiGo/client/pb/cmd0x6ff"
"github.com/Mrs4s/MiraiGo/client/pb/msg"
"github.com/Mrs4s/MiraiGo/internal/packets"
"github.com/Mrs4s/MiraiGo/internal/proto"
"github.com/Mrs4s/MiraiGo/utils"
)
func init() {
decoders["qidianservice.69"] = decodeLoginExtraResponse
decoders["HttpConn.0x6ff_501"] = decodeConnKeyResponse
}
// getQiDianAddressDetailList 外部联系人列表
func (c *QQClient) getQiDianAddressDetailList() ([]*FriendInfo, error) {
req := &cmd0x6ff.C519ReqBody{
SubCmd: proto.Uint32(33),
CrmCommonHead: &cmd0x6ff.C519CRMMsgHead{
KfUin: proto.Uint64(uint64(c.QiDian.MasterUin)),
VerNo: proto.Uint32(uint32(utils.ConvertSubVersionToInt(c.version.SortVersionName))),
CrmSubCmd: proto.Uint32(33),
LaborUin: proto.Uint64(uint64(c.Uin)),
},
GetAddressDetailListReqBody: &cmd0x6ff.GetAddressDetailListReqBody{
Timestamp2: proto.Uint64(0),
},
}
rspData, err := c.bigDataRequest(1305, req)
if err != nil {
return nil, errors.Wrap(err, "request error")
}
rsp := &cmd0x6ff.C519RspBody{}
if err = proto.Unmarshal(rspData, rsp); err != nil {
return nil, errors.Wrap(err, "unmarshal error")
}
if rsp.GetAddressDetailListRspBody == nil {
return nil, errors.New("rsp body is nil")
}
ret := []*FriendInfo{}
for _, detail := range rsp.GetAddressDetailListRspBody.AddressDetail {
if len(detail.Qq) == 0 {
c.Warning("address detail %v QQ is 0", string(detail.Name))
continue
}
ret = append(ret, &FriendInfo{
Uin: int64(detail.Qq[0].GetAccount()),
Nickname: string(detail.Name),
})
}
return ret, nil
}
func (c *QQClient) buildLoginExtraPacket() (uint16, []byte) {
seq := c.nextSeq()
req := &cmd0x3f6.C3F6ReqBody{
SubCmd: proto.Uint32(69),
CrmCommonHead: &cmd0x3f6.C3F6CRMMsgHead{
CrmSubCmd: proto.Uint32(69),
VerNo: proto.Uint32(uint32(utils.ConvertSubVersionToInt(c.version.SortVersionName))),
Clienttype: proto.Uint32(2),
},
SubcmdLoginProcessCompleteReqBody: &cmd0x3f6.QDUserLoginProcessCompleteReqBody{
Kfext: proto.Uint64(uint64(c.Uin)),
Pubno: &c.version.AppId,
Buildno: proto.Uint32(uint32(utils.ConvertSubVersionToInt(c.version.SortVersionName))),
TerminalType: proto.Uint32(2),
Status: proto.Uint32(10),
LoginTime: proto.Uint32(5),
HardwareInfo: proto.String(string(c.deviceInfo.Model)),
SoftwareInfo: proto.String(string(c.deviceInfo.Version.Release)),
Guid: c.deviceInfo.Guid,
AppName: &c.version.ApkId,
SubAppId: &c.version.AppId,
},
}
payload, _ := proto.Marshal(req)
packet := packets.BuildUniPacket(c.Uin, seq, "qidianservice.69", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
return seq, packet
}
func (c *QQClient) buildConnKeyRequestPacket() (uint16, []byte) {
seq := c.nextSeq()
req := &cmd0x6ff.C501ReqBody{
ReqBody: &cmd0x6ff.SubCmd0X501ReqBody{
Uin: proto.Uint64(uint64(c.Uin)),
IdcId: proto.Uint32(0),
Appid: proto.Uint32(16),
LoginSigType: proto.Uint32(1),
RequestFlag: proto.Uint32(3),
ServiceTypes: []uint32{1},
},
}
payload, _ := proto.Marshal(req)
packet := packets.BuildUniPacket(c.Uin, seq, "HttpConn.0x6ff_501", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
return seq, packet
}
func (c *QQClient) bigDataRequest(subCmd uint32, req proto.Message) ([]byte, error) {
if c.QiDian.bigDataReqSession == nil {
return nil, errors.New("please call conn key request method before")
}
data, _ := proto.Marshal(req)
head, _ := proto.Marshal(&msg.IMHead{
HeadType: proto.Uint32(4),
HttpconnHead: &msg.HttpConnHead{
Uin: proto.Uint64(uint64(c.Uin)),
Command: proto.Uint32(1791),
SubCommand: &subCmd,
Seq: proto.Uint32(uint32(c.nextHighwayApplySeq())),
Version: proto.Uint32(386), // todo: short version convert
Flag: proto.Uint32(1),
CompressType: proto.Uint32(0),
ErrorCode: proto.Uint32(0),
},
LoginSig: &msg.LoginSig{
Type: proto.Uint32(22),
Sig: c.QiDian.bigDataReqSession.SigSession,
},
})
tea := binary.NewTeaCipher(c.QiDian.bigDataReqSession.SessionKey)
body := tea.Encrypt(data)
url := fmt.Sprintf("http://%v/cgi-bin/httpconn", c.QiDian.bigDataReqAddrs[0])
httpReq, _ := http.NewRequest("POST", url, bytes.NewReader(binary.NewWriterF(func(w *binary.Writer) {
w.WriteByte(40)
w.WriteUInt32(uint32(len(head)))
w.WriteUInt32(uint32(len(body)))
w.Write(head)
w.Write(body)
w.WriteByte(41)
})))
rsp, err := http.DefaultClient.Do(httpReq)
if err != nil {
return nil, errors.Wrap(err, "request error")
}
defer func() { _ = rsp.Body.Close() }()
rspBody, _ := io.ReadAll(rsp.Body)
r := binary.NewReader(rspBody)
r.ReadByte()
l1 := int(r.ReadInt32())
l2 := int(r.ReadInt32())
r.ReadBytes(l1)
payload := r.ReadBytes(l2)
return tea.Decrypt(payload), nil
}
func decodeLoginExtraResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
rsp := cmd0x3f6.C3F6RspBody{}
if err := proto.Unmarshal(payload, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
if rsp.SubcmdLoginProcessCompleteRspBody == nil {
return nil, errors.New("login process resp is nil")
}
c.QiDian = &QiDianAccountInfo{
MasterUin: int64(rsp.SubcmdLoginProcessCompleteRspBody.GetCorpuin()),
ExtName: rsp.SubcmdLoginProcessCompleteRspBody.GetExtuinName(),
CreateTime: int64(rsp.SubcmdLoginProcessCompleteRspBody.GetOpenAccountTime()),
}
return nil, nil
}
func decodeConnKeyResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
rsp := cmd0x6ff.C501RspBody{}
if err := proto.Unmarshal(payload, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
if c.QiDian == nil {
return nil, errors.New("please call login extra before")
}
c.QiDian.bigDataReqSession = &bigDataSessionInfo{
SigSession: rsp.RspBody.SigSession,
SessionKey: rsp.RspBody.SessionKey,
}
for _, srv := range rsp.RspBody.Addrs {
if srv.GetServiceType() == 1 {
for _, addr := range srv.Addrs {
c.QiDian.bigDataReqAddrs = append(c.QiDian.bigDataReqAddrs, fmt.Sprintf("%v:%v", binary.UInt32ToIPV4Address(addr.GetIp()), addr.GetPort()))
}
}
}
return nil, nil
}