package net.minecraft.server; // CraftBukkit start import java.net.InetAddress; // CraftBukkit end import java.util.Map; import com.koloboke.collect.map.hash.HashObjLongMaps; public class HandshakeListener implements PacketHandshakingInListener { private static final com.google.gson.Gson gson = new com.google.gson.Gson(); // Spigot // CraftBukkit start - add fields private static final Map<InetAddress, Long> throttleTracker = HashObjLongMaps.newMutableMap(); private static int throttleCounter = 0; // CraftBukkit end private final MinecraftServer a; private final NetworkManager b; public HandshakeListener(MinecraftServer minecraftserver, NetworkManager networkmanager) { this.a = minecraftserver; this.b = networkmanager; } @Override public void a(PacketHandshakingInSetProtocol packethandshakinginsetprotocol) { switch (packethandshakinginsetprotocol.a()) { case LOGIN: this.b.setProtocol(EnumProtocol.LOGIN); ChatComponentText chatcomponenttext; // CraftBukkit start - Connection throttle try { long currentTime = System.currentTimeMillis(); long connectionThrottle = MinecraftServer.getServer().server.getConnectionThrottle(); InetAddress address = ((java.net.InetSocketAddress) this.b.getSocketAddress()).getAddress(); synchronized (throttleTracker) { if (throttleTracker.containsKey(address) && !"127.0.0.1".equals(address.getHostAddress()) && currentTime - throttleTracker.get(address) < connectionThrottle) { throttleTracker.put(address, currentTime); chatcomponenttext = new ChatComponentText("Connection throttled! Please wait before reconnecting."); this.b.sendPacket(new PacketLoginOutDisconnect(chatcomponenttext)); this.b.close(chatcomponenttext); return; } throttleTracker.put(address, currentTime); throttleCounter++; if (throttleCounter > 200) { throttleCounter = 0; // Cleanup stale entries java.util.Iterator iter = throttleTracker.entrySet().iterator(); while (iter.hasNext()) { java.util.Map.Entry<InetAddress, Long> entry = (java.util.Map.Entry) iter.next(); if (entry.getValue() > connectionThrottle) { iter.remove(); } } } } } catch (Throwable t) { org.apache.logging.log4j.LogManager.getLogger().debug("Failed to check connection throttle", t); } // CraftBukkit end if (packethandshakinginsetprotocol.b() > 316) { chatcomponenttext = new ChatComponentText( java.text.MessageFormat.format( org.spigotmc.SpigotConfig.outdatedServerMessage.replaceAll("'", "''"), "1.11.2" ) ); // Spigot this.b.sendPacket(new PacketLoginOutDisconnect(chatcomponenttext)); this.b.close(chatcomponenttext); } else if (packethandshakinginsetprotocol.b() < 316) { chatcomponenttext = new ChatComponentText( java.text.MessageFormat.format( org.spigotmc.SpigotConfig.outdatedClientMessage.replaceAll("'", "''"), "1.11.2" ) ); // Spigot this.b.sendPacket(new PacketLoginOutDisconnect(chatcomponenttext)); this.b.close(chatcomponenttext); } else { this.b.setPacketListener(new LoginListener(this.a, this.b)); // Paper start - handshake event boolean proxyLogicEnabled = org.spigotmc.SpigotConfig.bungee; boolean handledByEvent = false; // Try and handle the handshake through the event if (com.destroystokyo.paper.event.player.PlayerHandshakeEvent.getHandlerList().getRegisteredListeners().length != 0) { // Hello? Can you hear me? com.destroystokyo.paper.event.player.PlayerHandshakeEvent event = new com.destroystokyo.paper.event.player.PlayerHandshakeEvent(packethandshakinginsetprotocol.hostname, !proxyLogicEnabled); if (event.callEvent()) { // If we've failed somehow, let the client know so and go no further. if (event.isFailed()) { chatcomponenttext = new ChatComponentText(event.getFailMessage()); this.b.sendPacket(new PacketLoginOutDisconnect(chatcomponenttext)); this.b.close(chatcomponenttext); return; } packethandshakinginsetprotocol.hostname = event.getServerHostname(); this.b.l = new java.net.InetSocketAddress(event.getSocketAddressHostname(), ((java.net.InetSocketAddress) this.b.getSocketAddress()).getPort()); this.b.spoofedUUID = event.getUniqueId(); this.b.spoofedProfile = gson.fromJson(event.getPropertiesJson(), com.mojang.authlib.properties.Property[].class); handledByEvent = true; // Hooray, we did it! } } // Don't try and handle default logic if it's been handled by the event. if (!handledByEvent && proxyLogicEnabled) { // Paper end // Spigot Start //if (org.spigotmc.SpigotConfig.bungee) { // Paper - comment out, we check above! String[] split = packethandshakinginsetprotocol.hostname.split("\00"); if ( split.length == 3 || split.length == 4 ) { packethandshakinginsetprotocol.hostname = split[0]; b.l = new java.net.InetSocketAddress(split[1], ((java.net.InetSocketAddress) b.getSocketAddress()).getPort()); b.spoofedUUID = com.mojang.util.UUIDTypeAdapter.fromString( split[2] ); } else { chatcomponenttext = new ChatComponentText("If you wish to use IP forwarding, please enable it in your BungeeCord config as well!"); this.b.sendPacket(new PacketLoginOutDisconnect(chatcomponenttext)); this.b.close(chatcomponenttext); return; } if ( split.length == 4 ) { b.spoofedProfile = gson.fromJson(split[3], com.mojang.authlib.properties.Property[].class); } } // Spigot End ((LoginListener) this.b.i()).hostname = packethandshakinginsetprotocol.hostname + ":" + packethandshakinginsetprotocol.port; // CraftBukkit - set hostname } break; case STATUS: this.b.setProtocol(EnumProtocol.STATUS); this.b.setPacketListener(new PacketStatusListener(this.a, this.b)); break; default: throw new UnsupportedOperationException("Invalid intention " + packethandshakinginsetprotocol.a()); } } @Override public void a(IChatBaseComponent ichatbasecomponent) {} }