diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt index 1d8b43a65..42e29a2f7 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt @@ -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(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 { diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/SyncCookie.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/SyncCookie.kt index bfccab92c..6a0411386 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/SyncCookie.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/SyncCookie.kt @@ -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 diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt index cc1e30ba7..dd6a1c7c6 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt @@ -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 ) { - 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.withUse(block: I.() -> R): R { } finally { this.release(IoBuffer.Pool) } +} + +@UseExperimental(ExperimentalContracts::class) +internal inline fun I.withUse(block: I.() -> R): R { + contract { + callsInPlace(block, kotlin.contracts.InvocationKind.EXACTLY_ONCE) + } + return try { + block(this) + } finally { + this.close() + } } \ No newline at end of file diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt index 3b93a559e..8990c7d96 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt @@ -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 ) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/list/FriendListPacket.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/list/FriendListPacket.kt index ca27136d0..fddb08186 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/list/FriendListPacket.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/list/FriendListPacket.kt @@ -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() diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayPool.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayPool.kt index 27ad698bf..87a0c3bcb 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayPool.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayPool.kt @@ -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 = ByteArrayPoolImpl