Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Packet cooldowns and content validation #4782

Open
wants to merge 27 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
689ca4d
EmoteListPacket RAM Overflow
OurLobanov Jun 17, 2024
70e0361
Update CodecProcessor.java
OurLobanov Jun 17, 2024
575c8fd
EmoteListPacket suk
OurLobanov Jun 17, 2024
a9973e7
text limit TextPacket CommandRequestPacket
OurLobanov Jun 17, 2024
be4a8e6
Against possible leaks memory
OurLobanov Jun 17, 2024
ea6dd5d
Package limits
OurLobanov Jun 17, 2024
74d9a3b
Merge branch 'GeyserMC:master' into exploit
OurLobanov Jun 17, 2024
cea4a42
Fix моей тупости
OurLobanov Jun 20, 2024
2e9e46d
Merge branch 'GeyserMC:master' into exploit
OurLobanov Jun 21, 2024
383d16d
ok
OurLobanov Jun 21, 2024
644cbb0
ResourcePack limit
OurLobanov Jun 21, 2024
fde62f4
soru
OurLobanov Jun 21, 2024
45f0a7f
CommandRequestPacket limit protocol
OurLobanov Jun 22, 2024
3ae2184
Merge branch 'GeyserMC:master' into exploit
OurLobanov Jun 22, 2024
a571ff2
Merge branch 'GeyserMC:master' into exploit
OurLobanov Jun 24, 2024
958d13b
Cooldown fix
OurLobanov Jun 24, 2024
e33e8b5
TextPacket limits in the protocol
OurLobanov Jun 24, 2024
ba8ae5f
Emotes now work
OurLobanov Jun 24, 2024
ac51ec1
b
OurLobanov Jun 26, 2024
c682c5a
п
OurLobanov Jun 26, 2024
d7652bf
hadle RequestNetworkSettingsPacket
OurLobanov Jun 26, 2024
e6ff354
FIXME
OurLobanov Jun 26, 2024
32380c3
add packets
OurLobanov Jun 26, 2024
1ebb1b0
del import
OurLobanov Jun 28, 2024
0f63c26
Merge branch 'GeyserMC:master' into exploit
OurLobanov Jul 4, 2024
c79b9ec
Merge branch 'GeyserMC:master' into exploit
OurLobanov Jul 7, 2024
6e0ded8
import
OurLobanov Jul 7, 2024
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 @@ -28,6 +28,7 @@
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.protocol.bedrock.data.EmoteFlag;
import org.cloudburstmc.protocol.bedrock.packet.EmotePacket;
import org.geysermc.geyser.api.entity.EntityData;
import org.geysermc.geyser.api.entity.type.GeyserEntity;
Expand Down Expand Up @@ -71,6 +72,8 @@ public void showEmote(@NonNull GeyserPlayerEntity emoter, @NonNull String emoteI
packet.setXuid("");
packet.setPlatformId(""); // BDS sends empty
packet.setEmoteId(emoteId);
packet.getFlags().add(EmoteFlag.SERVER_SIDE);
packet.getFlags().add(EmoteFlag.MUTE_EMOTE_CHAT);
session.sendUpstreamPacket(packet);
}

