فهرست منبع

Fix wrong decoding on ConfigPushSvc and update default server list (#1015)

* Fix wrong decoding on ConfigPushSvc and update default server list

* Fix a bug which won't update server list

* Improve wording

* Fix an encoding error

* Fix wording

* Naming consistently

* Improve ServerListPush message

Co-authored-by: Him188 <[email protected]>

Co-authored-by: Him188 <[email protected]>
sandtechnology 5 سال پیش
والد
کامیت
9699218601

+ 1 - 2
mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt

@@ -14,7 +14,6 @@ package net.mamoe.mirai.internal.network
 import kotlinx.atomicfu.AtomicBoolean
 import kotlinx.atomicfu.AtomicInt
 import kotlinx.atomicfu.atomic
-import kotlinx.coroutines.CompletableDeferred
 import kotlinx.io.core.BytePacketBuilder
 import kotlinx.io.core.String
 import kotlinx.io.core.toByteArray
@@ -46,7 +45,7 @@ internal fun getRandomByteArray(length: Int): ByteArray = ByteArray(length) { Ra
 // [114.221.148.179:14000, 113.96.13.125:8080, 14.22.3.51:8080, 42.81.172.207:443, 114.221.144.89:80, 125.94.60.148:14000, 42.81.192.226:443, 114.221.148.233:8080, msfwifi.3g.qq.com:8080, 42.81.172.22:80]
 
 internal val DefaultServerList: MutableSet<Pair<String, Int>> =
-    "114.221.148.179:14000, 113.96.13.125:8080, 14.22.3.51:8080, 42.81.172.207:443, 114.221.144.89:80, 125.94.60.148:14000, 42.81.192.226:443, 114.221.148.233:8080, msfwifi.3g.qq.com:8080, 42.81.172.22:80"
+    "msfwifi.3g.qq.com:8080, 14.215.138.110:8080, 113.96.12.224:8080, 157.255.13.77:14000, 120.232.18.27:443, 183.3.235.162:14000, 163.177.89.195:443, 183.232.94.44:80, 203.205.255.224:8080, 203.205.255.221:8080"
         .split(", ")
         .map {
             val host = it.substringBefore(':')

+ 5 - 5
mirai-core/src/commonMain/kotlin/network/handler/QQAndroidBotNetworkHandler.kt

@@ -368,14 +368,14 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
                     logger.warning { "Missing ConfigPushSvc.PushReq. Switching server..." }
                     bot.launch { BotOfflineEvent.RequireReconnect(bot).broadcast() }
                 } else {
-                    logger.warning { "Missing ConfigPushSvc.PushReq. Using latest response. File uploading may be affected." }
+                    logger.warning { "Missing ConfigPushSvc.PushReq. Using the latest response. File uploading may be affected." }
                 }
             }
-            is ConfigPushSvc.PushReq.PushReqResponse.Success -> {
-                logger.info { "ConfigPushSvc.PushReq: Success." }
+            is ConfigPushSvc.PushReq.PushReqResponse.ConfigPush -> {
+                logger.info { "ConfigPushSvc.PushReq: Config updated." }
             }
-            is ConfigPushSvc.PushReq.PushReqResponse.ChangeServer -> {
-                logger.info { "ConfigPushSvc.PushReq: Require reconnect" }
+            is ConfigPushSvc.PushReq.PushReqResponse.ServerListPush -> {
+                logger.info { "ConfigPushSvc.PushReq: Server updated." }
                 // handled in ConfigPushSvc
                 return@launch
             }

+ 49 - 0
mirai-core/src/commonMain/kotlin/network/protocol/data/jce/ConfigPush.kt

@@ -147,6 +147,55 @@ internal class PushReq(
     @TarsId(3) @JvmField val seq: Long
 ) : JceStruct, Packet
 
+@Serializable
+internal data class ServerListPush(
+    @TarsId(1) val mobileSSOServerList: List<ServerInfo>,
+    @TarsId(3) val wifiSSOServerList: List<ServerInfo>,
+    @TarsId(4) val reconnectNeeded: Int = 0,
+    //@JvmField @TarsId(5)  val skipped:Byte? = 0,
+    //@JvmField @TarsId(6)  val skipped:Byte? = 0,
+    //@JvmField @TarsId(7)  val skipped:Int? = 1,
+    @TarsId(8) val mobileHttpServerList: List<ServerInfo>,
+    @TarsId(9) val wifiHttpServerList: List<ServerInfo>,
+    @TarsId(10) val quicServerList: List<ServerInfo>,
+    @TarsId(11) val ssoServerListIpv6: List<ServerInfo>,
+    @TarsId(12) val httpServerListIpv6: List<ServerInfo>,
+    @TarsId(13) val quicServerListIpv6: List<ServerInfo>,
+    /**
+     * wifi下&1==1则启用
+     * 移动数据(mobile)下&2==2则启用
+     */
+    @TarsId(14) val ipv6ConfigVal: Byte? = 0,
+    //@JvmField @TarsId(15) val netTestDelay:Int? = 0,
+    @TarsId(16) val configDesc: String? = ""
+) : JceStruct {
+
+    @Serializable
+    data class ServerInfo(
+        @TarsId(1) val host: String,
+        @TarsId(2) val port: Int,
+        //@JvmField @TarsId(3) val skipped: Byte = 0,
+        //@JvmField @TarsId(4) val skipped: Byte = 0,
+        /**
+         * 2,3->http
+         * 0,1->socket
+         */
+        //@JvmField @TarsId(5) val protocolType: Byte? = 0,
+        //@JvmField @TarsId(6) val skipped: Int? = 8,
+        //@JvmField @TarsId(7) val skipped: Byte? = 0,
+        @TarsId(8) val location: String = "",
+        /**
+         * cm->China mobile 中国移动
+         * uni->China unicom 中国联通
+         * others->其他
+         */
+        @TarsId(9) val ispName: String = ""
+    ) : JceStruct {
+        override fun toString(): String {
+            return "$host:$port"
+        }
+    }
+}
 @Serializable
 internal class PushResp(
     @TarsId(1) @JvmField val type: Int,

+ 83 - 89
mirai-core/src/commonMain/kotlin/network/protocol/packet/login/ConfigPushSvc.kt

@@ -12,7 +12,6 @@ package net.mamoe.mirai.internal.network.protocol.packet.login
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
 import kotlinx.io.core.ByteReadPacket
-import kotlinx.serialization.Serializable
 import net.mamoe.mirai.event.AbstractEvent
 import net.mamoe.mirai.event.Event
 import net.mamoe.mirai.event.broadcast
@@ -24,15 +23,15 @@ import net.mamoe.mirai.internal.network.Packet
 import net.mamoe.mirai.internal.network.protocol.data.jce.FileStoragePushFSSvcList
 import net.mamoe.mirai.internal.network.protocol.data.jce.PushResp
 import net.mamoe.mirai.internal.network.protocol.data.jce.RequestPacket
+import net.mamoe.mirai.internal.network.protocol.data.jce.ServerListPush
 import net.mamoe.mirai.internal.network.protocol.data.proto.Subcmd0x501
 import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacketFactory
 import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket
 import net.mamoe.mirai.internal.network.protocol.packet.buildResponseUniPacket
-import net.mamoe.mirai.internal.utils.io.JceStruct
+import net.mamoe.mirai.internal.utils.NetworkType
 import net.mamoe.mirai.internal.utils.io.serialization.jceRequestSBuffer
 import net.mamoe.mirai.internal.utils.io.serialization.loadAs
 import net.mamoe.mirai.internal.utils.io.serialization.readUniPacket
-import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId
 import net.mamoe.mirai.internal.utils.io.serialization.writeJceStruct
 import net.mamoe.mirai.utils.info
 import net.mamoe.mirai.utils.toUHexString
@@ -46,66 +45,46 @@ internal class ConfigPushSvc {
     ) {
         override val canBeCached: Boolean get() = false
 
-        sealed class PushReqResponse : Packet, Event, AbstractEvent(), Packet.NoEventLog {
-            class Success(
-                val struct: PushReqJceStruct
-            ) : PushReqResponse() {
+        sealed class PushReqResponse(val struct: PushReqJceStruct) : Packet, Event, AbstractEvent(), Packet.NoEventLog {
+            class Unknown(struct: PushReqJceStruct) : PushReqResponse(struct) {
                 override fun toString(): String {
-                    return "ConfigPushSvc.PushReq.PushReqResponse.Success"
+                    return "ConfigPushSvc.PushReq.PushReqResponse.Unknown"
                 }
             }
 
-            @Serializable
-            data class ChangeServer(
-                @TarsId(1) val serverList: List<ServerInfo>,
-                // @TarsId(3) val serverList2: List<ServerInfo>,
-                // @TarsId(8) val serverList3: List<ServerInfo>,
-            ) : JceStruct, PushReqResponse() {
+            class LogAction(struct: PushReqJceStruct) : PushReqResponse(struct) {
                 override fun toString(): String {
-                    return "ConfigPushSvc.PushReq.PushReqResponse.ChangeServer"
+                    return "ConfigPushSvc.PushReq.PushReqResponse.LogAction"
                 }
+            }
 
-                @Serializable
-                data class ServerInfo(
-                    /*
-                    skipping String1
-                    skipping Short
-                    skipping Byte
-                    skipping Zero
-                    skipping Zero
-                    skipping Byte
-                    skipping Byte
-                    skipping String1
-                    skipping String1
-                     */
-                    @TarsId(1) val host: String,
-                    @TarsId(2) val port: Int,
-                    @TarsId(8) val location: String
-                ) : JceStruct {
-                    override fun toString(): String {
-                        return "$host:$port"
-                    }
+            class ServerListPush(struct: PushReqJceStruct) : PushReqResponse(struct) {
+                override fun toString(): String {
+                    return "ConfigPushSvc.PushReq.PushReqResponse.ServerListPush"
+                }
+            }
+
+            class ConfigPush(struct: PushReqJceStruct) : PushReqResponse(struct) {
+                override fun toString(): String {
+                    return "ConfigPushSvc.PushReq.PushReqResponse.ConfigPush"
                 }
             }
+
+
         }
 
         override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): PushReqResponse {
             val pushReq = readUniPacket(PushReqJceStruct.serializer(), "PushReq")
             return when (pushReq.type) {
-                1 -> kotlin.runCatching {
-                    pushReq.jcebuf.loadAs(PushReqResponse.ChangeServer.serializer())
-                }.getOrElse {
-                    throw contextualBugReportException(
-                        "ConfigPush.ReqPush type=1",
-                        forDebug = pushReq.jcebuf.toUHexString(),
-                    )
-                }
-                else -> PushReqResponse.Success(pushReq)
+                1 -> PushReqResponse.ServerListPush(pushReq)
+                2 -> PushReqResponse.ConfigPush(pushReq)
+                3 -> PushReqResponse.LogAction(pushReq)
+                else -> PushReqResponse.Unknown(pushReq)
             }
         }
 
         override suspend fun QQAndroidBot.handle(packet: PushReqResponse, sequenceId: Int): OutgoingPacket? {
-            fun handleSuccess(packet: PushReqResponse.Success) {
+            fun handleConfigPush(packet: PushReqResponse.ConfigPush) {
                 val pushReq = packet.struct
 
                 // FS server
@@ -148,64 +127,79 @@ internal class ConfigPushSvc {
                 )
             }
 
-            fun handleRequireReconnect(resp: PushReqResponse.ChangeServer) {
-                bot.logger.info { "Server requires reconnect." }
-                bot.logger.info { "Server list: ${resp.serverList.joinToString()}." }
+            fun handleServerListPush(resp: PushReqResponse.ServerListPush) {
+                bot.network.logger.info { "Server list updated." }
+                val serverListPush = kotlin.runCatching {
+                    resp.struct.jcebuf.loadAs(ServerListPush.serializer())
+                }.getOrElse {
+                    throw contextualBugReportException(
+                        "ConfigPush.ReqPush type=1",
+                        forDebug = resp.struct.jcebuf.toUHexString(),
+                    )
+                }
+                val pushServerList = if (client.networkType == NetworkType.WIFI) {
+                    serverListPush.wifiSSOServerList
+                } else {
+                    serverListPush.mobileSSOServerList
+                }
 
-                if (resp.serverList.isNotEmpty()) {
+                bot.logger.info { "Server list: ${pushServerList.joinToString()}." }
+
+                if (pushServerList.isNotEmpty()) {
                     bot.serverList.clear()
-                    resp.serverList.shuffled().forEach {
+                    pushServerList.shuffled().forEach {
                         bot.serverList.add(it.host to it.port)
                     }
                 }
                 bot.bdhSyncer.saveToCache()
                 bot.bdhSyncer.saveServerListToCache()
-
-                bot.launch {
-                    delay(1000)
-                    BotOfflineEvent.RequireReconnect(bot).broadcast()
+                if (serverListPush.reconnectNeeded == 1) {
+                    bot.logger.info { "Server request to change server." }
+                    bot.launch {
+                        delay(1000)
+                        BotOfflineEvent.RequireReconnect(bot).broadcast()
+                    }
                 }
             }
 
             when (packet) {
-                is PushReqResponse.Success -> {
-                    handleSuccess(packet)
-                    if (!client.wLoginSigInfoInitialized) return null // concurrently doing reconnection
-                    return buildResponseUniPacket(
-                        client,
-                        sequenceId = sequenceId,
-                        key = client.wLoginSigInfo.d2Key
-                    ) {
-                        writeJceStruct(
-                            RequestPacket.serializer(),
-                            RequestPacket(
-                                requestId = 0,
-                                version = 3,
-                                servantName = "QQService.ConfigPushSvc.MainServant",
-                                funcName = "PushResp",
-                                sBuffer = jceRequestSBuffer(
-                                    "PushResp",
-                                    PushResp.serializer(),
-                                    PushResp(
-                                        type = packet.struct.type,
-                                        seq = packet.struct.seq,
-                                        jcebuf = if (packet.struct.type == 3) packet.struct.jcebuf else null
-                                    )
-                                )
-                            )
-                        )
-                        // writePacket(this.build().debugPrintThis())
-                    }
+                is PushReqResponse.ConfigPush -> {
+                    handleConfigPush(packet)
                 }
-                is PushReqResponse.ChangeServer -> {
-                    handleRequireReconnect(packet)
-                    return null
+                is PushReqResponse.ServerListPush -> {
+                    handleServerListPush(packet)
                 }
-                else -> {
-                    // handled in QQABot
-                    return null
+                is PushReqResponse.LogAction, is PushReqResponse.Unknown -> {
+                    //ignore
                 }
             }
+            //Always send resp
+            if (!client.wLoginSigInfoInitialized) return null // concurrently doing reconnection
+            return buildResponseUniPacket(
+                client,
+                sequenceId = sequenceId,
+                key = client.wLoginSigInfo.d2Key
+            ) {
+                writeJceStruct(
+                    RequestPacket.serializer(),
+                    RequestPacket(
+                        requestId = client.nextRequestPacketRequestId(),
+                        version = 3,
+                        servantName = "QQService.ConfigPushSvc.MainServant",
+                        funcName = "PushResp",
+                        sBuffer = jceRequestSBuffer(
+                            "PushResp",
+                            PushResp.serializer(),
+                            PushResp(
+                                type = packet.struct.type,
+                                seq = packet.struct.seq,
+                                jcebuf = if (packet.struct.type == 3) packet.struct.jcebuf else null
+                            )
+                        )
+                    )
+                )
+                // writePacket(this.build().debugPrintThis())
+            }
         }
     }
-}
+}