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

View File

@ -5,6 +5,8 @@ import io.ktor.client.engine.cio.CIO
import io.ktor.util.KtorExperimentalAPI
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.io.pool.useInstance
import net.mamoe.mirai.utils.io.ByteArrayPool
import java.io.ByteArrayOutputStream
import java.io.DataInput
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 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()
inflater.reset()
val output = ByteArrayOutputStream()
inflater.setInput(this)
val buffer = ByteArray(128)
while (!inflater.finished()) {
output.write(buffer, 0, inflater.inflate(buffer))
ByteArrayOutputStream().use { output ->
inflater.setInput(this, offset, length)
ByteArrayPool.useInstance {
while (!inflater.finished()) {
output.write(it, 0, inflater.inflate(it))
}
}
inflater.end()
return output.toByteArray()
}
inflater.end()
return output.toByteArray()
}
actual fun newCoroutineDispatcher(threadCount: Int): CoroutineDispatcher {

View File

@ -24,7 +24,10 @@ expect val deviceName: String
*/
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 算法
@ -48,4 +51,10 @@ expect fun localIpAddress(): String
*/
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(
n: Int = remaining.toInt()//not that safe but adequate
): 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>
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 {
return this[tag]?: error(lazyMessage(tag))
return this[tag] ?: error(lazyMessage(tag))
}
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.asCoroutineDispatcher
import kotlinx.io.core.copyTo
import kotlinx.io.pool.useInstance
import kotlinx.io.streams.asInput
import kotlinx.io.streams.asOutput
import net.mamoe.mirai.utils.io.ByteArrayPool
import java.io.*
import java.net.InetAddress
import java.security.MessageDigest
@ -56,18 +58,23 @@ actual fun localIpAddress(): String = InetAddress.getLocalHost().hostAddress
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()
inflater.reset()
val input = this
val output = ByteArrayOutputStream()
inflater.setInput(input)
val buffer = ByteArray(128)
while (!inflater.finished()) {
output.write(buffer, 0, inflater.inflate(buffer))
ByteArrayOutputStream().use { output ->
inflater.setInput(this, offset, length)
ByteArrayPool.useInstance {
while (!inflater.finished()) {
output.write(it, 0, inflater.inflate(it))
}
}
inflater.end()
return output.toByteArray()
}
inflater.end()
return output.toByteArray()
}
actual fun newCoroutineDispatcher(threadCount: Int): CoroutineDispatcher {