mirror of
https://github.com/ViaVersion/VIAaaS.git
synced 2024-11-25 12:35:36 +01:00
cleanup, fix 1.7 kick
This commit is contained in:
parent
23c7a1672f
commit
1efaab98c2
@ -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 }
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
@ -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")
|
||||
}
|
||||
|
@ -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]
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}"
|
||||
)
|
||||
})
|
||||
|
@ -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")
|
||||
|
Loading…
Reference in New Issue
Block a user