219 lines
6.4 KiB
Go
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
|
|
}
|