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

135 lines
2.7 KiB
Go

package highway
import (
"fmt"
"net"
"sync/atomic"
"github.com/pkg/errors"
"github.com/Mrs4s/MiraiGo/binary"
"github.com/Mrs4s/MiraiGo/client/pb"
"github.com/Mrs4s/MiraiGo/internal/proto"
)
// see com/tencent/mobileqq/highway/utils/BaseConstants.java#L120-L121
const (
_REQ_CMD_DATA = "PicUp.DataUp"
_REQ_CMD_HEART_BREAK = "PicUp.Echo"
)
type Addr struct {
IP uint32
Port int
}
func (a Addr) AsNetIP() net.IP {
return net.IPv4(byte(a.IP>>24), byte(a.IP>>16), byte(a.IP>>8), byte(a.IP))
}
func (a Addr) String() string {
return fmt.Sprintf("%v:%v", binary.UInt32ToIPV4Address(a.IP), a.Port)
}
type Session struct {
Uin string
AppID int32
SigSession []byte
SessionKey []byte
SsoAddr []Addr
seq int32
/*
idleMu sync.Mutex
idleCount int
idle *idle
*/
}
const highwayMaxResponseSize int32 = 1024 * 100 // 100k
func (s *Session) AddrLength() int {
return len(s.SsoAddr)
}
func (s *Session) AppendAddr(ip, port uint32) {
addr := Addr{
IP: ip,
Port: int(port),
}
s.SsoAddr = append(s.SsoAddr, addr)
}
func (s *Session) nextSeq() int32 {
return atomic.AddInt32(&s.seq, 2)
}
func (s *Session) sendHeartbreak(conn net.Conn) error {
head, _ := proto.Marshal(&pb.ReqDataHighwayHead{
MsgBasehead: &pb.DataHighwayHead{
Version: 1,
Uin: s.Uin,
Command: _REQ_CMD_HEART_BREAK,
Seq: s.nextSeq(),
Appid: s.AppID,
Dataflag: 4096,
CommandId: 0,
LocaleId: 2052,
},
})
buffers := frame(head, nil)
_, err := buffers.WriteTo(conn)
return err
}
func (s *Session) sendEcho(conn net.Conn) error {
err := s.sendHeartbreak(conn)
if err != nil {
return errors.Wrap(err, "echo error")
}
if _, err = readResponse(binary.NewNetworkReader(conn)); err != nil {
return errors.Wrap(err, "echo error")
}
return nil
}
func readResponse(r *binary.NetworkReader) (*pb.RspDataHighwayHead, error) {
_, err := r.ReadByte()
if err != nil {
return nil, errors.Wrap(err, "failed to read byte")
}
hl, _ := r.ReadInt32()
a2, _ := r.ReadInt32()
if hl > highwayMaxResponseSize || a2 > highwayMaxResponseSize {
return nil, errors.Errorf("highway response invild. head size: %v body size: %v", hl, a2)
}
head, _ := r.ReadBytes(int(hl))
_, _ = r.ReadBytes(int(a2)) // skip payload
_, _ = r.ReadByte()
rsp := new(pb.RspDataHighwayHead)
if err = proto.Unmarshal(head, rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
return rsp, nil
}
/*
const maxIdleConn = 5
type idle struct {
conn net.Conn
delay int64
next *idle
}
// getConn ...
func (s *Session) getConn() net.Conn {
s.idleMu.Lock()
defer s.idleMu.Unlock()
conn := s.idle.conn
s.idle = s.idle.next
return conn
}
*/