mirror of
https://github.com/ViaVersion/VIAaaS.git
synced 2024-11-28 13:05:16 +01:00
backend injection, fixed 'remaining bytes!!!', some cleanup, fix listening in browser
This commit is contained in:
parent
b69bf7cb1f
commit
ffb8980fb2
@ -10,6 +10,7 @@ import io.netty.handler.codec.MessageToMessageCodec
|
|||||||
import io.netty.handler.flow.FlowControlHandler
|
import io.netty.handler.flow.FlowControlHandler
|
||||||
import io.netty.handler.timeout.ReadTimeoutHandler
|
import io.netty.handler.timeout.ReadTimeoutHandler
|
||||||
import us.myles.ViaVersion.api.data.UserConnection
|
import us.myles.ViaVersion.api.data.UserConnection
|
||||||
|
import us.myles.ViaVersion.api.protocol.ProtocolPipeline
|
||||||
import us.myles.ViaVersion.api.type.Type
|
import us.myles.ViaVersion.api.type.Type
|
||||||
import us.myles.ViaVersion.exception.CancelDecoderException
|
import us.myles.ViaVersion.exception.CancelDecoderException
|
||||||
import us.myles.ViaVersion.exception.CancelEncoderException
|
import us.myles.ViaVersion.exception.CancelEncoderException
|
||||||
@ -21,15 +22,27 @@ import javax.crypto.Cipher
|
|||||||
|
|
||||||
object ChannelInit : ChannelInitializer<Channel>() {
|
object ChannelInit : ChannelInitializer<Channel>() {
|
||||||
override fun initChannel(ch: Channel) {
|
override fun initChannel(ch: Channel) {
|
||||||
val user = UserConnection(ch)
|
|
||||||
CloudPipeline(user)
|
|
||||||
ch.pipeline().addLast("timeout", ReadTimeoutHandler(30, TimeUnit.SECONDS))
|
ch.pipeline().addLast("timeout", ReadTimeoutHandler(30, TimeUnit.SECONDS))
|
||||||
// "crypto"
|
// "crypto"
|
||||||
.addLast("frame", FrameCodec())
|
.addLast("frame", FrameCodec())
|
||||||
// "compress" / dummy "decompress"
|
// "compress" / dummy "decompress"
|
||||||
.addLast("flow-handler", FlowControlHandler())
|
.addLast("flow-handler", FlowControlHandler())
|
||||||
|
.addLast("handler", CloudMinecraftHandler(ConnectionData(
|
||||||
|
frontChannel = ch,
|
||||||
|
), other = null, frontEnd = true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BackendInit(val connectionData: ConnectionData) : ChannelInitializer<Channel>() {
|
||||||
|
override fun initChannel(ch: Channel) {
|
||||||
|
val user = UserConnection(ch, true)
|
||||||
|
ProtocolPipeline(user)
|
||||||
|
ch.pipeline().addLast("timeout", ReadTimeoutHandler(30, TimeUnit.SECONDS))
|
||||||
|
// "crypto"
|
||||||
|
.addLast("frame", FrameCodec())
|
||||||
|
// compress
|
||||||
.addLast("via-codec", CloudViaCodec(user))
|
.addLast("via-codec", CloudViaCodec(user))
|
||||||
.addLast("handler", CloudMinecraftHandler(user, null, frontEnd = true))
|
.addLast("handler", CloudMinecraftHandler(connectionData, connectionData.frontChannel, frontEnd = false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,16 +62,6 @@ class CloudCrypto(val cipherDecode: Cipher, var cipherEncode: Cipher) : MessageT
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class BackendInit(val user: UserConnection) : ChannelInitializer<Channel>() {
|
|
||||||
override fun initChannel(ch: Channel) {
|
|
||||||
ch.pipeline().addLast("timeout", ReadTimeoutHandler(30, TimeUnit.SECONDS))
|
|
||||||
// "crypto"
|
|
||||||
.addLast("frame", FrameCodec())
|
|
||||||
// compress
|
|
||||||
.addLast("handler", CloudMinecraftHandler(user, null, frontEnd = false))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CloudCompressionCodec(val threshold: Int) : MessageToMessageCodec<ByteBuf, ByteBuf>() {
|
class CloudCompressionCodec(val threshold: Int) : MessageToMessageCodec<ByteBuf, ByteBuf>() {
|
||||||
// https://github.com/Gerrygames/ClientViaVersion/blob/master/src/main/java/de/gerrygames/the5zig/clientviaversion/netty/CompressionEncoder.java
|
// https://github.com/Gerrygames/ClientViaVersion/blob/master/src/main/java/de/gerrygames/the5zig/clientviaversion/netty/CompressionEncoder.java
|
||||||
private val inflater: Inflater =
|
private val inflater: Inflater =
|
||||||
|
@ -3,7 +3,6 @@ package com.github.creeper123123321.viaaas
|
|||||||
import com.google.common.net.UrlEscapers
|
import com.google.common.net.UrlEscapers
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import de.gerrygames.viarewind.netty.EmptyChannelHandler
|
|
||||||
import io.ktor.client.request.*
|
import io.ktor.client.request.*
|
||||||
import io.netty.bootstrap.Bootstrap
|
import io.netty.bootstrap.Bootstrap
|
||||||
import io.netty.buffer.ByteBuf
|
import io.netty.buffer.ByteBuf
|
||||||
@ -17,8 +16,6 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import us.myles.ViaVersion.api.data.StoredObject
|
|
||||||
import us.myles.ViaVersion.api.data.UserConnection
|
|
||||||
import us.myles.ViaVersion.api.protocol.ProtocolVersion
|
import us.myles.ViaVersion.api.protocol.ProtocolVersion
|
||||||
import us.myles.ViaVersion.api.type.Type
|
import us.myles.ViaVersion.api.type.Type
|
||||||
import us.myles.ViaVersion.exception.CancelCodecException
|
import us.myles.ViaVersion.exception.CancelCodecException
|
||||||
@ -39,29 +36,34 @@ import javax.naming.directory.InitialDirContext
|
|||||||
|
|
||||||
val chLogger = LoggerFactory.getLogger("VIAaaS MC Handler")
|
val chLogger = LoggerFactory.getLogger("VIAaaS MC Handler")
|
||||||
|
|
||||||
class HandlerData(
|
class ConnectionData(
|
||||||
userConnection: UserConnection,
|
val frontChannel: Channel,
|
||||||
var state: MinecraftConnectionState,
|
var backChannel: Channel? = null,
|
||||||
var protocolId: Int? = null,
|
var state: MinecraftConnectionState = HandshakeState(),
|
||||||
|
var frontOnline: Boolean? = null, // todo
|
||||||
var frontName: String? = null,
|
var frontName: String? = null,
|
||||||
var backName: String? = null,
|
var backName: String? = null,
|
||||||
var backServerId: String? = null,
|
|
||||||
var backPublicKey: PublicKey? = null,
|
var backPublicKey: PublicKey? = null,
|
||||||
var backToken: ByteArray? = null,
|
var backToken: ByteArray? = null,
|
||||||
var frontToken: ByteArray? = null,
|
var frontToken: ByteArray? = null,
|
||||||
var frontId: String? = null
|
var frontServerId: String? = null,
|
||||||
) : StoredObject(userConnection)
|
var backServerId: String? = null,
|
||||||
|
var frontVer: Int? = null,
|
||||||
|
var backVer: Int? = null,
|
||||||
|
) {
|
||||||
|
val frontHandler get() = frontChannel.pipeline().get(CloudMinecraftHandler::class.java)
|
||||||
|
val backHandler get() = backChannel?.pipeline()?.get(CloudMinecraftHandler::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
class CloudMinecraftHandler(
|
class CloudMinecraftHandler(
|
||||||
val user: UserConnection,
|
val data: ConnectionData,
|
||||||
var other: Channel?,
|
var other: Channel?,
|
||||||
val frontEnd: Boolean
|
val frontEnd: Boolean
|
||||||
) : SimpleChannelInboundHandler<ByteBuf>() {
|
) : SimpleChannelInboundHandler<ByteBuf>() {
|
||||||
val data get() = user.get(HandlerData::class.java)!!
|
var remoteAddress: SocketAddress? = null
|
||||||
var address: SocketAddress? = null
|
|
||||||
|
|
||||||
override fun channelRead0(ctx: ChannelHandlerContext, msg: ByteBuf) {
|
override fun channelRead0(ctx: ChannelHandlerContext, msg: ByteBuf) {
|
||||||
if (ctx.channel().isActive && !user.isPendingDisconnect && msg.isReadable) {
|
if (ctx.channel().isActive && msg.isReadable) {
|
||||||
data.state.handleMessage(this, ctx, msg)
|
data.state.handleMessage(this, ctx, msg)
|
||||||
if (msg.isReadable) throw IllegalStateException("Remaining bytes!!!")
|
if (msg.isReadable) throw IllegalStateException("Remaining bytes!!!")
|
||||||
//other?.write(msg.retain())
|
//other?.write(msg.retain())
|
||||||
@ -69,10 +71,7 @@ class CloudMinecraftHandler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun channelActive(ctx: ChannelHandlerContext) {
|
override fun channelActive(ctx: ChannelHandlerContext) {
|
||||||
address = ctx.channel().remoteAddress()
|
remoteAddress = ctx.channel().remoteAddress()
|
||||||
if (user.get(HandlerData::class.java) == null) {
|
|
||||||
user.put(HandlerData(user, HandshakeState()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun channelInactive(ctx: ChannelHandlerContext) {
|
override fun channelInactive(ctx: ChannelHandlerContext) {
|
||||||
@ -106,12 +105,11 @@ interface MinecraftConnectionState {
|
|||||||
)
|
)
|
||||||
|
|
||||||
fun disconnect(handler: CloudMinecraftHandler, msg: String) {
|
fun disconnect(handler: CloudMinecraftHandler, msg: String) {
|
||||||
chLogger.info("Disconnected ${handler.address}: $msg")
|
chLogger.info("Disconnected ${handler.remoteAddress}: $msg")
|
||||||
handler.user.isPendingDisconnect = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onInactivated(handler: CloudMinecraftHandler) {
|
fun onInactivated(handler: CloudMinecraftHandler) {
|
||||||
chLogger.info(handler.address?.toString() + " inactivated")
|
chLogger.info(handler.remoteAddress?.toString() + " inactivated")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,76 +117,80 @@ class HandshakeState : MinecraftConnectionState {
|
|||||||
override fun handleMessage(handler: CloudMinecraftHandler, ctx: ChannelHandlerContext, msg: ByteBuf) {
|
override fun handleMessage(handler: CloudMinecraftHandler, ctx: ChannelHandlerContext, msg: ByteBuf) {
|
||||||
val packet = PacketRegistry.decode(
|
val packet = PacketRegistry.decode(
|
||||||
msg,
|
msg,
|
||||||
ProtocolVersion.getProtocol(handler.user.protocolInfo!!.serverProtocolVersion),
|
ProtocolVersion.v1_8, // we still dont know what protocol it is
|
||||||
State.HANDSHAKE,
|
State.HANDSHAKE,
|
||||||
handler.frontEnd
|
handler.frontEnd
|
||||||
)
|
)
|
||||||
if (packet !is HandshakePacket) throw IllegalArgumentException("Invalid packet!")
|
if (packet !is HandshakePacket) throw IllegalArgumentException("Invalid packet!")
|
||||||
handler.data.protocolId = packet.protocolId
|
|
||||||
|
handler.data.frontVer = packet.protocolId
|
||||||
when (packet.nextState.ordinal) {
|
when (packet.nextState.ordinal) {
|
||||||
1 -> handler.data.state = StatusState
|
1 -> handler.data.state = StatusState
|
||||||
2 -> handler.data.state = LoginState
|
2 -> handler.data.state = LoginState
|
||||||
else -> throw IllegalStateException("Invalid next state")
|
else -> throw IllegalStateException("Invalid next state")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!handler.user.get(CloudData::class.java)!!.hadHostname && VIAaaSConfig.requireHostName) {
|
val parsed = VIAaaSAddress().parse(packet.address.substringBefore(0.toChar()), VIAaaSConfig.hostName)
|
||||||
|
val backProto = parsed.protocol ?: 47 // todo autodetection
|
||||||
|
val hadHostname = parsed.viaSuffix != null
|
||||||
|
|
||||||
|
packet.address = parsed.realAddress!!
|
||||||
|
packet.port = parsed.port ?: if (VIAaaSConfig.defaultBackendPort == -1) {
|
||||||
|
packet.port
|
||||||
|
} else {
|
||||||
|
VIAaaSConfig.defaultBackendPort
|
||||||
|
}
|
||||||
|
|
||||||
|
handler.data.backVer = backProto
|
||||||
|
handler.data.frontOnline = parsed.online
|
||||||
|
handler.data.backName = parsed.altUsername
|
||||||
|
|
||||||
|
val playerAddr = handler.data.frontHandler.remoteAddress
|
||||||
|
chLogger.info("Connecting $playerAddr (${handler.data.frontVer}) -> ${packet.address}:${packet.port} ($backProto)")
|
||||||
|
|
||||||
|
if (!hadHostname && VIAaaSConfig.requireHostName) {
|
||||||
throw UnsupportedOperationException("This VIAaaS instance requires you to use the hostname")
|
throw UnsupportedOperationException("This VIAaaS instance requires you to use the hostname")
|
||||||
}
|
}
|
||||||
|
|
||||||
handler.user.channel!!.setAutoRead(false)
|
handler.data.frontChannel.setAutoRead(false)
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
GlobalScope.launch(Dispatchers.IO) {
|
||||||
val frontHandler = handler.user.channel!!.pipeline().get(CloudMinecraftHandler::class.java)
|
val frontHandler = handler.data.frontHandler
|
||||||
try {
|
try {
|
||||||
var srvResolvedAddr = packet.address
|
val srvResolved = resolveSrv(packet.address, packet.port)
|
||||||
var srvResolvedPort = packet.port
|
packet.address = srvResolved.first
|
||||||
if (srvResolvedPort == 25565) {
|
packet.port = srvResolved.second
|
||||||
try {
|
|
||||||
// https://github.com/GeyserMC/Geyser/blob/99e72f35b308542cf0dbfb5b58816503c3d6a129/connector/src/main/java/org/geysermc/connector/GeyserConnector.java
|
|
||||||
val attr = InitialDirContext()
|
|
||||||
.getAttributes("dns:///_minecraft._tcp.$srvResolvedAddr", arrayOf("SRV"))["SRV"]
|
|
||||||
if (attr != null && attr.size() > 0) {
|
|
||||||
val record = (attr.get(0) as String).split(" ")
|
|
||||||
srvResolvedAddr = record[3]
|
|
||||||
srvResolvedPort = record[2].toInt()
|
|
||||||
}
|
|
||||||
} catch (ignored: NameNotFoundException) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val socketAddr = InetSocketAddress(InetAddress.getByName(srvResolvedAddr), srvResolvedPort)
|
|
||||||
val addrInfo = socketAddr.address
|
|
||||||
if (VIAaaSConfig.blockLocalAddress && (addrInfo.isSiteLocalAddress
|
|
||||||
|| addrInfo.isLoopbackAddress
|
|
||||||
|| addrInfo.isLinkLocalAddress
|
|
||||||
|| addrInfo.isAnyLocalAddress)
|
|
||||||
) throw SecurityException("Local addresses aren't allowed")
|
|
||||||
|
|
||||||
val bootstrap = Bootstrap().handler(BackendInit(handler.user))
|
val socketAddr = InetSocketAddress(InetAddress.getByName(packet.address), packet.port)
|
||||||
|
|
||||||
|
if (checkLocalAddress(socketAddr.address)) {
|
||||||
|
throw SecurityException("Local addresses aren't allowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
val bootstrap = Bootstrap().handler(BackendInit(handler.data))
|
||||||
.channelFactory(channelSocketFactory())
|
.channelFactory(channelSocketFactory())
|
||||||
.group(handler.user.channel!!.eventLoop())
|
.group(handler.data.frontChannel.eventLoop())
|
||||||
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 15_000) // Half of mc timeout
|
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 15_000) // Half of mc timeout
|
||||||
.connect(socketAddr)
|
.connect(socketAddr)
|
||||||
|
|
||||||
bootstrap.addListener {
|
bootstrap.addListener {
|
||||||
if (it.isSuccess) {
|
if (it.isSuccess) {
|
||||||
CloudHeadProtocol.logger.info("Connected ${frontHandler.address} -> $socketAddr")
|
chLogger.info("Connected ${frontHandler.remoteAddress} -> $socketAddr")
|
||||||
|
|
||||||
val backChan = bootstrap.channel() as SocketChannel
|
val backChan = bootstrap.channel() as SocketChannel
|
||||||
backChan.pipeline().get(CloudMinecraftHandler::class.java).other = handler.user.channel
|
handler.data.backChannel = backChan
|
||||||
frontHandler.other = backChan
|
frontHandler.other = backChan
|
||||||
|
|
||||||
packet.address = srvResolvedAddr
|
|
||||||
packet.port = srvResolvedPort
|
|
||||||
forward(handler, packet)
|
forward(handler, packet)
|
||||||
backChan.flush()
|
backChan.flush()
|
||||||
|
|
||||||
handler.user.channel!!.setAutoRead(true)
|
handler.data.frontChannel.setAutoRead(true)
|
||||||
} else {
|
} else {
|
||||||
// We're in the event loop
|
// We're in the event loop
|
||||||
frontHandler.disconnect("Couldn't connect: " + it.cause().toString())
|
frontHandler.disconnect("Couldn't connect: " + it.cause().toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
handler.user.channel!!.eventLoop().submit {
|
handler.data.frontChannel.eventLoop().submit {
|
||||||
frontHandler.disconnect("Couldn't connect: $e")
|
frontHandler.disconnect("Couldn't connect: $e")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -196,7 +198,7 @@ class HandshakeState : MinecraftConnectionState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun disconnect(handler: CloudMinecraftHandler, msg: String) {
|
override fun disconnect(handler: CloudMinecraftHandler, msg: String) {
|
||||||
handler.user.channel?.close() // Not worth logging
|
handler.data.frontChannel.close() // Not worth logging
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onInactivated(handler: CloudMinecraftHandler) {
|
override fun onInactivated(handler: CloudMinecraftHandler) {
|
||||||
@ -208,7 +210,7 @@ object LoginState : MinecraftConnectionState {
|
|||||||
override fun handleMessage(handler: CloudMinecraftHandler, ctx: ChannelHandlerContext, msg: ByteBuf) {
|
override fun handleMessage(handler: CloudMinecraftHandler, ctx: ChannelHandlerContext, msg: ByteBuf) {
|
||||||
val packet = PacketRegistry.decode(
|
val packet = PacketRegistry.decode(
|
||||||
msg,
|
msg,
|
||||||
ProtocolVersion.getProtocol(handler.user.protocolInfo!!.serverProtocolVersion),
|
ProtocolVersion.getProtocol(handler.data.frontVer!!),
|
||||||
State.LOGIN,
|
State.LOGIN,
|
||||||
handler.frontEnd
|
handler.frontEnd
|
||||||
)
|
)
|
||||||
@ -232,16 +234,24 @@ object LoginState : MinecraftConnectionState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleCompression(handler: CloudMinecraftHandler, setCompression: SetCompression) {
|
private fun handleCompression(handler: CloudMinecraftHandler, setCompression: SetCompression) {
|
||||||
val pipe = handler.user.channel!!.pipeline()
|
val pipe = handler.data.frontChannel.pipeline()
|
||||||
val threshold = setCompression.threshold
|
val threshold = setCompression.threshold
|
||||||
|
|
||||||
val backPipe = pipe.get(CloudMinecraftHandler::class.java).other!!.pipeline()
|
val backPipe = pipe.get(CloudMinecraftHandler::class.java).other!!.pipeline()
|
||||||
|
if (threshold != -1) {
|
||||||
backPipe.addAfter("frame", "compress", CloudCompressionCodec(threshold))
|
backPipe.addAfter("frame", "compress", CloudCompressionCodec(threshold))
|
||||||
|
} else if (backPipe.get("compress") != null) {
|
||||||
|
backPipe.remove("compress")
|
||||||
|
}
|
||||||
|
|
||||||
forward(handler, setCompression)
|
forward(handler, setCompression)
|
||||||
|
|
||||||
|
if (threshold != -1) {
|
||||||
pipe.addAfter("frame", "compress", CloudCompressionCodec(threshold))
|
pipe.addAfter("frame", "compress", CloudCompressionCodec(threshold))
|
||||||
pipe.addAfter("frame", "decompress", EmptyChannelHandler()) // ViaRewind compat workaround
|
// todo viarewind backend compression
|
||||||
|
} else if (pipe.get("compress") != null) {
|
||||||
|
pipe.remove("compress")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleCryptoRequest(handler: CloudMinecraftHandler, cryptoRequest: CryptoRequest) {
|
fun handleCryptoRequest(handler: CloudMinecraftHandler, cryptoRequest: CryptoRequest) {
|
||||||
@ -261,7 +271,7 @@ object LoginState : MinecraftConnectionState {
|
|||||||
it
|
it
|
||||||
}
|
}
|
||||||
data.frontToken = token
|
data.frontToken = token
|
||||||
data.frontId = id
|
data.frontServerId = id
|
||||||
|
|
||||||
cryptoRequest.serverId = id
|
cryptoRequest.serverId = id
|
||||||
cryptoRequest.publicKey = mcCryptoKey.public
|
cryptoRequest.publicKey = mcCryptoKey.public
|
||||||
@ -281,9 +291,9 @@ object LoginState : MinecraftConnectionState {
|
|||||||
val aesEn = mcCfb8(frontKey, Cipher.ENCRYPT_MODE)
|
val aesEn = mcCfb8(frontKey, Cipher.ENCRYPT_MODE)
|
||||||
val aesDe = mcCfb8(frontKey, Cipher.DECRYPT_MODE)
|
val aesDe = mcCfb8(frontKey, Cipher.DECRYPT_MODE)
|
||||||
|
|
||||||
handler.user.channel!!.pipeline().addBefore("frame", "crypto", CloudCrypto(aesDe, aesEn))
|
handler.data.frontChannel.pipeline().addBefore("frame", "crypto", CloudCrypto(aesDe, aesEn))
|
||||||
|
|
||||||
generateServerHash(handler.data.frontId!!, frontKey, mcCryptoKey.public)
|
generateServerHash(handler.data.frontServerId!!, frontKey, mcCryptoKey.public)
|
||||||
}
|
}
|
||||||
|
|
||||||
val backKey = ByteArray(16).let {
|
val backKey = ByteArray(16).let {
|
||||||
@ -293,14 +303,13 @@ object LoginState : MinecraftConnectionState {
|
|||||||
|
|
||||||
val backHash = generateServerHash(handler.data.backServerId!!, backKey, handler.data.backPublicKey!!)
|
val backHash = generateServerHash(handler.data.backServerId!!, backKey, handler.data.backPublicKey!!)
|
||||||
|
|
||||||
handler.user.channel!!.setAutoRead(false)
|
handler.data.frontChannel.setAutoRead(false)
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
GlobalScope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
val profile = httpClient.get<JsonObject?>(
|
val profile = httpClient.get<JsonObject?>(
|
||||||
"https://sessionserver.mojang.com/session/minecraft/hasJoined?username=" +
|
"https://sessionserver.mojang.com/session/minecraft/hasJoined?username=" +
|
||||||
"${
|
UrlEscapers.urlFormParameterEscaper().escape(handler.data.frontName!!) +
|
||||||
UrlEscapers.urlFormParameterEscaper().escape(handler.data.frontName!!)
|
"&serverId=$frontHash"
|
||||||
}&serverId=$frontHash"
|
|
||||||
)
|
)
|
||||||
?: throw IllegalArgumentException("Couldn't authenticate with session servers")
|
?: throw IllegalArgumentException("Couldn't authenticate with session servers")
|
||||||
|
|
||||||
@ -308,7 +317,7 @@ object LoginState : MinecraftConnectionState {
|
|||||||
fromUndashed(profile.get("id")!!.asString),
|
fromUndashed(profile.get("id")!!.asString),
|
||||||
handler.data.backName!!,
|
handler.data.backName!!,
|
||||||
backHash,
|
backHash,
|
||||||
handler.address!!, // Frontend handler
|
handler.remoteAddress!!, // Frontend handler
|
||||||
handler.data.backPublicKey!!
|
handler.data.backPublicKey!!
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -331,13 +340,13 @@ object LoginState : MinecraftConnectionState {
|
|||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
handler.disconnect("Online mode error: $e")
|
handler.disconnect("Online mode error: $e")
|
||||||
}
|
}
|
||||||
handler.user.channel!!.setAutoRead(true)
|
handler.data.frontChannel.setAutoRead(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleLoginStart(handler: CloudMinecraftHandler, loginStart: LoginStart) {
|
fun handleLoginStart(handler: CloudMinecraftHandler, loginStart: LoginStart) {
|
||||||
handler.data.frontName = loginStart.username
|
handler.data.frontName = loginStart.username
|
||||||
handler.data.backName = handler.user.get(CloudData::class.java)!!.altName ?: handler.data.frontName
|
handler.data.backName = handler.data.backName ?: handler.data.frontName
|
||||||
|
|
||||||
loginStart.username = handler.data.backName!!
|
loginStart.username = handler.data.backName!!
|
||||||
forward(handler, loginStart)
|
forward(handler, loginStart)
|
||||||
@ -350,9 +359,9 @@ object LoginState : MinecraftConnectionState {
|
|||||||
try {
|
try {
|
||||||
packet.writeByte(0) // id 0 disconnect
|
packet.writeByte(0) // id 0 disconnect
|
||||||
Type.STRING.write(packet, Gson().toJson("[VIAaaS] §c$msg"))
|
Type.STRING.write(packet, Gson().toJson("[VIAaaS] §c$msg"))
|
||||||
handler.user
|
handler.data.frontChannel
|
||||||
.sendRawPacketFuture(packet.retain())
|
.writeAndFlush(packet.retain())
|
||||||
.addListener { handler.user.channel?.close() }
|
.addListener { handler.data.frontChannel.close() }
|
||||||
} finally {
|
} finally {
|
||||||
packet.release()
|
packet.release()
|
||||||
}
|
}
|
||||||
@ -364,7 +373,8 @@ object StatusState : MinecraftConnectionState {
|
|||||||
val i = msg.readerIndex()
|
val i = msg.readerIndex()
|
||||||
if (Type.VAR_INT.readPrimitive(msg) !in 0..1) throw IllegalArgumentException("Invalid packet id!")
|
if (Type.VAR_INT.readPrimitive(msg) !in 0..1) throw IllegalArgumentException("Invalid packet id!")
|
||||||
msg.readerIndex(i)
|
msg.readerIndex(i)
|
||||||
handler.other!!.write(msg.retain())
|
handler.other!!.write(msg.retainedSlice())
|
||||||
|
msg.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun disconnect(handler: CloudMinecraftHandler, msg: String) {
|
override fun disconnect(handler: CloudMinecraftHandler, msg: String) {
|
||||||
@ -377,8 +387,8 @@ object StatusState : MinecraftConnectionState {
|
|||||||
packet, """{"version": {"name": "VIAaaS", "protocol": -1}, "players":
|
packet, """{"version": {"name": "VIAaaS", "protocol": -1}, "players":
|
||||||
| {"max": 0, "online": 0, "sample": []}, "description": {"text": ${Gson().toJson("§c$msg")}}}""".trimMargin()
|
| {"max": 0, "online": 0, "sample": []}, "description": {"text": ${Gson().toJson("§c$msg")}}}""".trimMargin()
|
||||||
)
|
)
|
||||||
handler.user.sendRawPacketFuture(packet.retain())
|
handler.data.frontChannel.writeAndFlush(packet.retain())
|
||||||
.addListener { handler.user.channel?.close() }
|
.addListener { handler.data.frontChannel.close() }
|
||||||
} finally {
|
} finally {
|
||||||
packet.release()
|
packet.release()
|
||||||
}
|
}
|
||||||
@ -390,12 +400,13 @@ object PlayState : MinecraftConnectionState {
|
|||||||
val i = msg.readerIndex()
|
val i = msg.readerIndex()
|
||||||
if (Type.VAR_INT.readPrimitive(msg) !in 0..127) throw IllegalArgumentException("Invalid packet id!")
|
if (Type.VAR_INT.readPrimitive(msg) !in 0..127) throw IllegalArgumentException("Invalid packet id!")
|
||||||
msg.readerIndex(i)
|
msg.readerIndex(i)
|
||||||
handler.other!!.write(msg.retain())
|
handler.other!!.write(msg.retainedSlice())
|
||||||
|
msg.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun disconnect(handler: CloudMinecraftHandler, msg: String) {
|
override fun disconnect(handler: CloudMinecraftHandler, msg: String) {
|
||||||
super.disconnect(handler, msg)
|
super.disconnect(handler, msg)
|
||||||
handler.user.channel?.close()
|
handler.data.frontChannel.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -440,9 +451,32 @@ fun generateServerHash(serverId: String, sharedSecret: ByteArray?, key: PublicKe
|
|||||||
private fun forward(handler: CloudMinecraftHandler, packet: Packet) {
|
private fun forward(handler: CloudMinecraftHandler, packet: Packet) {
|
||||||
val msg = ByteBufAllocator.DEFAULT.buffer()
|
val msg = ByteBufAllocator.DEFAULT.buffer()
|
||||||
try {
|
try {
|
||||||
PacketRegistry.encode(packet, msg, ProtocolVersion.getProtocol(handler.data.protocolId!!))
|
PacketRegistry.encode(packet, msg, ProtocolVersion.getProtocol(handler.data.frontVer!!))
|
||||||
handler.other!!.write(msg.retain())
|
handler.other!!.write(msg.retain())
|
||||||
} finally {
|
} finally {
|
||||||
msg.release()
|
msg.release()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun resolveSrv(address: String, port: Int): Pair<String, Int> {
|
||||||
|
if (port == 25565) {
|
||||||
|
try {
|
||||||
|
// https://github.com/GeyserMC/Geyser/blob/99e72f35b308542cf0dbfb5b58816503c3d6a129/connector/src/main/java/org/geysermc/connector/GeyserConnector.java
|
||||||
|
val attr = InitialDirContext()
|
||||||
|
.getAttributes("dns:///_minecraft._tcp.$address", arrayOf("SRV"))["SRV"]
|
||||||
|
if (attr != null && attr.size() > 0) {
|
||||||
|
val record = (attr.get(0) as String).split(" ")
|
||||||
|
return record[3] to record[2].toInt()
|
||||||
|
}
|
||||||
|
} catch (ignored: NameNotFoundException) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return address to port
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkLocalAddress(inetAddress: InetAddress): Boolean {
|
||||||
|
return VIAaaSConfig.blockLocalAddress && (inetAddress.isSiteLocalAddress
|
||||||
|
|| inetAddress.isLoopbackAddress
|
||||||
|
|| inetAddress.isLinkLocalAddress
|
||||||
|
|| inetAddress.isAnyLocalAddress)
|
||||||
|
}
|
@ -185,8 +185,7 @@ class CloudTask(val obj: Future<*>) : TaskId {
|
|||||||
|
|
||||||
object CloudVersionProvider : VersionProvider() {
|
object CloudVersionProvider : VersionProvider() {
|
||||||
override fun getServerProtocol(connection: UserConnection): Int {
|
override fun getServerProtocol(connection: UserConnection): Int {
|
||||||
val data = connection.get(CloudData::class.java)
|
val ver = connection.channel!!.pipeline().get(CloudMinecraftHandler::class.java).data.backVer
|
||||||
val ver = data?.backendVer
|
|
||||||
if (ver != null) return ver
|
if (ver != null) return ver
|
||||||
return super.getServerProtocol(connection)
|
return super.getServerProtocol(connection)
|
||||||
}
|
}
|
||||||
|
@ -1,76 +0,0 @@
|
|||||||
package com.github.creeper123123321.viaaas
|
|
||||||
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import us.myles.ViaVersion.api.PacketWrapper
|
|
||||||
import us.myles.ViaVersion.api.data.StoredObject
|
|
||||||
import us.myles.ViaVersion.api.data.UserConnection
|
|
||||||
import us.myles.ViaVersion.api.protocol.Protocol
|
|
||||||
import us.myles.ViaVersion.api.protocol.ProtocolPipeline
|
|
||||||
import us.myles.ViaVersion.api.protocol.SimpleProtocol
|
|
||||||
import us.myles.ViaVersion.api.remapper.PacketRemapper
|
|
||||||
import us.myles.ViaVersion.api.type.Type
|
|
||||||
import us.myles.ViaVersion.packets.State
|
|
||||||
|
|
||||||
class CloudPipeline(userConnection: UserConnection) : ProtocolPipeline(userConnection) {
|
|
||||||
override fun registerPackets() {
|
|
||||||
super.registerPackets()
|
|
||||||
add(CloudHeadProtocol) // add() will add tail protocol
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun add(protocol: Protocol<*, *, *, *>?) {
|
|
||||||
super.add(protocol)
|
|
||||||
pipes().removeIf { it == CloudHeadProtocol }
|
|
||||||
pipes().add(0, CloudHeadProtocol)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object CloudHeadProtocol : SimpleProtocol() {
|
|
||||||
val logger = LoggerFactory.getLogger("CloudHandlerProtocol")
|
|
||||||
override fun registerPackets() {
|
|
||||||
this.registerIncoming(State.HANDSHAKE, 0, 0, object : PacketRemapper() {
|
|
||||||
override fun registerMap() {
|
|
||||||
handler { wrapper: PacketWrapper ->
|
|
||||||
val playerVer = wrapper.passthrough(Type.VAR_INT)
|
|
||||||
val addr = wrapper.read(Type.STRING) // Server Address
|
|
||||||
val receivedPort = wrapper.read(Type.UNSIGNED_SHORT)
|
|
||||||
|
|
||||||
val parsed = VIAaaSAddress().parse(addr.substringBefore(0.toChar()), VIAaaSConfig.hostName)
|
|
||||||
val backPort = parsed.port ?: if (VIAaaSConfig.defaultBackendPort == -1) {
|
|
||||||
receivedPort
|
|
||||||
} else {
|
|
||||||
VIAaaSConfig.defaultBackendPort
|
|
||||||
}
|
|
||||||
val backAddr = parsed.realAddress
|
|
||||||
val backProto = parsed.protocol ?: 47
|
|
||||||
|
|
||||||
wrapper.write(Type.STRING, backAddr)
|
|
||||||
wrapper.write(Type.UNSIGNED_SHORT, backPort)
|
|
||||||
|
|
||||||
val playerAddr = wrapper.user().channel!!.pipeline()
|
|
||||||
.get(CloudMinecraftHandler::class.java)!!.address
|
|
||||||
logger.info("Connecting $playerAddr ($playerVer) -> $backAddr:$backPort ($backProto)")
|
|
||||||
|
|
||||||
wrapper.user().put(
|
|
||||||
CloudData(
|
|
||||||
userConnection = wrapper.user(),
|
|
||||||
backendVer = backProto,
|
|
||||||
frontOnline = parsed.online,
|
|
||||||
altName = parsed.altUsername,
|
|
||||||
hadHostname = parsed.viaSuffix != null
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
wrapper.passthrough(Type.VAR_INT) // Next state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class CloudData(
|
|
||||||
val userConnection: UserConnection,
|
|
||||||
var backendVer: Int,
|
|
||||||
var frontOnline: Boolean,
|
|
||||||
var altName: String?,
|
|
||||||
var hadHostname: Boolean
|
|
||||||
) : StoredObject(userConnection)
|
|
@ -105,7 +105,7 @@ fun main(args: Array<String>) {
|
|||||||
MappingDataLoader.enableMappingsCache()
|
MappingDataLoader.enableMappingsCache()
|
||||||
Via.getManager().init()
|
Via.getManager().init()
|
||||||
CloudRewind.init(ViaRewindConfigImpl(File("config/viarewind.yml")))
|
CloudRewind.init(ViaRewindConfigImpl(File("config/viarewind.yml")))
|
||||||
CloudBackwards.init(File("config/viabackwards.yml"))
|
CloudBackwards.init(File("config/viabackwards"))
|
||||||
|
|
||||||
val parent = eventLoopGroup()
|
val parent = eventLoopGroup()
|
||||||
val child = eventLoopGroup()
|
val child = eventLoopGroup()
|
||||||
|
@ -38,9 +38,6 @@ import java.util.concurrent.TimeUnit
|
|||||||
import java.util.concurrent.TimeoutException
|
import java.util.concurrent.TimeoutException
|
||||||
import kotlin.collections.set
|
import kotlin.collections.set
|
||||||
|
|
||||||
|
|
||||||
// todo https://minecraft.id/documentation
|
|
||||||
|
|
||||||
val viaWebServer = WebDashboardServer()
|
val viaWebServer = WebDashboardServer()
|
||||||
val webLogger = LoggerFactory.getLogger("VIAaaS Web")
|
val webLogger = LoggerFactory.getLogger("VIAaaS Web")
|
||||||
|
|
||||||
|
@ -221,11 +221,11 @@ function renderActions() {
|
|||||||
add.innerText = "Listen to " + username;
|
add.innerText = "Listen to " + username;
|
||||||
add.href = "#";
|
add.href = "#";
|
||||||
add.onclick = () => {
|
add.onclick = () => {
|
||||||
mcauth_code = null;
|
|
||||||
socket.send(JSON.stringify({
|
socket.send(JSON.stringify({
|
||||||
"action": "minecraft_id_login",
|
"action": "minecraft_id_login",
|
||||||
"username": username,
|
"username": username,
|
||||||
"code": mcauth_code}));
|
"code": mcauth_code}));
|
||||||
|
mcauth_code = null;
|
||||||
};
|
};
|
||||||
actions.appendChild(p);
|
actions.appendChild(p);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user