1
0
mirror of https://github.com/Mrs4s/MiraiGo.git synced 2025-05-07 12:43:32 +08:00
MiraiGo/client/highway.go
2020-12-31 15:43:06 +08:00

232 lines
6.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package client
import (
"bytes"
"crypto/md5"
binary2 "encoding/binary"
"encoding/hex"
"fmt"
"github.com/Mrs4s/MiraiGo/binary"
"github.com/Mrs4s/MiraiGo/client/pb"
"github.com/Mrs4s/MiraiGo/utils"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
"io"
"net"
"net/http"
"strconv"
"time"
)
func (c *QQClient) highwayUpload(ip uint32, port int, updKey, data []byte, cmdId int32) error {
addr := net.TCPAddr{
IP: make([]byte, 4),
Port: port,
}
binary2.LittleEndian.PutUint32(addr.IP, ip)
conn, err := net.DialTCP("tcp", nil, &addr)
if err != nil {
return errors.Wrap(err, "failed to connect to highway server")
}
defer conn.Close()
h := md5.Sum(data)
pkt := c.buildImageUploadPacket(data, updKey, cmdId, h)
r := binary.NewNetworkReader(conn)
for _, p := range pkt {
_, err = conn.Write(p)
if err != nil {
return errors.Wrap(err, "failed to write")
}
_, err = r.ReadByte()
if err != nil {
return errors.Wrap(err, "failed to read byte")
}
hl, _ := r.ReadInt32()
a2, _ := r.ReadInt32()
payload, _ := r.ReadBytes(int(hl))
_, _ = r.ReadBytes(int(a2))
r.ReadByte()
rsp := new(pb.RspDataHighwayHead)
if err = proto.Unmarshal(payload, rsp); err != nil {
return errors.Wrap(err, "failed to unmarshal protobuf message")
}
if rsp.ErrorCode != 0 {
return errors.New("upload failed")
}
}
return nil
}
func (c *QQClient) highwayUploadByBDH(stream io.ReadSeeker, cmdId int32, ext []byte) ([]byte, error) {
// TODO: encrypted upload support.
if len(c.srvSsoAddrs) == 0 {
return nil, errors.New("srv addrs not found. maybe miss some packet?")
}
if c.highwaySession == nil {
return nil, errors.New("highway session not found. maybe miss some packet?")
}
h := md5.New()
length, _ := io.Copy(h, stream)
chunkSize := 8192 * 8
fh := h.Sum(nil)
_, _ = stream.Seek(0, io.SeekStart)
conn, err := net.DialTimeout("tcp", c.srvSsoAddrs[0], time.Second*20)
if err != nil {
return nil, errors.Wrap(err, "connect error")
}
offset := 0
reader := binary.NewNetworkReader(conn)
ticket := c.highwaySession.SigSession
if err = c.highwaySendHeartbreak(conn); err != nil {
return nil, errors.Wrap(err, "echo error")
}
if _, _, err = highwayReadResponse(reader); err != nil {
return nil, errors.Wrap(err, "echo error")
}
var rspExt []byte
for {
chunk := make([]byte, chunkSize)
rl, err := io.ReadFull(stream, chunk)
if err == io.EOF {
break
}
if err == io.ErrUnexpectedEOF {
chunk = chunk[:rl]
}
ch := md5.Sum(chunk)
head, _ := proto.Marshal(&pb.ReqDataHighwayHead{
MsgBasehead: &pb.DataHighwayHead{
Version: 1,
Uin: strconv.FormatInt(c.Uin, 10),
Command: "PicUp.DataUp",
Seq: c.nextGroupDataTransSeq(),
Appid: int32(c.version.AppId),
Dataflag: 4096,
CommandId: cmdId,
LocaleId: 2052,
},
MsgSeghead: &pb.SegHead{
Filesize: length,
Dataoffset: int64(offset),
Datalength: int32(rl),
Serviceticket: ticket,
Md5: ch[:],
FileMd5: fh[:],
},
ReqExtendinfo: ext,
})
offset += rl
_, err = conn.Write(binary.NewWriterF(func(w *binary.Writer) {
w.WriteByte(40)
w.WriteUInt32(uint32(len(head)))
w.WriteUInt32(uint32(len(chunk)))
w.Write(head)
w.Write(chunk)
w.WriteByte(41)
}))
if err != nil {
return nil, errors.Wrap(err, "write conn error")
}
rspHead, _, err := highwayReadResponse(reader)
if err != nil {
return nil, errors.Wrap(err, "highway upload error")
}
if rspHead.ErrorCode != 0 {
return nil, errors.New("upload failed")
}
if rspHead.RspExtendinfo != nil {
rspExt = rspHead.RspExtendinfo
}
if rspHead.MsgSeghead != nil && rspHead.MsgSeghead.Serviceticket != nil {
ticket = rspHead.MsgSeghead.Serviceticket
}
}
return rspExt, nil
}
func (c *QQClient) highwaySendHeartbreak(conn net.Conn) error {
head, _ := proto.Marshal(&pb.ReqDataHighwayHead{
MsgBasehead: &pb.DataHighwayHead{
Version: 1,
Uin: strconv.FormatInt(c.Uin, 10),
Command: "PicUp.Echo",
Seq: c.nextGroupDataTransSeq(),
Appid: int32(c.version.AppId),
Dataflag: 4096,
CommandId: 0,
LocaleId: 2052,
},
})
_, err := conn.Write(binary.NewWriterF(func(w *binary.Writer) {
w.WriteByte(40)
w.WriteUInt32(uint32(len(head)))
w.WriteUInt32(0)
w.Write(head)
w.WriteByte(41)
}))
return err
}
func highwayReadResponse(r *binary.NetworkReader) (*pb.RspDataHighwayHead, []byte, error) {
_, err := r.ReadByte()
if err != nil {
return nil, nil, errors.Wrap(err, "failed to read byte")
}
hl, _ := r.ReadInt32()
a2, _ := r.ReadInt32()
head, _ := r.ReadBytes(int(hl))
payload, _ := r.ReadBytes(int(a2))
_, _ = r.ReadByte()
rsp := new(pb.RspDataHighwayHead)
if err = proto.Unmarshal(head, rsp); err != nil {
return nil, nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
return rsp, payload, nil
}
// 只是为了写的跟上面一样长(bushi当然也应该是最快的玩法
func (c *QQClient) uploadPtt(ip string, port int32, updKey, fileKey, data, md5 []byte) error {
url := make([]byte, 512)[:0]
url = append(url, "http://"...)
url = append(url, ip...)
url = append(url, ':')
url = strconv.AppendInt(url, int64(port), 10)
url = append(url, "/?ver=4679&ukey="...)
p := len(url)
url = url[:p+len(updKey)*2]
hex.Encode(url[p:], updKey)
url = append(url, "&filekey="...)
p = len(url)
url = url[:p+len(fileKey)*2]
hex.Encode(url[p:], fileKey)
url = append(url, "&filesize="...)
url = strconv.AppendInt(url, int64(len(data)), 10)
url = append(url, "&bmd5="...)
p = len(url)
url = url[:p+32]
hex.Encode(url[p:], md5)
url = append(url, "&mType=pttDu&voice_encodec=1"...)
_, err := utils.HttpPostBytes(string(url), data)
return errors.Wrap(err, "failed to upload ptt")
}
func (c *QQClient) uploadGroupHeadPortrait(groupCode int64, img []byte) error {
url := fmt.Sprintf(
"http://htdata3.qq.com/cgi-bin/httpconn?htcmd=0x6ff0072&ver=5520&ukey=%v&range=0&uin=%v&seq=23&groupuin=%v&filetype=3&imagetype=5&userdata=0&subcmd=1&subver=101&clip=0_0_0_0&filesize=%v",
c.getSKey(),
c.Uin,
groupCode,
len(img),
)
req, err := http.NewRequest("POST", url, bytes.NewReader(img))
req.Header["User-Agent"] = []string{"Dalvik/2.1.0 (Linux; U; Android 7.1.2; PCRT00 Build/N2G48H)"}
req.Header["Content-Type"] = []string{"multipart/form-data;boundary=****"}
rsp, err := http.DefaultClient.Do(req)
if err != nil {
return errors.Wrap(err, "failed to upload group head portrait")
}
rsp.Body.Close()
return nil
}