Fix friendlist

This commit is contained in:
Him188 2020-02-02 17:35:42 +08:00
parent bfcd265016
commit 0c6526afb8
6 changed files with 58 additions and 24 deletions

View File

@ -29,6 +29,7 @@ import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.unsafeWeakRef
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.jvm.Volatile
@Suppress("MemberVisibilityCanBePrivate")
@UseExperimental(MiraiInternalAPI::class)
@ -123,7 +124,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
val data = FriendList.GetFriendGroupList(
bot.client,
currentFriendCount,
20,
150,
0,
0
).sendAndExpect<FriendList.GetFriendGroupList.Response>(timeoutMillis = 1000)
@ -161,6 +162,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
/**
* 缓存超时处理的 [Job]. 超时后将清空缓存, 以免阻碍后续包的处理
*/
@Volatile
private var cachedPacketTimeoutJob: Job? = null
/**
* 缓存的包
@ -169,6 +171,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
/**
* 缓存的包还差多少长度
*/
@Volatile
private var expectingRemainingLength: Long = 0
/**
@ -256,8 +259,11 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
if (cache == null) {
// 没有缓存
var length: Int = rawInput.readInt() - 4
if (length < 0) {
if (length and 0xFFFF != length) {
cachedPacket.value = rawInput
expectingRemainingLength = length.toLong() and 0xFFFF
// 丢包了. 后半部分包提前到达
PacketLogger.error { "丢包了." }
return
}
if (rawInput.remaining == length.toLong()) {
@ -267,7 +273,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
return
}
// 循环所有完整的包
while (rawInput.remaining > length) {
while (rawInput.remaining >= length) {
parsePacketAsync(rawInput.readPacket(length))
length = rawInput.readInt() - 4
@ -284,20 +290,27 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
}
} else {
// 有缓存
if (rawInput.remaining >= expectingRemainingLength) {
val expectingLength = expectingRemainingLength
if (expectingLength and 0xFFFF != expectingLength) {
processPacket(buildPacket {
writePacket(rawInput)
writeInt(expectingLength.toInt())
writePacket(cache)
})
}
if (rawInput.remaining >= expectingLength) {
// 剩余长度够, 连接上去, 处理这个包.
parsePacketAsync(buildPacket {
writePacket(cache)
writePacket(rawInput, expectingRemainingLength)
writePacket(rawInput, expectingLength)
})
cachedPacket.value = null // 缺少的长度已经给上了.
cachedPacketTimeoutJob?.cancel()
if (rawInput.remaining != 0L) {
return processPacket(rawInput) // 继续处理剩下内容
} else {
// 处理好了.
cachedPacketTimeoutJob?.cancel()
return
}
} else {

View File

@ -11,13 +11,15 @@ class SyncCookie(
@SerialId(2) val time: Long, // 1580277992
@SerialId(3) val unknown1: Long = Random.nextLong(),// 678328038
@SerialId(4) val unknown2: Long = Random.nextLong(), // 1687142153
@SerialId(5) val const1: Long = Random.nextLong(), // 1458467940
@SerialId(11) val const2: Long = Random.nextLong(), // 2683038258
@SerialId(5) val const1: Long = const1_, // 1458467940
@SerialId(11) val const2: Long = const2_, // 2683038258
@SerialId(12) val unknown3: Long = 0x1d,
@SerialId(13) val lastSyncTime: Long? = null,
@SerialId(14) val unknown4: Long = 0
) : ProtoBuf
private val const1_: Long = Random.nextLong()
private val const2_: Long = Random.nextLong()
/*
@Serializable

View File

@ -147,6 +147,7 @@ internal object KnownPacketFactories {
// login
val flag1 = readInt()
PacketLogger.verbose("开始处理一个包")
PacketLogger.verbose("flag1(0A/0B) = ${flag1.toUByte().toUHexString()}")
// 00 00 05 30
// 00 00 00 0A // flag 1
@ -240,10 +241,13 @@ internal object KnownPacketFactories {
val ssoSequenceId: Int
val dataCompressed: Int
// head
input.readIoBuffer(input.readInt() - 4).withUse {
input.readPacket(input.readInt() - 4).withUse {
ssoSequenceId = readInt()
PacketLogger.verbose("sequenceId = $ssoSequenceId")
check(readInt() == 0)
val returnCode = readInt()
if (returnCode != 0) {
error("returnCode = $returnCode")
}
val extraData = readBytes(readInt() - 4)
PacketLogger.verbose("(sso/inner)extraData = ${extraData.toUHexString()}")
@ -280,7 +284,7 @@ internal object KnownPacketFactories {
ssoSequenceId: Int,
consumer: PacketConsumer<T>
) {
readIoBuffer(readInt() - 4).withUse {
readPacket(readInt() - 4).withUse {
check(readByte().toInt() == 2)
this.discardExact(2) // 27 + 2 + body.size
this.discardExact(2) // const, =8001
@ -292,30 +296,30 @@ internal object KnownPacketFactories {
this.discardExact(1) // const = 0
val packet = when (encryptionMethod) {
4 -> { // peer public key, ECDH
var data = this.decryptBy(bot.client.ecdh.keyPair.initialShareKey, this.readRemaining - 1)
var data = this.decryptBy(bot.client.ecdh.keyPair.initialShareKey, (this.remaining - 1).toInt())
val peerShareKey = bot.client.ecdh.calculateShareKeyByPeerPublicKey(readUShortLVByteArray().adjustToPublicKey())
data = data.decryptBy(peerShareKey)
packetFactory.decode(bot, data.toReadPacket())
packetFactory.decode(bot, data)
}
0 -> {
val data = if (bot.client.loginState == 0) {
ByteArrayPool.useInstance { byteArrayBuffer ->
val size = this.readRemaining - 1
val size = (this.remaining - 1).toInt()
this.readFully(byteArrayBuffer, 0, size)
runCatching {
byteArrayBuffer.decryptBy(bot.client.ecdh.keyPair.initialShareKey, size)
}.getOrElse {
byteArrayBuffer.decryptBy(bot.client.randomKey, size)
} // 这里实际上应该用 privateKey(另一个random出来的key)
}.toReadPacket() // 这里实际上应该用 privateKey(另一个random出来的key)
}
} else {
this.decryptBy(bot.client.randomKey, 0, this.readRemaining - 1)
this.decryptBy(bot.client.randomKey, 0, (this.remaining - 1).toInt())
}
packetFactory.decode(bot, data.toReadPacket())
packetFactory.decode(bot, data)
}
else -> error("Illegal encryption method. expected 0 or 4, got $encryptionMethod")
@ -337,4 +341,16 @@ internal inline fun <I : IoBuffer, R> I.withUse(block: I.() -> R): R {
} finally {
this.release(IoBuffer.Pool)
}
}
@UseExperimental(ExperimentalContracts::class)
internal inline fun <I : ByteReadPacket, R> I.withUse(block: I.() -> R): R {
contract {
callsInPlace(block, kotlin.contracts.InvocationKind.EXACTLY_ONCE)
}
return try {
block(this)
} finally {
this.close()
}
}

View File

@ -19,7 +19,10 @@ import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgSvc
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.SyncCookie
import net.mamoe.mirai.qqandroid.network.protocol.packet.*
import net.mamoe.mirai.qqandroid.network.protocol.packet.IncomingPacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket
import net.mamoe.mirai.qqandroid.utils.toMessageChain
import net.mamoe.mirai.qqandroid.utils.toRichTextElems
import net.mamoe.mirai.utils.MiraiInternalAPI
@ -229,7 +232,7 @@ internal class MessageSvc {
),
msgSeq = client.atomicNextMessageSequenceId(),
//msgRand = Random.nextInt() and 0x7FFF,
syncCookie = client.c2cMessageSync.syncCookie?.takeIf { it.isNotEmpty() } ?: EMPTY_BYTE_ARRAY
syncCookie = SyncCookie(time = currentTimeSeconds).toByteArray(SyncCookie.serializer())
//SyncCookie(currentTimeSeconds, Random.nextLong().absoluteValue, Random.nextLong().absoluteValue).toByteArray(SyncCookie.serializer())
// msgVia = 1
)

View File

@ -16,7 +16,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket
import net.mamoe.mirai.utils.io.discardExact
import net.mamoe.mirai.utils.io.debugIfFail
internal class FriendList {
@ -120,8 +120,8 @@ internal class FriendList {
}
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response {
this.discardExact(4)
val res = this.decodeUniPacket(GetFriendListResp.serializer())
//this.discardExact(4)
val res = this.debugIfFail { this.decodeUniPacket(GetFriendListResp.serializer()) }
return Response(
res.totoalFriendCount,
res.vecFriendInfo.orEmpty()

View File

@ -4,7 +4,7 @@ import kotlinx.io.pool.DefaultPool
import kotlinx.io.pool.ObjectPool
internal const val DEFAULT_BYTE_ARRAY_POOL_SIZE = 256
internal const val DEFAULT_BYTE_ARRAY_SIZE = 8192
internal const val DEFAULT_BYTE_ARRAY_SIZE = 81920
val ByteArrayPool: ObjectPool<ByteArray> = ByteArrayPoolImpl