Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## 1.0.9 — FastLoginPlus native login channel association

- Associate the ProtocolLib `TemporaryPlayer` generated during PacketEvents pre-login callbacks with its actual Netty injector instead of leaving `TemporaryPlayerFactory.getInjectorFromPlayer()` null. FastLoginPlus uses that accessor during Mojang session verification.
- Resolve the real `net.minecraft.network.Connection` from the channel's existing `packet_handler`, and update cached Netty injectors that were created before the native handler was installed. FastLoginPlus needs the genuine Connection (not the bare Netty Channel) to enable packet encryption.
- Explicitly refuse binding a non-active channel or an injector lacking a native Connection; never fall back to cracked login in P2P code and never circumvent Mojang verification.
- Handle FastLoginPlus's legacy method lookup `a(SecretKey)` on remapped Minecraft 1.21.11 where the native Connection method is named `setEncryptionKey(SecretKey)`; fallback only for one unambiguous void/SecretKey method.
- Keep v1.0.8's encryption wrapper constructor fix and v1.0.7's temporary-user receive/send transport.

## 1.0.8 — Constructor-safe PacketEvents wrapper allocation

- Fix `NullPointerException: wrappper.bufferLock is null` when FastLoginPlus sends `LOGIN.SERVER.ENCRYPTION_REQUEST` with a `PacketContainer` constructed from scratch.
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib2PacketEvents</artifactId>
<version>1.0.8</version>
<version>1.0.9</version>
<packaging>jar</packaging>

<name>ProtocolLib2PacketEvents</name>
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/com/comphenix/protocol/ProtocolLib.java
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,14 @@ public void onPlayerQuit(PlayerQuitEvent event) {
}
}

/** Resolve the pre-login facade to its native connection for auth plugins. */
public com.comphenix.protocol.injector.netty.Injector getTemporaryInjector(
com.github.retrooper.packetevents.protocol.player.User user,
org.bukkit.entity.Player temporary) {
return networkManagerInjector == null ? null
: networkManagerInjector.getTemporaryInjector(user, temporary);
}

