zip support

This commit is contained in:
Him188 2020-01-29 19:54:09 +08:00
parent b50f3e4d16
commit 6a8e6b93a0
6 changed files with 73 additions and 41 deletions

View File

@ -14,9 +14,9 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.login.data.RequestPacke
import net.mamoe.mirai.utils.DefaultLogger import net.mamoe.mirai.utils.DefaultLogger
import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.cryptor.adjustToPublicKey import net.mamoe.mirai.utils.cryptor.adjustToPublicKey
import net.mamoe.mirai.utils.cryptor.contentToString
import net.mamoe.mirai.utils.cryptor.decryptBy import net.mamoe.mirai.utils.cryptor.decryptBy
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.unzip
import kotlin.contracts.ExperimentalContracts import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract import kotlin.contracts.contract
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
@ -126,6 +126,8 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
}?.let { }?.let {
// 处理内层真实的包 // 处理内层真实的包
if (it.packetFactory == null) { if (it.packetFactory == null) {
PacketLogger.warning("找不到 PacketFactory")
PacketLogger.verbose("传递给 PacketFactory 的数据 = ${it.data.useBytes { data, length -> data.toUHexString(length = length) }}")
return return
} }
@ -147,7 +149,6 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
PacketLogger.error("任何key都无法解密: ${data.take(size).toUHexString()}") PacketLogger.error("任何key都无法解密: ${data.take(size).toUHexString()}")
return return
} }
} }
} }
} }
@ -183,7 +184,7 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
private fun parseSsoFrame(bot: QQAndroidBot, input: ByteReadPacket): IncomingPacket { private fun parseSsoFrame(bot: QQAndroidBot, input: ByteReadPacket): IncomingPacket {
val commandName: String val commandName: String
val ssoSequenceId: Int val ssoSequenceId: Int
val dataCompressed: Int
// head // head
input.readIoBuffer(input.readInt() - 4).withUse { input.readIoBuffer(input.readInt() - 4).withUse {
ssoSequenceId = readInt() ssoSequenceId = readInt()
@ -196,26 +197,25 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
val unknown = readBytes(readInt() - 4) val unknown = readBytes(readInt() - 4)
if (unknown.toInt() != 0x02B05B8B) DebugLogger.debug("got new unknown: ${unknown.toUHexString()}") if (unknown.toInt() != 0x02B05B8B) DebugLogger.debug("got new unknown: ${unknown.toUHexString()}")
readInt().let { dataCompressed = readInt()
if (it != 0) {
DebugLogger.debug("!! 得到一个原本是 0, 现在是 ${it.contentToString()}")
if (it == 1){
PacketLogger.info("无法处理的数据 = ${input.readBytes().toUHexString()}")
return IncomingPacket(null, ssoSequenceId, input)
}
} }
val packet = when (dataCompressed) {
0 -> input
1 -> {
input.discardExact(4)
input.useBytes { data, length ->
data.unzip(length = length)
}.toReadPacket()
} }
else -> error("unknown dataCompressed flag: $dataCompressed")
} }
// body // body
val packetFactory = findPacketFactory(commandName) val packetFactory = findPacketFactory(commandName)
bot.logger.verbose(commandName) bot.logger.info("Received: $commandName")
if (packetFactory == null) { return IncomingPacket(packetFactory, ssoSequenceId, packet)
bot.logger.warning("找不到包 PacketFactory")
PacketLogger.verbose("传递给 PacketFactory 的数据 = ${input.readBytes().toUHexString()}")
}
return IncomingPacket(packetFactory, ssoSequenceId, input)
} }
private suspend fun <T : Packet> ByteReadPacket.parseOicqResponse( private suspend fun <T : Packet> ByteReadPacket.parseOicqResponse(
@ -224,14 +224,13 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
ssoSequenceId: Int, ssoSequenceId: Int,
consumer: PacketConsumer<T> consumer: PacketConsumer<T>
) { ) {
val qq: Long
readIoBuffer(readInt() - 4).withUse { readIoBuffer(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
this.readUShort() // commandId this.readUShort() // commandId
this.readShort() // const, =0x0001 this.readShort() // const, =0x0001
qq = this.readUInt().toLong() this.readUInt().toLong() // qq
val encryptionMethod = this.readUShort().toInt() val encryptionMethod = this.readUShort().toInt()
this.discardExact(1) // const = 0 this.discardExact(1) // const = 0

View File

@ -6,8 +6,9 @@ import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.qqandroid.io.JceStruct import net.mamoe.mirai.qqandroid.io.JceStruct
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
@Suppress("ArrayInDataClass")
@Serializable @Serializable
internal class RequestPushNotify( internal data class RequestPushNotify(
@SerialId(0) val uin: Long = 0L, @SerialId(0) val uin: Long = 0L,
@SerialId(1) val ctype: Byte = 0, @SerialId(1) val ctype: Byte = 0,
@SerialId(2) val strService: String?, @SerialId(2) val strService: String?,

View File

@ -5,6 +5,8 @@ import io.ktor.client.engine.cio.CIO
import io.ktor.util.KtorExperimentalAPI import io.ktor.util.KtorExperimentalAPI
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.io.pool.useInstance
import net.mamoe.mirai.utils.io.ByteArrayPool
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.DataInput import java.io.DataInput
import java.io.EOFException import java.io.EOFException
@ -83,17 +85,23 @@ actual fun crc32(key: ByteArray): Int = CRC32().apply { update(key) }.value.toIn
*/ */
actual fun solveIpAddress(hostname: String): String = InetAddress.getByName(hostname).hostAddress actual fun solveIpAddress(hostname: String): String = InetAddress.getByName(hostname).hostAddress
actual fun ByteArray.unzip(): ByteArray { actual fun ByteArray.unzip(offset: Int, length: Int): ByteArray {
this.checkOffsetAndLength(offset, length)
if (length == 0) return ByteArray(0)
val inflater = Inflater() val inflater = Inflater()
inflater.reset() inflater.reset()
val output = ByteArrayOutputStream() ByteArrayOutputStream().use { output ->
inflater.setInput(this) inflater.setInput(this, offset, length)
val buffer = ByteArray(128) ByteArrayPool.useInstance {
while (!inflater.finished()) { while (!inflater.finished()) {
output.write(buffer, 0, inflater.inflate(buffer)) output.write(it, 0, inflater.inflate(it))
} }
}
inflater.end() inflater.end()
return output.toByteArray() return output.toByteArray()
}
} }
actual fun newCoroutineDispatcher(threadCount: Int): CoroutineDispatcher { actual fun newCoroutineDispatcher(threadCount: Int): CoroutineDispatcher {

View File

@ -24,7 +24,10 @@ expect val deviceName: String
*/ */
expect fun crc32(key: ByteArray): Int expect fun crc32(key: ByteArray): Int
expect fun ByteArray.unzip(): ByteArray /**
* zip 压缩
*/
expect fun ByteArray.unzip(offset: Int = 0, length: Int = this.size - offset): ByteArray
/** /**
* MD5 算法 * MD5 算法
@ -49,3 +52,9 @@ expect fun localIpAddress(): String
expect val Http: HttpClient expect val Http: HttpClient
expect fun newCoroutineDispatcher(threadCount: Int): CoroutineDispatcher expect fun newCoroutineDispatcher(threadCount: Int): CoroutineDispatcher
internal fun ByteArray.checkOffsetAndLength(offset: Int, length: Int){
require(offset >= 0) { "offset shouldn't be negative: $offset" }
require(length >= 0) { "length shouldn't be negative: $length" }
require(offset + length <= this.size) { "offset ($offset) + length ($length) > array.size (${this.size})" }
}

View File

@ -38,6 +38,14 @@ fun ByteReadPacket.transferTo(outputStream: OutputStream) {
} }
} }
fun <R> ByteReadPacket.useBytes(
n: Int = remaining.toInt(),//not that safe but adequate
block: (data: ByteArray, length: Int) -> R
): R = ByteArrayPool.useInstance {
this.readFully(it, 0, n)
block(it, n)
}
fun ByteReadPacket.readRemainingBytes( fun ByteReadPacket.readRemainingBytes(
n: Int = remaining.toInt()//not that safe but adequate n: Int = remaining.toInt()//not that safe but adequate
): ByteArray = ByteArray(n).also { readAvailable(it, 0, n) } ): ByteArray = ByteArray(n).also { readAvailable(it, 0, n) }
@ -79,11 +87,11 @@ private inline fun <R> inline(block: () -> R): R = block()
typealias TlvMap = MutableMap<Int, ByteArray> typealias TlvMap = MutableMap<Int, ByteArray>
fun TlvMap.getOrFail(tag: Int): ByteArray { fun TlvMap.getOrFail(tag: Int): ByteArray {
return this[tag]?: error("cannot find tlv 0x${tag.toUHexString("")}($tag)") return this[tag] ?: error("cannot find tlv 0x${tag.toUHexString("")}($tag)")
} }
inline fun TlvMap.getOrFail(tag: Int, lazyMessage: (tag: Int) -> String): ByteArray { inline fun TlvMap.getOrFail(tag: Int, lazyMessage: (tag: Int) -> String): ByteArray {
return this[tag]?: error(lazyMessage(tag)) return this[tag] ?: error(lazyMessage(tag))
} }
fun Input.readTLVMap(tagSize: Int = 2): TlvMap = readTLVMap(true, tagSize) fun Input.readTLVMap(tagSize: Int = 2): TlvMap = readTLVMap(true, tagSize)

View File

@ -7,8 +7,10 @@ import io.ktor.client.engine.cio.CIO
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.io.core.copyTo import kotlinx.io.core.copyTo
import kotlinx.io.pool.useInstance
import kotlinx.io.streams.asInput import kotlinx.io.streams.asInput
import kotlinx.io.streams.asOutput import kotlinx.io.streams.asOutput
import net.mamoe.mirai.utils.io.ByteArrayPool
import java.io.* import java.io.*
import java.net.InetAddress import java.net.InetAddress
import java.security.MessageDigest import java.security.MessageDigest
@ -56,18 +58,23 @@ actual fun localIpAddress(): String = InetAddress.getLocalHost().hostAddress
actual val Http: HttpClient get() = HttpClient(CIO) actual val Http: HttpClient get() = HttpClient(CIO)
actual fun ByteArray.unzip(): ByteArray { actual fun ByteArray.unzip(offset: Int, length: Int): ByteArray {
this.checkOffsetAndLength(offset, length)
if (length == 0) return ByteArray(0)
val inflater = Inflater() val inflater = Inflater()
inflater.reset() inflater.reset()
val input = this ByteArrayOutputStream().use { output ->
val output = ByteArrayOutputStream() inflater.setInput(this, offset, length)
inflater.setInput(input) ByteArrayPool.useInstance {
val buffer = ByteArray(128)
while (!inflater.finished()) { while (!inflater.finished()) {
output.write(buffer, 0, inflater.inflate(buffer)) output.write(it, 0, inflater.inflate(it))
} }
}
inflater.end() inflater.end()
return output.toByteArray() return output.toByteArray()
}
} }
actual fun newCoroutineDispatcher(threadCount: Int): CoroutineDispatcher { actual fun newCoroutineDispatcher(threadCount: Int): CoroutineDispatcher {