diff --git a/pom.xml b/pom.xml index 9b4b2b9..7a85e9b 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ com.eu.habbo Habbo - 2.5.0 + 3.0.0 \ No newline at end of file diff --git a/src/main/java/org/krews/plugin/nitro/Utils.java b/src/main/java/org/krews/plugin/nitro/Utils.java new file mode 100644 index 0000000..5f643e4 --- /dev/null +++ b/src/main/java/org/krews/plugin/nitro/Utils.java @@ -0,0 +1,22 @@ +package org.krews.plugin.nitro; + +import java.net.URI; + +public class Utils { + public static String getDomainNameFromUrl(String url) throws Exception { + URI uri = new URI(url); + String domain = uri.getHost(); + return domain.startsWith("www.") ? domain.substring(4) : domain; + } + + public static boolean isWhitelisted(String toCheck, String[] whitelist) { + for(String whitelistEntry : whitelist) { + if(whitelistEntry.startsWith("*")) { + if(toCheck.endsWith(whitelistEntry.substring(1)) || ("." + toCheck).equals(whitelistEntry.substring(1))) return true; + } else { + if(toCheck.equals(whitelistEntry)) return true; + } + } + return false; + } +} diff --git a/src/main/java/org/krews/plugin/nitro/main.java b/src/main/java/org/krews/plugin/nitro/main.java index df881f1..5b2bb7e 100644 --- a/src/main/java/org/krews/plugin/nitro/main.java +++ b/src/main/java/org/krews/plugin/nitro/main.java @@ -6,12 +6,16 @@ import com.eu.habbo.plugin.EventHandler; import com.eu.habbo.plugin.EventListener; import com.eu.habbo.plugin.HabboPlugin; import com.eu.habbo.plugin.events.emulator.EmulatorLoadedEvent; +import com.eu.habbo.plugin.events.users.UserGetIPAddressEvent; +import io.netty.channel.Channel; +import io.netty.util.AttributeKey; import org.krews.plugin.nitro.websockets.NetworkChannelInitializer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class main extends HabboPlugin implements EventListener { private static final Logger LOGGER = LoggerFactory.getLogger(main.class); + public static final AttributeKey WS_IP = AttributeKey.valueOf("WS_IP"); public void onEnable() throws Exception { Emulator.getPluginManager().registerEvents(this, this); @@ -34,6 +38,7 @@ public class main extends HabboPlugin implements EventListener { Emulator.getConfig().register("websockets.whitelist", "localhost"); Emulator.getConfig().register("ws.nitro.host", "0.0.0.0"); Emulator.getConfig().register("ws.nitro.port", "2096"); + Emulator.getConfig().register("ws.nitro.ip.header", ""); NetworkChannelInitializer wsChannelHandler = new NetworkChannelInitializer(); Emulator.getGameServer().getServerBootstrap().childHandler(wsChannelHandler); @@ -43,4 +48,15 @@ public class main extends HabboPlugin implements EventListener { LOGGER.info("OFFICIAL PLUGIN - Nitro Websockets has started!"); LOGGER.info("Nitro Websockets Listening on " + (wsChannelHandler.isSSL() ? "wss://" : "ws://") + Emulator.getConfig().getValue("ws.nitro.host", "0.0.0.0") + ":" + Emulator.getConfig().getInt("ws.nitro.port", 2096)); } + + @EventHandler + public void onUserGetIPEvent(UserGetIPAddressEvent e) { + Channel channel = e.habbo.getClient().getChannel(); + if(channel != null && channel.hasAttr(main.WS_IP)) { + String ip = channel.attr(main.WS_IP).get(); + if(!ip.isEmpty()) { + e.setUpdatedIp(ip); + } + } + } } diff --git a/src/main/java/org/krews/plugin/nitro/websockets/NetworkChannelInitializer.java b/src/main/java/org/krews/plugin/nitro/websockets/NetworkChannelInitializer.java index 07cafdd..a8c46b7 100644 --- a/src/main/java/org/krews/plugin/nitro/websockets/NetworkChannelInitializer.java +++ b/src/main/java/org/krews/plugin/nitro/websockets/NetworkChannelInitializer.java @@ -14,6 +14,7 @@ import io.netty.handler.logging.LoggingHandler; import io.netty.handler.ssl.SslContext; import io.netty.handler.timeout.IdleStateHandler; import org.krews.plugin.nitro.websockets.codec.WebSocketCodec; +import org.krews.plugin.nitro.websockets.handlers.CustomHTTPHandler; import org.krews.plugin.nitro.websockets.ssl.SSLCertificateLoader; public class NetworkChannelInitializer extends ChannelInitializer { @@ -37,6 +38,7 @@ public class NetworkChannelInitializer extends ChannelInitializer } ch.pipeline().addLast("httpCodec", new HttpServerCodec()); ch.pipeline().addLast("objectAggregator", new HttpObjectAggregator(65536)); + ch.pipeline().addLast("customHttpHandler", new CustomHTTPHandler()); ch.pipeline().addLast("protocolHandler", new WebSocketServerProtocolHandler("/", true)); ch.pipeline().addLast("websocketCodec", new WebSocketCodec()); diff --git a/src/main/java/org/krews/plugin/nitro/websockets/codec/WebSocketCodec.java b/src/main/java/org/krews/plugin/nitro/websockets/codec/WebSocketCodec.java index 3bf7960..d8c6059 100644 --- a/src/main/java/org/krews/plugin/nitro/websockets/codec/WebSocketCodec.java +++ b/src/main/java/org/krews/plugin/nitro/websockets/codec/WebSocketCodec.java @@ -1,23 +1,14 @@ package org.krews.plugin.nitro.websockets.codec; -import com.eu.habbo.Emulator; import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageCodec; -import io.netty.handler.codec.http.websocketx.*; -import org.krews.plugin.nitro.main; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; +import io.netty.handler.codec.http.websocketx.WebSocketFrame; -import java.net.URI; -import java.net.URISyntaxException; import java.util.List; public class WebSocketCodec extends MessageToMessageCodec { - - private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketCodec.class); - @Override protected void encode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { out.add(new BinaryWebSocketFrame(in).retain()); @@ -27,43 +18,5 @@ public class WebSocketCodec extends MessageToMessageCodec out) { out.add(in.content().retain()); } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - // only allow websockets connections from the whitelist - if(evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) { - WebSocketServerProtocolHandler.HandshakeComplete handshake = (WebSocketServerProtocolHandler.HandshakeComplete)evt; - String origin = getDomainName(handshake.requestHeaders().get("Origin")); - - if(!isWhitelisted(origin)) { - ctx.channel().writeAndFlush(new CloseWebSocketFrame(403, "Origin forbidden")).addListener(ChannelFutureListener.CLOSE); - } - } - else { - super.userEventTriggered(ctx, evt); - } - } - - public static String getDomainName(String url) throws URISyntaxException { - URI uri = new URI(url); - String domain = uri.getHost(); - return domain.startsWith("www.") ? domain.substring(4) : domain; - } - - public static boolean isWhitelisted(String origin) { - String[] allowedOrigins = Emulator.getConfig().getValue("websockets.whitelist", "localhost").split(","); - for(String entry : allowedOrigins) { - if(entry.startsWith("*")) { - if(origin.endsWith(entry.substring(1)) || ("." + origin).equals(entry.substring(1))) return true; - } else { - if(origin.equals(entry)) return true; - } - } - - if(Emulator.getConfig().getBoolean("debug.mode")) { - LOGGER.info("Origin not allowed: " + origin); - } - return false; - } } diff --git a/src/main/java/org/krews/plugin/nitro/websockets/handlers/CustomHTTPHandler.java b/src/main/java/org/krews/plugin/nitro/websockets/handlers/CustomHTTPHandler.java new file mode 100644 index 0000000..16153f1 --- /dev/null +++ b/src/main/java/org/krews/plugin/nitro/websockets/handlers/CustomHTTPHandler.java @@ -0,0 +1,53 @@ +package org.krews.plugin.nitro.websockets.handlers; + +import com.eu.habbo.Emulator; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.handler.codec.http.*; +import io.netty.util.ReferenceCountUtil; +import org.krews.plugin.nitro.Utils; +import org.krews.plugin.nitro.main; + +public class CustomHTTPHandler extends ChannelInboundHandlerAdapter { + private static final String ORIGIN_HEADER = "Origin"; + //private static final String FORWARD_PROXY_REALIP = "X-Forwarded-For"; + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + if(msg instanceof HttpMessage) { + if(!handleHttpRequest(ctx, (HttpMessage) msg)) + { + ReferenceCountUtil.release(msg);//discard message + return; + } + } + super.channelRead(ctx, msg); + ctx.pipeline().remove(this); + } + + public boolean handleHttpRequest(ChannelHandlerContext ctx, HttpMessage req) { + String origin = "error"; + + try { + if(req.headers().contains(ORIGIN_HEADER)) { + origin = Utils.getDomainNameFromUrl(req.headers().get(ORIGIN_HEADER)); + } + } catch (Exception ignored) { } + + if(!Utils.isWhitelisted(origin, Emulator.getConfig().getValue("websockets.whitelist", "localhost").split(","))) { + FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.FORBIDDEN, Unpooled.wrappedBuffer("Origin forbidden".getBytes())); + ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); + return false; + } + + String header = Emulator.getConfig().getValue("ws.nitro.ip.header", ""); + + if(!header.isEmpty() && req.headers().contains(header)) { + String ip = req.headers().get(header); + ctx.channel().attr(main.WS_IP).set(ip); + } + return true; + } +}