Simplify packet structure

This commit is contained in:
Him188 2020-01-07 14:57:19 +08:00
parent 0f1328ef00
commit b02a5a7922
3 changed files with 66 additions and 42 deletions

View File

@ -94,10 +94,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
} }
override suspend fun awaitDisconnection() { override suspend fun awaitDisconnection() {
while (true) { supervisor.join()
delay(100)
// TODO: 2019/12/31
}
} }
override fun dispose(cause: Throwable?) { override fun dispose(cause: Throwable?) {

View File

@ -22,11 +22,6 @@ internal class OutgoingPacket constructor(
name: String?, name: String?,
val packetId: PacketId, val packetId: PacketId,
val sequenceId: Int, val sequenceId: Int,
// TODO: 2020/1/6 这个 sequenceId 设计有问题.
// 02 03 包里面的那个应该并不是 sequenceId.
// 它应该是固定的 0x001.
// 应该在这里填入 SSO 的 sequenceId.
// 同时考虑修改名称. 这可能不应该叫做 SSO. 它在全程都有
val delegate: ByteReadPacket val delegate: ByteReadPacket
) { ) {
val name: String by lazy { val name: String by lazy {
@ -47,30 +42,40 @@ private val EMPTY_BYTE_ARRAY = ByteArray(0)
* int extra data size + 4 * int extra data size + 4
* byte[] extra data * byte[] extra data
* byte 0 * byte 0
* int [uinAccount].length + 4 * int uinAccount.length + 4
* byte[] uinAccount * byte[] uinAccount
* *
* byte[] body encrypted by 16 zero * byte[] body encrypted by 16 zero
*/ */
internal inline fun PacketFactory<*, *>.buildLoginOutgoingPacket( internal inline fun PacketFactory<*, *>.buildLoginOutgoingPacket(
uinAccount: String, client: QQAndroidClient,
subAppId: Long,
extraData: ByteArray = EMPTY_BYTE_ARRAY, extraData: ByteArray = EMPTY_BYTE_ARRAY,
name: String? = null, name: String? = null,
id: PacketId = this.id, id: PacketId = this.id,
ssoExtraData: ByteReadPacket = BRP_STUB,
sequenceId: Int = PacketFactory.atomicNextSequenceId(), sequenceId: Int = PacketFactory.atomicNextSequenceId(),
body: BytePacketBuilder.() -> Unit body: BytePacketBuilder.(sequenceId: Int) -> Unit
): OutgoingPacket = OutgoingPacket(name, id, sequenceId, buildPacket { ): OutgoingPacket = OutgoingPacket(name, id, sequenceId, buildPacket {
writeIntLVPacket(lengthOffset = { it + 4 }) { writeIntLVPacket(lengthOffset = { it + 4 }) {
writeInt(0x00_00_00_0A) writeInt(0x00_00_00_0A)
writeByte(0x02) writeByte(0x02)
writeInt(extraData.size + 4) extraData.let {
writeFully(extraData) writeInt(it.size + 4)
writeFully(it)
}
writeByte(0x00) writeByte(0x00)
writeInt(uinAccount.length + 4) client.account.id.toString().let {
writeStringUtf8(uinAccount) writeInt(it.length + 4)
writeStringUtf8(it)
}
encryptAndWrite(KEY_16_ZEROS, body) encryptAndWrite(KEY_16_ZEROS) {
writeLoginSsoPacket(client, subAppId, id, ssoExtraData, sequenceId) {
body(sequenceId)
}
}
} }
}) })
@ -79,20 +84,38 @@ private val BRP_STUB = ByteReadPacket(EMPTY_BYTE_ARRAY)
/** /**
* The second outermost packet for login * The second outermost packet for login
* *
* int headRemaining.size+4
* int sequenceId
* int subAppId
* int subAppId
* hex "01 00 00 00 00 00 00 00 00 00 01 00" // unknown values
* int extraData.size+4
* byte[] extraData
* int commandName.length+4
* byte[] commandName
* int 4+4
* int 0x02B05B8B
* int imei.length+4
* byte[] imei
* int 0+4
* int ksid.length+4
* byte[] ksid
* int 0+4
* *
* int bodyRemaining.size+4
* byte[] body()
*/ */
@UseExperimental(MiraiInternalAPI::class) @UseExperimental(MiraiInternalAPI::class)
internal inline fun BytePacketBuilder.writeLoginSsoPacket( private inline fun BytePacketBuilder.writeLoginSsoPacket(
client: QQAndroidClient, client: QQAndroidClient,
subAppId: Long, subAppId: Long,
packetId: PacketId, packetId: PacketId,
extraData: ByteReadPacket = BRP_STUB, extraData: ByteReadPacket = BRP_STUB,
body: BytePacketBuilder.(ssoSequenceId: Int) -> Unit sequenceId: Int,
body: BytePacketBuilder.() -> Unit
) { ) {
val ssoSequenceId = client.nextSsoSequenceId()
// head
writeIntLVPacket(lengthOffset = { it + 4 }) { writeIntLVPacket(lengthOffset = { it + 4 }) {
writeInt(ssoSequenceId) writeInt(sequenceId)
writeInt(subAppId.toInt()) writeInt(subAppId.toInt())
writeInt(subAppId.toInt()) writeInt(subAppId.toInt())
writeHex("01 00 00 00 00 00 00 00 00 00 01 00") writeHex("01 00 00 00 00 00 00 00 00 00 01 00")
@ -126,9 +149,7 @@ internal inline fun BytePacketBuilder.writeLoginSsoPacket(
} }
// body // body
writeIntLVPacket(lengthOffset = { it + 4 }) { writeIntLVPacket(lengthOffset = { it + 4 }, builder = body)
body(ssoSequenceId)
}
} }
/** /**
@ -245,7 +266,7 @@ internal interface EncryptMethodECDH : EncryptMethod {
* byte 2 // head flag * byte 2 // head flag
* short 27 + 2 + remaining.length * short 27 + 2 + remaining.length
* ushort client.protocolVersion // const 8001 * ushort client.protocolVersion // const 8001
* ushort sequenceId * ushort 0x0001
* uint client.account.id * uint client.account.id
* byte 3 // const * byte 3 // const
* ubyte encryptMethod.value // [EncryptMethod] * ubyte encryptMethod.value // [EncryptMethod]
@ -257,7 +278,7 @@ internal interface EncryptMethodECDH : EncryptMethod {
* byte 3 // tail * byte 3 // tail
*/ */
@UseExperimental(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class) @UseExperimental(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class)
internal inline fun BytePacketBuilder.writeRequestPacket( internal inline fun BytePacketBuilder.writeOicqRequestPacket(
client: QQAndroidClient, client: QQAndroidClient,
encryptMethod: EncryptMethod, encryptMethod: EncryptMethod,
packetId: PacketId, packetId: PacketId,
@ -271,7 +292,7 @@ internal inline fun BytePacketBuilder.writeRequestPacket(
writeByte(0x02) // head writeByte(0x02) // head
writeShort((27 + 2 + body.remaining).toShort()) // orthodox algorithm writeShort((27 + 2 + body.remaining).toShort()) // orthodox algorithm
writeShort(client.protocolVersion) writeShort(client.protocolVersion)
writeShort(1) writeShort(1) // const??
writeShort(packetId.commandId.toShort()) writeShort(packetId.commandId.toShort())
writeQQ(client.account.id) writeQQ(client.account.id)
writeByte(3) // originally const writeByte(3) // originally const

View File

@ -21,17 +21,17 @@ class LoginPacketDecrypter(override val value: ByteArray) : DecrypterByteArray {
internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse, LoginPacketDecrypter>(LoginPacketDecrypter) { internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse, LoginPacketDecrypter>(LoginPacketDecrypter) {
init { init {
this._id = PacketId(commandId = 0x0810, commandName = "wtlogin.login", subCommandId = 9) this._id = PacketId(commandId = 0x0810, commandName = "wtlogin.login")
} }
operator fun invoke( object SubCommand9 {
client: QQAndroidClient
): OutgoingPacket = buildLoginOutgoingPacket(client.account.id.toString()) {
val appId = 16L val appId = 16L
val subAppId = 537062845L val subAppId = 537062845L
writeLoginSsoPacket(client, subAppId, id) { ssoSequenceId -> operator fun invoke(
writeRequestPacket(client, EncryptMethodECDH135(client.ecdh), id) { client: QQAndroidClient
): OutgoingPacket = buildLoginOutgoingPacket(client, 537062845L) { sequenceId ->
writeOicqRequestPacket(client, EncryptMethodECDH135(client.ecdh), id) {
writeShort(9) // subCommand writeShort(9) // subCommand
writeShort(LoginType.PASSWORD.value.toShort()) writeShort(LoginType.PASSWORD.value.toShort())
@ -100,7 +100,7 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse, Log
// ignored t16a because array5 is null // ignored t16a because array5 is null
t154(ssoSequenceId) t154(sequenceId)
t141(client.device.simInfo, client.networkType, client.device.apn) t141(client.device.simInfo, client.networkType, client.device.apn)
t8(2052) t8(2052)
@ -129,8 +129,10 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse, Log
t187(client.device.macAddress) t187(client.device.macAddress)
t188(client.device.androidId) t188(client.device.androidId)
if (client.device.imsiMd5.isNotEmpty()) {
t194(client.device.imsiMd5) val imsi = client.device.imsiMd5
if (imsi.isNotEmpty()) {
t194(imsi)
} }
t191() t191()
@ -158,32 +160,36 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse, Log
// ignored t318 because not logging in by QR // ignored t318 because not logging in by QR
} }
} }
suspend fun ByteReadPacket.decode(bot: QQAndroidBot): LoginPacketResponse {
TODO("not implemented")
}
} }
class LoginPacketResponse : Packet class LoginPacketResponse : Packet
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): LoginPacketResponse { override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): LoginPacketResponse {
TODO("not implemented") return when (val subCommand = readShort().toInt()) {
9 -> SubCommand9.run { decode(bot) }
else -> error("Unknown subCommand: $subCommand")
}
} }
} }
@Suppress("FunctionName") @Suppress("FunctionName")
internal fun PacketId(commandId: Int, commandName: String, subCommandId: Int) = object : PacketId { internal fun PacketId(commandId: Int, commandName: String) = object : PacketId {
override val commandId: Int get() = commandId override val commandId: Int get() = commandId
override val commandName: String get() = commandName override val commandName: String get() = commandName
override val subCommandId: Int get() = subCommandId
} }
internal interface PacketId { internal interface PacketId {
val commandId: Int // ushort actually val commandId: Int // ushort actually
val commandName: String val commandName: String
val subCommandId: Int // ushort actually
} }
internal object NullPacketId : PacketId { internal object NullPacketId : PacketId {
override val commandId: Int get() = error("uninitialized") override val commandId: Int get() = error("uninitialized")
override val commandName: String get() = error("uninitialized") override val commandName: String get() = error("uninitialized")
override val subCommandId: Int get() = error("uninitialized")
} }