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

View File

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

View File

@ -147,6 +147,7 @@ internal object KnownPacketFactories {
// login // login
val flag1 = readInt() val flag1 = readInt()
PacketLogger.verbose("开始处理一个包")
PacketLogger.verbose("flag1(0A/0B) = ${flag1.toUByte().toUHexString()}") PacketLogger.verbose("flag1(0A/0B) = ${flag1.toUByte().toUHexString()}")
// 00 00 05 30 // 00 00 05 30
// 00 00 00 0A // flag 1 // 00 00 00 0A // flag 1
@ -240,10 +241,13 @@ internal object KnownPacketFactories {
val ssoSequenceId: Int val ssoSequenceId: Int
val dataCompressed: Int val dataCompressed: Int
// head // head
input.readIoBuffer(input.readInt() - 4).withUse { input.readPacket(input.readInt() - 4).withUse {
ssoSequenceId = readInt() ssoSequenceId = readInt()
PacketLogger.verbose("sequenceId = $ssoSequenceId") PacketLogger.verbose("sequenceId = $ssoSequenceId")
check(readInt() == 0) val returnCode = readInt()
if (returnCode != 0) {
error("returnCode = $returnCode")
}
val extraData = readBytes(readInt() - 4) val extraData = readBytes(readInt() - 4)
PacketLogger.verbose("(sso/inner)extraData = ${extraData.toUHexString()}") PacketLogger.verbose("(sso/inner)extraData = ${extraData.toUHexString()}")
@ -280,7 +284,7 @@ internal object KnownPacketFactories {
ssoSequenceId: Int, ssoSequenceId: Int,
consumer: PacketConsumer<T> consumer: PacketConsumer<T>
) { ) {
readIoBuffer(readInt() - 4).withUse { readPacket(readInt() - 4).withUse {
check(readByte().toInt() == 2) check(readByte().toInt() == 2)
this.discardExact(2) // 27 + 2 + body.size this.discardExact(2) // 27 + 2 + body.size
this.discardExact(2) // const, =8001 this.discardExact(2) // const, =8001
@ -292,30 +296,30 @@ internal object KnownPacketFactories {
this.discardExact(1) // const = 0 this.discardExact(1) // const = 0
val packet = when (encryptionMethod) { val packet = when (encryptionMethod) {
4 -> { // peer public key, ECDH 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()) val peerShareKey = bot.client.ecdh.calculateShareKeyByPeerPublicKey(readUShortLVByteArray().adjustToPublicKey())
data = data.decryptBy(peerShareKey) data = data.decryptBy(peerShareKey)
packetFactory.decode(bot, data.toReadPacket()) packetFactory.decode(bot, data)
} }
0 -> { 0 -> {
val data = if (bot.client.loginState == 0) { val data = if (bot.client.loginState == 0) {
ByteArrayPool.useInstance { byteArrayBuffer -> ByteArrayPool.useInstance { byteArrayBuffer ->
val size = this.readRemaining - 1 val size = (this.remaining - 1).toInt()
this.readFully(byteArrayBuffer, 0, size) this.readFully(byteArrayBuffer, 0, size)
runCatching { runCatching {
byteArrayBuffer.decryptBy(bot.client.ecdh.keyPair.initialShareKey, size) byteArrayBuffer.decryptBy(bot.client.ecdh.keyPair.initialShareKey, size)
}.getOrElse { }.getOrElse {
byteArrayBuffer.decryptBy(bot.client.randomKey, size) byteArrayBuffer.decryptBy(bot.client.randomKey, size)
} // 这里实际上应该用 privateKey(另一个random出来的key) }.toReadPacket() // 这里实际上应该用 privateKey(另一个random出来的key)
} }
} else { } 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") else -> error("Illegal encryption method. expected 0 or 4, got $encryptionMethod")
@ -338,3 +342,15 @@ internal inline fun <I : IoBuffer, R> I.withUse(block: I.() -> R): R {
this.release(IoBuffer.Pool) 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.MsgComm
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgSvc 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.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.toMessageChain
import net.mamoe.mirai.qqandroid.utils.toRichTextElems import net.mamoe.mirai.qqandroid.utils.toRichTextElems
import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.MiraiInternalAPI
@ -229,7 +232,7 @@ internal class MessageSvc {
), ),
msgSeq = client.atomicNextMessageSequenceId(), msgSeq = client.atomicNextMessageSequenceId(),
//msgRand = Random.nextInt() and 0x7FFF, //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()) //SyncCookie(currentTimeSeconds, Random.nextLong().absoluteValue, Random.nextLong().absoluteValue).toByteArray(SyncCookie.serializer())
// msgVia = 1 // 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.OutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket 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 { internal class FriendList {
@ -120,8 +120,8 @@ internal class FriendList {
} }
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response { override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response {
this.discardExact(4) //this.discardExact(4)
val res = this.decodeUniPacket(GetFriendListResp.serializer()) val res = this.debugIfFail { this.decodeUniPacket(GetFriendListResp.serializer()) }
return Response( return Response(
res.totoalFriendCount, res.totoalFriendCount,
res.vecFriendInfo.orEmpty() res.vecFriendInfo.orEmpty()

View File

@ -4,7 +4,7 @@ import kotlinx.io.pool.DefaultPool
import kotlinx.io.pool.ObjectPool import kotlinx.io.pool.ObjectPool
internal const val DEFAULT_BYTE_ARRAY_POOL_SIZE = 256 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 val ByteArrayPool: ObjectPool<ByteArray> = ByteArrayPoolImpl