Expand Down
111 changes: 101 additions & 10 deletions core/src/main/java/org/geysermc/geyser/network/CodecProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,20 @@
import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.MobEquipmentSerializer_v291;
import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.PlayerHotbarSerializer_v291;
import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.SetEntityLinkSerializer_v291;
import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.SetEntityMotionSerializer_v291;
import org.cloudburstmc.protocol.bedrock.codec.v390.serializer.PlayerSkinSerializer_v390;
import org.cloudburstmc.protocol.bedrock.codec.v407.serializer.InventoryContentSerializer_v407;
import org.cloudburstmc.protocol.bedrock.codec.v407.serializer.InventorySlotSerializer_v407;
import org.cloudburstmc.protocol.bedrock.codec.v422.serializer.FilterTextSerializer_v422;
import org.cloudburstmc.protocol.bedrock.codec.v486.serializer.BossEventSerializer_v486;
import org.cloudburstmc.protocol.bedrock.codec.v554.serializer.TextSerializer_v554;
import org.cloudburstmc.protocol.bedrock.codec.v557.serializer.SetEntityDataSerializer_v557;
import org.cloudburstmc.protocol.bedrock.codec.v567.serializer.CommandRequestSerializer_v567;
import org.cloudburstmc.protocol.bedrock.codec.v630.serializer.SetPlayerInventoryOptionsSerializer_v360;
import org.cloudburstmc.protocol.bedrock.codec.v662.serializer.SetEntityMotionSerializer_v662;
import org.cloudburstmc.protocol.bedrock.codec.v685.serializer.TextSerializer_v685;
import org.cloudburstmc.protocol.bedrock.data.inventory.InventoryLayout;
import org.cloudburstmc.protocol.bedrock.data.inventory.InventoryTabLeft;
import org.cloudburstmc.protocol.bedrock.data.inventory.InventoryTabRight;
import org.cloudburstmc.protocol.bedrock.packet.AnvilDamagePacket;
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
import org.cloudburstmc.protocol.bedrock.packet.BossEventPacket;
Expand All @@ -48,11 +55,14 @@
import org.cloudburstmc.protocol.bedrock.packet.ClientCheatAbilityPacket;
import org.cloudburstmc.protocol.bedrock.packet.ClientToServerHandshakePacket;
import org.cloudburstmc.protocol.bedrock.packet.CodeBuilderSourcePacket;
import org.cloudburstmc.protocol.bedrock.packet.CommandRequestPacket;
import org.cloudburstmc.protocol.bedrock.packet.CraftingEventPacket;
import org.cloudburstmc.protocol.bedrock.packet.CreatePhotoPacket;
import org.cloudburstmc.protocol.bedrock.packet.DebugInfoPacket;
import org.cloudburstmc.protocol.bedrock.packet.EditorNetworkPacket;
import org.cloudburstmc.protocol.bedrock.packet.EmoteListPacket;
import org.cloudburstmc.protocol.bedrock.packet.EntityFallPacket;
import org.cloudburstmc.protocol.bedrock.packet.FilterTextPacket;
import org.cloudburstmc.protocol.bedrock.packet.GameTestRequestPacket;
import org.cloudburstmc.protocol.bedrock.packet.InventoryContentPacket;
import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket;
Expand All @@ -74,10 +84,12 @@
import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket;
import org.cloudburstmc.protocol.bedrock.packet.SetEntityLinkPacket;
import org.cloudburstmc.protocol.bedrock.packet.SetEntityMotionPacket;
import org.cloudburstmc.protocol.bedrock.packet.SetPlayerInventoryOptionsPacket;
import org.cloudburstmc.protocol.bedrock.packet.SettingsCommandPacket;
import org.cloudburstmc.protocol.bedrock.packet.SimpleEventPacket;
import org.cloudburstmc.protocol.bedrock.packet.SubChunkRequestPacket;
import org.cloudburstmc.protocol.bedrock.packet.SubClientLoginPacket;
import org.cloudburstmc.protocol.bedrock.packet.TextPacket;
import org.cloudburstmc.protocol.bedrock.packet.TickSyncPacket;
import org.cloudburstmc.protocol.common.util.VarInts;

Expand Down Expand Up @@ -136,6 +148,84 @@ public void deserialize(ByteBuf buffer, BedrockCodecHelper helper, InventorySlot
}
};

