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 { dependencies {
implementation(kotlin("stdlib-jdk8")) implementation(kotlin("stdlib-jdk8"))
implementation(kotlin("reflect"))
implementation("com.viaversion:viaversion:4.0.0-21w19a-SNAPSHOT") { isTransitive = false } implementation("com.viaversion:viaversion:4.0.0-21w19a-SNAPSHOT") { isTransitive = false }
implementation("com.viaversion:viabackwards: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.PublicKey
import java.security.SecureRandom import java.security.SecureRandom
import java.util.* import java.util.*
import java.util.concurrent.TimeUnit
import javax.crypto.Cipher import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec 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) {
ch.writeAndFlush(obj).addListener(ChannelFutureListener.CLOSE) // 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) } fun ByteBuf.readByteArray(length: Int) = ByteArray(length).also { readBytes(it) }
suspend fun hasJoined(username: String, hash: String): JsonObject { 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 generate128Bits() = ByteArray(16).also { secureRandom.nextBytes(it) }
fun generateServerId() = ByteArray(13).let { fun generateServerId() = ByteArray(13).let {
secureRandom.nextBytes(it) 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 // 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.FrameCodec
import com.viaversion.aas.codec.MinecraftCodec import com.viaversion.aas.codec.MinecraftCodec
import com.viaversion.aas.handler.autoprotocol.ProtocolDetectorHandler
import com.viaversion.viaversion.connection.UserConnectionImpl import com.viaversion.viaversion.connection.UserConnectionImpl
import com.viaversion.viaversion.protocol.ProtocolPipelineImpl import com.viaversion.viaversion.protocol.ProtocolPipelineImpl
import io.netty.channel.Channel import io.netty.channel.Channel
@ -21,11 +20,6 @@ class BackEndInit(val connectionData: ConnectionData) : ChannelInitializer<Chann
.addLast("via-codec", ViaCodec(user)) .addLast("via-codec", ViaCodec(user))
.addLast("timeout", ReadTimeoutHandler(30, TimeUnit.SECONDS)) .addLast("timeout", ReadTimeoutHandler(30, TimeUnit.SECONDS))
.addLast("mc", MinecraftCodec()) .addLast("mc", MinecraftCodec())
.also {
if (connectionData.viaBackServerVer == null) {
it.addLast("protocol-detector", ProtocolDetectorHandler(connectionData))
}
}
.addLast("handler", MinecraftHandler(connectionData, frontEnd = false)) .addLast("handler", MinecraftHandler(connectionData, frontEnd = false))
} }
} }

View File

@ -17,7 +17,7 @@ class MinecraftHandler(
) : SimpleChannelInboundHandler<Packet>() { ) : SimpleChannelInboundHandler<Packet>() {
lateinit var endRemoteAddress: SocketAddress lateinit var endRemoteAddress: SocketAddress
val other: Channel? get() = if (frontEnd) data.backChannel else data.frontChannel val other: Channel? get() = if (frontEnd) data.backChannel else data.frontChannel
var msgDisconnected = false var loggedDc = false
override fun channelRead0(ctx: ChannelHandlerContext, packet: Packet) { override fun channelRead0(ctx: ChannelHandlerContext, packet: Packet) {
if (ctx.channel().isActive) { 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.config.VIAaaSConfig
import com.viaversion.aas.codec.packet.Packet import com.viaversion.aas.codec.packet.Packet
import com.viaversion.aas.readRemainingBytes
import com.viaversion.aas.send import com.viaversion.aas.send
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion 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.channel.ChannelPipeline
import io.netty.handler.proxy.Socks5ProxyHandler import io.netty.handler.proxy.Socks5ProxyHandler
import java.net.InetSocketAddress import java.net.InetSocketAddress
@ -12,11 +16,33 @@ fun forward(handler: MinecraftHandler, packet: Packet, flush: Boolean = false) {
send(handler.other!!, packet, flush) 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) { fun addSocks5(pipe: ChannelPipeline) {
val addr = VIAaaSConfig.backendSocks5ProxyAddress val addr = VIAaaSConfig.backendSocks5ProxyAddress
if (addr != null) { if (addr != null) {
pipe.addFirst(Socks5ProxyHandler(InetSocketAddress(addr, VIAaaSConfig.backendSocks5ProxyPort))) 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 package com.viaversion.aas.handler.autoprotocol
import com.google.gson.JsonParser 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.MinecraftHandler
import com.viaversion.aas.handler.state.MinecraftConnectionState import com.viaversion.aas.handler.state.MinecraftConnectionState
import com.viaversion.aas.mcLogger 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.parseProtocol
import com.viaversion.aas.util.StacklessException import com.viaversion.aas.util.StacklessException
import com.viaversion.viaversion.api.protocol.packet.State 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) { override fun handlePacket(handler: MinecraftHandler, ctx: ChannelHandlerContext, packet: Packet) {
handler.data.frontChannel.close() 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 val ver = JsonParser.parseString(packet.json).asJsonObject
.getAsJsonObject("version").get("protocol").asInt.parseProtocol() .getAsJsonObject("version")
.get("protocol").asInt.parseProtocol()
future.complete(ver) future.complete(ver)
mcLogger.info("A.D.: ${handler.endRemoteAddress} $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.childLoop
import com.viaversion.aas.codec.FrameCodec import com.viaversion.aas.codec.FrameCodec
import com.viaversion.aas.codec.MinecraftCodec 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.ConnectionData
import com.viaversion.aas.handler.MinecraftHandler import com.viaversion.aas.handler.MinecraftHandler
import com.viaversion.aas.handler.addSocks5 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.aas.send
import com.viaversion.viaversion.api.protocol.packet.State import com.viaversion.viaversion.api.protocol.packet.State
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion import com.viaversion.viaversion.api.protocol.version.ProtocolVersion
import io.netty.bootstrap.Bootstrap import io.netty.bootstrap.Bootstrap
import io.netty.channel.Channel import io.netty.channel.Channel
import io.netty.channel.ChannelFuture import io.netty.channel.ChannelFutureListener
import io.netty.channel.ChannelInitializer import io.netty.channel.ChannelInitializer
import io.netty.channel.ChannelOption import io.netty.channel.ChannelOption
import io.netty.handler.timeout.ReadTimeoutHandler import io.netty.handler.timeout.ReadTimeoutHandler
import io.netty.resolver.NoopAddressResolverGroup import io.netty.resolver.NoopAddressResolverGroup
import io.netty.util.concurrent.Future
import java.net.InetSocketAddress import java.net.InetSocketAddress
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
import java.util.concurrent.ExecutionException
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
object ProtocolDetector { object ProtocolDetector {
private val SERVER_VER = CacheBuilder.newBuilder() private val SERVER_VER = CacheBuilder.newBuilder()
.expireAfterAccess(100, TimeUnit.SECONDS) .expireAfterAccess(100, TimeUnit.SECONDS)
.build(CacheLoader.from { address: InetSocketAddress? -> .build<InetSocketAddress, CompletableFuture<ProtocolVersion>>(CacheLoader.from { address ->
val future = CompletableFuture<ProtocolVersion>() val future = CompletableFuture<ProtocolVersion>()
try { try {
val ch: ChannelFuture = Bootstrap() val ch = Bootstrap()
.group(childLoop) .group(childLoop)
.resolver(NoopAddressResolverGroup.INSTANCE) .resolver(NoopAddressResolverGroup.INSTANCE)
.channelFactory(channelSocketFactory(childLoop)) .channelFactory(channelSocketFactory(childLoop))
@ -42,7 +39,11 @@ object ProtocolDetector {
.option(ChannelOption.IP_TOS, 0x18) .option(ChannelOption.IP_TOS, 0x18)
.handler(object : ChannelInitializer<Channel>() { .handler(object : ChannelInitializer<Channel>() {
override fun initChannel(channel: 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) } channel.pipeline().also { addSocks5(it) }
.addLast("timeout", ReadTimeoutHandler(30, TimeUnit.SECONDS)) .addLast("timeout", ReadTimeoutHandler(30, TimeUnit.SECONDS))
.addLast("frame", FrameCodec()) .addLast("frame", FrameCodec())
@ -51,33 +52,24 @@ object ProtocolDetector {
} }
}) })
.connect(address!!) .connect(address!!)
ch.addListener { future1: Future<in Void> -> ch.addListener(ChannelFutureListener {
if (!future1.isSuccess) { if (!it.isSuccess) {
future.completeExceptionally(future1.cause()) future.completeExceptionally(it.cause())
} else { } else {
ch.channel().eventLoop().execute { val handshake = Handshake()
val handshake = Handshake() handshake.address = address.hostString
handshake.address = address.hostString handshake.port = address.port
handshake.port = address.port handshake.protocolId = -1
handshake.protocolId = -1 handshake.nextState = State.STATUS
handshake.nextState = State.STATUS send(ch.channel(), handshake)
send(ch.channel(), handshake) send(ch.channel(), StatusRequest(), flush = true)
send(ch.channel(), StatusRequest(), flush = true)
}
} }
} })
} catch (throwable: Throwable) { } catch (throwable: Throwable) {
future.completeExceptionally(throwable) future.completeExceptionally(throwable)
} }
future future
}) })
fun detectVersion(address: InetSocketAddress): CompletableFuture<ProtocolVersion> { fun detectVersion(address: InetSocketAddress): CompletableFuture<ProtocolVersion> = SERVER_VER[address]
return try {
SERVER_VER[address]
} catch (e: ExecutionException) {
mcLogger.warn("Protocol auto detector error: ", e)
CompletableFuture.completedFuture(null)
}
}
} }

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) { override fun disconnect(handler: MinecraftHandler, msg: String) {
super.disconnect(handler, msg)
handler.data.frontChannel.close() // Not worth logging 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.*
import com.viaversion.aas.codec.CompressionCodec import com.viaversion.aas.codec.CompressionCodec
import com.viaversion.aas.codec.CryptoCodec 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.Packet
import com.viaversion.aas.codec.packet.login.* 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.aas.util.StacklessException
import com.viaversion.viaversion.api.protocol.packet.State import com.viaversion.viaversion.api.protocol.packet.State
import io.netty.channel.Channel import io.netty.channel.Channel
@ -32,6 +32,8 @@ class LoginState : MinecraftConnectionState {
var started = false var started = false
override val state: State override val state: State
get() = State.LOGIN get() = State.LOGIN
override val logDc: Boolean
get() = true
override fun handlePacket(handler: MinecraftHandler, ctx: ChannelHandlerContext, packet: Packet) { override fun handlePacket(handler: MinecraftHandler, ctx: ChannelHandlerContext, packet: Packet) {
when (packet) { when (packet) {

View File

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

View File

@ -1,27 +1,23 @@
package com.viaversion.aas.handler.state package com.viaversion.aas.handler.state
import com.google.gson.JsonPrimitive 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.Packet
import com.viaversion.aas.codec.packet.UnknownPacket import com.viaversion.aas.codec.packet.UnknownPacket
import com.viaversion.aas.codec.packet.play.Kick import com.viaversion.aas.codec.packet.play.Kick
import com.viaversion.aas.codec.packet.play.PluginMessage 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.parseProtocol
import com.viaversion.aas.readRemainingBytes
import com.viaversion.aas.util.StacklessException import com.viaversion.aas.util.StacklessException
import com.viaversion.aas.writeFlushClose import com.viaversion.aas.writeFlushClose
import com.viaversion.viaversion.api.protocol.packet.State 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 import io.netty.channel.ChannelHandlerContext
object PlayState : MinecraftConnectionState { object PlayState : MinecraftConnectionState {
override val state: State override val state: State
get() = State.PLAY get() = State.PLAY
override val logDc: Boolean
get() = true
override fun handlePacket(handler: MinecraftHandler, ctx: ChannelHandlerContext, packet: Packet) { override fun handlePacket(handler: MinecraftHandler, ctx: ChannelHandlerContext, packet: Packet) {
when { when {
@ -35,32 +31,25 @@ object PlayState : MinecraftConnectionState {
when (pluginMessage.channel) { when (pluginMessage.channel) {
"MC|Brand", "brand", "minecraft:brand" -> { "MC|Brand", "brand", "minecraft:brand" -> {
if (!VIAaaSConfig.showBrandInfo) return 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)) { val brand = "${
pluginMessage.data = brand.toByteArray(Charsets.UTF_8) decodeBrand(
} else { pluginMessage.data,
val buf = ByteBufAllocator.DEFAULT.buffer() is17(handler)
try { )
Type.STRING.write(buf, brand) }${" (VIAaaS C: ${handler.data.frontVer?.parseProtocol()} S: ${handler.data.viaBackServerVer?.parseProtocol()})"}"
pluginMessage.data = readRemainingBytes(buf)
} finally { pluginMessage.data = encodeBrand(brand, is17(handler))
buf.release()
}
}
} }
} }
} }
override fun disconnect(handler: MinecraftHandler, msg: String) { override fun disconnect(handler: MinecraftHandler, msg: String) {
super.disconnect(handler, msg) super.disconnect(handler, msg)
writeFlushClose(handler.data.frontChannel, writeFlushClose(
Kick().also { it.msg = JsonPrimitive("[VIAaaS] §c$msg").toString() }) 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.JsonArray
import com.google.gson.JsonObject import com.google.gson.JsonObject
import com.google.gson.JsonParser 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.Packet
import com.viaversion.aas.codec.packet.UnknownPacket import com.viaversion.aas.codec.packet.UnknownPacket
import com.viaversion.aas.codec.packet.status.StatusResponse 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.parseProtocol
import com.viaversion.aas.util.StacklessException import com.viaversion.aas.util.StacklessException
import com.viaversion.aas.writeFlushClose import com.viaversion.aas.writeFlushClose
@ -20,6 +20,8 @@ import java.util.*
object StatusState : MinecraftConnectionState { object StatusState : MinecraftConnectionState {
override val state: State override val state: State
get() = State.STATUS get() = State.STATUS
override val logDc: Boolean
get() = true
override fun handlePacket(handler: MinecraftHandler, ctx: ChannelHandlerContext, packet: Packet) { override fun handlePacket(handler: MinecraftHandler, ctx: ChannelHandlerContext, packet: Packet) {
if (packet is UnknownPacket) throw StacklessException("Invalid 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("id", UUID.nameUUIDFromBytes("VIAaaS".toByteArray(Charsets.UTF_8)).toString())
it.addProperty( it.addProperty(
"name", "name",
"§9VIAaaS§r C: §7${handler.data.frontVer!!.parseProtocol()}§r S: §7${ "§9VIAaaS§r C: §7${handler.data.frontVer?.parseProtocol()}§r S: §7${
handler.data.viaBackServerVer!!.parseProtocol() handler.data.viaBackServerVer?.parseProtocol()
}" }"
) )
}) })

View File

@ -2,29 +2,38 @@ package com.viaversion.aas.handler.state
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import com.viaversion.aas.* import com.viaversion.aas.*
import com.viaversion.aas.codec.packet.handshake.Handshake
import com.viaversion.aas.config.VIAaaSConfig import com.viaversion.aas.config.VIAaaSConfig
import com.viaversion.aas.handler.BackEndInit import com.viaversion.aas.handler.BackEndInit
import com.viaversion.aas.handler.MinecraftHandler import com.viaversion.aas.handler.MinecraftHandler
import com.viaversion.aas.handler.autoprotocol.ProtocolDetector
import com.viaversion.aas.handler.forward import com.viaversion.aas.handler.forward
import com.viaversion.aas.codec.packet.handshake.Handshake
import com.viaversion.aas.util.StacklessException import com.viaversion.aas.util.StacklessException
import com.viaversion.viaversion.api.protocol.packet.State import com.viaversion.viaversion.api.protocol.packet.State
import io.netty.bootstrap.Bootstrap import io.netty.bootstrap.Bootstrap
import io.netty.channel.ChannelFuture import io.netty.channel.Channel
import io.netty.channel.ChannelFutureListener import io.netty.channel.ChannelFutureListener
import io.netty.channel.ChannelOption import io.netty.channel.ChannelOption
import io.netty.channel.socket.SocketChannel import io.netty.channel.socket.SocketChannel
import io.netty.resolver.NoopAddressResolverGroup import io.netty.resolver.NoopAddressResolverGroup
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.future.await
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withTimeoutOrNull
import java.net.Inet4Address import java.net.Inet4Address
import java.net.InetAddress import java.net.InetAddress
import java.net.InetSocketAddress 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() val loop = handler.data.frontChannel.eventLoop()
return Bootstrap() Bootstrap()
.handler(BackEndInit(handler.data)) .handler(BackEndInit(handler.data))
.channelFactory(channelSocketFactory(loop.parent())) .channelFactory(channelSocketFactory(loop.parent()))
.group(loop) .group(loop)
@ -35,58 +44,85 @@ private fun createBackChannel(handler: MinecraftHandler, socketAddr: InetSocketA
.resolver(NoopAddressResolverGroup.INSTANCE) .resolver(NoopAddressResolverGroup.INSTANCE)
.connect(socketAddr) .connect(socketAddr)
.addListener(ChannelFutureListener { .addListener(ChannelFutureListener {
if (it.isSuccess) { try {
if (!it.isSuccess) throw it.cause()
mcLogger.info("+ ${handler.endRemoteAddress} -> $socketAddr") mcLogger.info("+ ${handler.endRemoteAddress} -> $socketAddr")
handler.data.backChannel = it.channel() as SocketChannel handler.data.backChannel = it.channel() as SocketChannel
val packet = Handshake() GlobalScope.launch {
packet.nextState = state if (handler.data.viaBackServerVer == null) {
packet.protocolId = handler.data.frontVer!! try {
packet.address = socketAddr.hostString val detectedProtocol = withTimeoutOrNull(10_000) {
packet.port = socketAddr.port ProtocolDetector.detectVersion(socketAddr).await()
}
forward(handler, packet, true) 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)
}
}
handler.data.frontChannel.setAutoRead(true) val packet = Handshake()
packet.nextState = state
packet.protocolId = handler.data.frontVer!!
packet.address = socketAddr.hostString
packet.port = socketAddr.port
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, handler: MinecraftHandler,
iterator: Iterator<InetSocketAddress>, addresses: Iterable<InetSocketAddress>,
state: State, state: State,
success: () -> Unit,
) { ) {
val fail = { e: Throwable -> var latestException: Exception? = null
if (!iterator.hasNext()) { for (socketAddr in addresses) {
// We're in the event loop try {
handler.disconnect("Couldn't connect: $e") if ((socketAddr.address != null && checkLocalAddress(socketAddr.address))
} else if (handler.data.frontChannel.isActive) { || matchesAddress(socketAddr, VIAaaSConfig.blockedBackAddresses)
tryBackAddress(handler, iterator, state, success) || !matchesAddress(socketAddr, VIAaaSConfig.allowedBackAddresses)
) {
throw StacklessException("Not allowed")
}
createBackChannel(handler, socketAddr, state).await()
return // Finally it worked!
} catch (e: Exception) {
latestException = e
} }
} }
try {
val socketAddr = iterator.next()
if ((socketAddr.address != null && checkLocalAddress(socketAddr.address)) throw StacklessException("Latest attempt failed", latestException)
|| matchesAddress(socketAddr, VIAaaSConfig.blockedBackAddresses) }
|| !matchesAddress(socketAddr, VIAaaSConfig.allowedBackAddresses)
) {
throw StacklessException("Not allowed")
}
createBackChannel(handler, socketAddr, state).addListener { private fun resolveBackendAddresses(hostAndPort: HostAndPort): List<InetSocketAddress> {
if (it.isSuccess) { val srvResolved = resolveSrv(hostAndPort)
success()
} else { val removedEndDot = srvResolved.host.replace(Regex("\\.$"), "")
fail(it.cause())
} return when {
} removedEndDot.endsWith(".onion", ignoreCase = true) ->
} catch (e: Exception) { listOf(InetSocketAddress.createUnresolved(removedEndDot, srvResolved.port))
handler.data.frontChannel.eventLoop().submit { else -> InetAddress.getAllByName(srvResolved.host)
fail(e) .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) }
} }
} }
@ -94,23 +130,12 @@ fun connectBack(handler: MinecraftHandler, address: String, port: Int, state: St
handler.data.frontChannel.setAutoRead(false) handler.data.frontChannel.setAutoRead(false)
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
try { 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 { tryBackAddresses(handler, addresses, state)
removedEndDot.endsWith(".onion", ignoreCase = true) -> success()
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)
} catch (e: Exception) { } catch (e: Exception) {
handler.data.frontChannel.eventLoop().submit { handler.data.frontChannel.eventLoop().submit {
handler.disconnect("Couldn't connect: $e") handler.disconnect("Couldn't connect: $e")