1
0
mirror of https://github.com/GeyserMC/Floodgate.git synced 2025-12-19 14:59:20 +00:00

Fixes #9 and added a debug option for the bungee plugin

This commit is contained in:
Tim203
2020-03-08 23:18:30 +01:00
parent 446dee699c
commit f0dba0e1f5
7 changed files with 179 additions and 22 deletions

View File

@@ -2,7 +2,7 @@ package org.geysermc.floodgate;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.channel.SimpleChannelInboundHandler;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.geysermc.floodgate.HandshakeHandler.HandshakeResult; import org.geysermc.floodgate.HandshakeHandler.HandshakeResult;
import org.geysermc.floodgate.injector.BukkitInjector; import org.geysermc.floodgate.injector.BukkitInjector;
@@ -14,13 +14,12 @@ import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.util.List;
import java.util.UUID; import java.util.UUID;
import static org.geysermc.floodgate.util.ReflectionUtil.*; import static org.geysermc.floodgate.util.ReflectionUtil.*;
@RequiredArgsConstructor @RequiredArgsConstructor
public class PacketHandler extends MessageToMessageDecoder<Object> { public class PacketHandler extends SimpleChannelInboundHandler<Object> {
private static BukkitPlugin plugin = BukkitPlugin.getInstance(); private static BukkitPlugin plugin = BukkitPlugin.getInstance();
private static HandshakeHandler handshakeHandler; private static HandshakeHandler handshakeHandler;
@@ -52,7 +51,7 @@ public class PacketHandler extends MessageToMessageDecoder<Object> {
private boolean bungee; private boolean bungee;
@Override @Override
protected void decode(ChannelHandlerContext ctx, Object packet, List<Object> out) throws Exception { protected void channelRead0(ChannelHandlerContext ctx, Object packet) throws Exception {
boolean isHandhake = handshakePacketClass.isInstance(packet); boolean isHandhake = handshakePacketClass.isInstance(packet);
boolean isLogin = loginStartPacketClass.isInstance(packet); boolean isLogin = loginStartPacketClass.isInstance(packet);
@@ -94,7 +93,6 @@ public class PacketHandler extends MessageToMessageDecoder<Object> {
setValue(networkManager, getFieldOfType(networkManagerClass, SocketAddress.class, false), newAddress); setValue(networkManager, getFieldOfType(networkManagerClass, SocketAddress.class, false), newAddress);
} }
plugin.getLogger().info("Added " + fPlayer.getJavaUsername() + " " + fPlayer.getJavaUniqueId()); plugin.getLogger().info("Added " + fPlayer.getJavaUsername() + " " + fPlayer.getJavaUniqueId());
out.add(packet);
} else if (isLogin) { } else if (isLogin) {
if (!bungee) { if (!bungee) {
// we have to fake the offline player cycle // we have to fake the offline player cycle
@@ -109,9 +107,12 @@ public class PacketHandler extends MessageToMessageDecoder<Object> {
setValue(loginListener, protocolStateField, readyToAcceptState); // LoginLister#protocolState = READY_TO_ACCEPT setValue(loginListener, protocolStateField, readyToAcceptState); // LoginLister#protocolState = READY_TO_ACCEPT
// The tick of LoginListener will do the rest // The tick of LoginListener will do the rest
} }
// out.add(packet); don't let this packet through as we want to skip the login cycle
} }
} finally { } finally {
// don't let the packet through if the packet is the login packet
// because we want to skip the login cycle
if (!isLogin) ctx.fireChannelRead(packet);
if (isHandhake && bungee || isLogin && !bungee || fPlayer == null) { if (isHandhake && bungee || isLogin && !bungee || fPlayer == null) {
// remove the injection of the client because we're finished // remove the injection of the client because we're finished
BukkitInjector.removeInjectedClient(future, ctx.channel()); BukkitInjector.removeInjectedClient(future, ctx.channel());

View File

@@ -25,6 +25,12 @@
<version>${bungee-version}</version> <version>${bungee-version}</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-protocol</artifactId>
<version>1.15-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>org.geysermc</groupId> <groupId>org.geysermc</groupId>
<artifactId>floodgate-common</artifactId> <artifactId>floodgate-common</artifactId>

View File

@@ -0,0 +1,108 @@
package org.geysermc.floodgate;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.*;
import io.netty.channel.socket.ServerSocketChannel;
import io.netty.handler.codec.MessageToByteEncoder;
import lombok.RequiredArgsConstructor;
import net.md_5.bungee.protocol.MinecraftEncoder;
import net.md_5.bungee.protocol.Varint21LengthFieldPrepender;
import org.geysermc.floodgate.util.ReflectionUtil;
import java.lang.reflect.Field;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
public class BungeeDebugger {
public BungeeDebugger() {
init();
}
private void init() {
Class<?> pipelineUtils = ReflectionUtil.getPrefixedClass("netty.PipelineUtils");
Field framePrepender = ReflectionUtil.getField(pipelineUtils, "framePrepender");
ReflectionUtil.setFinalValue(null, framePrepender, new CustomVarint21LengthFieldPrepender());
}
@ChannelHandler.Sharable
private static class CustomVarint21LengthFieldPrepender extends Varint21LengthFieldPrepender {
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
// we're getting called before the encoder and decoder are added,
// so we have to wait with a nice while loop :D
ctx.executor().execute(() -> {
System.out.println("Channel: " + ctx.channel().isActive() + " " + ctx.channel().isOpen() + " " + ctx.channel().isRegistered());
while (ctx.channel().isOpen()) {
System.out.println("Trying to find decoder for " + getHostString(ctx, true) + " " + getParentName(ctx, true));
if (ctx.channel().pipeline().get(MinecraftEncoder.class) != null) {
System.out.println("Found decoder for " + getHostString(ctx, true));
ctx.channel().pipeline().addLast(
"floodgate-debug-init",
new BungeeChannelInitializer(
ctx.channel().parent() instanceof ServerSocketChannel
)
);
break;
}
}
});
}
}
public static String getHostString(ChannelHandlerContext ctx, boolean alwaysString) {
SocketAddress address = ctx.channel().remoteAddress();
return address != null ? ((InetSocketAddress) address).getHostString() : (alwaysString ? "null" : null);
}
public static String getParentName(ChannelHandlerContext ctx, boolean alwaysString) {
Channel parent = ctx.channel().parent();
return parent != null ? parent.getClass().getSimpleName() : (alwaysString ? "null" : null);
}
public static int executorsCount(ChannelHandlerContext ctx) {
return ((MultithreadEventLoopGroup) ctx.channel().eventLoop().parent()).executorCount();
}
@RequiredArgsConstructor
private static class BungeeChannelInitializer extends ChannelInitializer<Channel> {
private final boolean player;
@Override
protected void initChannel(Channel channel) throws Exception {
System.out.println("Init " + (player ? "Bungee" : "Server"));
// can't add our debugger when the inbound-boss is missing
if (channel.pipeline().get("packet-encoder") == null) return;
if (channel.pipeline().get("packet-decoder") == null) return;
channel.pipeline()
.addBefore("packet-decoder", "floodgate-debug-in", new BungeeInPacketHandler(player))
.addBefore("packet-encoder", "floodgate-debug-out", new BungeeOutPacketHandler(player));
}
}
@RequiredArgsConstructor
private static class BungeeInPacketHandler extends SimpleChannelInboundHandler<ByteBuf> {
private final boolean player;
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
int index = msg.readerIndex();
System.out.println((player ? "Player -> Bungee" : "Server -> Bungee") + ":\n" + ByteBufUtil.prettyHexDump(msg));
msg.readerIndex(index);
ctx.fireChannelRead(msg.retain());
}
}
@RequiredArgsConstructor
private static class BungeeOutPacketHandler extends MessageToByteEncoder<ByteBuf> {
private final boolean player;
@Override
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception {
int index = msg.readerIndex();
System.out.println((player ? "Bungee -> Player" : "Bungee -> Server") + ":\n" + ByteBufUtil.prettyHexDump(msg));
msg.readerIndex(index);
out.writeBytes(msg);
}
}
}

View File

@@ -25,6 +25,7 @@ public class BungeePlugin extends Plugin implements Listener {
private static Field extraHandshakeData; private static Field extraHandshakeData;
@Getter private BungeeFloodgateConfig config; @Getter private BungeeFloodgateConfig config;
private BungeeDebugger debugger;
private HandshakeHandler handshakeHandler; private HandshakeHandler handshakeHandler;
@Override @Override
@@ -40,6 +41,16 @@ public class BungeePlugin extends Plugin implements Listener {
@Override @Override
public void onEnable() { public void onEnable() {
getProxy().getPluginManager().registerListener(this, this); getProxy().getPluginManager().registerListener(this, this);
if (config.isDebug()) {
debugger = new BungeeDebugger();
}
}
@Override
public void onDisable() {
if (config.isDebug()) {
getLogger().warning("Please note that it is not possible to reload this plugin when debug mode is enabled. At least for now");
}
} }
@EventHandler(priority = EventPriority.LOW) @EventHandler(priority = EventPriority.LOW)
@@ -93,15 +104,15 @@ public class BungeePlugin extends Plugin implements Listener {
SocketAddress remoteAddress = ReflectionUtil.getCastedValue(channelWrapper, "remoteAddress", SocketAddress.class); SocketAddress remoteAddress = ReflectionUtil.getCastedValue(channelWrapper, "remoteAddress", SocketAddress.class);
if (!(remoteAddress instanceof InetSocketAddress)) { if (!(remoteAddress instanceof InetSocketAddress)) {
getLogger().info( getLogger().info(
"Player " + player.getUsername() + " doesn't use a InetSocketAddress. " + "Player " + player.getUsername() + " doesn't use an InetSocketAddress. " +
"It uses " + remoteAddress.getClass().getSimpleName() + ". Ignoring the player, I guess." "It uses " + remoteAddress.getClass().getSimpleName() + ". Ignoring the player, I guess."
); );
return; } else {
ReflectionUtil.setValue(
channelWrapper, "remoteAddress",
new InetSocketAddress(result.getBedrockData().getIp(), ((InetSocketAddress) remoteAddress).getPort())
);
} }
ReflectionUtil.setValue(
channelWrapper, "remoteAddress",
new InetSocketAddress(result.getBedrockData().getIp(), ((InetSocketAddress) remoteAddress).getPort())
);
event.completeIntent(this); event.completeIntent(this);
}); });
} }
@@ -117,7 +128,8 @@ public class BungeePlugin extends Plugin implements Listener {
} }
static { static {
Class<?> initial_handler = ReflectionUtil.getClass("net.md_5.bungee.connection.InitialHandler"); ReflectionUtil.setPrefix("net.md_5.bungee");
Class<?> initial_handler = ReflectionUtil.getPrefixedClass("connection.InitialHandler");
extraHandshakeData = ReflectionUtil.getField(initial_handler, "extraDataInHandshake"); extraHandshakeData = ReflectionUtil.getField(initial_handler, "extraDataInHandshake");
} }
} }