private static final BedrockPacketSerializer<SetPlayerInventoryOptionsPacket> SET_PLAYER_INVENTORY_OPTIONS_SERIALIZER = new SetPlayerInventoryOptionsSerializer_v360() {
@Override
public void deserialize(ByteBuf buffer, BedrockCodecHelper helper, SetPlayerInventoryOptionsPacket packet) {
int leftTabIndex = VarInts.readInt(buffer);
int rightTabIndex = VarInts.readInt(buffer);

packet.setLeftTab(leftTabIndex >= 0 && leftTabIndex < InventoryTabLeft.VALUES.length ? InventoryTabLeft.VALUES[leftTabIndex] : InventoryTabLeft.NONE);
packet.setRightTab(rightTabIndex >= 0 && rightTabIndex < InventoryTabRight.VALUES.length ? InventoryTabRight.VALUES[rightTabIndex] : InventoryTabRight.NONE);

packet.setFiltering(buffer.readBoolean());

int layoutIndex = VarInts.readInt(buffer);
packet.setLayout(layoutIndex >= 0 && layoutIndex < InventoryLayout.VALUES.length ? InventoryLayout.VALUES[layoutIndex] : InventoryLayout.NONE);

int craftingLayoutIndex = VarInts.readInt(buffer);
packet.setCraftingLayout(craftingLayoutIndex >= 0 && craftingLayoutIndex < InventoryLayout.VALUES.length ? InventoryLayout.VALUES[craftingLayoutIndex] : InventoryLayout.NONE);
}
};

private static final BedrockPacketSerializer<FilterTextPacket> FILTER_TEXT = new FilterTextSerializer_v422() {
@Override
public void deserialize(ByteBuf buffer, BedrockCodecHelper helper, FilterTextPacket packet) {
packet.setText(helper.readStringMaxLen(buffer, 513));
packet.setFromServer(buffer.readBoolean());
}
};

private static final BedrockPacketSerializer<CommandRequestPacket> COMMAND_REQUEST_SERIALIZER = new CommandRequestSerializer_v567() {
@Override
public void deserialize(ByteBuf buffer, BedrockCodecHelper helper, CommandRequestPacket packet) {
packet.setCommand(helper.readStringMaxLen(buffer, 513));
packet.setCommandOriginData(helper.readCommandOrigin(buffer));
packet.setInternal(buffer.readBoolean());
packet.setVersion(VarInts.readInt(buffer));
}
};

private static final BedrockPacketSerializer<TextPacket> TEXT_SERIALIZER_V554 = new TextSerializer_v554() {
@Override
public void deserialize(ByteBuf buffer, BedrockCodecHelper helper, TextPacket packet) {
TextPacket.Type type = TextPacket.Type.values()[buffer.readUnsignedByte()];
packet.setType(type);
packet.setNeedsTranslation(buffer.readBoolean());

if (type == TextPacket.Type.CHAT) {
packet.setSourceName(helper.readString(buffer));
//The client does not send more than 512 characters, and we do not need to decode other TextPacket.Type
packet.setMessage(helper.readStringMaxLen(buffer, 513));
} else {
throw new IllegalArgumentException("Unsupported TextType " + type);
}

packet.setXuid(helper.readString(buffer));
packet.setPlatformChatId(helper.readString(buffer));
}
};

private static final BedrockPacketSerializer<TextPacket> TEXT_SERIALIZER = new TextSerializer_v685() {
@Override
public void deserialize(ByteBuf buffer, BedrockCodecHelper helper, TextPacket packet) {
TextPacket.Type type = TextPacket.Type.values()[buffer.readUnsignedByte()];
packet.setType(type);
packet.setNeedsTranslation(buffer.readBoolean());

if (type == TextPacket.Type.CHAT) {
packet.setSourceName(helper.readString(buffer));
//The client does not send more than 512 characters, and we do not need to decode other TextPacket.Type
packet.setMessage(helper.readStringMaxLen(buffer, 513));
} else {
throw new IllegalArgumentException("Unsupported TextType " + type);
}

packet.setXuid(helper.readString(buffer));
packet.setPlatformChatId(helper.readString(buffer));
packet.setFilteredMessage(helper.readString(buffer));
}
};

/**
* Serializer that does nothing when trying to deserialize BossEventPacket since it is not used from the client.
*/
Expand Down Expand Up @@ -181,15 +271,6 @@ public void deserialize(ByteBuf buffer, BedrockCodecHelper helper, SetEntityData
}
};

