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