mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-24 06:50:08 +08:00
Fix packet parsing
This commit is contained in:
parent
1e6a41ca7c
commit
d7f67e5159
@ -1,6 +1,7 @@
|
|||||||
package net.mamoe.mirai.qqandroid.network
|
package net.mamoe.mirai.qqandroid.network
|
||||||
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.io.core.ByteReadPacket
|
||||||
import kotlinx.io.core.use
|
import kotlinx.io.core.use
|
||||||
import net.mamoe.mirai.data.Packet
|
import net.mamoe.mirai.data.Packet
|
||||||
import net.mamoe.mirai.event.broadcast
|
import net.mamoe.mirai.event.broadcast
|
||||||
@ -36,6 +37,27 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
|||||||
LoginPacket.SubCommand9(bot.client).sendAndExpect<LoginPacket.LoginPacketResponse>()
|
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() {
|
private suspend fun processReceive() {
|
||||||
while (channel.isOpen) {
|
while (channel.isOpen) {
|
||||||
val rawInput = try {
|
val rawInput = try {
|
||||||
@ -52,25 +74,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
|||||||
bot.logger.error("Caught unexpected exceptions", e)
|
bot.logger.error("Caught unexpected exceptions", e)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
launchPacketProcessor(rawInput)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,9 +100,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
|||||||
fun filter(packetId: PacketId, sequenceId: Int) = this.packetId == packetId && this.sequenceId == sequenceId
|
fun filter(packetId: PacketId, sequenceId: Int) = this.packetId == packetId && this.sequenceId == sequenceId
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun awaitDisconnection() {
|
override suspend fun awaitDisconnection() = supervisor.join()
|
||||||
supervisor.join()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun dispose(cause: Throwable?) {
|
override fun dispose(cause: Throwable?) {
|
||||||
println("Closed")
|
println("Closed")
|
||||||
|
@ -6,6 +6,7 @@ import kotlinx.io.core.ByteReadPacket
|
|||||||
import kotlinx.io.core.toByteArray
|
import kotlinx.io.core.toByteArray
|
||||||
import net.mamoe.mirai.BotAccount
|
import net.mamoe.mirai.BotAccount
|
||||||
import net.mamoe.mirai.qqandroid.QQAndroidBot
|
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.Context
|
||||||
import net.mamoe.mirai.qqandroid.utils.DeviceInfo
|
import net.mamoe.mirai.qqandroid.utils.DeviceInfo
|
||||||
import net.mamoe.mirai.qqandroid.utils.NetworkType
|
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.MiraiExperimentalAPI
|
||||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||||
import net.mamoe.mirai.utils.cryptor.ECDH
|
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.io.hexToBytes
|
||||||
import net.mamoe.mirai.utils.unsafeWeakRef
|
import net.mamoe.mirai.utils.unsafeWeakRef
|
||||||
|
|
||||||
@ -40,6 +42,11 @@ internal open class QQAndroidClient(
|
|||||||
val device: DeviceInfo = SystemDeviceInfo(context),
|
val device: DeviceInfo = SystemDeviceInfo(context),
|
||||||
bot: QQAndroidBot
|
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 context by context.unsafeWeakRef()
|
||||||
val bot: QQAndroidBot by bot.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 rollbackSig: ByteArray? = null
|
||||||
var ipFromT149: ByteArray? = null
|
var ipFromT149: ByteArray? = null
|
||||||
/**
|
/**
|
||||||
@ -100,7 +107,7 @@ internal open class QQAndroidClient(
|
|||||||
/**
|
/**
|
||||||
* t108 时更新
|
* t108 时更新
|
||||||
*/
|
*/
|
||||||
var ksid: String = "|454001228437590|A8.2.0.27f6ea96"
|
var ksid: ByteArray = "|454001228437590|A8.2.0.27f6ea96".toByteArray()
|
||||||
/**
|
/**
|
||||||
* t186
|
* t186
|
||||||
*/
|
*/
|
||||||
@ -110,19 +117,23 @@ internal open class QQAndroidClient(
|
|||||||
*/
|
*/
|
||||||
var loginExtraData: LoginExtraData? = null
|
var loginExtraData: LoginExtraData? = null
|
||||||
lateinit var wFastLoginInfo: WFastLoginInfo
|
lateinit var wFastLoginInfo: WFastLoginInfo
|
||||||
lateinit var reserveUinInfo: ReserveUinInfo
|
var reserveUinInfo: ReserveUinInfo? = null
|
||||||
var wLoginSigInfo: WLoginSigInfo? = null
|
var wLoginSigInfo: WLoginSigInfo? = null
|
||||||
var tlv113: ByteArray? = null
|
var tlv113: ByteArray? = null
|
||||||
lateinit var qrPushSig: ByteArray
|
lateinit var qrPushSig: ByteArray
|
||||||
|
|
||||||
lateinit var mainDisplayName: String
|
lateinit var mainDisplayName: ByteArray
|
||||||
}
|
}
|
||||||
|
|
||||||
class ReserveUinInfo(
|
class ReserveUinInfo(
|
||||||
val imgType: ByteArray,
|
val imgType: ByteArray,
|
||||||
val imgFormat: ByteArray,
|
val imgFormat: ByteArray,
|
||||||
val imgUrl: ByteArray
|
val imgUrl: ByteArray
|
||||||
)
|
) {
|
||||||
|
override fun toString(): String {
|
||||||
|
return "ReserveUinInfo(imgType=${imgType.contentToString()}, imgFormat=${imgFormat.contentToString()}, imgUrl=${imgUrl.contentToString()})"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class WFastLoginInfo(
|
class WFastLoginInfo(
|
||||||
val outA1: ByteReadPacket,
|
val outA1: ByteReadPacket,
|
||||||
@ -130,7 +141,11 @@ class WFastLoginInfo(
|
|||||||
var iconUrl: String = "",
|
var iconUrl: String = "",
|
||||||
var profileUrl: String = "",
|
var profileUrl: String = "",
|
||||||
var userJson: String = ""
|
var userJson: String = ""
|
||||||
)
|
) {
|
||||||
|
override fun toString(): String {
|
||||||
|
return "WFastLoginInfo(outA1=$outA1, adUrl='$adUrl', iconUrl='$iconUrl', profileUrl='$profileUrl', userJson='$userJson')"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class WLoginSimpleInfo(
|
class WLoginSimpleInfo(
|
||||||
val uin: Long, // uin
|
val uin: Long, // uin
|
||||||
@ -142,14 +157,22 @@ class WLoginSimpleInfo(
|
|||||||
val imgFormat: ByteArray,
|
val imgFormat: ByteArray,
|
||||||
val imgUrl: ByteArray,
|
val imgUrl: ByteArray,
|
||||||
val mainDisplayName: 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(
|
class LoginExtraData(
|
||||||
val uin: Long,
|
val uin: Long,
|
||||||
val ip: ByteArray,
|
val ip: ByteArray,
|
||||||
val time: Int,
|
val time: Int,
|
||||||
val version: Int
|
val version: Int
|
||||||
)
|
) {
|
||||||
|
override fun toString(): String {
|
||||||
|
return "LoginExtraData(uin=$uin, ip=${ip.contentToString()}, time=$time, version=$version)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class WLoginSigInfo(
|
class WLoginSigInfo(
|
||||||
val uin: Long,
|
val uin: Long,
|
||||||
@ -193,7 +216,11 @@ class WLoginSigInfo(
|
|||||||
val wtSessionTicket: WtSessionTicket,
|
val wtSessionTicket: WtSessionTicket,
|
||||||
val wtSessionTicketKey: ByteArray,
|
val wtSessionTicketKey: ByteArray,
|
||||||
val deviceToken: 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 UserStSig(data: ByteArray, creationTime: Long) : KeyWithCreationTime(data, creationTime)
|
||||||
class LSKey(data: ByteArray, creationTime: Long, expireTime: Long) : KeyWithExpiry(data, creationTime, expireTime)
|
class LSKey(data: ByteArray, creationTime: Long, expireTime: Long) : KeyWithExpiry(data, creationTime, expireTime)
|
||||||
|
@ -144,8 +144,8 @@ private inline fun BytePacketBuilder.writeLoginSsoPacket(
|
|||||||
writeInt(4)
|
writeInt(4)
|
||||||
|
|
||||||
client.ksid.let {
|
client.ksid.let {
|
||||||
writeShort((it.length + 2).toShort())
|
writeShort((it.size + 2).toShort())
|
||||||
writeStringUtf8(it)
|
writeFully(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
writeInt(4)
|
writeInt(4)
|
||||||
|
@ -48,6 +48,7 @@ private val DECRYPTER_16_ZERO = ByteArray(16)
|
|||||||
|
|
||||||
internal typealias PacketConsumer = suspend (packet: Packet, packetId: PacketId, ssoSequenceId: Int) -> Unit
|
internal typealias PacketConsumer = suspend (packet: Packet, packetId: PacketId, ssoSequenceId: Int) -> Unit
|
||||||
|
|
||||||
|
@UseExperimental(ExperimentalUnsignedTypes::class)
|
||||||
internal object KnownPacketFactories : List<PacketFactory<*, *>> by mutableListOf(
|
internal object KnownPacketFactories : List<PacketFactory<*, *>> by mutableListOf(
|
||||||
LoginPacket
|
LoginPacket
|
||||||
) {
|
) {
|
||||||
@ -60,7 +61,11 @@ internal object KnownPacketFactories : List<PacketFactory<*, *>> by mutableListO
|
|||||||
suspend fun parseIncomingPacket(bot: QQAndroidBot, rawInput: ByteReadPacket, consumer: PacketConsumer) =
|
suspend fun parseIncomingPacket(bot: QQAndroidBot, rawInput: ByteReadPacket, consumer: PacketConsumer) =
|
||||||
rawInput.debugPrintIfFail("Incoming packet") {
|
rawInput.debugPrintIfFail("Incoming packet") {
|
||||||
require(remaining < Int.MAX_VALUE) { "rawInput is too long" }
|
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? " }
|
check(remaining.toInt() == expectedLength) { "Invalid packet length. Expected $expectedLength, got ${rawInput.remaining} Probably packets merged? " }
|
||||||
// login
|
// login
|
||||||
when (val flag1 = readInt()) {
|
when (val flag1 = readInt()) {
|
||||||
|
@ -10,6 +10,11 @@ import net.mamoe.mirai.utils.io.*
|
|||||||
import net.mamoe.mirai.utils.md5
|
import net.mamoe.mirai.utils.md5
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显式表示一个 [ByteArray] 是一个 tlv 的 body
|
||||||
|
*/
|
||||||
|
inline class Tlv(val value: ByteArray)
|
||||||
|
|
||||||
inline class LoginType(
|
inline class LoginType(
|
||||||
val value: Int
|
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.SerialId
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.protobuf.ProtoBuf
|
import kotlinx.serialization.protobuf.ProtoBuf
|
||||||
|
import net.mamoe.mirai.utils.cryptor.contentToString
|
||||||
import net.mamoe.mirai.utils.unsafeWeakRef
|
import net.mamoe.mirai.utils.unsafeWeakRef
|
||||||
|
|
||||||
abstract class DeviceInfo(
|
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 {
|
interface Version {
|
||||||
val incremental: ByteArray
|
val incremental: ByteArray
|
||||||
val release: ByteArray
|
val release: ByteArray
|
||||||
val codename: ByteArray
|
val codename: ByteArray
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -45,4 +45,8 @@ actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) {
|
|||||||
actual fun calculateShareKeyByPeerPublicKey(peerPublicKey: ECDHPublicKey): ByteArray {
|
actual fun calculateShareKeyByPeerPublicKey(peerPublicKey: ECDHPublicKey): ByteArray {
|
||||||
return calculateShareKey(keyPair.privateKey, peerPublicKey)
|
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
|
abstract val network: BotNetworkHandler
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登录
|
* 登录.
|
||||||
|
*
|
||||||
|
* 最终调用 [net.mamoe.mirai.network.BotNetworkHandler.login]
|
||||||
*
|
*
|
||||||
* @throws LoginFailedException
|
* @throws LoginFailedException
|
||||||
*/
|
*/
|
||||||
|
@ -28,6 +28,8 @@ expect class ECDH(keyPair: ECDHKeyPair) {
|
|||||||
fun generateKeyPair(): ECDHKeyPair
|
fun generateKeyPair(): ECDHKeyPair
|
||||||
fun calculateShareKey(privateKey: ECDHPrivateKey, publicKey: ECDHPublicKey): ByteArray
|
fun calculateShareKey(privateKey: ECDHPrivateKey, publicKey: ECDHPublicKey): ByteArray
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun toString(): String
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("FunctionName")
|
@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 {
|
class ProtoMap(map: MutableMap<ProtoFieldId, Any>) : MutableMap<ProtoFieldId, Any> by map {
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val indent: String = " "
|
internal val indent: String = " "
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): 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 UInt -> "0x" + this.toUHexString("") + "($this)"
|
||||||
is UByte -> "0x" + this.toUHexString() + "($this)"
|
is UByte -> "0x" + this.toUHexString() + "($this)"
|
||||||
is UShort -> "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 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}}"
|
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 {
|
fun ByteReadPacket.readProtoMap(length: Long = this.remaining): ProtoMap {
|
||||||
val map = ProtoMap(mutableMapOf())
|
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.GroupInternalId
|
||||||
import net.mamoe.mirai.contact.groupId
|
import net.mamoe.mirai.contact.groupId
|
||||||
import net.mamoe.mirai.contact.groupInternalId
|
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.JvmName
|
||||||
import kotlin.jvm.JvmSynthetic
|
import kotlin.jvm.JvmSynthetic
|
||||||
|
|
||||||
@ -76,14 +78,14 @@ fun Input.readTLVMap(tagSize: Int = 2): MutableMap<Int, ByteArray> = readTLVMap(
|
|||||||
@Suppress("DuplicatedCode")
|
@Suppress("DuplicatedCode")
|
||||||
fun Input.readTLVMap(expectingEOF: Boolean = true, tagSize: Int): MutableMap<Int, ByteArray> {
|
fun Input.readTLVMap(expectingEOF: Boolean = true, tagSize: Int): MutableMap<Int, ByteArray> {
|
||||||
val map = mutableMapOf<Int, ByteArray>()
|
val map = mutableMapOf<Int, ByteArray>()
|
||||||
var type: Int = 0
|
var key = 0
|
||||||
|
|
||||||
while (inline {
|
while (inline {
|
||||||
try {
|
try {
|
||||||
type = when (tagSize) {
|
key = when (tagSize) {
|
||||||
1 -> readByte().toInt()
|
1 -> readUByte().toInt()
|
||||||
2 -> readShort().toInt()
|
2 -> readUShort().toInt()
|
||||||
4 -> readInt()
|
4 -> readUInt().toInt()
|
||||||
else -> error("Unsupported tag size: $tagSize")
|
else -> error("Unsupported tag size: $tagSize")
|
||||||
}
|
}
|
||||||
} catch (e: Exception) { // java.nio.BufferUnderflowException is not a EOFException...
|
} 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
|
throw e
|
||||||
}
|
}
|
||||||
type
|
key
|
||||||
}.toUByte() != UByte.MAX_VALUE) {
|
}.toUByte() != UByte.MAX_VALUE) {
|
||||||
|
|
||||||
check(!map.containsKey(type)) {
|
if (map.containsKey(key)) {
|
||||||
"Count not readTLVMap: duplicated key 0x${type.toUInt().toUHexString("")}. " +
|
DebugLogger.error(
|
||||||
"map=$map" +
|
@Suppress("IMPLICIT_CAST_TO_ANY")
|
||||||
", duplicating value=${this.readUShortLVByteArray().toUHexString()}" +
|
"""
|
||||||
", remaining=" + if (expectingEOF) this.readBytes().toUHexString() else "[Not expecting EOF]"
|
Error readTLVMap:
|
||||||
}
|
duplicated key ${when (tagSize) {
|
||||||
try {
|
1 -> key.toByte()
|
||||||
map[type] = this.readUShortLVByteArray()
|
2 -> key.toShort()
|
||||||
} catch (e: Exception) { // BufferUnderflowException, java.io.EOFException
|
4 -> key
|
||||||
if (expectingEOF) {
|
else -> assertUnreachable()
|
||||||
return map
|
}.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
|
return map
|
||||||
|
@ -51,13 +51,13 @@ fun UShort.toByteArray(): ByteArray = with(toUInt()) {
|
|||||||
fun Short.toUHexString(separator: String = " "): String = this.toUShort().toUHexString(separator)
|
fun Short.toUHexString(separator: String = " "): String = this.toUShort().toUHexString(separator)
|
||||||
|
|
||||||
fun UShort.toUHexString(separator: String = " "): String =
|
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 =
|
fun ULong.toUHexString(separator: String = " "): String =
|
||||||
this.toLong().toUHexString(separator)
|
this.toLong().toUHexString(separator)
|
||||||
|
|
||||||
fun Long.toUHexString(separator: String = " "): String =
|
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
|
* 255 -> 00 FF
|
||||||
|
@ -51,4 +51,8 @@ actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) {
|
|||||||
actual fun calculateShareKeyByPeerPublicKey(peerPublicKey: ECDHPublicKey): ByteArray {
|
actual fun calculateShareKeyByPeerPublicKey(peerPublicKey: ECDHPublicKey): ByteArray {
|
||||||
return calculateShareKey(keyPair.privateKey, peerPublicKey)
|
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