Files
2026-02-17 13:06:23 +08:00

219 lines
6.4 KiB
Go

package mmtls
import (
"errors"
"sync/atomic"
"xiawan/wx/clientsdk/baseutils"
"golang.org/x/net/proxy"
)
// InitMMTLSInfoLong 初始化MMTLS信息通过长链接
func InitMMTLSInfoLong(dialer proxy.Dialer, longHostName string, shortHostName string, pskList []*Psk) (*MMInfo, error) {
// 初始化MMInfo
mmInfo := CreateNewMMInfo()
mmInfo.ShortHost = shortHostName
mmInfo.LongHost = longHostName
mmInfo.Dialer = dialer
// 第一次设置为空,后面设置成前面握手返回的Psk列表
mmInfo.ShortPskList = pskList
// 随机生成ClientEcdhKeys,握手用
mmInfo.ClientEcdhKeys = CreateClientEcdhKeys()
// 开始握手
err := MMHandShakeByLongLink(mmInfo)
if err != nil {
return nil, err
}
// 发送一次心跳包
err = SendHeartBeat(mmInfo)
if err != nil {
return nil, err
}
// 握手成功,设置短链接的HOST 和 新的URL
shortURL := []byte("/mmtls/")
mmInfo.ShortURL = string(append(shortURL, []byte(baseutils.RandomSmallHexString(8))[0:]...))
return mmInfo, nil
}
// MMHandShakeByLongLink 长链接握手(与短链接握手类似,只需选择其中一种握手方式,微信是采用长链接握手,每次登陆前都需要握手)
func MMHandShakeByLongLink(mmInfo *MMInfo) error {
// 发送握手请求 - ClientHello
clientHelloData := CreateHandShakeClientHelloData(mmInfo)
sendData := CreateRecordData(ServerHandShakeType, clientHelloData)
// 发送ClientHello
err := MMTCPSendData(mmInfo, sendData)
if err != nil {
return err
}
// 接收响应
retItems, err := MMTCPRecvItems(mmInfo)
if err != nil {
return err
}
// 处理握手信息
clientFinishedData, err := DealHandShakePackItems(mmInfo, retItems, clientHelloData)
if err != nil {
return err
}
// 发送clientFinished, 长链接必须要发送,但服务器不会响应
err = MMTCPSendData(mmInfo, clientFinishedData)
if err != nil {
return err
}
return nil
}
// SendHeartBeat 发送心跳包
func SendHeartBeat(mmInfo *MMInfo) error {
// 发送心跳包
heartData, err := GetHeartBeatData(mmInfo)
err = MMTCPSendData(mmInfo, heartData)
if err != nil {
return err
}
// 接收心跳包响应
retItem, err := MMTCPRecvOneItem(mmInfo)
if err != nil {
return err
}
// 解析心跳包响应数据,但不需处理
_, err = MMLongUnPackData(mmInfo, retItem)
if err != nil {
return err
}
return nil
}
// GetHeartBeatData 获取心跳包数据
func GetHeartBeatData(mmInfo *MMInfo) ([]byte, error) {
retBytes := make([]byte, 0)
bodyData := make([]byte, 0)
// LongPackTagInfo
packTagInfo := &LongPackHeaderInfo{}
packTagInfo.HeaderLen = 16
packTagInfo.Version = MMLongVersion
packTagInfo.Operation = MMLongOperationSmartHeartBeat
packTagInfo.SequenceNumber = 0xffffffff
tagInfoBytes := LongPackHeaderInfoSerialize(packTagInfo)
// BodyData
dataLength := uint32(len(tagInfoBytes) + 4)
bodyData = append(bodyData, baseutils.Int32ToBytes(dataLength)[0:]...)
bodyData = append(bodyData, tagInfoBytes[0:]...)
// RecordHead
tmpLen := uint16(dataLength + 16)
recordHeader := GetRecordDataByLength(BodyType, tmpLen)
clientSeq := atomic.AddUint32(&mmInfo.LONGClientSeq, 1) - 1
// 加密
tmpNonce := GetNonce(mmInfo.LongHdkfKey.EncodeNonce, uint32(clientSeq))
tmpAad := []byte{0x00, 0x00, 0x00, 0x00}
tmpAad = append(tmpAad, baseutils.Int32ToBytes(uint32(clientSeq))...)
tmpAad = append(tmpAad, recordHeader...)
encodeData, err := AesGcmEncrypt(mmInfo.LongHdkfKey.EncodeAesKey, tmpNonce, tmpAad, bodyData)
if err != nil {
return retBytes, err
}
// 组包
retBytes = append(retBytes, recordHeader...)
retBytes = append(retBytes, encodeData...)
return retBytes, nil
}
// MMLongPackData 长链接方式 打包请求数据
func MMLongPackData(mmInfo *MMInfo, opCode uint32, data []byte) ([]byte, error) {
retBytes := make([]byte, 0)
bodyData := make([]byte, 0)
// LongPackTagInfo
packTagInfo := &LongPackHeaderInfo{}
packTagInfo.HeaderLen = 16
packTagInfo.Version = MMLongVersion
packTagInfo.Operation = opCode
packTagInfo.SequenceNumber = atomic.LoadUint32(&mmInfo.LONGClientSeq)
tagInfoBytes := LongPackHeaderInfoSerialize(packTagInfo)
// BodyData
dataLength := uint32(len(data) + len(tagInfoBytes) + 4)
bodyData = append(bodyData, baseutils.Int32ToBytes(dataLength)[0:]...)
bodyData = append(bodyData, tagInfoBytes[0:]...)
bodyData = append(bodyData, data[0:]...)
// RecordHead
tmpLen := uint16(dataLength + 16)
recordHeader := GetRecordDataByLength(BodyType, tmpLen)
// 加密
tmpNonce := GetNonce(mmInfo.LongHdkfKey.EncodeNonce, atomic.LoadUint32(&mmInfo.LONGClientSeq))
tmpAad := []byte{0x00, 0x00, 0x00, 0x00}
tmpAad = append(tmpAad, baseutils.Int32ToBytes(atomic.LoadUint32(&mmInfo.LONGClientSeq))...)
tmpAad = append(tmpAad, recordHeader...)
encodeData, err := AesGcmEncrypt(mmInfo.LongHdkfKey.EncodeAesKey, tmpNonce, tmpAad, bodyData)
if err != nil {
return retBytes, err
}
// 组包,ClientSeq 索引值+1
atomic.AddUint32(&mmInfo.LONGClientSeq, 1)
//mmInfo.LONGClientSeq++
retBytes = append(retBytes, recordHeader...)
retBytes = append(retBytes, encodeData...)
return retBytes, nil
}
// MMLongUnPackData 长链接方式 解包数据
func MMLongUnPackData(mmInfo *MMInfo, packItem *PackItem) (*LongRecvInfo, error) {
// 解密
tmpNonce := GetNonce(mmInfo.LongHdkfKey.DecodeNonce, atomic.LoadUint32(&mmInfo.LONGServerSeq))
tmpAad := []byte{0x00, 0x00, 0x00, 0x00}
tmpAad = append(tmpAad, baseutils.Int32ToBytes(atomic.LoadUint32(&mmInfo.LONGServerSeq))...)
tmpAad = append(tmpAad, packItem.RecordHead...)
DecodeData, err := AesGcmDecrypt(mmInfo.LongHdkfKey.DecodeAesKey, tmpNonce, tmpAad, packItem.PackData)
if err != nil {
return nil, err
}
// TotalLength
current := 0
totalLength := baseutils.BytesToInt32(DecodeData[current : current+4])
current = current + 4
if totalLength != uint32(len(DecodeData)) {
return nil, errors.New("MMLongUnPackData err: totalLength != uint32(len(DecodeData))")
}
headerInfo, err := LongPackHeaderInfoDeSerialize(DecodeData[current : current+12])
current = current + 12
if err != nil {
return nil, err
}
retLongRecvInfo := &LongRecvInfo{}
retLongRecvInfo.HeaderInfo = headerInfo
// 如果大于 说明是业务包,如果等于则是长链接心跳包
if totalLength > uint32(headerInfo.HeaderLen) {
retLongRecvInfo.RespData = DecodeData[current:]
}
// ServerSeq 索引值+1
//mmInfo.LONGServerSeq = mmInfo.LONGServerSeq + 1
atomic.AddUint32(&mmInfo.LONGServerSeq, 1)
return retLongRecvInfo, nil
}