code seems cursed, hide websocket error, try to implement allow/block list

This commit is contained in:
creeper123123321 2021-02-02 19:57:58 -03:00
parent fef215f119
commit a8f75f91bd
5 changed files with 68 additions and 25 deletions

View File

@ -19,18 +19,17 @@ import java.util.zip.Deflater
import java.util.zip.Inflater
import javax.crypto.Cipher
object ChannelInit : ChannelInitializer<Channel>() {
object FrontChannelInit : ChannelInitializer<Channel>() {
override fun initChannel(ch: Channel) {
ch.pipeline().addLast("timeout", ReadTimeoutHandler(30, TimeUnit.SECONDS))
ch.pipeline()
.addLast("timeout", ReadTimeoutHandler(30, TimeUnit.SECONDS))
// "crypto"
.addLast("frame", FrameCodec())
// "compress" / dummy "decompress"
// "compress"
.addLast("flow-handler", FlowControlHandler())
.addLast(
"handler", CloudMinecraftHandler(
ConnectionData(
frontChannel = ch,
), other = null, frontEnd = true
ConnectionData(frontChannel = ch), other = null, frontEnd = true
)
)
}

View File

@ -1,6 +1,7 @@
package com.github.creeper123123321.viaaas
import com.google.common.net.UrlEscapers
import com.google.common.primitives.Ints
import com.google.gson.Gson
import com.google.gson.JsonObject
import io.ktor.client.request.*
@ -34,6 +35,7 @@ import javax.crypto.spec.SecretKeySpec
import javax.naming.NameNotFoundException
import javax.naming.directory.InitialDirContext
val mcLogger = LoggerFactory.getLogger("VIAaaS MC")
class ConnectionData(
@ -89,8 +91,8 @@ class CloudMinecraftHandler(
override fun exceptionCaught(ctx: ChannelHandlerContext, cause: Throwable) {
if (cause is CancelCodecException) return
mcLogger.info("Exception: ", cause)
disconnect("Exception: $cause")
mcLogger.debug("Exception: ", cause)
}
fun disconnect(s: String) {
@ -154,7 +156,6 @@ class HandshakeState : MinecraftConnectionState {
handler.data.frontChannel.setAutoRead(false)
GlobalScope.launch(Dispatchers.IO) {
val frontHandler = handler.data.frontHandler
try {
val srvResolved = resolveSrv(packet.address, packet.port)
packet.address = srvResolved.first
@ -162,11 +163,14 @@ class HandshakeState : MinecraftConnectionState {
val socketAddr = InetSocketAddress(InetAddress.getByName(packet.address), packet.port)
if (checkLocalAddress(socketAddr.address)) {
throw SecurityException("Local addresses aren't allowed")
if (checkLocalAddress(socketAddr.address)
|| matchesAddress(socketAddr, VIAaaSConfig.blockedBackAddresses)
|| !matchesAddress(socketAddr, VIAaaSConfig.allowedBackAddresses)) {
throw SecurityException("Not allowed")
}
val bootstrap = Bootstrap().handler(BackendInit(handler.data))
val bootstrap = Bootstrap()
.handler(BackendInit(handler.data))
.channelFactory(channelSocketFactory())
.group(handler.data.frontChannel.eventLoop())
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 15_000) // Half of mc timeout
@ -174,24 +178,23 @@ class HandshakeState : MinecraftConnectionState {
bootstrap.addListener {
if (it.isSuccess) {
mcLogger.info("Connected ${frontHandler.remoteAddress} -> $socketAddr")
mcLogger.info("Connected ${handler.remoteAddress} -> $socketAddr")
val backChan = bootstrap.channel() as SocketChannel
handler.data.backChannel = backChan
frontHandler.other = backChan
handler.other = backChan
forward(handler, packet)
backChan.flush()
forward(handler, packet, true)
handler.data.frontChannel.setAutoRead(true)
} else {
// We're in the event loop
frontHandler.disconnect("Couldn't connect: " + it.cause().toString())
handler.disconnect("Couldn't connect: " + it.cause().toString())
}
}
} catch (e: Exception) {
handler.data.frontChannel.eventLoop().submit {
frontHandler.disconnect("Couldn't connect: $e")
handler.disconnect("Couldn't connect: $e")
}
}
}
@ -329,8 +332,7 @@ object LoginState : MinecraftConnectionState {
cryptoResponse.encryptedKey = encryptRsa(handler.data.backPublicKey!!, backKey)
cryptoResponse.encryptedToken =
encryptRsa(handler.data.backPublicKey!!, handler.data.backToken!!)
forward(handler, cryptoResponse)
backChan.flush()
forward(handler, cryptoResponse, true)
val backAesEn = mcCfb8(backKey, Cipher.ENCRYPT_MODE)
val backAesDe = mcCfb8(backKey, Cipher.DECRYPT_MODE)
@ -448,11 +450,15 @@ fun generateServerHash(serverId: String, sharedSecret: ByteArray?, key: PublicKe
return twosComplementHexdigest(digest.digest())
}
private fun forward(handler: CloudMinecraftHandler, packet: Packet) {
private fun forward(handler: CloudMinecraftHandler, packet: Packet, flush: Boolean = false) {
val msg = ByteBufAllocator.DEFAULT.buffer()
try {
PacketRegistry.encode(packet, msg, ProtocolVersion.getProtocol(handler.data.frontVer!!))
handler.other!!.write(msg.retain())
if (flush) {
handler.other!!.writeAndFlush(msg.retain())
} else {
handler.other!!.write(msg.retain())
}
} finally {
msg.release()
}
@ -479,4 +485,26 @@ private fun checkLocalAddress(inetAddress: InetAddress): Boolean {
|| inetAddress.isLoopbackAddress
|| inetAddress.isLinkLocalAddress
|| inetAddress.isAnyLocalAddress)
}
}
private fun matchesAddress(addr: InetSocketAddress, list: List<String>): Boolean {
return (matchAddress(addr.hostString, list)
|| (addr.address != null && (matchAddress(addr.address.hostAddress, list)
|| matchAddress(addr.address.hostName, list))))
}
private fun matchAddress(addr: String, list: List<String>): Boolean {
if (list.contains("*")) return true
val parts = addr.split(".").filter(String::isNotEmpty)
val isNumericIp = parts.size == 4 && parts.all { Ints.tryParse(it) != null }
return (0..parts.size).any { i: Int ->
val query: String = if (isNumericIp) {
parts.filterIndexed { it, _ -> it <= i }.joinToString(".") +
if (i != 3) ".*" else ""
} else {
(if (i != 0) "*." else "") +
parts.filterIndexed { it, _ -> it >= i }.joinToString(".")
}
list.contains(query)
}
}

View File

@ -127,7 +127,7 @@ fun main(args: Array<String>) {
val future = ServerBootstrap()
.group(parent, child)
.channelFactory(channelServerSocketFactory())
.childHandler(ChannelInit)
.childHandler(FrontChannelInit)
.childOption(ChannelOption.IP_TOS, 0x18)
.childOption(ChannelOption.TCP_NODELAY, true)
.bind(InetAddress.getByName(VIAaaSConfig.bindAddress), VIAaaSConfig.port)
@ -279,6 +279,18 @@ object VIAaaSConfig : Config(File("config/viaaas.yml")) {
val blockLocalAddress: Boolean get() = this.getBoolean("block-local-address", true)
val requireHostName: Boolean get() = this.getBoolean("require-host-name", true)
val defaultBackendPort: Int get() = this.getInt("default-backend-port", 25565)
val blockedBackAddresses: List<String>
get() = this.get(
"blocked-back-addresses",
List::class.java,
emptyList<String>()
)!!.map { it as String }
val allowedBackAddresses: List<String>
get() = this.get(
"allowed-back-addresses",
List::class.java,
emptyList<String>()
)!!.map { it as String }
}
class VIAaaSAddress {

View File

@ -63,7 +63,7 @@ class ViaWebApp {
} catch (e: Exception) {
e.printStackTrace()
viaWebServer.onException(this, e)
this.close(CloseReason(CloseReason.Codes.INTERNAL_ERROR, e.toString()))
this.close(CloseReason(CloseReason.Codes.INTERNAL_ERROR, "INTERNAL ERROR"))
} finally {
viaWebServer.disconnected(this)
}

View File

@ -21,4 +21,8 @@ block-local-address: true
require-host-name: true
# Default port to be used to connect to backend server
# Use -1 to reuse the port sent by client, useful for transparent proxying
default-backend-port: 25565
default-backend-port: 25565
# If some server is in this list, it will be blocked. This has priority over allowed-back-addresses
blocked-back-addresses: ["*.hypixel.net", "hypixel.net"]
# VIAaaS will only allow if it matches an address in this list
allowed-back-addresses: ["*"]