mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-08 17:20:11 +08:00
Fix packet parsing
This commit is contained in:
parent
1e6a41ca7c
commit
d7f67e5159
@ -1,6 +1,7 @@
|
||||
package net.mamoe.mirai.qqandroid.network
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.use
|
||||
import net.mamoe.mirai.data.Packet
|
||||
import net.mamoe.mirai.event.broadcast
|
||||
@ -36,6 +37,27 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
LoginPacket.SubCommand9(bot.client).sendAndExpect<LoginPacket.LoginPacketResponse>()
|
||||
}
|
||||
|
||||
internal fun launchPacketProcessor(rawInput: ByteReadPacket): Job {
|
||||
return launch(CoroutineName("Incoming Packet handler")) {
|
||||
rawInput.debugPrint("Received").use { input ->
|
||||
if (input.remaining == 0L) {
|
||||
bot.logger.error("Empty packet received. Consider if bad packet was sent.")
|
||||
return@launch
|
||||
}
|
||||
KnownPacketFactories.parseIncomingPacket(bot, input) { packet: Packet, packetId: PacketId, sequenceId: Int ->
|
||||
if (PacketReceivedEvent(packet).broadcast().cancelled) {
|
||||
return@parseIncomingPacket
|
||||
}
|
||||
packetListeners.forEach { listener ->
|
||||
if (listener.filter(packetId, sequenceId) && packetListeners.remove(listener)) {
|
||||
listener.complete(packet)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun processReceive() {
|
||||
while (channel.isOpen) {
|
||||
val rawInput = try {
|
||||
@ -52,25 +74,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
bot.logger.error("Caught unexpected exceptions", e)
|
||||
continue
|
||||
}
|
||||
|
||||
launch(CoroutineName("Incoming Packet handler")) {
|
||||
rawInput.debugPrint("Received").use { input ->
|
||||
if (input.remaining == 0L) {
|
||||
bot.logger.error("Empty packet received. Consider if bad packet was sent.")
|
||||
return@launch
|
||||
}
|
||||
KnownPacketFactories.parseIncomingPacket(bot, input) { packet: Packet, packetId: PacketId, sequenceId: Int ->
|
||||
if (PacketReceivedEvent(packet).broadcast().cancelled) {
|
||||
return@parseIncomingPacket
|
||||
}
|
||||
packetListeners.forEach { listener ->
|
||||
if (listener.filter(packetId, sequenceId) && packetListeners.remove(listener)) {
|
||||
listener.complete(packet)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
launchPacketProcessor(rawInput)
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,9 +100,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
fun filter(packetId: PacketId, sequenceId: Int) = this.packetId == packetId && this.sequenceId == sequenceId
|
||||
}
|
||||
|
||||
override suspend fun awaitDisconnection() {
|
||||
supervisor.join()
|
||||
}
|
||||
override suspend fun awaitDisconnection() = supervisor.join()
|
||||
|
||||
override fun dispose(cause: Throwable?) {
|
||||
println("Closed")
|
||||
|
@ -6,6 +6,7 @@ import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.toByteArray
|
||||
import net.mamoe.mirai.BotAccount
|
||||
import net.mamoe.mirai.qqandroid.QQAndroidBot
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.Tlv
|
||||
import net.mamoe.mirai.qqandroid.utils.Context
|
||||
import net.mamoe.mirai.qqandroid.utils.DeviceInfo
|
||||
import net.mamoe.mirai.qqandroid.utils.NetworkType
|
||||
@ -13,6 +14,7 @@ import net.mamoe.mirai.qqandroid.utils.SystemDeviceInfo
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.cryptor.ECDH
|
||||
import net.mamoe.mirai.utils.cryptor.contentToString
|
||||
import net.mamoe.mirai.utils.io.hexToBytes
|
||||
import net.mamoe.mirai.utils.unsafeWeakRef
|
||||
|
||||
@ -40,6 +42,11 @@ internal open class QQAndroidClient(
|
||||
val device: DeviceInfo = SystemDeviceInfo(context),
|
||||
bot: QQAndroidBot
|
||||
) {
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
override fun toString(): String { // net.mamoe.mirai.utils.cryptor.ProtoKt.contentToString
|
||||
return "QQAndroidClient(account=$account, ecdh=$ecdh, device=$device, tgtgtKey=${tgtgtKey.contentToString()}, randomKey=${randomKey.contentToString()}, miscBitMap=$miscBitMap, mainSigMap=$mainSigMap, subSigMap=$subSigMap, _ssoSequenceId=$_ssoSequenceId, openAppId=$openAppId, apkVersionName=${apkVersionName.contentToString()}, loginState=$loginState, appClientVersion=$appClientVersion, networkType=$networkType, apkSignatureMd5=${apkSignatureMd5.contentToString()}, protocolVersion=$protocolVersion, apkId=${apkId.contentToString()}, t150=${t150?.contentToString()}, rollbackSig=${rollbackSig?.contentToString()}, ipFromT149=${ipFromT149?.contentToString()}, timeDifference=$timeDifference, uin=$uin, t530=${t530?.contentToString()}, t528=${t528?.contentToString()}, ksid='$ksid', pwdFlag=$pwdFlag, loginExtraData=$loginExtraData, wFastLoginInfo=$wFastLoginInfo, reserveUinInfo=$reserveUinInfo, wLoginSigInfo=$wLoginSigInfo, tlv113=${tlv113?.contentToString()}, qrPushSig=${qrPushSig.contentToString()}, mainDisplayName='$mainDisplayName')"
|
||||
}
|
||||
|
||||
val context by context.unsafeWeakRef()
|
||||
val bot: QQAndroidBot by bot.unsafeWeakRef()
|
||||
|
||||
@ -81,7 +88,7 @@ internal open class QQAndroidClient(
|
||||
*/
|
||||
|
||||
|
||||
var t150: ByteArray? = null
|
||||
var t150: Tlv? = null
|
||||
var rollbackSig: ByteArray? = null
|
||||
var ipFromT149: ByteArray? = null
|
||||
/**
|
||||
@ -100,7 +107,7 @@ internal open class QQAndroidClient(
|
||||
/**
|
||||
* t108 时更新
|
||||
*/
|
||||
var ksid: String = "|454001228437590|A8.2.0.27f6ea96"
|
||||
var ksid: ByteArray = "|454001228437590|A8.2.0.27f6ea96".toByteArray()
|
||||
/**
|
||||
* t186
|
||||
*/
|
||||
@ -110,19 +117,23 @@ internal open class QQAndroidClient(
|
||||
*/
|
||||
var loginExtraData: LoginExtraData? = null
|
||||
lateinit var wFastLoginInfo: WFastLoginInfo
|
||||
lateinit var reserveUinInfo: ReserveUinInfo
|
||||
var reserveUinInfo: ReserveUinInfo? = null
|
||||
var wLoginSigInfo: WLoginSigInfo? = null
|
||||
var tlv113: ByteArray? = null
|
||||
lateinit var qrPushSig: ByteArray
|
||||
|
||||
lateinit var mainDisplayName: String
|
||||
lateinit var mainDisplayName: ByteArray
|
||||
}
|
||||
|
||||
class ReserveUinInfo(
|
||||
val imgType: ByteArray,
|
||||
val imgFormat: ByteArray,
|
||||
val imgUrl: ByteArray
|
||||
)
|
||||
) {
|
||||
override fun toString(): String {
|
||||
return "ReserveUinInfo(imgType=${imgType.contentToString()}, imgFormat=${imgFormat.contentToString()}, imgUrl=${imgUrl.contentToString()})"
|
||||
}
|
||||
}
|
||||
|
||||
class WFastLoginInfo(
|
||||
val outA1: ByteReadPacket,
|
||||
@ -130,7 +141,11 @@ class WFastLoginInfo(
|
||||
var iconUrl: String = "",
|
||||
var profileUrl: String = "",
|
||||
var userJson: String = ""
|
||||
)
|
||||
) {
|
||||
override fun toString(): String {
|
||||
return "WFastLoginInfo(outA1=$outA1, adUrl='$adUrl', iconUrl='$iconUrl', profileUrl='$profileUrl', userJson='$userJson')"
|
||||
}
|
||||
}
|
||||
|
||||
class WLoginSimpleInfo(
|
||||
val uin: Long, // uin
|
||||
@ -142,14 +157,22 @@ class WLoginSimpleInfo(
|
||||
val imgFormat: ByteArray,
|
||||
val imgUrl: ByteArray,
|
||||
val mainDisplayName: ByteArray
|
||||
)
|
||||
) {
|
||||
override fun toString(): String {
|
||||
return "WLoginSimpleInfo(uin=$uin, face=$face, age=$age, gender=$gender, nick='$nick', imgType=${imgType.contentToString()}, imgFormat=${imgFormat.contentToString()}, imgUrl=${imgUrl.contentToString()}, mainDisplayName=${mainDisplayName.contentToString()})"
|
||||
}
|
||||
}
|
||||
|
||||
class LoginExtraData(
|
||||
val uin: Long,
|
||||
val ip: ByteArray,
|
||||
val time: Int,
|
||||
val version: Int
|
||||
)
|
||||
) {
|
||||
override fun toString(): String {
|
||||
return "LoginExtraData(uin=$uin, ip=${ip.contentToString()}, time=$time, version=$version)"
|
||||
}
|
||||
}
|
||||
|
||||
class WLoginSigInfo(
|
||||
val uin: Long,
|
||||
@ -193,7 +216,11 @@ class WLoginSigInfo(
|
||||
val wtSessionTicket: WtSessionTicket,
|
||||
val wtSessionTicketKey: ByteArray,
|
||||
val deviceToken: ByteArray
|
||||
)
|
||||
) {
|
||||
override fun toString(): String {
|
||||
return "WLoginSigInfo(uin=$uin, encryptA1=${encryptA1.contentToString()}, noPicSig=${noPicSig.contentToString()}, G=${G.contentToString()}, dpwd=${dpwd.contentToString()}, randSeed=${randSeed.contentToString()}, simpleInfo=$simpleInfo, appPri=$appPri, a2ExpiryTime=$a2ExpiryTime, loginBitmap=$loginBitmap, tgt=${tgt.contentToString()}, a2CreationTime=$a2CreationTime, tgtKey=${tgtKey.contentToString()}, userStSig=$userStSig, userStKey=${userStKey.contentToString()}, userStWebSig=$userStWebSig, userA5=$userA5, userA8=$userA8, lsKey=$lsKey, sKey=$sKey, userSig64=$userSig64, openId=${openId.contentToString()}, openKey=$openKey, vKey=$vKey, accessToken=$accessToken, d2=$d2, d2Key=${d2Key.contentToString()}, sid=$sid, aqSig=$aqSig, psKey=$psKey, superKey=${superKey.contentToString()}, payToken=${payToken.contentToString()}, pf=${pf.contentToString()}, pfKey=${pfKey.contentToString()}, da2=${da2.contentToString()}, wtSessionTicket=$wtSessionTicket, wtSessionTicketKey=${wtSessionTicketKey.contentToString()}, deviceToken=${deviceToken.contentToString()})"
|
||||
}
|
||||
}
|
||||
|
||||
class UserStSig(data: ByteArray, creationTime: Long) : KeyWithCreationTime(data, creationTime)
|
||||
class LSKey(data: ByteArray, creationTime: Long, expireTime: Long) : KeyWithExpiry(data, creationTime, expireTime)
|
||||
|
@ -144,8 +144,8 @@ private inline fun BytePacketBuilder.writeLoginSsoPacket(
|
||||
writeInt(4)
|
||||
|
||||
client.ksid.let {
|
||||
writeShort((it.length + 2).toShort())
|
||||
writeStringUtf8(it)
|
||||
writeShort((it.size + 2).toShort())
|
||||
writeFully(it)
|
||||
}
|
||||
|
||||
writeInt(4)
|
||||
|
@ -48,6 +48,7 @@ private val DECRYPTER_16_ZERO = ByteArray(16)
|
||||
|
||||
internal typealias PacketConsumer = suspend (packet: Packet, packetId: PacketId, ssoSequenceId: Int) -> Unit
|
||||
|
||||
@UseExperimental(ExperimentalUnsignedTypes::class)
|
||||
internal object KnownPacketFactories : List<PacketFactory<*, *>> by mutableListOf(
|
||||
LoginPacket
|
||||
) {
|
||||
@ -60,7 +61,11 @@ internal object KnownPacketFactories : List<PacketFactory<*, *>> by mutableListO
|
||||
suspend fun parseIncomingPacket(bot: QQAndroidBot, rawInput: ByteReadPacket, consumer: PacketConsumer) =
|
||||
rawInput.debugPrintIfFail("Incoming packet") {
|
||||
require(remaining < Int.MAX_VALUE) { "rawInput is too long" }
|
||||
val expectedLength = readInt() - 4
|
||||
val expectedLength = readUInt().toInt() - 4
|
||||
if (expectedLength > 16e7) {
|
||||
bot.logger.warning("Detect incomplete packet, ignoring.")
|
||||
return@debugPrintIfFail
|
||||
}
|
||||
check(remaining.toInt() == expectedLength) { "Invalid packet length. Expected $expectedLength, got ${rawInput.remaining} Probably packets merged? " }
|
||||
// login
|
||||
when (val flag1 = readInt()) {
|
||||
|
@ -10,6 +10,11 @@ import net.mamoe.mirai.utils.io.*
|
||||
import net.mamoe.mirai.utils.md5
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
* 显式表示一个 [ByteArray] 是一个 tlv 的 body
|
||||
*/
|
||||
inline class Tlv(val value: ByteArray)
|
||||
|
||||
inline class LoginType(
|
||||
val value: Int
|
||||
) {
|
||||
|
File diff suppressed because one or more lines are too long
@ -3,6 +3,7 @@ package net.mamoe.mirai.qqandroid.utils
|
||||
import kotlinx.serialization.SerialId
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.protobuf.ProtoBuf
|
||||
import net.mamoe.mirai.utils.cryptor.contentToString
|
||||
import net.mamoe.mirai.utils.unsafeWeakRef
|
||||
|
||||
abstract class DeviceInfo(
|
||||
@ -75,9 +76,15 @@ abstract class DeviceInfo(
|
||||
)
|
||||
}
|
||||
|
||||
override fun toString(): String { // net.mamoe.mirai.utils.cryptor.ProtoKt.contentToString
|
||||
return "DeviceInfo(display=${display.contentToString()}, product=${product.contentToString()}, device=${device.contentToString()}, board=${board.contentToString()}, brand=${brand.contentToString()}, model=${model.contentToString()}, bootloader=${bootloader.contentToString()}, fingerprint=${fingerprint.contentToString()}, bootId=${bootId.contentToString()}, procVersion=${procVersion.contentToString()}, baseBand=${baseBand.contentToString()}, version=$version, simInfo=${simInfo.contentToString()}, osType=${osType.contentToString()}, macAddress=${macAddress.contentToString()}, wifiBSSID=${wifiBSSID?.contentToString()}, wifiSSID=${wifiSSID?.contentToString()}, imsiMd5=${imsiMd5.contentToString()}, imei='$imei', ipAddress='$ipAddress', androidId=${androidId.contentToString()}, apn=${apn.contentToString()})"
|
||||
}
|
||||
|
||||
interface Version {
|
||||
val incremental: ByteArray
|
||||
val release: ByteArray
|
||||
val codename: ByteArray
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -45,4 +45,8 @@ actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) {
|
||||
actual fun calculateShareKeyByPeerPublicKey(peerPublicKey: ECDHPublicKey): ByteArray {
|
||||
return calculateShareKey(keyPair.privateKey, peerPublicKey)
|
||||
}
|
||||
|
||||
actual override fun toString(): String {
|
||||
return "ECDH(keyPair=$keyPair)"
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package net.mamoe.mirai.utils.cryptor
|
||||
|
||||
|
||||
actual fun Any.contentToStringReflectively(prefix: String): String {
|
||||
val newPrefix = prefix + ProtoMap.indent
|
||||
return (this::class.simpleName ?: "<UnnamedClass>") + "#" + this::class.hashCode() + " {\n" +
|
||||
this::class.java.fields.toMutableSet().apply { addAll(this::class.java.declaredFields) }.asSequence().filterNot { it.name.contains("$") || it.name == "Companion" || it.isSynthetic }
|
||||
.joinToStringPrefixed(
|
||||
prefix = newPrefix
|
||||
) {
|
||||
it.isAccessible = true
|
||||
it.name + "=" + kotlin.runCatching {
|
||||
val value = it.get(this)
|
||||
if (value == this) "<this>"
|
||||
else value.contentToString(newPrefix)
|
||||
}.getOrElse { "<!>" }
|
||||
} + "\n$prefix}"
|
||||
}
|
@ -93,7 +93,9 @@ abstract class Bot : CoroutineScope {
|
||||
abstract val network: BotNetworkHandler
|
||||
|
||||
/**
|
||||
* 登录
|
||||
* 登录.
|
||||
*
|
||||
* 最终调用 [net.mamoe.mirai.network.BotNetworkHandler.login]
|
||||
*
|
||||
* @throws LoginFailedException
|
||||
*/
|
||||
|
@ -28,6 +28,8 @@ expect class ECDH(keyPair: ECDHKeyPair) {
|
||||
fun generateKeyPair(): ECDHKeyPair
|
||||
fun calculateShareKey(privateKey: ECDHPrivateKey, publicKey: ECDHPublicKey): ByteArray
|
||||
}
|
||||
|
||||
override fun toString(): String
|
||||
}
|
||||
|
||||
@Suppress("FunctionName")
|
||||
|
@ -96,7 +96,7 @@ fun protoFieldNumber(number: UInt): Int = number.toInt().ushr(3)
|
||||
class ProtoMap(map: MutableMap<ProtoFieldId, Any>) : MutableMap<ProtoFieldId, Any> by map {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
val indent: String = " "
|
||||
internal val indent: String = " "
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
@ -117,7 +117,12 @@ class ProtoMap(map: MutableMap<ProtoFieldId, Any>) : MutableMap<ProtoFieldId, An
|
||||
}*/
|
||||
}
|
||||
|
||||
internal fun Any.contentToString(prefix: String = ""): String = when (this) {
|
||||
fun <T> Sequence<T>.joinToStringPrefixed(prefix: String, transform: (T) -> CharSequence): String {
|
||||
return this.joinToString(prefix = "$prefix${ProtoMap.indent}", separator = "\n$prefix${ProtoMap.indent}", transform = transform)
|
||||
}
|
||||
|
||||
fun Any?.contentToString(prefix: String = ""): String = when (this) {
|
||||
is Unit -> "Unit"
|
||||
is UInt -> "0x" + this.toUHexString("") + "($this)"
|
||||
is UByte -> "0x" + this.toUHexString() + "($this)"
|
||||
is UShort -> "0x" + this.toUHexString("") + "($this)"
|
||||
@ -131,12 +136,38 @@ internal fun Any.contentToString(prefix: String = ""): String = when (this) {
|
||||
|
||||
is Boolean -> if (this) "true" else "false"
|
||||
|
||||
is ByteArray -> this.toUHexString()// + " (${this.encodeToString()})"
|
||||
is ByteArray -> {
|
||||
if (this.size == 0) "<Empty ByteArray>"
|
||||
else this.toUHexString()// + " (${this.encodeToString()})"
|
||||
}
|
||||
is UByteArray -> {
|
||||
if (this.size == 0) "<Empty UByteArray>"
|
||||
else this.toUHexString()// + " (${this.encodeToString()})"
|
||||
}
|
||||
|
||||
is ProtoMap -> "ProtoMap(size=$size){\n" + this.toStringPrefixed("$prefix${ProtoMap.indent}${ProtoMap.indent}") + "\n$prefix${ProtoMap.indent}}"
|
||||
else -> this.toString()
|
||||
is Collection<*> -> this.joinToString(prefix = "[", postfix = "]") { it.contentToString() }
|
||||
is Map<*, *> -> this.entries.joinToString(prefix = "{", postfix = "}") { it.key.contentToString() + "=" + it.value.contentToString() }
|
||||
else -> {
|
||||
if (this == null) "null"
|
||||
else if (this::class.isData) this.toString()
|
||||
else {
|
||||
if (this::class.qualifiedName?.startsWith("net.mamoe.mirai.") == true) {
|
||||
this.contentToStringReflectively(prefix + ProtoMap.indent)
|
||||
} else this.toString()
|
||||
/*
|
||||
(this::class.simpleName ?: "<UnnamedClass>") + "#" + this::class.hashCode() + "{\n" +
|
||||
this::class.members.asSequence().filterIsInstance<KProperty<*>>().filter { !it.isSuspend && it.visibility == KVisibility.PUBLIC }
|
||||
.joinToStringPrefixed(
|
||||
prefix = ProtoMap.indent
|
||||
) { it.name + "=" + kotlin.runCatching { it.call(it).contentToString(ProtoMap.indent) }.getOrElse { "<!>" } }
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect fun Any.contentToStringReflectively(prefix: String = ""): String
|
||||
|
||||
fun ByteReadPacket.readProtoMap(length: Long = this.remaining): ProtoMap {
|
||||
val map = ProtoMap(mutableMapOf())
|
||||
|
||||
|
@ -9,6 +9,8 @@ import net.mamoe.mirai.contact.GroupId
|
||||
import net.mamoe.mirai.contact.GroupInternalId
|
||||
import net.mamoe.mirai.contact.groupId
|
||||
import net.mamoe.mirai.contact.groupInternalId
|
||||
import net.mamoe.mirai.utils.assertUnreachable
|
||||
import net.mamoe.mirai.utils.cryptor.contentToString
|
||||
import kotlin.jvm.JvmName
|
||||
import kotlin.jvm.JvmSynthetic
|
||||
|
||||
@ -76,14 +78,14 @@ fun Input.readTLVMap(tagSize: Int = 2): MutableMap<Int, ByteArray> = readTLVMap(
|
||||
@Suppress("DuplicatedCode")
|
||||
fun Input.readTLVMap(expectingEOF: Boolean = true, tagSize: Int): MutableMap<Int, ByteArray> {
|
||||
val map = mutableMapOf<Int, ByteArray>()
|
||||
var type: Int = 0
|
||||
var key = 0
|
||||
|
||||
while (inline {
|
||||
try {
|
||||
type = when (tagSize) {
|
||||
1 -> readByte().toInt()
|
||||
2 -> readShort().toInt()
|
||||
4 -> readInt()
|
||||
key = when (tagSize) {
|
||||
1 -> readUByte().toInt()
|
||||
2 -> readUShort().toInt()
|
||||
4 -> readUInt().toInt()
|
||||
else -> error("Unsupported tag size: $tagSize")
|
||||
}
|
||||
} catch (e: Exception) { // java.nio.BufferUnderflowException is not a EOFException...
|
||||
@ -92,22 +94,33 @@ fun Input.readTLVMap(expectingEOF: Boolean = true, tagSize: Int): MutableMap<Int
|
||||
}
|
||||
throw e
|
||||
}
|
||||
type
|
||||
key
|
||||
}.toUByte() != UByte.MAX_VALUE) {
|
||||
|
||||
check(!map.containsKey(type)) {
|
||||
"Count not readTLVMap: duplicated key 0x${type.toUInt().toUHexString("")}. " +
|
||||
"map=$map" +
|
||||
", duplicating value=${this.readUShortLVByteArray().toUHexString()}" +
|
||||
", remaining=" + if (expectingEOF) this.readBytes().toUHexString() else "[Not expecting EOF]"
|
||||
}
|
||||
try {
|
||||
map[type] = this.readUShortLVByteArray()
|
||||
} catch (e: Exception) { // BufferUnderflowException, java.io.EOFException
|
||||
if (expectingEOF) {
|
||||
return map
|
||||
if (map.containsKey(key)) {
|
||||
DebugLogger.error(
|
||||
@Suppress("IMPLICIT_CAST_TO_ANY")
|
||||
"""
|
||||
Error readTLVMap:
|
||||
duplicated key ${when (tagSize) {
|
||||
1 -> key.toByte()
|
||||
2 -> key.toShort()
|
||||
4 -> key
|
||||
else -> assertUnreachable()
|
||||
}.contentToString()}
|
||||
map=${map.contentToString()}
|
||||
duplicating value=${this.readUShortLVByteArray().toUHexString()}
|
||||
""".trimIndent()
|
||||
)
|
||||
} else {
|
||||
try {
|
||||
map[key] = this.readUShortLVByteArray()
|
||||
} catch (e: Exception) { // BufferUnderflowException, java.io.EOFException
|
||||
// if (expectingEOF) {
|
||||
// return map
|
||||
// }
|
||||
throw e
|
||||
}
|
||||
throw e
|
||||
}
|
||||
}
|
||||
return map
|
||||
|
@ -51,13 +51,13 @@ fun UShort.toByteArray(): ByteArray = with(toUInt()) {
|
||||
fun Short.toUHexString(separator: String = " "): String = this.toUShort().toUHexString(separator)
|
||||
|
||||
fun UShort.toUHexString(separator: String = " "): String =
|
||||
(this.toInt().shr(8).toUShort() and 255u).toByte().toUHexString() + separator + (this and 255u).toByte().toUHexString()
|
||||
this.toInt().shr(8).toUShort().toUByte().toUHexString() + separator + this.toUByte().toUHexString()
|
||||
|
||||
fun ULong.toUHexString(separator: String = " "): String =
|
||||
this.toLong().toUHexString(separator)
|
||||
|
||||
fun Long.toUHexString(separator: String = " "): String =
|
||||
this.ushr(32).toUInt().toUHexString(separator) + separator + this.ushr(32).toUInt().toUHexString(separator)
|
||||
this.ushr(32).toUInt().toUHexString(separator) + separator + this.toUInt().toUHexString(separator)
|
||||
|
||||
/**
|
||||
* 255 -> 00 FF
|
||||
|
@ -51,4 +51,8 @@ actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) {
|
||||
actual fun calculateShareKeyByPeerPublicKey(peerPublicKey: ECDHPublicKey): ByteArray {
|
||||
return calculateShareKey(keyPair.privateKey, peerPublicKey)
|
||||
}
|
||||
|
||||
actual override fun toString(): String {
|
||||
return "ECDH(keyPair=$keyPair)"
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package net.mamoe.mirai.utils.cryptor
|
||||
|
||||
import java.lang.reflect.Field
|
||||
import kotlin.reflect.full.allSuperclasses
|
||||
|
||||
|
||||
actual fun Any.contentToStringReflectively(prefix: String): String {
|
||||
val newPrefix = prefix + ProtoMap.indent
|
||||
return (this::class.simpleName ?: "<UnnamedClass>") + "#" + this::class.hashCode() + " {\n" +
|
||||
this.allFieldsFromSuperClassesMatching { it.packageName.startsWith("net.mamoe.mirai") }
|
||||
.distinctBy { it.name }
|
||||
.filterNot { it.name.contains("$") || it.name == "Companion" || it.isSynthetic || it.name == "serialVersionUID" }
|
||||
.joinToStringPrefixed(
|
||||
prefix = newPrefix
|
||||
) {
|
||||
it.trySetAccessible()
|
||||
it.name + "=" + kotlin.runCatching {
|
||||
val value = it.get(this)
|
||||
if (value == this) "<this>"
|
||||
else value.contentToString(newPrefix)
|
||||
}.getOrElse { "<!>" }
|
||||
} + "\n$prefix}"
|
||||
}
|
||||
|
||||
internal fun Any.allFieldsFromSuperClassesMatching(classFilter: (Class<out Any>) -> Boolean): Sequence<Field> {
|
||||
return (this::class.java.takeIf(classFilter)?.declaredFields?.asSequence() ?: sequenceOf<Field>()) + this::class.allSuperclasses
|
||||
.asSequence()
|
||||
.map { it.java }
|
||||
.filter(classFilter)
|
||||
.flatMap { it.declaredFields.asSequence() }
|
||||
}
|
Loading…
Reference in New Issue
Block a user