@Override
public void onDisable() {
if (internalListener != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@ public Injector fromPlayer(Player player) {
public Injector fromChannel(Channel channel) {
if (channel == null) return EmptyInjector.WITHOUT_PLAYER;
Injector existing = byChannel.get(channel);
if (existing != null) return existing;
if (existing != null) {
if (existing instanceof NettyChannelInjector netty) netty.refreshNativeConnection();
return existing;
}
Injector created = new NettyChannelInjector(null, null, channel, listeners, this, reporter);
Injector previous = byChannel.putIfAbsent(channel, created);
return previous == null ? created : previous;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ public class NettyChannelInjector implements Injector {
private static final String ENCODER = "p2p-wire-encoder";
private static final String OUTBOUND = "p2p-outbound";
private volatile Player player;
private final Object networkManager;
// The Netty channel is discovered before the native Minecraft Connection
// handler may be in the pipeline. Refresh it as soon as the first LOGIN
// packet arrives so FastLoginPlus can enable AES encryption on the real
// NMS Connection. It must not be the raw Netty Channel or a dummy handle.
private volatile Object networkManager;
private final Channel channel;
private final ListenerManager listenerManager;
private final InjectionFactory factory;
Expand All @@ -49,11 +53,34 @@ public NettyChannelInjector(Player player, Object networkManager, Channel channe
this.player = player;
this.networkManager = networkManager;
this.channel = channel;
refreshNativeConnection();
this.listenerManager = listenerManager;
this.factory = factory;
this.reporter = reporter;
this.protocol = player == null ? PacketType.Protocol.HANDSHAKING : PacketType.Protocol.PLAY;
}
/**
* Find the actual net.minecraft.network.Connection handler already installed
* by the Minecraft server. No field/type guessing, no reflection into the
* packet pipeline and no mutation of its handler order.
*/
public void refreshNativeConnection() {
if (networkManager != null || channel == null) return;
io.netty.channel.ChannelHandler packetHandler = channel.pipeline().get("packet_handler");
if (packetHandler == null) return;
for (Class<?> clazz = packetHandler.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
if ("net.minecraft.network.Connection".equals(clazz.getName())) {
networkManager = packetHandler;
return;
}
}
}

public boolean hasNativeConnection() {
refreshNativeConnection();
return networkManager != null;
}

public SocketAddress getAddress() { return channel == null ? null : channel.remoteAddress(); }
public int getProtocolVersion() {
if (player == null || PacketEvents.getAPI() == null) return -1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,29 @@ public NetworkManagerInjector(Plugin plugin, ListenerManager listenerManager, Er

public Injector getInjector(Player player) { return factory.fromPlayer(player); }

/**
* ProtocolLib's temporary player is not a CraftPlayer, so never pass it to
* PacketEvents PlayerManager.getChannel(Player): resolve the channel from
* the pre-login User instead. FastLoginPlus relies on this exact facade's
* getInjectorFromPlayer() to locate the real Netty channel and NMS
* Connection for session verification and encryption.
*/
public Injector getTemporaryInjector(com.github.retrooper.packetevents.protocol.player.User user,
Player temporary) {
if (closed || user == null || temporary == null) return null;
if (!(user.getChannel() instanceof Channel channel) || !channel.isActive()) return null;
Injector injector = factory.fromChannel(channel);
if (!(injector instanceof com.comphenix.protocol.injector.netty.channel.NettyChannelInjector netty)) {
return null;
}
netty.refreshNativeConnection();
// A stale/partial channel cannot authenticate a login. Fail closed:
// don't give FastLoginPlus an injector whose networkManager is null.
if (!netty.hasNativeConnection()) return null;
netty.setPlayer(temporary);
return netty;
}

public void invalidate(Player player) {
if (player == null) return;
Injector injector = factory.invalidate(player);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ public static Player create(User user) {
}
Player player = TemporaryPlayerFactory.createTemporaryPlayer();
USERS.put(player, user);
// The pre-login facade must carry the same native Connection as its
// PacketEvents User. Without it, FastLoginPlus's ProtocolLibCompat
// considers every player disconnected during Mojang session checks.
org.bukkit.plugin.Plugin plugin = com.comphenix.protocol.ProtocolLibrary.getPlugin();
if (plugin instanceof com.comphenix.protocol.ProtocolLib bridge) {
com.comphenix.protocol.injector.netty.Injector injector =
bridge.getTemporaryInjector(user, player);
if (injector != null) TemporaryPlayerFactory.setInjectorForPlayer(player, injector);
}
return player;
}

Expand Down
18 changes: 18 additions & 0 deletions src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,24 @@ public Method getMethodByParameters(String name, Class<?>... parameters) {
return accessible(method);
}
}
// FastLoginPlus still asks for the obfuscated Minecraft Connection
// method "a(SecretKey)". On remapped Paper/Leaf 1.21.11 the equivalent
// is named setEncryptionKey(SecretKey). Resolve by exact signature only
// when it is unique: never guess an arbitrary encryption/packet method.
if ("a".equals(name) && parameters.length == 1
&& parameters[0] == javax.crypto.SecretKey.class) {
Method found = null;
for (Method method : getMethods()) {
if (!java.util.Arrays.equals(method.getParameterTypes(), parameters)
|| method.getReturnType() != void.class) continue;
if (found != null && !found.equals(method)) {
throw new IllegalArgumentException("Ambiguous SecretKey encryption methods on "
+ source.getName());
}
found = method;
}
if (found != null) return accessible(found);
}
throw new IllegalArgumentException("Unable to find method " + name + " on " + source.getName());
}

Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/plugin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ main: com.comphenix.protocol.ProtocolLib
version: 5.4.0
author: CyoriaSMP Team
description: A drop-in ProtocolLib compatibility layer powered by PacketEvents.
p2p-version: 1.0.8
p2p-version: 1.0.9
protocol-lib-api-version: 5.4.0
depend: [packetevents]
provides: [ProtocolLib]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.comphenix.protocol.injector.netty.channel;

import io.netty.channel.embedded.EmbeddedChannel;
import java.lang.reflect.Field;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class PreLoginNativeConnectionTest {
@Test
void findsLateNativePacketHandlerForFastLoginCipher() throws Exception {
EmbeddedChannel channel = new EmbeddedChannel();
NettyChannelInjector injector = new NettyChannelInjector(null, null, channel, null, null, null);
assertFalse(injector.hasNativeConnection(), "cannot fabricate a native NMS Connection");
net.minecraft.network.Connection nativeConn = new net.minecraft.network.Connection();
channel.pipeline().addLast("packet_handler", nativeConn);
injector.refreshNativeConnection();
assertTrue(injector.hasNativeConnection());
Field field = NettyChannelInjector.class.getDeclaredField("networkManager");
field.setAccessible(true);
assertSame(nativeConn, field.get(injector), "FastLoginPlus must receive actual NMS packet handler");
channel.finishAndReleaseAll();
}

@Test
void neverConfusesOtherChannelHandlerWithNmsConnection() {
EmbeddedChannel channel = new EmbeddedChannel();
NettyChannelInjector injector = new NettyChannelInjector(null, null, channel, null, null, null);
channel.pipeline().addLast("packet_handler", new io.netty.channel.ChannelInboundHandlerAdapter());
assertFalse(injector.hasNativeConnection());
channel.finishAndReleaseAll();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.comphenix.protocol.reflect;

import javax.crypto.SecretKey;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class FuzzyReflectionSecretKeyTest {
public static class ModernConnection {
public void setEncryptionKey(SecretKey key) { }
}
public static class AmbiguousConnection {
public void setEncryptionKey(SecretKey key) { }
public void anotherMethod(SecretKey key) { }
}
public static class OtherMethod {
public void send(Object obj) { }
}

@Test
void remappedModernMinecraftKeySetterIsDiscoverableByOldName() {
var result = FuzzyReflection.fromClass(ModernConnection.class)
.getMethodByParameters("a", SecretKey.class);
assertEquals("setEncryptionKey", result.getName());
}

@Test
void ambiguousCryptoMethodsDoNotGetPickedArbitrarily() {
assertThrows(IllegalArgumentException.class,
() -> FuzzyReflection.fromClass(AmbiguousConnection.class)
.getMethodByParameters("a", SecretKey.class));
}

@Test
void normalMethodNameLookupStaysStrict() {
assertThrows(IllegalArgumentException.class,
() -> FuzzyReflection.fromClass(OtherMethod.class)
.getMethodByParameters("a", Object.class));
}
}
4 changes: 4 additions & 0 deletions src/test/java/net/minecraft/network/Connection.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package net.minecraft.network;

/** Test-only stand-in for Paper's registered packet_handler. */
public class Connection extends io.netty.channel.ChannelInboundHandlerAdapter { }
Loading