From 7608e43669fc010e6221c6b41e13ede14c6d4cd4 Mon Sep 17 00:00:00 2001 From: Dereku Date: Wed, 4 Sep 2024 10:06:08 +0700 Subject: [PATCH] Removed Legacy response; update modern response. 1.5 --- pom.xml | 2 +- .../net/rubeta/legacyping/ServerThread.java | 109 ++++++++++++------ 2 files changed, 73 insertions(+), 38 deletions(-) diff --git a/pom.xml b/pom.xml index 35934c0..6427ea9 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ net.rubeta LegacyPing - 1.4 + 1.5 jar LegacyPing diff --git a/src/main/java/net/rubeta/legacyping/ServerThread.java b/src/main/java/net/rubeta/legacyping/ServerThread.java index 180e4bf..2a46093 100644 --- a/src/main/java/net/rubeta/legacyping/ServerThread.java +++ b/src/main/java/net/rubeta/legacyping/ServerThread.java @@ -5,16 +5,19 @@ import org.bukkit.craftbukkit.CraftServer; import java.io.*; import java.net.*; +import java.nio.charset.StandardCharsets; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; public class ServerThread extends Thread { private static final String MODERN_RESPONSE = - "{\"version\": {\"name\": \"%version%\",\"protocol\": -%protocol%}," + + "{\"version\": {\"name\": \"%version%\",\"protocol\": %protocol%}," + "\"players\": {\"max\": %max%,\"online\": %now%}," + "\"description\": {\"text\": \"%motd%\"}," + "\"enforcesSecureChat\": false,\"previewsChat\": true}"; + private static final String DISCONNECT_REASON = "[\"\",{\"text\":\"This is b1.7.3 server. Visit \"}," + + "{\"text\":\"https://rubeta.net\",\"underlined\":true},{\"text\":\" for more information.\"}]"; private final ServerSocket serverSocket; private final AtomicBoolean working = new AtomicBoolean(false); private final Server minecraftServer; @@ -25,7 +28,7 @@ public class ServerThread extends Thread { this.minecraftServer = server; this.setName("LegacyPing thread listening " + host + ":" + port); this.version = version; - this.protocol = Math.abs(protocol); + this.protocol = protocol; final InetAddress address = InetAddress.getByName(host); this.serverSocket = new ServerSocket(port, 10, address); this.serverSocket.setSoTimeout((int) TimeUnit.SECONDS.toMillis(10L)); @@ -47,6 +50,7 @@ public class ServerThread extends Thread { new ServerThread("", 25565).start(); } + @SuppressWarnings("unused") @Override public void run() { this.working.set(true); @@ -54,17 +58,32 @@ public class ServerThread extends Thread { try { final Socket socket = this.serverSocket.accept(); socket.setTrafficClass(SocketOptions.TCP_NODELAY & SocketOptions.IP_TOS); - System.out.println("[LegacyPing] Got new ping from " + socket.getRemoteSocketAddress()); - final InputStream inputStream = socket.getInputStream(); - final OutputStream outputStream = socket.getOutputStream(); - final int firstByte = inputStream.read(); - if (firstByte == 254) { - this.responseForLegacyPing(outputStream); +// System.out.println("[LegacyPing] Got new ping from " + socket.getRemoteSocketAddress()); + final DataInputStream inputStream = new DataInputStream(socket.getInputStream()); + final DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream()); + final int packetLength = inputStream.readByte() | inputStream.readByte() << 1; // idk why it doesn't a VarInt + final int protocolVersion = this.readVarInt(inputStream); + final String hostname = this.readUtf(inputStream); + final int port = inputStream.readShort(); + final int status = this.readVarInt(inputStream); +// System.out.println(packetLength + "; Protocol version: " + protocolVersion + ", host: " + hostname +// + ", port: " + port + ", status: " + status); + if (status != 2) { + this.responseForModernPing(outputStream, MODERN_RESPONSE); } else { - this.responseForModernPing(outputStream); + this.responseForModernPing(outputStream, DISCONNECT_REASON); } outputStream.flush(); - //socket.close(); + + inputStream.readShort(); // legacy data + + this.writeVarInt(outputStream, this.readVarInt(inputStream)); // Packet length (9) + this.writeVarInt(outputStream, this.readVarInt(inputStream)); // Packet id (1) + outputStream.writeLong(inputStream.readLong()); // timestamp + + inputStream.close(); + outputStream.close(); + socket.close(); } catch (SocketTimeoutException ignore) { } catch (IOException ex) { System.out.println("Exception caught: " + ex); @@ -80,32 +99,20 @@ public class ServerThread extends Thread { } } - @SuppressWarnings("UnnecessaryUnicodeEscape") - private void responseForLegacyPing(final OutputStream outputStream) throws IOException { - final DataOutputStream clientOutput = new DataOutputStream(outputStream); - clientOutput.write(255); - final String response = this.getMotd() + '\u00a7' + this.getOnlinePlayers() + '\u00a7' + this.getMaxPlayers(); - clientOutput.writeShort(response.length()); - for (char c : response.toCharArray()) { - clientOutput.writeChar(c); - } - // Packet data end - } - - private void responseForModernPing(final OutputStream outputStream) throws IOException { - final String responseJson = MODERN_RESPONSE + private void responseForModernPing(final DataOutputStream outputStream, String response) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos); + final String responseJson = response .replace("%version%", String.valueOf(this.version)) .replace("%protocol%", String.valueOf(this.protocol)) .replace("%max%", String.valueOf(this.getMaxPlayers())) .replace("%now%", String.valueOf(this.getOnlinePlayers())) .replace("%motd%", this.getMotd()); - byte[] bytes = responseJson.getBytes(); - final DataOutputStream clientOutput = new DataOutputStream(outputStream); - this.writeVarInt(clientOutput, bytes.length + 3); - this.writeVarInt(clientOutput, 0); - this.writeVarInt(clientOutput, bytes.length); - clientOutput.write(bytes); - // Packet data end + this.writeVarInt(dos, 0); + this.writeUtf(dos, responseJson); + dos.close(); + this.writeVarInt(outputStream, baos.size()); + outputStream.write(baos.toByteArray()); } private String getMotd() { @@ -120,14 +127,42 @@ public class ServerThread extends Thread { return this.minecraftServer == null ? 37 : this.minecraftServer.getMaxPlayers(); } - private void writeVarInt(DataOutputStream out, int paramInt) throws IOException { - while (true) { - if ((paramInt & 0xFFFFFF80) == 0) { - out.writeByte(paramInt); - return; + public int readVarInt(DataInputStream in) throws IOException { + int i = 0; + int j = 0; + byte b0; + do { + b0 = in.readByte(); + i |= (b0 & 127) << j++ * 7; + if (j > 5) { + throw new RuntimeException("VarInt too big"); } - out.writeByte(paramInt & 0x7F | 0x80); + } while((b0 & 128) == 128); + return i; + } + + private void writeVarInt(DataOutputStream out, int paramInt) throws IOException { + while((paramInt & 0xffffff80) != 0) { + out.writeByte(paramInt & 0x7f | 0x80); paramInt >>>= 7; } + out.writeByte(paramInt); + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + private String readUtf(DataInputStream in) throws IOException { + int length = this.readVarInt(in); + byte[] data = new byte[length]; + in.read(data); + return new String(data, StandardCharsets.UTF_8); + } + + private void writeUtf(DataOutputStream out, String string) throws IOException { + byte[] data = string.getBytes(StandardCharsets.UTF_8); + if (data.length > 32767) { + throw new RuntimeException("String too big (was " + data.length + " bytes encoded, max 32767"); + } + this.writeVarInt(out, data.length); + out.write(data); } }