diff --git a/game/src/main/kotlin/org/openrs2/game/net/crossdomain/CrossDomainChannelHandler.kt b/game/src/main/kotlin/org/openrs2/game/net/crossdomain/CrossDomainChannelHandler.kt new file mode 100644 index 00000000..f8b47ddf --- /dev/null +++ b/game/src/main/kotlin/org/openrs2/game/net/crossdomain/CrossDomainChannelHandler.kt @@ -0,0 +1,46 @@ +package org.openrs2.game.net.crossdomain + +import io.netty.buffer.Unpooled +import io.netty.channel.ChannelFutureListener +import io.netty.channel.ChannelHandler +import io.netty.channel.ChannelHandlerContext +import io.netty.channel.SimpleChannelInboundHandler +import io.netty.handler.codec.http.HttpHeaderNames +import io.netty.handler.codec.http.HttpMethod +import io.netty.handler.codec.http.HttpRequest +import io.netty.handler.codec.http.HttpResponseStatus +import org.openrs2.game.net.http.Http + +@ChannelHandler.Sharable +public object CrossDomainChannelHandler : SimpleChannelInboundHandler() { + private const val ENDPOINT = "/crossdomain.xml" + private val POLICY = """ + + + + + + + """.trimIndent().plus("\n").toByteArray(Charsets.UTF_8) + + override fun handlerAdded(ctx: ChannelHandlerContext) { + ctx.read() + } + + override fun channelRead0(ctx: ChannelHandlerContext, msg: HttpRequest) { + if (msg.method() != HttpMethod.GET || msg.uri() != ENDPOINT) { + ctx.write(Http.createResponse(HttpResponseStatus.BAD_REQUEST)).addListener(ChannelFutureListener.CLOSE) + return + } + + val response = Http.createResponse(HttpResponseStatus.OK) + response.headers().add(HttpHeaderNames.CONTENT_TYPE, Http.TEXT_X_CROSS_DOMAIN_POLICY) + response.headers().add(HttpHeaderNames.CONTENT_LENGTH, POLICY.size) + ctx.write(response, ctx.voidPromise()) + ctx.write(Unpooled.wrappedBuffer(POLICY)).addListener(ChannelFutureListener.CLOSE) + } + + override fun channelReadComplete(ctx: ChannelHandlerContext) { + ctx.flush() + } +} diff --git a/game/src/main/kotlin/org/openrs2/game/net/http/Http.kt b/game/src/main/kotlin/org/openrs2/game/net/http/Http.kt new file mode 100644 index 00000000..9b836bcd --- /dev/null +++ b/game/src/main/kotlin/org/openrs2/game/net/http/Http.kt @@ -0,0 +1,20 @@ +package org.openrs2.game.net.http + +import io.netty.handler.codec.http.DefaultHttpResponse +import io.netty.handler.codec.http.HttpHeaderNames +import io.netty.handler.codec.http.HttpHeaderValues +import io.netty.handler.codec.http.HttpResponse +import io.netty.handler.codec.http.HttpResponseStatus +import io.netty.handler.codec.http.HttpVersion + +public object Http { + public const val MAX_CONTENT_LENGTH: Int = 65536 + public const val TEXT_X_CROSS_DOMAIN_POLICY: String = "text/x-cross-domain-policy" + + public fun createResponse(status: HttpResponseStatus): HttpResponse { + val response = DefaultHttpResponse(HttpVersion.HTTP_1_1, status) + response.headers().add(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE) + response.headers().add(HttpHeaderNames.SERVER, "OpenRS2") + return response + } +} diff --git a/game/src/main/kotlin/org/openrs2/game/net/http/HttpChannelHandler.kt b/game/src/main/kotlin/org/openrs2/game/net/http/HttpChannelHandler.kt index 71a96a0b..63116b82 100644 --- a/game/src/main/kotlin/org/openrs2/game/net/http/HttpChannelHandler.kt +++ b/game/src/main/kotlin/org/openrs2/game/net/http/HttpChannelHandler.kt @@ -4,13 +4,10 @@ import io.netty.channel.ChannelFutureListener import io.netty.channel.ChannelHandler import io.netty.channel.ChannelHandlerContext import io.netty.channel.SimpleChannelInboundHandler -import io.netty.handler.codec.http.DefaultHttpResponse import io.netty.handler.codec.http.HttpHeaderNames import io.netty.handler.codec.http.HttpHeaderValues import io.netty.handler.codec.http.HttpRequest -import io.netty.handler.codec.http.HttpResponse import io.netty.handler.codec.http.HttpResponseStatus -import io.netty.handler.codec.http.HttpVersion import org.openrs2.game.net.FileProvider import javax.inject.Inject import javax.inject.Singleton @@ -27,17 +24,17 @@ public class HttpChannelHandler @Inject constructor( override fun channelRead0(ctx: ChannelHandlerContext, msg: HttpRequest) { val uri = msg.uri() if (!uri.startsWith("/")) { - ctx.write(createResponse(HttpResponseStatus.BAD_REQUEST)).addListener(ChannelFutureListener.CLOSE) + ctx.write(Http.createResponse(HttpResponseStatus.BAD_REQUEST)).addListener(ChannelFutureListener.CLOSE) return } val file = fileProvider.get(uri.substring(1)) if (file == null) { - ctx.write(createResponse(HttpResponseStatus.NOT_FOUND)).addListener(ChannelFutureListener.CLOSE) + ctx.write(Http.createResponse(HttpResponseStatus.NOT_FOUND)).addListener(ChannelFutureListener.CLOSE) return } - val response = createResponse(HttpResponseStatus.OK) + val response = Http.createResponse(HttpResponseStatus.OK) response.headers().add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_OCTET_STREAM) response.headers().add(HttpHeaderNames.CONTENT_LENGTH, file.count()) @@ -48,11 +45,4 @@ public class HttpChannelHandler @Inject constructor( override fun channelReadComplete(ctx: ChannelHandlerContext) { ctx.flush() } - - private fun createResponse(status: HttpResponseStatus): HttpResponse { - val response = DefaultHttpResponse(HttpVersion.HTTP_1_1, status) - response.headers().add(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE) - response.headers().add(HttpHeaderNames.SERVER, "OpenRS2") - return response - } } diff --git a/game/src/main/kotlin/org/openrs2/game/net/http/HttpChannelInitializer.kt b/game/src/main/kotlin/org/openrs2/game/net/http/HttpChannelInitializer.kt index 2b77fd23..674aed2a 100644 --- a/game/src/main/kotlin/org/openrs2/game/net/http/HttpChannelInitializer.kt +++ b/game/src/main/kotlin/org/openrs2/game/net/http/HttpChannelInitializer.kt @@ -16,12 +16,8 @@ public class HttpChannelInitializer @Inject constructor( ch.pipeline().addLast( HttpRequestDecoder(), HttpResponseEncoder(), - HttpObjectAggregator(MAX_CONTENT_LENGTH), + HttpObjectAggregator(Http.MAX_CONTENT_LENGTH), handler ) } - - private companion object { - private const val MAX_CONTENT_LENGTH = 65536 - } } diff --git a/game/src/main/kotlin/org/openrs2/game/net/login/LoginChannelHandler.kt b/game/src/main/kotlin/org/openrs2/game/net/login/LoginChannelHandler.kt index 756a9985..f6acb2f6 100644 --- a/game/src/main/kotlin/org/openrs2/game/net/login/LoginChannelHandler.kt +++ b/game/src/main/kotlin/org/openrs2/game/net/login/LoginChannelHandler.kt @@ -5,8 +5,13 @@ import io.netty.channel.ChannelFutureListener import io.netty.channel.ChannelHandlerContext import io.netty.channel.SimpleChannelInboundHandler import io.netty.handler.codec.DelimiterBasedFrameDecoder +import io.netty.handler.codec.http.HttpObjectAggregator +import io.netty.handler.codec.http.HttpRequestDecoder +import io.netty.handler.codec.http.HttpResponseEncoder import io.netty.handler.codec.string.StringDecoder import org.openrs2.buffer.copiedBuffer +import org.openrs2.game.net.crossdomain.CrossDomainChannelHandler +import org.openrs2.game.net.http.Http import org.openrs2.game.net.jaggrab.JaggrabChannelHandler import org.openrs2.game.net.js5.Js5ChannelHandler import org.openrs2.protocol.Rs2Decoder @@ -32,6 +37,7 @@ public class LoginChannelHandler @Inject constructor( when (msg) { is LoginRequest.InitJs5RemoteConnection -> handleInitJs5RemoteConnection(ctx, msg) is LoginRequest.InitJaggrabConnection -> handleInitJaggrabConnection(ctx) + is LoginRequest.InitCrossDomainConnection -> handleInitCrossDomainConnection(ctx) } } @@ -67,6 +73,21 @@ public class LoginChannelHandler @Inject constructor( ctx.pipeline().remove(this) } + private fun handleInitCrossDomainConnection(ctx: ChannelHandlerContext) { + ctx.pipeline().addLast( + HttpRequestDecoder(), + HttpResponseEncoder(), + HttpObjectAggregator(Http.MAX_CONTENT_LENGTH), + CrossDomainChannelHandler + ) + + ctx.fireChannelRead(Unpooled.wrappedBuffer(G)) + + ctx.pipeline().remove(Rs2Decoder::class.java) + ctx.pipeline().remove(Rs2Encoder::class.java) + ctx.pipeline().remove(this) + } + override fun channelReadComplete(ctx: ChannelHandlerContext) { ctx.flush() } @@ -75,5 +96,6 @@ public class LoginChannelHandler @Inject constructor( private const val BUILD = 550 private const val JAGGRAB_MAX_FRAME_LENGTH = 4096 private val JAGGRAB_DELIMITER = Unpooled.unreleasableBuffer(copiedBuffer("\n\n")) + private val G = byteArrayOf('G'.code.toByte()) } } diff --git a/protocol/src/main/kotlin/org/openrs2/protocol/Protocol.kt b/protocol/src/main/kotlin/org/openrs2/protocol/Protocol.kt index d3e79808..8e020618 100644 --- a/protocol/src/main/kotlin/org/openrs2/protocol/Protocol.kt +++ b/protocol/src/main/kotlin/org/openrs2/protocol/Protocol.kt @@ -1,6 +1,7 @@ package org.openrs2.protocol import org.openrs2.protocol.login.ClientOutOfDateCodec +import org.openrs2.protocol.login.InitCrossDomainConnectionCodec import org.openrs2.protocol.login.InitJaggrabConnectionCodec import org.openrs2.protocol.login.InitJs5RemoteConnectionCodec import org.openrs2.protocol.login.IpLimitCodec @@ -32,7 +33,8 @@ public class Protocol(vararg codecs: PacketCodec<*>) { public val LOGIN_UPSTREAM: Protocol = Protocol( InitJs5RemoteConnectionCodec, InitJaggrabConnectionCodec, - RequestWorldListCodec + RequestWorldListCodec, + InitCrossDomainConnectionCodec ) public val LOGIN_DOWNSTREAM: Protocol = Protocol( Js5OkCodec, diff --git a/protocol/src/main/kotlin/org/openrs2/protocol/login/InitCrossDomainConnectionCodec.kt b/protocol/src/main/kotlin/org/openrs2/protocol/login/InitCrossDomainConnectionCodec.kt new file mode 100644 index 00000000..06ad927a --- /dev/null +++ b/protocol/src/main/kotlin/org/openrs2/protocol/login/InitCrossDomainConnectionCodec.kt @@ -0,0 +1,8 @@ +package org.openrs2.protocol.login + +import org.openrs2.protocol.EmptyPacketCodec + +public object InitCrossDomainConnectionCodec : EmptyPacketCodec( + opcode = 'G'.code, + packet = LoginRequest.InitCrossDomainConnection +) diff --git a/protocol/src/main/kotlin/org/openrs2/protocol/login/LoginRequest.kt b/protocol/src/main/kotlin/org/openrs2/protocol/login/LoginRequest.kt index 490fb66f..81c25b83 100644 --- a/protocol/src/main/kotlin/org/openrs2/protocol/login/LoginRequest.kt +++ b/protocol/src/main/kotlin/org/openrs2/protocol/login/LoginRequest.kt @@ -6,4 +6,5 @@ public sealed class LoginRequest : Packet { public data class InitJs5RemoteConnection(public val build: Int) : LoginRequest() public object InitJaggrabConnection : LoginRequest() public data class RequestWorldList(public val checksum: Int) : LoginRequest() + public object InitCrossDomainConnection : LoginRequest() }