cleanup, fix 1.7 kick

This commit is contained in:
creeper123123321 2021-05-16 20:58:25 -03:00
parent 23c7a1672f
commit 1efaab98c2
14 changed files with 182 additions and 224 deletions

View File

@ -56,6 +56,7 @@ repositories {
dependencies {
implementation(kotlin("stdlib-jdk8"))
implementation(kotlin("reflect"))
implementation("com.viaversion:viaversion:4.0.0-21w19a-SNAPSHOT") { isTransitive = false }
implementation("com.viaversion:viabackwards:4.0.0-21w19a-SNAPSHOT") { isTransitive = false }

View File

@ -24,6 +24,7 @@ import java.security.PrivateKey
import java.security.PublicKey
import java.security.SecureRandom
import java.util.*
import java.util.concurrent.TimeUnit
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
@ -150,11 +151,20 @@ fun send(ch: Channel, obj: Any, flush: Boolean = false) {
}
}
fun writeFlushClose(ch: Channel, obj: Any) {
fun writeFlushClose(ch: Channel, obj: Any, delay: Boolean = false) {
// https://github.com/VelocityPowered/Velocity/blob/dev/1.1.0/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java#L252
ch.setAutoRead(false)
val action = {
ch.writeAndFlush(obj).addListener(ChannelFutureListener.CLOSE)
}
if (delay) {
ch.eventLoop().schedule(action, 250, TimeUnit.MILLISECONDS)
} else {
action()
}
}
fun readRemainingBytes(byteBuf: ByteBuf) = Type.REMAINING_BYTES.read(byteBuf)
fun readRemainingBytes(byteBuf: ByteBuf) = Type.REMAINING_BYTES.read(byteBuf)!!
fun ByteBuf.readByteArray(length: Int) = ByteArray(length).also { readBytes(it) }
suspend fun hasJoined(username: String, hash: String): JsonObject {
@ -168,7 +178,7 @@ suspend fun hasJoined(username: String, hash: String): JsonObject {
fun generate128Bits() = ByteArray(16).also { secureRandom.nextBytes(it) }
fun generateServerId() = ByteArray(13).let {
secureRandom.nextBytes(it)
Base64.getEncoder().withoutPadding().encodeToString(it)
Base64.getEncoder().withoutPadding().encodeToString(it)!!
// https://developer.mozilla.org/en-US/docs/Glossary/Base64 133% of original
}

View File

@ -2,7 +2,6 @@ package com.viaversion.aas.handler
import com.viaversion.aas.codec.FrameCodec
import com.viaversion.aas.codec.MinecraftCodec
import com.viaversion.aas.handler.autoprotocol.ProtocolDetectorHandler
import com.viaversion.viaversion.connection.UserConnectionImpl
import com.viaversion.viaversion.protocol.ProtocolPipelineImpl
import io.netty.channel.Channel
@ -21,11 +20,6 @@ class BackEndInit(val connectionData: ConnectionData) : ChannelInitializer<Chann
.addLast("via-codec", ViaCodec(user))
.addLast("timeout", ReadTimeoutHandler(30, TimeUnit.SECONDS))
.addLast("mc", MinecraftCodec())
.also {
if (connectionData.viaBackServerVer == null) {
it.addLast("protocol-detector", ProtocolDetectorHandler(connectionData))
}
}
.addLast("handler", MinecraftHandler(connectionData, frontEnd = false))
}
}

View File

@ -17,7 +17,7 @@ class MinecraftHandler(
) : SimpleChannelInboundHandler<Packet>() {
lateinit var endRemoteAddress: SocketAddress
val other: Channel? get() = if (frontEnd) data.backChannel else data.frontChannel
var msgDisconnected = false
var loggedDc = false
override fun channelRead0(ctx: ChannelHandlerContext, packet: Packet) {
if (ctx.channel().isActive) {

View File

@ -2,8 +2,12 @@ package com.viaversion.aas.handler
import com.viaversion.aas.config.VIAaaSConfig
import com.viaversion.aas.codec.packet.Packet
import com.viaversion.aas.readRemainingBytes
import com.viaversion.aas.send
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion
import com.viaversion.viaversion.api.type.Type
import io.netty.buffer.ByteBufAllocator
import io.netty.buffer.Unpooled
import io.netty.channel.ChannelPipeline
import io.netty.handler.proxy.Socks5ProxyHandler
import java.net.InetSocketAddress
@ -12,7 +16,7 @@ fun forward(handler: MinecraftHandler, packet: Packet, flush: Boolean = false) {
send(handler.other!!, packet, flush)
}
fun is1_7(handler: MinecraftHandler) = handler.data.frontVer!! <= ProtocolVersion.v1_7_6.version
fun is17(handler: MinecraftHandler) = handler.data.frontVer!! <= ProtocolVersion.v1_7_6.version
fun addSocks5(pipe: ChannelPipeline) {
val addr = VIAaaSConfig.backendSocks5ProxyAddress
@ -20,3 +24,25 @@ fun addSocks5(pipe: ChannelPipeline) {
pipe.addFirst(Socks5ProxyHandler(InetSocketAddress(addr, VIAaaSConfig.backendSocks5ProxyPort)))
}
}
fun decodeBrand(data: ByteArray, is17: Boolean): String {
return if (is17) {
String(data, Charsets.UTF_8)
} else {
Type.STRING.read(Unpooled.wrappedBuffer(data))
}
}
fun encodeBrand(string: String, is17: Boolean): ByteArray {
return if (is17) {
string.toByteArray(Charsets.UTF_8)
} else {
val buf = ByteBufAllocator.DEFAULT.buffer()
try {
Type.STRING.write(buf, string)
readRemainingBytes(buf)
} finally {
buf.release()
}
}
}

View File

@ -1,11 +1,11 @@
package com.viaversion.aas.handler.autoprotocol
import com.google.gson.JsonParser
import com.viaversion.aas.codec.packet.Packet
import com.viaversion.aas.codec.packet.status.StatusResponse
import com.viaversion.aas.handler.MinecraftHandler
import com.viaversion.aas.handler.state.MinecraftConnectionState
import com.viaversion.aas.mcLogger
import com.viaversion.aas.codec.packet.Packet
import com.viaversion.aas.codec.packet.status.StatusResponse
import com.viaversion.aas.parseProtocol
import com.viaversion.aas.util.StacklessException
import com.viaversion.viaversion.api.protocol.packet.State
@ -19,9 +19,10 @@ class ProtocolDetectionState(val future: CompletableFuture<ProtocolVersion>) : M
override fun handlePacket(handler: MinecraftHandler, ctx: ChannelHandlerContext, packet: Packet) {
handler.data.frontChannel.close()
if (packet !is StatusResponse) throw StacklessException("unexpected packet")
if (packet !is StatusResponse) throw StacklessException("Unexpected packet")
val ver = JsonParser.parseString(packet.json).asJsonObject
.getAsJsonObject("version").get("protocol").asInt.parseProtocol()
.getAsJsonObject("version")
.get("protocol").asInt.parseProtocol()
future.complete(ver)
mcLogger.info("A.D.: ${handler.endRemoteAddress} $ver")
}

View File

@ -6,35 +6,32 @@ import com.viaversion.aas.channelSocketFactory
import com.viaversion.aas.childLoop
import com.viaversion.aas.codec.FrameCodec
import com.viaversion.aas.codec.MinecraftCodec
import com.viaversion.aas.codec.packet.handshake.Handshake
import com.viaversion.aas.codec.packet.status.StatusRequest
import com.viaversion.aas.handler.ConnectionData
import com.viaversion.aas.handler.MinecraftHandler
import com.viaversion.aas.handler.addSocks5
import com.viaversion.aas.mcLogger
import com.viaversion.aas.codec.packet.handshake.Handshake
import com.viaversion.aas.codec.packet.status.StatusRequest
import com.viaversion.aas.send
import com.viaversion.viaversion.api.protocol.packet.State
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion
import io.netty.bootstrap.Bootstrap
import io.netty.channel.Channel
import io.netty.channel.ChannelFuture
import io.netty.channel.ChannelFutureListener
import io.netty.channel.ChannelInitializer
import io.netty.channel.ChannelOption
import io.netty.handler.timeout.ReadTimeoutHandler
import io.netty.resolver.NoopAddressResolverGroup
import io.netty.util.concurrent.Future
import java.net.InetSocketAddress
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ExecutionException
import java.util.concurrent.TimeUnit
object ProtocolDetector {
private val SERVER_VER = CacheBuilder.newBuilder()
.expireAfterAccess(100, TimeUnit.SECONDS)
.build(CacheLoader.from { address: InetSocketAddress? ->
.build<InetSocketAddress, CompletableFuture<ProtocolVersion>>(CacheLoader.from { address ->
val future = CompletableFuture<ProtocolVersion>()
try {
val ch: ChannelFuture = Bootstrap()
val ch = Bootstrap()
.group(childLoop)
.resolver(NoopAddressResolverGroup.INSTANCE)
.channelFactory(channelSocketFactory(childLoop))
@ -42,7 +39,11 @@ object ProtocolDetector {
.option(ChannelOption.IP_TOS, 0x18)
.handler(object : ChannelInitializer<Channel>() {
override fun initChannel(channel: Channel) {
val data = ConnectionData(channel, state = ProtocolDetectionState(future), frontVer = -1)
val data = ConnectionData(
channel,
state = ProtocolDetectionState(future),
frontVer = -1
)
channel.pipeline().also { addSocks5(it) }
.addLast("timeout", ReadTimeoutHandler(30, TimeUnit.SECONDS))
.addLast("frame", FrameCodec())
@ -51,11 +52,10 @@ object ProtocolDetector {
}
})
.connect(address!!)
ch.addListener { future1: Future<in Void> ->
if (!future1.isSuccess) {
future.completeExceptionally(future1.cause())
ch.addListener(ChannelFutureListener {
if (!it.isSuccess) {
future.completeExceptionally(it.cause())
} else {
ch.channel().eventLoop().execute {
val handshake = Handshake()
handshake.address = address.hostString
handshake.port = address.port
@ -64,20 +64,12 @@ object ProtocolDetector {
send(ch.channel(), handshake)
send(ch.channel(), StatusRequest(), flush = true)
}
}
}
})
} catch (throwable: Throwable) {
future.completeExceptionally(throwable)
}
future
})
fun detectVersion(address: InetSocketAddress): CompletableFuture<ProtocolVersion> {
return try {
SERVER_VER[address]
} catch (e: ExecutionException) {
mcLogger.warn("Protocol auto detector error: ", e)
CompletableFuture.completedFuture(null)
}
}
fun detectVersion(address: InetSocketAddress): CompletableFuture<ProtocolVersion> = SERVER_VER[address]
}

View File

@ -1,86 +0,0 @@
package com.viaversion.aas.handler.autoprotocol
import com.viaversion.aas.handler.ConnectionData
import com.viaversion.aas.handler.MinecraftHandler
import com.viaversion.aas.mcLogger
import io.netty.channel.ChannelDuplexHandler
import io.netty.channel.ChannelHandlerContext
import io.netty.channel.ChannelPromise
import java.net.InetSocketAddress
import java.util.concurrent.TimeUnit
import java.util.function.Consumer
// https://github.com/ViaVersion/ViaFabric/blob/mc-1.16/src/main/java/com/github/creeper123123321/viafabric/handler/clientside/ProtocolDetectionHandler.java
class ProtocolDetectorHandler(val connectionData: ConnectionData) : ChannelDuplexHandler() {
private val queuedMessages = ArrayDeque<Pair<Any, ChannelPromise>>()
private var hold = true
private var pendentFlush = false
@Throws(Exception::class)
override fun channelActive(ctx: ChannelHandlerContext) {
super.channelActive(ctx)
val handler = ctx.channel().pipeline().get(MinecraftHandler::class.java)
val address = handler.endRemoteAddress
if (address is InetSocketAddress) {
val timeoutRun = ctx.executor().schedule({
mcLogger.warn("Timeout protocol A.D. $address")
ctx.pipeline().remove(this)
}, 10, TimeUnit.SECONDS)
ProtocolDetector.detectVersion(address).whenComplete { protocol, _ ->
if (protocol != null && protocol.version != -1) {
connectionData.viaBackServerVer = protocol.version
} else {
connectionData.viaBackServerVer = -1 // fallback
}
ctx.pipeline().remove(this)
timeoutRun.cancel(false)
}
// Let's cache it before we need it
}
}
@Throws(Exception::class)
override fun write(ctx: ChannelHandlerContext, msg: Any, promise: ChannelPromise) {
if (!hold) {
drainQueue(ctx)
super.write(ctx, msg, promise)
} else {
queuedMessages.add(Pair(msg, promise))
}
}
@Throws(Exception::class)
override fun flush(ctx: ChannelHandlerContext) {
if (!hold) {
drainQueue(ctx)
super.flush(ctx)
} else {
pendentFlush = true
}
}
@Throws(Exception::class)
override fun channelInactive(ctx: ChannelHandlerContext) {
drainQueue(ctx)
super.channelInactive(ctx)
}
private fun drainQueue(ctx: ChannelHandlerContext) {
queuedMessages.forEach(Consumer {
ctx.write(
it.first,
it.second
)
})
queuedMessages.clear()
if (pendentFlush) ctx.flush()
pendentFlush = false
}
@Throws(Exception::class)
override fun handlerRemoved(ctx: ChannelHandlerContext) {
drainQueue(ctx)
super.handlerRemoved(ctx)
}
}

View File

@ -84,6 +84,7 @@ class HandshakeState : MinecraftConnectionState {
}
override fun disconnect(handler: MinecraftHandler, msg: String) {
super.disconnect(handler, msg)
handler.data.frontChannel.close() // Not worth logging
}

View File

@ -5,10 +5,10 @@ import com.google.gson.Gson
import com.viaversion.aas.*
import com.viaversion.aas.codec.CompressionCodec
import com.viaversion.aas.codec.CryptoCodec
import com.viaversion.aas.handler.MinecraftHandler
import com.viaversion.aas.handler.forward
import com.viaversion.aas.codec.packet.Packet
import com.viaversion.aas.codec.packet.login.*
import com.viaversion.aas.handler.MinecraftHandler
import com.viaversion.aas.handler.forward
import com.viaversion.aas.util.StacklessException
import com.viaversion.viaversion.api.protocol.packet.State
import io.netty.channel.Channel
@ -32,6 +32,8 @@ class LoginState : MinecraftConnectionState {
var started = false
override val state: State
get() = State.LOGIN
override val logDc: Boolean
get() = true
override fun handlePacket(handler: MinecraftHandler, ctx: ChannelHandlerContext, packet: Packet) {
when (packet) {

View File

@ -12,10 +12,11 @@ interface MinecraftConnectionState {
handler: MinecraftHandler, ctx: ChannelHandlerContext,
packet: Packet
)
val logDc: Boolean get() = false
fun disconnect(handler: MinecraftHandler, msg: String) {
if (!handler.msgDisconnected) {
handler.msgDisconnected = true
if (logDc && !handler.loggedDc) {
handler.loggedDc = true
mcLogger.info("DC ${handler.endRemoteAddress}: $msg")
}
}

View File

@ -1,27 +1,23 @@
package com.viaversion.aas.handler.state
import com.google.gson.JsonPrimitive
import com.viaversion.aas.config.VIAaaSConfig
import com.viaversion.aas.handler.MinecraftHandler
import com.viaversion.aas.handler.forward
import com.viaversion.aas.handler.is1_7
import com.viaversion.aas.codec.packet.Packet
import com.viaversion.aas.codec.packet.UnknownPacket
import com.viaversion.aas.codec.packet.play.Kick
import com.viaversion.aas.codec.packet.play.PluginMessage
import com.viaversion.aas.config.VIAaaSConfig
import com.viaversion.aas.handler.*
import com.viaversion.aas.parseProtocol
import com.viaversion.aas.readRemainingBytes
import com.viaversion.aas.util.StacklessException
import com.viaversion.aas.writeFlushClose
import com.viaversion.viaversion.api.protocol.packet.State
import com.viaversion.viaversion.api.type.Type
import io.netty.buffer.ByteBufAllocator
import io.netty.buffer.Unpooled
import io.netty.channel.ChannelHandlerContext
object PlayState : MinecraftConnectionState {
override val state: State
get() = State.PLAY
override val logDc: Boolean
get() = true
override fun handlePacket(handler: MinecraftHandler, ctx: ChannelHandlerContext, packet: Packet) {
when {
@ -35,32 +31,25 @@ object PlayState : MinecraftConnectionState {
when (pluginMessage.channel) {
"MC|Brand", "brand", "minecraft:brand" -> {
if (!VIAaaSConfig.showBrandInfo) return
val brand = if (is1_7(handler)) {
String(pluginMessage.data, Charsets.UTF_8)
} else {
Type.STRING.read(Unpooled.wrappedBuffer(pluginMessage.data))
} + " (VIAaaS C: ${handler.data.frontVer!!.parseProtocol()} S: ${
handler.data.viaBackServerVer!!.parseProtocol()
})"
if (is1_7(handler)) {
pluginMessage.data = brand.toByteArray(Charsets.UTF_8)
} else {
val buf = ByteBufAllocator.DEFAULT.buffer()
try {
Type.STRING.write(buf, brand)
pluginMessage.data = readRemainingBytes(buf)
} finally {
buf.release()
}
}
val brand = "${
decodeBrand(
pluginMessage.data,
is17(handler)
)
}${" (VIAaaS C: ${handler.data.frontVer?.parseProtocol()} S: ${handler.data.viaBackServerVer?.parseProtocol()})"}"
pluginMessage.data = encodeBrand(brand, is17(handler))
}
}
}
override fun disconnect(handler: MinecraftHandler, msg: String) {
super.disconnect(handler, msg)
writeFlushClose(handler.data.frontChannel,
Kick().also { it.msg = JsonPrimitive("[VIAaaS] §c$msg").toString() })
writeFlushClose(
handler.data.frontChannel,
Kick().also { it.msg = JsonPrimitive("[VIAaaS] §c$msg").toString() },
delay = is17(handler)
)
}
}

View File

@ -3,12 +3,12 @@ package com.viaversion.aas.handler.state
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import com.viaversion.aas.config.VIAaaSConfig
import com.viaversion.aas.handler.MinecraftHandler
import com.viaversion.aas.handler.forward
import com.viaversion.aas.codec.packet.Packet
import com.viaversion.aas.codec.packet.UnknownPacket
import com.viaversion.aas.codec.packet.status.StatusResponse
import com.viaversion.aas.config.VIAaaSConfig
import com.viaversion.aas.handler.MinecraftHandler
import com.viaversion.aas.handler.forward
import com.viaversion.aas.parseProtocol
import com.viaversion.aas.util.StacklessException
import com.viaversion.aas.writeFlushClose
@ -20,6 +20,8 @@ import java.util.*
object StatusState : MinecraftConnectionState {
override val state: State
get() = State.STATUS
override val logDc: Boolean
get() = true
override fun handlePacket(handler: MinecraftHandler, ctx: ChannelHandlerContext, packet: Packet) {
if (packet is UnknownPacket) throw StacklessException("Invalid packet")
@ -38,8 +40,8 @@ object StatusState : MinecraftConnectionState {
it.addProperty("id", UUID.nameUUIDFromBytes("VIAaaS".toByteArray(Charsets.UTF_8)).toString())
it.addProperty(
"name",
"§9VIAaaS§r C: §7${handler.data.frontVer!!.parseProtocol()}§r S: §7${
handler.data.viaBackServerVer!!.parseProtocol()
"§9VIAaaS§r C: §7${handler.data.frontVer?.parseProtocol()}§r S: §7${
handler.data.viaBackServerVer?.parseProtocol()
}"
)
})

View File

@ -2,29 +2,38 @@ package com.viaversion.aas.handler.state
import com.google.common.net.HostAndPort
import com.viaversion.aas.*
import com.viaversion.aas.codec.packet.handshake.Handshake
import com.viaversion.aas.config.VIAaaSConfig
import com.viaversion.aas.handler.BackEndInit
import com.viaversion.aas.handler.MinecraftHandler
import com.viaversion.aas.handler.autoprotocol.ProtocolDetector
import com.viaversion.aas.handler.forward
import com.viaversion.aas.codec.packet.handshake.Handshake
import com.viaversion.aas.util.StacklessException
import com.viaversion.viaversion.api.protocol.packet.State
import io.netty.bootstrap.Bootstrap
import io.netty.channel.ChannelFuture
import io.netty.channel.Channel
import io.netty.channel.ChannelFutureListener
import io.netty.channel.ChannelOption
import io.netty.channel.socket.SocketChannel
import io.netty.resolver.NoopAddressResolverGroup
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.future.await
import kotlinx.coroutines.launch
import kotlinx.coroutines.withTimeoutOrNull
import java.net.Inet4Address
import java.net.InetAddress
import java.net.InetSocketAddress
import java.util.concurrent.CompletableFuture
private fun createBackChannel(handler: MinecraftHandler, socketAddr: InetSocketAddress, state: State): ChannelFuture {
private fun createBackChannel(
handler: MinecraftHandler,
socketAddr: InetSocketAddress,
state: State
): CompletableFuture<Channel> {
val future = CompletableFuture<Channel>()
val loop = handler.data.frontChannel.eventLoop()
return Bootstrap()
Bootstrap()
.handler(BackEndInit(handler.data))
.channelFactory(channelSocketFactory(loop.parent()))
.group(loop)
@ -35,10 +44,30 @@ private fun createBackChannel(handler: MinecraftHandler, socketAddr: InetSocketA
.resolver(NoopAddressResolverGroup.INSTANCE)
.connect(socketAddr)
.addListener(ChannelFutureListener {
if (it.isSuccess) {
try {
if (!it.isSuccess) throw it.cause()
mcLogger.info("+ ${handler.endRemoteAddress} -> $socketAddr")
handler.data.backChannel = it.channel() as SocketChannel
GlobalScope.launch {
if (handler.data.viaBackServerVer == null) {
try {
val detectedProtocol = withTimeoutOrNull(10_000) {
ProtocolDetector.detectVersion(socketAddr).await()
}
if (detectedProtocol != null && detectedProtocol.version != -1) {
handler.data.viaBackServerVer = detectedProtocol.version
} else {
handler.data.viaBackServerVer = -1 // fallback
}
} catch (e: Exception) {
mcLogger.warn("Failed to auto-detect version for $socketAddr: $e")
mcLogger.debug("Stacktrace: ", e)
}
}
val packet = Handshake()
packet.nextState = state
packet.protocolId = handler.data.frontVer!!
@ -48,27 +77,23 @@ private fun createBackChannel(handler: MinecraftHandler, socketAddr: InetSocketA
forward(handler, packet, true)
handler.data.frontChannel.setAutoRead(true)
future.complete(it.channel())
}
} catch (e: Exception) {
future.completeExceptionally(it.cause())
}
})
return future
}
private fun tryBackAddress(
private suspend fun tryBackAddresses(
handler: MinecraftHandler,
iterator: Iterator<InetSocketAddress>,
addresses: Iterable<InetSocketAddress>,
state: State,
success: () -> Unit,
) {
val fail = { e: Throwable ->
if (!iterator.hasNext()) {
// We're in the event loop
handler.disconnect("Couldn't connect: $e")
} else if (handler.data.frontChannel.isActive) {
tryBackAddress(handler, iterator, state, success)
}
}
var latestException: Exception? = null
for (socketAddr in addresses) {
try {
val socketAddr = iterator.next()
if ((socketAddr.address != null && checkLocalAddress(socketAddr.address))
|| matchesAddress(socketAddr, VIAaaSConfig.blockedBackAddresses)
|| !matchesAddress(socketAddr, VIAaaSConfig.allowedBackAddresses)
@ -76,41 +101,41 @@ private fun tryBackAddress(
throw StacklessException("Not allowed")
}
createBackChannel(handler, socketAddr, state).addListener {
if (it.isSuccess) {
success()
} else {
fail(it.cause())
}
}
createBackChannel(handler, socketAddr, state).await()
return // Finally it worked!
} catch (e: Exception) {
handler.data.frontChannel.eventLoop().submit {
fail(e)
latestException = e
}
}
throw StacklessException("Latest attempt failed", latestException)
}
private fun resolveBackendAddresses(hostAndPort: HostAndPort): List<InetSocketAddress> {
val srvResolved = resolveSrv(hostAndPort)
val removedEndDot = srvResolved.host.replace(Regex("\\.$"), "")
return when {
removedEndDot.endsWith(".onion", ignoreCase = true) ->
listOf(InetSocketAddress.createUnresolved(removedEndDot, srvResolved.port))
else -> InetAddress.getAllByName(srvResolved.host)
.groupBy { it is Inet4Address }
.toSortedMap() // I'm sorry, IPv4, but my true love is IPv6... We can still be friends though...
.map { InetSocketAddress(it.value.random(), srvResolved.port) }
}
}
fun connectBack(handler: MinecraftHandler, address: String, port: Int, state: State, success: () -> Unit) {
handler.data.frontChannel.setAutoRead(false)
GlobalScope.launch(Dispatchers.IO) {
try {
val srvResolved = resolveSrv(HostAndPort.fromParts(address, port))
val addresses = resolveBackendAddresses(HostAndPort.fromParts(address, port))
val removedEndDot = srvResolved.host.replace(Regex("\\.$"), "")
if (addresses.isEmpty()) throw StacklessException("Hostname has no IP address")
val iterator = when {
removedEndDot.endsWith(".onion", ignoreCase = true) ->
listOf(InetSocketAddress.createUnresolved(removedEndDot, srvResolved.port))
.iterator()
else -> InetAddress.getAllByName(srvResolved.host)
.groupBy { it is Inet4Address }
.toSortedMap() // I'm sorry, IPv4, but my true love is IPv6... We can still be friends though...
.map { InetSocketAddress(it.value.random(), srvResolved.port) }
.iterator()
}
if (!iterator.hasNext()) throw StacklessException("Hostname has no IP address")
tryBackAddress(handler, iterator, state, success)
tryBackAddresses(handler, addresses, state)
success()
} catch (e: Exception) {
handler.data.frontChannel.eventLoop().submit {
handler.disconnect("Couldn't connect: $e")