StatSvc.Register: Provide SSO Ip and correct logic (#1240)

* StatSvc.Register: Provide SSO Ip and correct logic

* Remove unnessacy launch

Co-authored-by: Him188 <Him188@mamoe.net>

* Test for last polled ip

* Normal login test for last ip

* Fix test failed and remove debug code

* Fix unit test and build

* Optimize pollCurrent and pollAny method

* Use uOldSSOIp and uNewSSOIp only when protocol is PHONE

* Fix bug in toIpV4Long

* Fix new line in NettyBotNormalLoginTest.kt

* Using Inet4Address and toInt method for toIpV4Long

* Return Unsigned Long for toIpV4Long

* Remove unnessacy synchronized annotation

Co-authored-by: Him188 <Him188@mamoe.net>

* Using createAddress method instead of address

* Moving lastDisconnectedIp and lastConnectedIp to Server
List

* Fix build

* Fix build and remove empty line

* Keeping naming consistent

* Use bot.components instead of overrideComponents

Co-authored-by: Him188 <Him188@mamoe.net>

* Revert overrideComponents changes and add comment for overrideComponents

Co-authored-by: Him188 <Him188@mamoe.net>
This commit is contained in:
sandtechnology 2021-07-03 22:04:49 +08:00 committed by GitHub
parent 2713127466
commit b7869888f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 131 additions and 15 deletions

View File

@ -102,6 +102,16 @@ internal open class QQAndroidBot constructor(
override fun toString(): String = "StateChangedObserver(BotOnlineEventBroadcaster)" override fun toString(): String = "StateChangedObserver(BotOnlineEventBroadcaster)"
}, },
StateChangedObserver("LastConnectedAddressUpdater", State.OK) {
components[ServerList].run {
lastConnectedIP = getLastPolledIP()
}
},
StateChangedObserver("LastDisconnectedAddressUpdater", State.CLOSED) {
components[ServerList].run {
lastDisconnectedIP = lastConnectedIP
}
},
StateChangedObserver("BotOfflineEventBroadcaster", State.OK, State.CLOSED) { new -> StateChangedObserver("BotOfflineEventBroadcaster", State.OK, State.CLOSED) { new ->
// logging performed by BotOfflineEventMonitor // logging performed by BotOfflineEventMonitor
val cause = new.getCause() val cause = new.getCause()

View File

@ -58,6 +58,22 @@ internal interface ServerList {
*/ */
fun refresh() fun refresh()
/**
* Last disconnected ip
*/
var lastDisconnectedIP: String
/**
* Last connected ip
*/
var lastConnectedIP: String
/**
* Get last poll ip
*/
fun getLastPolledIP(): String
/** /**
* [Poll][Queue.poll] from current address list. Returns `null` if current address list is empty. * [Poll][Queue.poll] from current address list. Returns `null` if current address list is empty.
*/ */
@ -100,6 +116,9 @@ internal class ServerListImpl(
@Volatile @Volatile
private var current: Queue<ServerAddress> = ArrayDeque(initial) private var current: Queue<ServerAddress> = ArrayDeque(initial)
@Volatile
private var lastPolledAddress: ServerAddress? = null
@Synchronized @Synchronized
override fun setPreferred(list: Collection<ServerAddress>) { override fun setPreferred(list: Collection<ServerAddress>) {
logger.info { "Server list: ${list.joinToString()}." } logger.info { "Server list: ${list.joinToString()}." }
@ -121,12 +140,19 @@ internal class ServerListImpl(
} }
} }
override var lastDisconnectedIP: String = ""
override var lastConnectedIP: String = ""
override fun getLastPolledIP(): String = lastPolledAddress?.host ?: ""
/** /**
* [Poll][Queue.poll] from current address list. Returns `null` if current address list is empty. * [Poll][Queue.poll] from current address list. Returns `null` if current address list is empty.
*/ */
@Synchronized @Synchronized
override fun pollCurrent(): ServerAddress? { override fun pollCurrent(): ServerAddress? {
return current.poll() return current.poll()?.also { address ->
lastPolledAddress = address
}
} }
/** /**
@ -135,7 +161,9 @@ internal class ServerListImpl(
@Synchronized @Synchronized
override fun pollAny(): ServerAddress { override fun pollAny(): ServerAddress {
if (current.isEmpty()) refresh() if (current.isEmpty()) refresh()
return current.remove() return current.remove().also { address ->
lastPolledAddress = address
}
} }
override fun toString(): String { override fun toString(): String {

View File

@ -30,6 +30,7 @@ import net.mamoe.mirai.internal.message.contextualBugReportException
import net.mamoe.mirai.internal.network.* import net.mamoe.mirai.internal.network.*
import net.mamoe.mirai.internal.network.components.ContactCacheService import net.mamoe.mirai.internal.network.components.ContactCacheService
import net.mamoe.mirai.internal.network.components.ContactUpdater import net.mamoe.mirai.internal.network.components.ContactUpdater
import net.mamoe.mirai.internal.network.components.ServerList
import net.mamoe.mirai.internal.network.impl.netty.HeartbeatFailedException import net.mamoe.mirai.internal.network.impl.netty.HeartbeatFailedException
import net.mamoe.mirai.internal.network.protocol.data.jce.* import net.mamoe.mirai.internal.network.protocol.data.jce.*
import net.mamoe.mirai.internal.network.protocol.data.proto.Oidb0x769 import net.mamoe.mirai.internal.network.protocol.data.proto.Oidb0x769
@ -39,6 +40,8 @@ import net.mamoe.mirai.internal.network.protocol.packet.*
import net.mamoe.mirai.internal.utils.NetworkType import net.mamoe.mirai.internal.utils.NetworkType
import net.mamoe.mirai.internal.utils._miraiContentToString import net.mamoe.mirai.internal.utils._miraiContentToString
import net.mamoe.mirai.internal.utils.io.serialization.* import net.mamoe.mirai.internal.utils.io.serialization.*
import net.mamoe.mirai.internal.utils.toIpV4Long
import net.mamoe.mirai.utils.BotConfiguration
import net.mamoe.mirai.utils.currentTimeMillis import net.mamoe.mirai.utils.currentTimeMillis
import net.mamoe.mirai.utils.encodeToString import net.mamoe.mirai.utils.encodeToString
import net.mamoe.mirai.utils.toReadPacket import net.mamoe.mirai.utils.toReadPacket
@ -158,16 +161,27 @@ internal class StatSvc {
client: QQAndroidClient, client: QQAndroidClient,
regPushReason: RegPushReason = RegPushReason.appRegister regPushReason: RegPushReason = RegPushReason.appRegister
) = impl("online", client, 1L or 2 or 4, client.onlineStatus, regPushReason) { ) = impl("online", client, 1L or 2 or 4, client.onlineStatus, regPushReason) {
client.bot.components[ContactCacheService].friendListCache?.let { friendListCache: FriendListCache -> if (client.bot.configuration.protocol == BotConfiguration.MiraiProtocol.ANDROID_PHONE) {
iLargeSeq = friendListCache.friendListSeq client.bot.components[ServerList].run {
// timeStamp = friendListCache.timeStamp uOldSSOIp = lastDisconnectedIP.toIpV4Long()
uNewSSOIp = lastConnectedIP.toIpV4Long()
}
} else {
uOldSSOIp = 0
uNewSSOIp = 0
} }
client.bot.components[ContactCacheService].friendListCache?.let { friendListCache ->
iLargeSeq = friendListCache.friendListSeq
}
// timeStamp = friendListCache.timeStamp
strVendorName = "MIUI"
strVendorOSName = "?ONEPLUS A5000_23_17"
} }
fun offline( fun offline(
client: QQAndroidClient, client: QQAndroidClient,
regPushReason: RegPushReason = RegPushReason.appRegister regPushReason: RegPushReason = RegPushReason.appRegister
) = impl("offline", client, 0, OnlineStatus.OFFLINE, regPushReason) ) = impl("offline", client, 1L or 2 or 4, OnlineStatus.OFFLINE, regPushReason)
private fun impl( private fun impl(
name: String, name: String,
@ -221,10 +235,6 @@ internal class StatSvc {
strDevName = client.device.model.encodeToString(), strDevName = client.device.model.encodeToString(),
strDevType = client.device.model.encodeToString(), strDevType = client.device.model.encodeToString(),
strOSVer = client.device.version.release.encodeToString(), strOSVer = client.device.version.release.encodeToString(),
uOldSSOIp = 0,
uNewSSOIp = 0,
strVendorName = "MIUI",
strVendorOSName = "?ONEPLUS A5000_23_17",
// register 时还需要 // register 时还需要
/* /*
var44.uNewSSOIp = field_127445; var44.uNewSSOIp = field_127445;

View File

@ -11,6 +11,9 @@ package net.mamoe.mirai.internal.utils
import net.mamoe.mirai.contact.ContactOrBot import net.mamoe.mirai.contact.ContactOrBot
import net.mamoe.mirai.message.data.* import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.utils.toInt
import net.mamoe.mirai.utils.toLongUnsigned
import java.net.Inet4Address
internal fun Int.toIpV4AddressString(): String { internal fun Int.toIpV4AddressString(): String {
@ -27,6 +30,18 @@ internal fun Int.toIpV4AddressString(): String {
} }
} }
internal fun String.toIpV4Long(): Long {
return if (isEmpty()) {
0
} else {
try {
Inet4Address.getByName(this).address.toInt().toLongUnsigned()
} catch (e: UnknownHostException) {
-2
}
}
}
internal fun String.chineseLength(upTo: Int): Int { internal fun String.chineseLength(upTo: Int): Int {
return this.sumUpTo(upTo) { return this.sumUpTo(upTo) {
when (it) { when (it) {

View File

@ -27,6 +27,18 @@ internal class ServerListTest : AbstractTest() {
assertNotNull(ServerListImpl().pollCurrent()) assertNotNull(ServerListImpl().pollCurrent())
} }
@Test
fun `last poll ip is updated when polled`() {
val instance = ServerListImpl()
val old = instance.getLastPolledIP()
assertNotNull(old)
assert(old.isEmpty())
assertNotNull(instance.pollCurrent())
val new = instance.getLastPolledIP()
assertNotNull(new)
assertNotEquals(old, new)
}
@Test @Test
fun `not empty for initial`() { fun `not empty for initial`() {
assertNotNull(ServerListImpl().pollAny()) assertNotNull(ServerListImpl().pollAny())

View File

@ -34,7 +34,7 @@ internal abstract class AbstractNettyNHTestWithSelector : AbstractRealNetworkHan
val channel = AbstractNettyNHTest.NettyNHTestChannel() val channel = AbstractNettyNHTest.NettyNHTestChannel()
val selector = TestSelector<TestNettyNH> { val selector = TestSelector<TestNettyNH> {
object : TestNettyNH(bot, createContext(), address) { object : TestNettyNH(bot, createContext(), createAddress()) {
override suspend fun createConnection(decodePipeline: PacketDecodePipeline): Channel = channel override suspend fun createConnection(decodePipeline: PacketDecodePipeline): Channel = channel
} }
} }

View File

@ -132,11 +132,13 @@ internal sealed class AbstractRealNetworkHandlerTest<H : NetworkHandler> : Abstr
return instance return instance
} }
open fun createHandler(): NetworkHandler = factory.create(createContext(), address) open fun createHandler(): NetworkHandler = factory.create(createContext(), createAddress())
open fun createContext(): NetworkHandlerContextImpl = open fun createContext(): NetworkHandlerContextImpl =
NetworkHandlerContextImpl(bot, networkLogger, bot.createNetworkLevelComponents()) NetworkHandlerContextImpl(bot, networkLogger, bot.createNetworkLevelComponents())
val address: InetSocketAddress = InetSocketAddress.createUnresolved("localhost", 123) //Use overrideComponents to avoid StackOverflowError when applying components
open fun createAddress(): InetSocketAddress =
overrideComponents[ServerList].pollAny().let { InetSocketAddress.createUnresolved(it.host, it.port) }
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Assertions // Assertions

View File

@ -0,0 +1,39 @@
package net.mamoe.mirai.internal.network.impl.netty
import net.mamoe.mirai.internal.network.components.ServerList
import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTest
import net.mamoe.mirai.internal.network.framework.TestNettyNH
import net.mamoe.mirai.internal.network.handler.NetworkHandler
import net.mamoe.mirai.internal.test.runBlockingUnit
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
internal class NettyAddressChangedTest : AbstractNettyNHTest() {
@Test
fun `test login ip changes`() = runBlockingUnit {
networkLogger.debug("before login, Assuming both ip is empty")
val lastConnectedIpOld = bot.components[ServerList].lastConnectedIP
val lastDisconnectedIpOld = bot.components[ServerList].lastDisconnectedIP
assert(lastConnectedIpOld.isEmpty()) { "Assuming lastConnectedIp is empty" }
assert(lastDisconnectedIpOld.isEmpty()) { "Assuming lastDisconnectedIp is empty" }
networkLogger.debug("Do login, Assuming lastConnectedIp is NOT empty")
bot.login()
assertState(NetworkHandler.State.OK)
assertNotEquals(
lastConnectedIpOld,
bot.components[ServerList].lastConnectedIP,
"Assuming lastConnectedIp is NOT empty"
)
networkLogger.debug("Offline the bot, Assuming lastConnectedIp is equals lastDisconnectedIp")
(bot.network as TestNettyNH).setStateClosed()
assertState(NetworkHandler.State.CLOSED)
assertEquals(
bot.components[ServerList].lastConnectedIP,
bot.components[ServerList].lastDisconnectedIP,
"Assuming lastConnectedIp is equals lastDisconnectedIp"
)
}
}

View File

@ -26,7 +26,7 @@ import kotlin.test.assertFalse
internal class NettyBotNormalLoginTest : AbstractNettyNHTest() { internal class NettyBotNormalLoginTest : AbstractNettyNHTest() {
val selector = KeepAliveNetworkHandlerSelector(selectorLogger) { val selector = KeepAliveNetworkHandlerSelector(selectorLogger) {
super.factory.create(createContext(), address) super.factory.create(createContext(), createAddress())
} }
override fun createHandler(): NetworkHandler { override fun createHandler(): NetworkHandler {