Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@
import com.velocitypowered.api.event.player.CookieReceiveEvent;
import com.velocitypowered.api.event.player.GameProfileRequestEvent;
import com.velocitypowered.api.event.player.PlayerChooseInitialServerEvent;
import com.velocitypowered.api.network.HandshakeIntent;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.permission.PermissionFunction;
import com.velocitypowered.api.proxy.crypto.IdentifiedKey;
import com.velocitypowered.api.proxy.player.ResourcePackInfo;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.util.GameProfile;
import com.velocitypowered.api.util.UuidUtils;
Expand All @@ -38,13 +40,17 @@
import com.velocitypowered.proxy.config.VelocityConfiguration;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.connection.player.resourcepack.ResourcePackTransfer;
import com.velocitypowered.proxy.crypto.IdentifiedKeyImpl;
import com.velocitypowered.proxy.protocol.StateRegistry;
import com.velocitypowered.proxy.protocol.packet.LoginAcknowledgedPacket;
import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccessPacket;
import com.velocitypowered.proxy.protocol.packet.ServerboundCookieResponsePacket;
import com.velocitypowered.proxy.protocol.packet.SetCompressionPacket;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.CodecException;
import java.security.SignatureException;
import java.util.Collection;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
Expand All @@ -68,15 +74,17 @@ public class AuthSessionHandler implements MinecraftSessionHandler {
private GameProfile profile;
private @MonotonicNonNull ConnectedPlayer connectedPlayer;
private final boolean onlineMode;
private final CompletableFuture<byte[]> appliedResourcePacksFuture;
private State loginState = State.START; // 1.20.2+

AuthSessionHandler(VelocityServer server, LoginInboundConnection inbound,
GameProfile profile, boolean onlineMode) {
AuthSessionHandler(VelocityServer server, LoginInboundConnection inbound, GameProfile profile, boolean onlineMode,
CompletableFuture<byte[]> appliedResourcePacksFuture) {
this.server = Preconditions.checkNotNull(server, "server");
this.inbound = Preconditions.checkNotNull(inbound, "inbound");
this.profile = Preconditions.checkNotNull(profile, "profile");
this.onlineMode = onlineMode;
this.mcConnection = inbound.delegatedConnection();
this.appliedResourcePacksFuture = appliedResourcePacksFuture;
}

@Override
Expand Down Expand Up @@ -112,7 +120,7 @@ public void activated() {

return server.getEventManager()
.fire(new PermissionsSetupEvent(player, ConnectedPlayer.DEFAULT_PERMISSIONS))
.thenAcceptAsync(event -> {
.thenAcceptBothAsync(appliedResourcePacksFuture, (event, appliedResourcePacks) -> {
if (!mcConnection.isClosed()) {
// wait for permissions to load, then set the players permission function
final PermissionFunction function = event.createFunction(player);
Expand All @@ -124,6 +132,7 @@ public void activated() {
} else {
player.setPermissionFunction(function);
}
loadAppliedResourcePacks(player, appliedResourcePacks);
startLoginCompletion(player);
}
}, mcConnection.eventLoop());
Expand All @@ -133,6 +142,22 @@ public void activated() {
});
}

private void loadAppliedResourcePacks(ConnectedPlayer player, byte[] cookieData) {
if (player.getHandshakeIntent() != HandshakeIntent.TRANSFER || cookieData == null) {
return;
}

try {
Collection<ResourcePackInfo> appliedResourcePacks = ResourcePackTransfer.decodeAndValidateCookieData(
server.getConfiguration().getForwardingSecret(), cookieData);
player.resourcePackHandler().loadAppliedResourcePacks(appliedResourcePacks);
} catch (SignatureException e) {
logger.warn("Signature error while loading applied resource packs of {}", player.getUsername(), e);
} catch (IndexOutOfBoundsException | CodecException e) {
logger.warn("Error while decoding applied resource packs of {}", player.getUsername(), e);
}
}

private void startLoginCompletion(ConnectedPlayer player) {
int threshold = server.getConfiguration().getCompressionThreshold();
if (threshold >= 0 && mcConnection.getProtocolVersion().noLessThan(MINECRAFT_1_8)) {
Expand Down Expand Up @@ -160,7 +185,7 @@ private void startLoginCompletion(ConnectedPlayer player) {
}
}
} else {
logger.warn("A custom key type has been set for player " + player.getUsername());
logger.warn("A custom key type has been set for player {}", player.getUsername());
}
} else {
if (!Objects.equals(playerKey.getSignatureHolder(), playerUniqueId)) {
Expand Down Expand Up @@ -194,6 +219,11 @@ public boolean handle(LoginAcknowledgedPacket packet) {

@Override
public boolean handle(ServerboundCookieResponsePacket packet) {
if (packet.getKey().equals(ResourcePackTransfer.APPLIED_RESOURCE_PACKS_KEY)) {
appliedResourcePacksFuture.complete(packet.getPayload());
return true;
}

server.getEventManager()
.fire(new CookieReceiveEvent(connectedPlayer, packet.getKey(), packet.getPayload()))
.thenAcceptAsync(event -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation;
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
import com.velocitypowered.proxy.connection.player.bundle.BundleDelimiterHandler;
import com.velocitypowered.proxy.connection.player.resourcepack.ResourcePackTransfer;
import com.velocitypowered.proxy.connection.player.resourcepack.VelocityResourcePackInfo;
import com.velocitypowered.proxy.connection.player.resourcepack.handler.ResourcePackHandler;
import com.velocitypowered.proxy.connection.util.ConnectionMessages;
Expand Down Expand Up @@ -1028,12 +1029,21 @@ public void transferToHost(final InetSocketAddress address) {
if (resultedAddress == null) {
resultedAddress = address;
}
connection.write(new TransferPacket(
resultedAddress.getHostName(), resultedAddress.getPort()));
storeAppliedPacks();
connection.write(new TransferPacket(resultedAddress.getHostName(), resultedAddress.getPort()));
}
});
}

private void storeAppliedPacks() {
if (connection.getState() != StateRegistry.PLAY && connection.getState() != StateRegistry.CONFIG) {
return;
}
Collection<ResourcePackInfo> appliedResourcePacks = resourcePackHandler.getAppliedResourcePacks();
byte[] cookieData = ResourcePackTransfer.createCookieData(server.getConfiguration().getForwardingSecret(), appliedResourcePacks);
connection.write(new ClientboundStoreCookiePacket(ResourcePackTransfer.APPLIED_RESOURCE_PACKS_KEY, cookieData));
}

@Override
public void storeCookie(final Key key, final byte[] data) {
Preconditions.checkNotNull(key);
Expand All @@ -1042,8 +1052,7 @@ public void storeCookie(final Key key, final byte[] data) {
this.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_20_5),
"Player version must be at least 1.20.5 to be able to store cookies");

if (connection.getState() != StateRegistry.PLAY
&& connection.getState() != StateRegistry.CONFIG) {
if (connection.getState() != StateRegistry.PLAY && connection.getState() != StateRegistry.CONFIG) {
throw new IllegalStateException("Can only store cookie in CONFIGURATION or PLAY protocol");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,23 @@
import com.google.common.primitives.Longs;
import com.velocitypowered.api.event.connection.PreLoginEvent;
import com.velocitypowered.api.event.connection.PreLoginEvent.PreLoginComponentResult;
import com.velocitypowered.api.network.HandshakeIntent;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.crypto.IdentifiedKey;
import com.velocitypowered.api.util.GameProfile;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.connection.player.resourcepack.ResourcePackTransfer;
import com.velocitypowered.proxy.crypto.IdentifiedKeyImpl;
import com.velocitypowered.proxy.protocol.StateRegistry;
import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder;
import com.velocitypowered.proxy.protocol.packet.ClientboundCookieRequestPacket;
import com.velocitypowered.proxy.protocol.packet.EncryptionRequestPacket;
import com.velocitypowered.proxy.protocol.packet.EncryptionResponsePacket;
import com.velocitypowered.proxy.protocol.packet.LoginPluginResponsePacket;
import com.velocitypowered.proxy.protocol.packet.ServerLoginPacket;
import com.velocitypowered.proxy.protocol.packet.ServerboundCookieResponsePacket;
import com.velocitypowered.proxy.util.VelocityProperties;
import io.netty.buffer.ByteBuf;
import java.net.InetSocketAddress;
Expand All @@ -52,7 +56,9 @@
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.apache.logging.log4j.LogManager;
Expand All @@ -77,16 +83,26 @@ public class InitialLoginSessionHandler implements MinecraftSessionHandler {
private byte[] verify = EMPTY_BYTE_ARRAY;
private LoginState currentState = LoginState.LOGIN_PACKET_EXPECTED;
private final boolean forceKeyAuthentication;
private CompletableFuture<byte[]> appliedResourcePacksFuture;

InitialLoginSessionHandler(VelocityServer server, MinecraftConnection mcConnection,
LoginInboundConnection inbound) {
InitialLoginSessionHandler(VelocityServer server, MinecraftConnection mcConnection, LoginInboundConnection inbound) {
this.server = Preconditions.checkNotNull(server, "server");
this.mcConnection = Preconditions.checkNotNull(mcConnection, "mcConnection");
this.inbound = Preconditions.checkNotNull(inbound, "inbound");
this.forceKeyAuthentication = VelocityProperties.readBoolean(
"auth.forceSecureProfiles", server.getConfiguration().isForceKeyAuthentication());
}

@Override
public void activated() {
if (inbound.getHandshakeIntent() == HandshakeIntent.TRANSFER) {
appliedResourcePacksFuture = new CompletableFuture<byte[]>().completeOnTimeout(null, 20, TimeUnit.SECONDS);
mcConnection.write(new ClientboundCookieRequestPacket(ResourcePackTransfer.APPLIED_RESOURCE_PACKS_KEY));
} else {
appliedResourcePacksFuture = CompletableFuture.completedFuture(null);
}
}

@Override
public boolean handle(ServerLoginPacket packet) {
assertState(LoginState.LOGIN_PACKET_EXPECTED);
Expand Down Expand Up @@ -151,8 +167,8 @@ public boolean handle(ServerLoginPacket packet) {
this.currentState = LoginState.ENCRYPTION_REQUEST_SENT;
} else {
mcConnection.setActiveSessionHandler(StateRegistry.LOGIN,
new AuthSessionHandler(server, inbound,
GameProfile.forOfflinePlayer(login.getUsername()), false));
new AuthSessionHandler(server, inbound, GameProfile.forOfflinePlayer(login.getUsername()),
false, appliedResourcePacksFuture));
}
});
});
Expand Down Expand Up @@ -254,7 +270,7 @@ public boolean handle(EncryptionResponsePacket packet) {
}
// All went well, initialize the session.
mcConnection.setActiveSessionHandler(StateRegistry.LOGIN,
new AuthSessionHandler(server, inbound, profile, true));
new AuthSessionHandler(server, inbound, profile, true, appliedResourcePacksFuture));
} else if (response.statusCode() == 204) {
// Apparently an offline-mode user logged onto this online-mode proxy.
inbound.disconnect(
Expand Down Expand Up @@ -285,6 +301,15 @@ public boolean handle(EncryptionResponsePacket packet) {
return true;
}

@Override
public boolean handle(ServerboundCookieResponsePacket packet) {
if (packet.getKey().equals(ResourcePackTransfer.APPLIED_RESOURCE_PACKS_KEY)) {
appliedResourcePacksFuture.complete(packet.getPayload());
return true;
}
return false;
}

private EncryptionRequestPacket generateEncryptionRequest() {
byte[] verify = new byte[4];
ThreadLocalRandom.current().nextBytes(verify);
Expand Down
Loading