View File

@@ -26,6 +26,9 @@ public class FloodgateConfig {
@JsonProperty(value = "disconnect") @JsonProperty(value = "disconnect")
private DisconnectMessages messages; private DisconnectMessages messages;
@JsonProperty
private boolean debug;
@JsonIgnore @JsonIgnore
PrivateKey privateKey = null; PrivateKey privateKey = null;

View File

@@ -1,9 +1,6 @@
package org.geysermc.floodgate; package org.geysermc.floodgate;
import lombok.AccessLevel; import lombok.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NonNull;
import org.geysermc.floodgate.util.BedrockData; import org.geysermc.floodgate.util.BedrockData;
import org.geysermc.floodgate.util.EncryptionUtil; import org.geysermc.floodgate.util.EncryptionUtil;
@@ -53,7 +50,7 @@ public class HandshakeHandler {
} }
@AllArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor(access = AccessLevel.PROTECTED)
@Getter @Getter @ToString
public static class HandshakeResult { public static class HandshakeResult {
private ResultType resultType; private ResultType resultType;
private String[] handshakeData; private String[] handshakeData;

View File

@@ -3,10 +3,7 @@ package org.geysermc.floodgate.util;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import java.lang.reflect.AccessibleObject; import java.lang.reflect.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectionUtil { public class ReflectionUtil {
/** /**
@@ -94,6 +91,39 @@ public class ReflectionUtil {
} }
} }
public static boolean setFinalValue(Object instance, Field field, Object value) {
try {
makeAccessible(field);
Field modifiersField = null;
int modifiers = field.getModifiers();
if (Modifier.isFinal(modifiers)) {
try {
modifiersField = Field.class.getDeclaredField("modifiers");
} catch (NoSuchFieldException e) {
// Java 12 compatibility, thanks to https://github.com/powermock/powermock/pull/1010
Method getDeclaredFields0 = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class);
makeAccessible(getDeclaredFields0);
Field[] fields = (Field[]) getDeclaredFields0.invoke(Field.class, false);
for (Field classField : fields) {
if ("modifiers".equals(classField.getName())) {
modifiersField = classField;
break;
}
}
}
assert modifiersField != null;
makeAccessible(modifiersField);
modifiersField.setInt(field, modifiers & ~Modifier.FINAL);
}
setValue(instance, field, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public static Method getMethod(Class<?> clazz, String method, Class<?>... args) { public static Method getMethod(Class<?> clazz, String method, Class<?>... args) {
try { try {
return clazz.getMethod(method, args); return clazz.getMethod(method, args);