/**
* Serializer that does nothing when trying to deserialize SetEntityMotionPacket since it is not used from the client for codec v291.
*/
private static final BedrockPacketSerializer<SetEntityMotionPacket> SET_ENTITY_MOTION_SERIALIZER_V291 = new SetEntityMotionSerializer_v291() {
@Override
public void deserialize(ByteBuf buffer, BedrockCodecHelper helper, SetEntityMotionPacket packet) {
}
};

/**
* Serializer that does nothing when trying to deserialize SetEntityMotionPacket since it is not used from the client for codec v662.
*/
Expand Down Expand Up @@ -251,6 +332,7 @@ static BedrockCodec processCodec(BedrockCodec codec) {
.updateSerializer(SettingsCommandPacket.class, IGNORED_SERIALIZER)
.updateSerializer(AnvilDamagePacket.class, IGNORED_SERIALIZER)
.updateSerializer(RefreshEntitlementsPacket.class, IGNORED_SERIALIZER)
.updateSerializer(EmoteListPacket.class, IGNORED_SERIALIZER)
// Illegal when serverbound due to Geyser specific setup
.updateSerializer(InventoryContentPacket.class, INVENTORY_CONTENT_SERIALIZER)
.updateSerializer(InventorySlotPacket.class, INVENTORY_SLOT_SERIALIZER)
Expand All @@ -273,10 +355,19 @@ static BedrockCodec processCodec(BedrockCodec codec) {
.updateSerializer(SimpleEventPacket.class, IGNORED_SERIALIZER)
.updateSerializer(MultiplayerSettingsPacket.class, IGNORED_SERIALIZER);


if (codec.getProtocolVersion() < 685) {
// Ignored bidirectional packets
codecBuilder.updateSerializer(TickSyncPacket.class, IGNORED_SERIALIZER);
}
codecBuilder.updateSerializer(FilterTextPacket.class, FILTER_TEXT);
codecBuilder.updateSerializer(CommandRequestPacket.class, COMMAND_REQUEST_SERIALIZER);
codecBuilder.updateSerializer(SetPlayerInventoryOptionsPacket.class, SET_PLAYER_INVENTORY_OPTIONS_SERIALIZER);
if (codec.getProtocolVersion() >= 685) {
codecBuilder.updateSerializer(TextPacket.class, TEXT_SERIALIZER);
} else {
codecBuilder.updateSerializer(TextPacket.class, TEXT_SERIALIZER_V554);
}

return codecBuilder.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,16 @@
public class LoggingPacketHandler implements BedrockPacketHandler {
protected final GeyserImpl geyser;
protected final GeyserSession session;
protected final PacketCooldownManager cooldownHandler;

LoggingPacketHandler(GeyserImpl geyser, GeyserSession session) {
this.geyser = geyser;
this.session = session;
this.cooldownHandler = new PacketCooldownManager(session);
}

PacketSignal defaultHandler(BedrockPacket packet) {
this.cooldownHandler.handle(packet);
geyser.getLogger().debug("Handled packet: " + packet.getClass().getSimpleName());
return PacketSignal.HANDLED;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package org.geysermc.geyser.network;

import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import lombok.Getter;
import lombok.Setter;
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
import org.cloudburstmc.protocol.bedrock.packet.BlockEntityDataPacket;
import org.cloudburstmc.protocol.bedrock.packet.BlockPickRequestPacket;
import org.cloudburstmc.protocol.bedrock.packet.BookEditPacket;
import org.cloudburstmc.protocol.bedrock.packet.CommandRequestPacket;
import org.cloudburstmc.protocol.bedrock.packet.EntityPickRequestPacket;
import org.cloudburstmc.protocol.bedrock.packet.FilterTextPacket;
import org.cloudburstmc.protocol.bedrock.packet.LecternUpdatePacket;
import org.cloudburstmc.protocol.bedrock.packet.LoginPacket;
import org.cloudburstmc.protocol.bedrock.packet.ModalFormResponsePacket;
import org.cloudburstmc.protocol.bedrock.packet.RequestNetworkSettingsPacket;
import org.cloudburstmc.protocol.bedrock.packet.ResourcePackChunkRequestPacket;
import org.cloudburstmc.protocol.bedrock.packet.ResourcePackClientResponsePacket;
import org.cloudburstmc.protocol.bedrock.packet.SetLocalPlayerAsInitializedPacket;
import org.cloudburstmc.protocol.bedrock.packet.TextPacket;
import org.geysermc.geyser.session.GeyserSession;

import java.util.Map;

public class PacketCooldownManager {

private final GeyserSession session;
private final Map<String, CooldownSettings> packetCooldownSettings = new Object2ObjectOpenHashMap<>();
private final Map<String, CooldownTracker> activeCooldowns = new Object2ObjectOpenHashMap<>();

public PacketCooldownManager(GeyserSession session) {
this.session = session;

setPacketCooldown(LoginPacket.class, -1, 2);
setPacketCooldown(ResourcePackClientResponsePacket.class, -1, 4);
setPacketCooldown(ResourcePackChunkRequestPacket.class, -1, 0);
setPacketCooldown(RequestNetworkSettingsPacket.class, -1, 2);
setPacketCooldown(TextPacket.class, 1000, 50);
setPacketCooldown(CommandRequestPacket.class, 1000, 50);
setPacketCooldown(ModalFormResponsePacket.class, 1000, 50);
setPacketCooldown(BlockEntityDataPacket.class, 1000, 40);
setPacketCooldown(SetLocalPlayerAsInitializedPacket.class, 1000, 40);
setPacketCooldown(BlockPickRequestPacket.class, 1000, 40);
setPacketCooldown(EntityPickRequestPacket.class, 1000, 40);
setPacketCooldown(BookEditPacket.class, 1000, 40);
setPacketCooldown(FilterTextPacket.class, 1000, 40);
setPacketCooldown(LecternUpdatePacket.class, 1000, 10);
}

public void setPacketCooldown(Class<? extends BedrockPacket> packetClass, int cooldownMillis, int maxCount) {
packetCooldownSettings.put(packetClass.getSimpleName(), new CooldownSettings(cooldownMillis, maxCount));
}

private boolean isCooldown(BedrockPacket packet) {
String packetName = packet.getClass().getSimpleName();
CooldownSettings settings = packetCooldownSettings.get(packetName);
if (settings == null) return false;

CooldownTracker tracker = activeCooldowns.computeIfAbsent(packetName, k -> {
CooldownTracker newTracker = new CooldownTracker();
long cooldownMillis = settings.cooldownMillis();
if (cooldownMillis == -1) {
newTracker.setExpiryTime(-1);
} else {
newTracker.setExpiryTime(System.currentTimeMillis() + cooldownMillis);
}
return newTracker;
});
tracker.incrementCount();

if (tracker.getExpiryTime() != -1 && tracker.getExpiryTime() <= System.currentTimeMillis()) {
activeCooldowns.remove(packetName);
return false;
}

return tracker.getCount() >= settings.maxCount();
}

public void handle(BedrockPacket packet) {
String packetName = packet.getClass().getSimpleName();
if (!isCooldown(packet)) return;
if (session.getGeyser().getConfig().isDebugMode()) {
CooldownTracker tracker = activeCooldowns.get(packetName);
String message = session.getSocketAddress().getAddress().toString() + " -> Attempted to send too many packets " + packetName + " count " + tracker.getCount();
if (session.isLoggedIn()) {
message += " by user " + session.bedrockUsername();
}
session.getGeyser().getLogger().debug(message);
}
session.disconnect("many Packets " + packetName);
}

private record CooldownSettings(int cooldownMillis, int maxCount) {
}

@Getter
private class CooldownTracker {
private long count;
@Setter
private long expiryTime;

public void incrementCount() {
this.count++;
}
}
}
Loading