diff --git a/src/main/java/me/replydev/mcping/LegacyPinger.java b/src/main/java/me/replydev/mcping/LegacyPinger.java new file mode 100644 index 0000000..5bbaaeb --- /dev/null +++ b/src/main/java/me/replydev/mcping/LegacyPinger.java @@ -0,0 +1,111 @@ +package me.replydev.mcping; + +import me.replydev.mcping.data.FinalResponse; +import me.replydev.mcping.rawData.Players; +import me.replydev.mcping.rawData.Version; + +import java.io.*; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.nio.charset.StandardCharsets; + +/** + * Pinger for 1.6 protocol + * https://wiki.vg/Server_List_Ping#Client_to_server + */ +public class LegacyPinger { + private InetSocketAddress host; + private int timeout; + private int protocolVersion = -1; + + void setAddress(InetSocketAddress host) + { + this.host = host; + } + void setTimeout(int timeout) + { + this.timeout = timeout; + } + void setProtocolVersion(int protocolVersion) { + this.protocolVersion = protocolVersion; + } + + public LegacyPingResponse ping() throws IOException { + Socket socket = new Socket(); + socket.setSoTimeout(this.timeout); + socket.connect(this.host, this.timeout); + sendPing(new DataOutputStream(socket.getOutputStream())); + socket.close(); // the socked was never closed in the original version... + return readResponse(new DataInputStream(socket.getInputStream())); + } + + private void sendPing(DataOutputStream dataOutputStream) throws IOException { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + DataOutputStream handshake = new DataOutputStream(bytes); + handshake.writeByte(0xFE); + handshake.writeByte(0x01); + handshake.writeByte(0xFA); + byte[] hostNameBytes = host.getHostName().getBytes(StandardCharsets.UTF_16BE); + String hostStr = "MC|" + host.getHostName(); + byte[] hostStrBytes = hostStr.getBytes(StandardCharsets.UTF_16BE); + handshake.writeShort(hostStrBytes.length); + for (byte aByte : hostStrBytes) { + handshake.writeByte(aByte); + } + handshake.write(7 + hostStrBytes.length); + handshake.write(protocolVersion); + handshake.writeShort(hostNameBytes.length); + for (byte aByte : hostNameBytes) { + handshake.writeByte(aByte); + } + handshake.writeInt(host.getPort()); + dataOutputStream.write(bytes.toByteArray()); + } + + private LegacyPingResponse readResponse(DataInputStream dataInputStream) throws IOException { + dataInputStream.readByte(); // Single data identifier + dataInputStream.readShort(); + byte[] bytes = dataInputStream.readNBytes(dataInputStream.available()); + String str = new String(bytes, StandardCharsets.UTF_16BE); + String[] split = str.split("\\0000"); + return new LegacyPingResponse(Integer.parseInt(split[1]), split[2], split[3], Integer.parseInt(split[4]), Integer.parseInt(split[5])); + } + + public static final class LegacyPingResponse { + public final int protocolVersion; + public final String serverVersion; + public final String motd; + public final int players; + public final int maxPlayers; + + public LegacyPingResponse(int protocolVersion, String serverVersion, String motd, int players, int maxPlayers) { + this.protocolVersion = protocolVersion; + this.serverVersion = serverVersion; + this.motd = motd; + this.players = players; + this.maxPlayers = maxPlayers; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("LegacyPingResponse{"); + sb.append("protocolVersion=").append(protocolVersion); + sb.append(", serverVersion='").append(serverVersion).append('\''); + sb.append(", motd='").append(motd).append('\''); + sb.append(", players=").append(players); + sb.append(", maxPlayers=").append(maxPlayers); + sb.append('}'); + return sb.toString(); + } + + public FinalResponse toFinalResponse() { + Players players = new Players(); + players.setOnline(this.players); + players.setMax(this.maxPlayers); + Version version = new Version(); + version.setProtocol(this.protocolVersion); + version.setName(this.serverVersion); + return new FinalResponse(players, version, null, motd); + } + } +} diff --git a/src/main/java/me/replydev/mcping/MCPing.java b/src/main/java/me/replydev/mcping/MCPing.java new file mode 100644 index 0000000..86850cb --- /dev/null +++ b/src/main/java/me/replydev/mcping/MCPing.java @@ -0,0 +1,119 @@ +package me.replydev.mcping; + +import com.google.gson.Gson; +import me.replydev.mcping.LegacyPinger.LegacyPingResponse; +import me.replydev.mcping.data.*; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; + +public class MCPing +{ + private static final Logger LOGGER = LogManager.getLogger(MCPing.class.getName()); + + /** + * If the client is pinging to determine what version to use, by convention -1 should be set. + */ + public static final int PROTOCOL_VERSION_DISCOVERY = -1; + + public ResponseDetails getLegacyPingWithDetails(PingOptions options) throws IOException { + LegacyPinger pinger = new LegacyPinger(); + pinger.setProtocolVersion(options.getProtocolVersion()); + pinger.setAddress(new InetSocketAddress(options.getHostname(),options.getPort())); + pinger.setTimeout(options.getTimeout()); + pinger.setProtocolVersion(options.getProtocolVersion()); + LegacyPingResponse response = pinger.ping(); + return new ResponseDetails(response.toFinalResponse(), null, null, null, null, null, null, response, null); + } + + public ResponseDetails getPingWithDetails(PingOptions options) throws IOException + { + final Gson gson = new Gson(); + Pinger a = new Pinger(); + a.setAddress(new InetSocketAddress(options.getHostname(),options.getPort())); + a.setTimeout(options.getTimeout()); + a.setProtocolVersion(options.getProtocolVersion()); + String json = a.fetchData(); + try { + if(json != null){ + if (json.getBytes(StandardCharsets.UTF_8).length > 5000000) { + // got a response greater than 5mb, possible honeypot? + LOGGER.error("Got a json response > 5mb, possible honeypot?"); + LOGGER.error(options.getHostname() + ":" + options.getPort()); + return null; + } + if(json.contains("{")) + { + if(json.contains("\"modid\"") && json.contains("\"translate\"")){ //it's a forge response translate + ForgeResponseTranslate forgeResponseTranslate = gson.fromJson(json, ForgeResponseTranslate.class); + return new ResponseDetails(forgeResponseTranslate.toFinalResponse(), forgeResponseTranslate, null, null, null, null, null, null, json); + } + else if(json.contains("\"modid\"") && json.contains("\"text\"")){ //it's a normal forge response + ForgeResponse forgeResponse = gson.fromJson(json, ForgeResponse.class); + return new ResponseDetails(forgeResponse.toFinalResponse(), null, forgeResponse, null, null, null, null, null, json); + } + else if(json.contains("\"modid\"")){ //it's an old forge response + ForgeResponseOld forgeResponseOld = gson.fromJson(json, ForgeResponseOld.class); + return new ResponseDetails(forgeResponseOld.toFinalResponse(), null, null, forgeResponseOld, null, null, null, null, json); + } + else if(json.contains("\"extra\"")){ //it's an extra response + ExtraResponse extraResponse = gson.fromJson(json, ExtraResponse.class); + return new ResponseDetails(extraResponse.toFinalResponse(), null, null, null, extraResponse, null, null, null, json); + } + else if(json.contains("\"text\"")){ //it's a new response + NewResponse newResponse = gson.fromJson(json, NewResponse.class); + return new ResponseDetails(newResponse.toFinalResponse(), null, null, null, null, newResponse, null, null, json); + } + else { //it's an old response + OldResponse oldResponse = gson.fromJson(json, OldResponse.class); + return new ResponseDetails(oldResponse.toFinalResponse(), null, null, null, null, null, oldResponse, null, json); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + System.out.println(json); + } + + return null; + } + + public FinalResponse getPing(PingOptions options) throws IOException { + return getPingWithDetails(options).standard; + } + + public static class ResponseDetails { + public final FinalResponse standard; + public final ForgeResponseTranslate forgeTranslate; + public final ForgeResponse forge; + public final ForgeResponseOld oldForge; + public final ExtraResponse extraResponse; + public final NewResponse response; + public final OldResponse oldResponse; + public final LegacyPingResponse legacyResponse; + public final String json; + + public ResponseDetails(FinalResponse standard, + ForgeResponseTranslate forgeTranslate, + ForgeResponse forge, + ForgeResponseOld oldForge, + ExtraResponse extraResponse, + NewResponse response, + OldResponse oldResponse, + LegacyPingResponse legacyResponse, + String json) { + this.standard = standard; + this.forgeTranslate = forgeTranslate; + this.forge = forge; + this.oldForge = oldForge; + this.extraResponse = extraResponse; + this.response = response; + this.oldResponse = oldResponse; + this.legacyResponse = legacyResponse; + this.json = json; + } + } +} \ No newline at end of file diff --git a/src/main/java/me/replydev/mcping/PingOptions.java b/src/main/java/me/replydev/mcping/PingOptions.java new file mode 100644 index 0000000..56f2093 --- /dev/null +++ b/src/main/java/me/replydev/mcping/PingOptions.java @@ -0,0 +1,44 @@ +package me.replydev.mcping; + +public class PingOptions { + + private String hostname; + private int port; + private int timeout; + private int protocolVersion = -1; + + public PingOptions setHostname(String hostname) { + this.hostname = hostname; + return this; + } + + public PingOptions setPort(int port) { + this.port = port; + return this; + } + + public PingOptions setTimeout(int timeout) { + this.timeout = timeout; + return this; + } + + String getHostname() { + return this.hostname; + } + + int getPort() { + return this.port; + } + + public int getTimeout() { + return this.timeout; + } + + public int getProtocolVersion() { + return protocolVersion; + } + + public void setProtocolVersion(int protocol) { + this.protocolVersion = protocol; + } +} diff --git a/src/main/java/me/replydev/mcping/Pinger.java b/src/main/java/me/replydev/mcping/Pinger.java new file mode 100644 index 0000000..59f4a7f --- /dev/null +++ b/src/main/java/me/replydev/mcping/Pinger.java @@ -0,0 +1,105 @@ +package me.replydev.mcping; + +import java.io.*; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.nio.charset.StandardCharsets; + +class Pinger +{ + private InetSocketAddress host; + private int timeout; + // If the client is pinging to determine what version to use, by convention -1 should be set. + private int protocolVersion = -1; + + void setAddress(InetSocketAddress host) + { + this.host = host; + } + void setTimeout(int timeout) + { + this.timeout = timeout; + } + + public void setProtocolVersion(int protocolVersion) { + this.protocolVersion = protocolVersion; + } + + private int readVarInt(DataInputStream in) throws IOException { + int i = 0; + int j = 0; + int k; + do + { + k = in.readByte(); + i |= (k & 0x7F) << j++ * 7; + if (j > 5) { + //throw new RuntimeException("VarInt too big"); + return -1; + } + } while ((k & 0x80) == 128); + return i; + } + + private void writeVarInt(DataOutputStream out, int paramInt) throws IOException { + for (;;) + { + if ((paramInt & 0xFFFFFF80) == 0) + { + out.writeByte(paramInt); + return; + } + out.writeByte(paramInt & 0x7F | 0x80); + paramInt >>>= 7; + } + } + + public String fetchData() throws IOException + { + Socket socket = new Socket(); + socket.setSoTimeout(this.timeout); + socket.connect(this.host, this.timeout); + + OutputStream outputStream = socket.getOutputStream(); + DataOutputStream dataOutputStream = new DataOutputStream(outputStream); + InputStream inputStream = socket.getInputStream(); + ByteArrayOutputStream b = new ByteArrayOutputStream(); + DataOutputStream handshake = new DataOutputStream(b); + + handshake.writeByte(0); + writeVarInt(handshake, protocolVersion); + writeVarInt(handshake, this.host.getHostString().length()); + handshake.writeBytes(this.host.getHostString()); + handshake.writeShort(this.host.getPort()); + writeVarInt(handshake, 1); + + writeVarInt(dataOutputStream, b.size()); + dataOutputStream.write(b.toByteArray()); + dataOutputStream.writeByte(1); + dataOutputStream.writeByte(0); + DataInputStream dataInputStream = new DataInputStream(inputStream); + + int size = readVarInt(dataInputStream); + int id = readVarInt(dataInputStream); + int length = readVarInt(dataInputStream); + + if (size < 0 || id < 0 || length <= 0) + { + closeAll(b, dataInputStream, handshake, dataOutputStream, outputStream, inputStream, socket); + return null; + } + + byte[] in = new byte[length]; + dataInputStream.readFully(in); + closeAll(b, dataInputStream, handshake, dataOutputStream, outputStream, inputStream, socket); + return new String(in, StandardCharsets.UTF_8); //JSON + } + + public void closeAll(Closeable... closeables) throws IOException + { + for (Closeable closeable : closeables) + { + closeable.close(); + } + } +} diff --git a/src/main/java/me/replydev/mcping/data/ExtraResponse.java b/src/main/java/me/replydev/mcping/data/ExtraResponse.java new file mode 100644 index 0000000..a82a8c4 --- /dev/null +++ b/src/main/java/me/replydev/mcping/data/ExtraResponse.java @@ -0,0 +1,68 @@ +package me.replydev.mcping.data; + +import com.google.gson.annotations.SerializedName; +import me.replydev.mcping.rawData.ExtraDescription; + +public class ExtraResponse extends MCResponse { + + private static final String PAPER_VERSION_PREFIX = "Paper "; + private static final String SPIGOT_VERSION_PREFIX = "Spigot "; + private static final String VELOCITY_VERSION_PREFIX = "Velocity "; + private static final String BUNGEE_VERSION_PREFIX = "BungeeCord "; + private static final String WATERFALL_VERSION_PREFIX = "Waterfall "; + private static final String TACO_SPIGOT_VERSION_PREFIX = "TacoSpigot "; + private static final String CRAFT_BUKKIT_VERSION_PREFIX = "CraftBukkit "; + private static final String MOHIST_VERSION_PREFIX = "Mohist "; + private static final String PURPUR_VERSION_PREFIX = "Purpur "; + private static final String SPORTPAPER_VERSION_PREFIX = "SportPaper "; + private static final String FLAMECORD_VERSION_PREFIX = "FlameCord "; + + @SerializedName("description") + private ExtraDescription description; + + private Loader loader; + + public FinalResponse toFinalResponse(){ + if (version != null && version.getName().startsWith(SPIGOT_VERSION_PREFIX)) { + loader = Loader.SPIGOT; + version.setName(version.getName().substring(SPIGOT_VERSION_PREFIX.length()).trim()); + } else if (version != null && version.getName() != null && version.getName().startsWith(VELOCITY_VERSION_PREFIX)) { + loader = Loader.VELOCITY; + version.setName(version.getName().substring(VELOCITY_VERSION_PREFIX.length()).trim()); + } else if (version != null && version.getName() != null && version.getName().startsWith(BUNGEE_VERSION_PREFIX)) { + loader = Loader.BUNGEE; + version.setName(version.getName().substring(BUNGEE_VERSION_PREFIX.length()).trim()); + } else if (version != null && version.getName() != null && version.getName().startsWith(WATERFALL_VERSION_PREFIX)) { + loader = Loader.WATERFALL; + version.setName(version.getName().substring(WATERFALL_VERSION_PREFIX.length()).trim()); + } else if (version != null && version.getName() != null && version.getName().startsWith(PAPER_VERSION_PREFIX)) { + loader = Loader.PAPER; + version.setName(version.getName().substring(PAPER_VERSION_PREFIX.length()).trim()); + } else if (version != null && version.getName() != null && version.getName().startsWith(TACO_SPIGOT_VERSION_PREFIX)) { + loader = Loader.TACO_SPIGOT; + version.setName(version.getName().substring(TACO_SPIGOT_VERSION_PREFIX.length()).trim()); + } else if (version != null && version.getName() != null && version.getName().startsWith(CRAFT_BUKKIT_VERSION_PREFIX)) { + loader = Loader.CRAFT_BUKKIT; + version.setName(version.getName().substring(CRAFT_BUKKIT_VERSION_PREFIX.length()).trim()); + } else if (version != null && version.getName() != null && version.getName().startsWith(MOHIST_VERSION_PREFIX)) { + loader = Loader.MOHIST; + version.setName(version.getName().substring(MOHIST_VERSION_PREFIX.length()).trim()); + } else if (version != null && version.getName() != null && version.getName().startsWith(PURPUR_VERSION_PREFIX)) { + loader = Loader.PURPUR; + version.setName(version.getName().substring(PURPUR_VERSION_PREFIX.length()).trim()); + } else if (version != null && version.getName() != null && version.getName().startsWith(SPORTPAPER_VERSION_PREFIX)) { + loader = Loader.SPORTPAPER; + version.setName(version.getName().substring(SPORTPAPER_VERSION_PREFIX.length()).trim()); + } else if (version != null && version.getName() != null && version.getName().startsWith(FLAMECORD_VERSION_PREFIX)) { + loader = Loader.FLAMECORD; + version.setName(version.getName().substring(FLAMECORD_VERSION_PREFIX.length()).trim()); + } else { + loader = Loader.VANILLA; + } + return new FinalResponse(players,version,favicon,description.getText()); + } + + public Loader getLoader() { + return loader; + } +} diff --git a/src/main/java/me/replydev/mcping/data/FinalResponse.java b/src/main/java/me/replydev/mcping/data/FinalResponse.java new file mode 100644 index 0000000..bfb6acf --- /dev/null +++ b/src/main/java/me/replydev/mcping/data/FinalResponse.java @@ -0,0 +1,33 @@ +package me.replydev.mcping.data; + +import me.replydev.mcping.rawData.Players; +import me.replydev.mcping.rawData.Version; + +public class FinalResponse extends MCResponse { + + private final String description; + + public FinalResponse(Players players, Version version,String favicon,String description){ + this.description = Input.stripMinecraft(description); + this.favicon = favicon; + this.players = players; + this.version = version; + } + + public Players getPlayers() { + if (players == null) { + return new Players(); + } + return players; + } + + public Version getVersion() { + return version; + } + + public String getDescription() { + return description; + } + + public String getFavIcon () { return favicon; } +} diff --git a/src/main/java/me/replydev/mcping/data/ForgeResponse.java b/src/main/java/me/replydev/mcping/data/ForgeResponse.java new file mode 100644 index 0000000..b1fd425 --- /dev/null +++ b/src/main/java/me/replydev/mcping/data/ForgeResponse.java @@ -0,0 +1,43 @@ +package me.replydev.mcping.data; + +import com.google.gson.annotations.SerializedName; +import me.replydev.mcping.rawData.Description; +import me.replydev.mcping.rawData.ForgeModInfo; +import me.replydev.mcping.rawData.Players; +import me.replydev.mcping.rawData.Version; + +public class ForgeResponse { + + @SerializedName("description") + private Description description; + + @SerializedName("players") + private Players players; + + @SerializedName("version") + private Version version; + + @SerializedName("modinfo") + private ForgeModInfo modinfo; + + public FinalResponse toFinalResponse(){ + version.setName(version.getName()); + return new FinalResponse(players,version,"",description.getText()); + } + + public Description getDescription() { + return description; + } + + public Players getPlayers() { + return players; + } + + public Version getVersion() { + return version; + } + + public ForgeModInfo getModinfo() { + return modinfo; + } +} \ No newline at end of file diff --git a/src/main/java/me/replydev/mcping/data/ForgeResponseOld.java b/src/main/java/me/replydev/mcping/data/ForgeResponseOld.java new file mode 100644 index 0000000..6db1207 --- /dev/null +++ b/src/main/java/me/replydev/mcping/data/ForgeResponseOld.java @@ -0,0 +1,41 @@ +package me.replydev.mcping.data; + +import com.google.gson.annotations.SerializedName; +import me.replydev.mcping.rawData.ForgeModInfo; +import me.replydev.mcping.rawData.Players; +import me.replydev.mcping.rawData.Version; + +public class ForgeResponseOld { + + @SerializedName("description") + private String description; + + @SerializedName("players") + private Players players; + + @SerializedName("version") + private Version version; + + @SerializedName("modinfo") + private ForgeModInfo modinfo; + + public FinalResponse toFinalResponse(){ + return new FinalResponse(players,version,"",description); + } + + public String getDescription() { + return description; + } + + public Players getPlayers() { + return players; + } + + public Version getVersion() { + return version; + } + + public ForgeModInfo getModinfo() { + return modinfo; + } +} \ No newline at end of file diff --git a/src/main/java/me/replydev/mcping/data/ForgeResponseTranslate.java b/src/main/java/me/replydev/mcping/data/ForgeResponseTranslate.java new file mode 100644 index 0000000..685fef9 --- /dev/null +++ b/src/main/java/me/replydev/mcping/data/ForgeResponseTranslate.java @@ -0,0 +1,42 @@ +package me.replydev.mcping.data; + +import com.google.gson.annotations.SerializedName; +import me.replydev.mcping.rawData.ForgeDescriptionTranslate; +import me.replydev.mcping.rawData.ForgeModInfo; +import me.replydev.mcping.rawData.Players; +import me.replydev.mcping.rawData.Version; + +public class ForgeResponseTranslate { + + @SerializedName("description") + private ForgeDescriptionTranslate description; + + @SerializedName("players") + private Players players; + + @SerializedName("version") + private Version version; + + @SerializedName("modinfo") + private ForgeModInfo modinfo; + + public FinalResponse toFinalResponse(){ + return new FinalResponse(players,version,"",description.getTranslate()); + } + + public ForgeDescriptionTranslate getDescription() { + return description; + } + + public Players getPlayers() { + return players; + } + + public Version getVersion() { + return version; + } + + public ForgeModInfo getModinfo() { + return modinfo; + } +} diff --git a/src/main/java/me/replydev/mcping/data/Input.java b/src/main/java/me/replydev/mcping/data/Input.java new file mode 100644 index 0000000..6f68147 --- /dev/null +++ b/src/main/java/me/replydev/mcping/data/Input.java @@ -0,0 +1,21 @@ +package me.replydev.mcping.data; + +import org.apache.commons.lang3.StringUtils; + +import java.util.regex.Pattern; + +public class Input { + private static final Pattern STRIP_PATTERN = Pattern.compile("(? minPlayer) + { + String des = getGoodDescription(response.getDescription()); + String formattedline = "\033[31m(\033[0m" + hostname + ":" + port + "\033[31m)(\033[0m" + response.getPlayers().getOnline() + "/" + response.getPlayers().getMax() + "\033[31m)" + "(\033[0m" + response.getVersion().getName() + "\033[31m)" + "(\033[0m" + des + "\033[31m)\033[0m"; + /* bot checker */ + MinecraftServer minecraftServer = new MinecraftServer( + hostname, + port, + Protocolz.getProtocol(response.getVersion().getName()) + ); + String result = ""; + try { + minecraftServer.connect(); + minecraftServer.sendHandshakePacket(); + minecraftServer.sendLoginStartPacket(); + result = minecraftServer.check(); + Thread.sleep(1000L); + minecraftServer.disconnect(); + } catch (Exception e) { + result = "\033[31mFAILED \033[0m(\033[33mJAVA ERROR\033[0m)"; + } + formattedline+="\033[31m(\033[0m"+result+"\033[31m)\033[0m"; + /* fine bot checker */ + Info.serverFound++; + Info.serverNotFilteredFound++; + Log.logln(formattedline); + Filez.writeToFile(Filez.getCurrentPath(), purify(formattedline)); + } + else Info.serverNotFilteredFound++; + return; + } + catch (JsonSyntaxException e) + { + String notfound = "(" + hostname + ":" + port + ")(Json not readable)"; + System.out.println(notfound); + Info.serverNotFilteredFound++; + Filez.writeToFile(Filez.getCurrentPath(), notfound); + } + catch (NullPointerException e) + { + if(this.quboInstance.inputData.isDebugMode()) + System.out.println("WARN: NullPointerException for: " + hostname + ":" + port); + } + } catch (IOException ignored) {} + } + } + + private static String getGoodDescription(String des){ //ritorna la stringa senza spazi multipli e senza § + if(des == null) return ""; + des = des.replace("§0",""); + des = des.replace("§1",""); + des = des.replace("§2",""); + des = des.replace("§3",""); + des = des.replace("§4",""); + des = des.replace("§5",""); + des = des.replace("§6",""); + des = des.replace("§7",""); + des = des.replace("§8",""); + des = des.replace("§9",""); + des = des.replace("§a",""); + des = des.replace("§b",""); + des = des.replace("§c",""); + des = des.replace("§d",""); + des = des.replace("§e",""); + des = des.replace("§f",""); + des = des.replace("§l",""); + des = des.replace("§m",""); + des = des.replace("§n",""); + des = des.replace("§o",""); + des = des.replace("§r",""); + des= des.trim().replaceAll(" +", " "); //rimuove spazi multipli + des = des.replace("\n",""); + return des; + } +} \ No newline at end of file diff --git a/src/main/java/me/replydev/mcping/net/MinecraftServer.java b/src/main/java/me/replydev/mcping/net/MinecraftServer.java new file mode 100644 index 0000000..7f9281b --- /dev/null +++ b/src/main/java/me/replydev/mcping/net/MinecraftServer.java @@ -0,0 +1,200 @@ +package me.replydev.mcping.net; + +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.util.Random; +import java.util.UUID; + +public class MinecraftServer { + private String ip; + + private int port; + + private int protocol; + + private Socket socket; + + private DataOutputStream out; + + private DataInputStream in; + + public MinecraftServer(String server_ip, int server_port, int server_protocol) { + this.ip = server_ip; + this.port = server_port; + this.protocol = server_protocol; + } + + public static String getUUID() { + return UUID.randomUUID().toString().toLowerCase(); + } + + public static String getRandIP() { + Random rand = new Random(); + StringBuilder sb = new StringBuilder(); + sb.append(rand.nextInt(100)).append("."); + sb.append(rand.nextInt(100)).append("."); + sb.append(rand.nextInt(100)).append("."); + sb.append(rand.nextInt(100)); + return sb.toString(); + } + + public static String getRandName() { + Random rand = new Random(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 5; i++) { + char c = (char)(rand.nextInt(26) + 'a'); + if (rand.nextInt(2) == 0) { + c = Character.toUpperCase(c); + } + sb.append(c); + } + return sb.toString(); +} + + + + public void connect() throws IOException { + this.socket = new Socket(); + this.socket.connect(new InetSocketAddress(this.ip, this.port), 15000); + this.out = new DataOutputStream(this.socket.getOutputStream()); + this.in = new DataInputStream(this.socket.getInputStream()); + } + + public void sendHandshakePacket() throws IOException { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + DataOutputStream handshake = new DataOutputStream(bout); + handshake.writeByte(0); + writeVarInt(handshake, this.protocol); + String bungeeHack = String.valueOf(String.valueOf(this.ip)) + "\000"+getRandIP()+"\000"+getUUID(); + writeVarInt(handshake, bungeeHack.length()); + handshake.writeBytes(bungeeHack); + handshake.writeShort(this.port); + writeVarInt(handshake, 2); + writeVarInt(this.out, bout.size()); + this.out.write(bout.toByteArray()); + } + + public void sendLoginStartPacket() throws IOException { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + DataOutputStream loginStart = new DataOutputStream(bout); + loginStart.writeByte(0); + String name = getRandName(); + writeVarInt(loginStart, name.length()); + loginStart.writeBytes(name); + writeVarInt(this.out, bout.size()); + this.out.write(bout.toByteArray()); + } + + public String parseDisconnect(String text){ + /* componi il messaggio di disconnect */ + String pre = ""; + String msg = ""; + try{ + pre = text.split("\"translate\":\"")[1].split("\"")[0]; + }catch(Exception e){ + pre = "*error*"; + } + try{ + msg = text.split("\"with\":")[1].split("}")[0]; + }catch(Exception e){ + msg = "*error*"; + } + return "\033[3;91m"+pre+" \033[0;33m=>\033[36m "+msg+"\033[0m"; + } + + public String check() throws IOException { + readVarInt(this.in); + int id = readVarInt(this.in); + String result = ""; + String disconnect = ""; + if (id == 0) { + byte[] b = new byte[readVarInt(this.in)]; + this.in.readFully(b); + disconnect = new String(b); + } + if (disconnect.contains("You have to join through the proxy.")) + return "\033[31mIPWhitelist\033[0m"; + else if (disconnect.contains("Unable to authenticate.")) + return "\033[31mBungeeGuard\033[0m"; + else if (disconnect.contains("Unknown data in login hostname, did you forget to enable BungeeCord in spigot.yml?")) + return "\033[32mDisable IPFORWARD\033[0m"; + else if (disconnect.contains("You are not white-listed on this server!")) + return "\033[33mWhiteList\033[0m"; + else if (disconnect.contains("You are not whitelisted on this server!")) + return "\033[33mWhiteList\033[0m"; + else if (disconnect.toLowerCase().contains("bungeeguard")) + return "\033[33mprobably BungeeGuard\033[0m"; + else if (disconnect.toLowerCase().contains("must connect with")) + return "\033[33mprobably OnlyProxyJoin\033[0m"; + else if (disconnect.toLowerCase().contains("connect with velocity")) + return "\033[33mprobably Velocity\033[0m"; + else if (disconnect.contains("This server has mods that require FML/Forge to be installed on the client. Contact your server admin for more details.")) + return "\033[33mForge\033[0m"; + else if ( + disconnect.toLowerCase().contains("forge")|| + disconnect.toLowerCase().contains("fml")|| + disconnect.toLowerCase().contains("mod") + ) + return "\033[33mprobably Forge\033[0m"; + switch (id) { + case -1: + result = "\033[91mDISCONNECT \033[0m(\033[91mCONNECTION TERMINATED\033[0m)"; + return result; + case 0: + result = parseDisconnect(disconnect); + return result; + case 1: + result = "\033[33mPREMIUM\033[0m"; + return result; + case 2: + result = "\033[32mSUCCESS\033[0m"; + return result; + case 3: + result = "\033[32mSUCCESS \033[0m(\033[32mIpForward\033[0m)"; + return result; + } + result = "\033[34mUnknown [probably velocity] (" + id + ")\033[0m"; + return result; + } + + public void disconnect() throws IOException { + if (this.socket.isClosed()) + return; + this.socket.close(); + } + + public String getIp() { + return String.valueOf(String.valueOf(this.ip)) + ":" + this.port; + } + + + public void writeVarInt(DataOutputStream out, int paramInt) throws IOException { + while (true) { + if ((paramInt & 0xFFFFFF80) == 0) { + out.writeByte(paramInt); + return; + } + out.writeByte(paramInt & 0x7F | 0x80); + paramInt >>>= 7; + } + } + + public int readVarInt(DataInputStream in) throws IOException { + int i = 0; + int j = 0; + while (true) { + byte k = in.readByte(); + i |= (k & Byte.MAX_VALUE) << j++ * 7; + if (j <= 5) { + if ((k & 0x80) != 128) + return i; + continue; + } + throw new RuntimeException("VarInt too big"); + } + } +} \ No newline at end of file diff --git a/src/main/java/me/replydev/mcping/net/Protocolz.java b/src/main/java/me/replydev/mcping/net/Protocolz.java new file mode 100644 index 0000000..2adb69f --- /dev/null +++ b/src/main/java/me/replydev/mcping/net/Protocolz.java @@ -0,0 +1,360 @@ +package me.replydev.mcping.net; + +public class Protocolz { + + public static int getProtocol(String version){ + + + if (version.contains("1.19.3")){ + return 761; + } + + + + if (version.contains("1.19.2")){ + return 760; + } + + + + if (version.contains("1.19.1")){ + return 760; + } + + + + if (version.contains("1.19")){ + return 759; + } + + + + if (version.contains("1.18.2")){ + return 758; + } + + + + if (version.contains("1.18.1")){ + return 757; + } + + + + if (version.contains("1.18")){ + return 757; + } + + + + if (version.contains("1.17.1")){ + return 756; + } + + + + if (version.contains("1.17")){ + return 755; + } + + + + if (version.contains("1.16.5")){ + return 754; + } + + + + if (version.contains("1.16.4")){ + return 754; + } + + + + if (version.contains("1.16.3")){ + return 753; + } + + + + if (version.contains("1.16.2")){ + return 751; + } + + + + if (version.contains("1.16.1")){ + return 736; + } + + + + if (version.contains("1.16")){ + return 735; + } + + + + if (version.contains("1.15.2")){ + return 578; + } + + + + if (version.contains("1.15.1")){ + return 575; + } + + + + if (version.contains("1.15")){ + return 573; + } + + + + if (version.contains("1.14.4")){ + return 498; + } + + + + if (version.contains("1.14.3")){ + return 490; + } + + + + if (version.contains("1.14.2")){ + return 485; + } + + + + if (version.contains("1.14.1")){ + return 480; + } + + + + if (version.contains("1.14")){ + return 477; + } + + + + if (version.contains("1.13.2")){ + return 404; + } + + + + if (version.contains("1.13.1")){ + return 401; + } + + + + if (version.contains("1.13")){ + return 393; + } + + + + if (version.contains("1.12.2")){ + return 340; + } + + + + if (version.contains("1.12.1")){ + return 338; + } + + + + if (version.contains("1.12")){ + return 335; + } + + + + if (version.contains("1.11.2")){ + return 316; + } + + + + if (version.contains("1.11.1")){ + return 316; + } + + + + if (version.contains("1.11")){ + return 315; + } + + + + if (version.contains("1.10.2")){ + return 210; + } + + + + if (version.contains("1.10.1")){ + return 210; + } + + + + if (version.contains("1.10")){ + return 210; + } + + + + if (version.contains("1.9.4")){ + return 110; + } + + + + if (version.contains("1.9.3")){ + return 110; + } + + + + if (version.contains("1.9.2")){ + return 109; + } + + + + if (version.contains("1.9.1")){ + return 108; + } + + + + if (version.contains("1.9")){ + return 107; + } + + + + if (version.contains("1.8.9")){ + return 47; + } + + + + if (version.contains("1.8.8")){ + return 47; + } + + + + if (version.contains("1.8.7")){ + return 47; + } + + + + if (version.contains("1.8.6")){ + return 47; + } + + + + if (version.contains("1.8.5")){ + return 47; + } + + + + if (version.contains("1.8.4")){ + return 47; + } + + + + if (version.contains("1.8.3")){ + return 47; + } + + + + if (version.contains("1.8.2")){ + return 47; + } + + + + if (version.contains("1.8.1")){ + return 47; + } + + + + if (version.contains("1.8")){ + return 47; + } + + + + if (version.contains("1.7.10")){ + return 5; + } + + + + if (version.contains("1.7.9")){ + return 5; + } + + + + if (version.contains("1.7.8")){ + return 5; + } + + + + if (version.contains("1.7.7")){ + return 5; + } + + + + if (version.contains("1.7.6")){ + return 5; + } + + + + if (version.contains("1.7.5")){ + return 4; + } + + + + if (version.contains("1.7.4")){ + return 4; + } + + + + if (version.contains("1.7.2")){ + return 4; + } + + + + return 47; + } + + +} diff --git a/src/main/java/me/replydev/mcping/net/SimplePing.java b/src/main/java/me/replydev/mcping/net/SimplePing.java new file mode 100644 index 0000000..22102e6 --- /dev/null +++ b/src/main/java/me/replydev/mcping/net/SimplePing.java @@ -0,0 +1,40 @@ +package me.replydev.mcping.net; + +import me.replydev.utils.SystemSpecs; + +import java.io.IOException; +import java.net.InetAddress; + +public class SimplePing { + + private final InetAddress address; + private final int timeout; + + public SimplePing(InetAddress address,int timeout){ + this.address = address; + this.timeout = timeout; + } + + public boolean isAlive(){ + SystemSpecs specs = new SystemSpecs(); + if(specs.getOperatingSystem().contains("windows")) + { + WindowsPinger pinger = new WindowsPinger(timeout); + boolean response; + try { + response = pinger.ping(address,2); + return response; + } catch (IOException e) { + return true; + } + } + else + { + TCPPinger pinger = new TCPPinger(timeout); + boolean response; + + response = pinger.ping(address,2); + return response; + } + } +} diff --git a/src/main/java/me/replydev/mcping/net/TCPPinger.java b/src/main/java/me/replydev/mcping/net/TCPPinger.java new file mode 100644 index 0000000..c30fecc --- /dev/null +++ b/src/main/java/me/replydev/mcping/net/TCPPinger.java @@ -0,0 +1,93 @@ +package me.replydev.mcping.net; + +import java.io.Closeable; +import java.io.IOException; +import java.net.*; + +//I would not recommend this for non-Windows systems. What would happen if those ports are closed but the host is still alive? +//Instead, use the ICMP protocol, this is more like a mini-port scanner for designated ports. +//InetAddress::isReachable works well too. (very well infact) +class TCPPinger +{ + + // try different ports in sequence, starting with 80 (which is most probably not filtered) + private static final int[] PROBE_TCP_PORTS = {80, 443, 8080, 22, 7}; + private final int timeout; + public TCPPinger(int timeout) { + this.timeout = timeout; + } + + public boolean ping(InetAddress address, int count) + { + //PingResult result = new PingResult(subject.getAddress(), count); + + Socket socket; + for (int i = 0; i < count && !Thread.currentThread().isInterrupted(); i++) + { + socket = new Socket(); + // cycle through different ports until a working one is found + int probePort = PROBE_TCP_PORTS[i % PROBE_TCP_PORTS.length]; + // change the first port to the requested one, if it is available + + //long startTime = System.currentTimeMillis(); + try + { + // set some optimization options + socket.setReuseAddress(true); + socket.setReceiveBufferSize(32); + //int timeout = result.isTimeoutAdaptationAllowed() ? min(result.getLongestTime() * 2, this.timeout) : this.timeout; + socket.connect(new InetSocketAddress(address, probePort), timeout); + if (socket.isConnected()) + { + // it worked - success + //success(result, startTime); + closeQuietly(socket); + return true; + // it worked! - remember the current port + //workingPort = probePort; + } + } + catch (SocketTimeoutException ignore) {} + catch (NoRouteToHostException e) { break; } //this means that the host is down + catch (IOException e) + { + String msg = e.getMessage(); + + // RST should result in ConnectException, but not all Java implementations respect that + if (msg.contains(/*Connection*/"refused")) + { + // we've got an RST packet from the host - it is alive + closeQuietly(socket); + return true; + //success(result, startTime); + } + else + { + // this should result in NoRouteToHostException or ConnectException, but not all Java implementation respect that + if (msg.contains(/*No*/"route to host") || msg.contains(/*Host is*/"down") || msg.contains(/*Network*/"unreachable") || msg.contains(/*Socket*/"closed")) { + // host is down + break; + } + } + } + finally + { + closeQuietly(socket); + } + } + + return false; + } + + //A socket is already a closeable, this method is redundant + + + private static void closeQuietly(Closeable closeable) { + if (closeable != null) try { + closeable.close(); + } + catch (IOException ignore) { + } + } + +} \ No newline at end of file diff --git a/src/main/java/me/replydev/mcping/net/WinIpHlp.java b/src/main/java/me/replydev/mcping/net/WinIpHlp.java new file mode 100644 index 0000000..bc33fea --- /dev/null +++ b/src/main/java/me/replydev/mcping/net/WinIpHlp.java @@ -0,0 +1,19 @@ +package me.replydev.mcping.net; + + + +import java.net.InetAddress; + +class WinIpHlp { + public static WinIpHlpDll.IpAddrByVal toIpAddr(InetAddress address) { + WinIpHlpDll.IpAddrByVal addr = new WinIpHlpDll.IpAddrByVal(); + addr.bytes = address.getAddress(); + return addr; + } + + public static WinIpHlpDll.Ip6SockAddrByRef toIp6Addr(InetAddress address) { + WinIpHlpDll.Ip6SockAddrByRef addr = new WinIpHlpDll.Ip6SockAddrByRef(); + addr.bytes = address.getAddress(); + return addr; + } +} diff --git a/src/main/java/me/replydev/mcping/net/WinIpHlpDll.java b/src/main/java/me/replydev/mcping/net/WinIpHlpDll.java new file mode 100644 index 0000000..b8e339f --- /dev/null +++ b/src/main/java/me/replydev/mcping/net/WinIpHlpDll.java @@ -0,0 +1,167 @@ +package me.replydev.mcping.net; + +import com.sun.jna.Library; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.Structure; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +import static java.lang.reflect.Modifier.isStatic; + +/** + * JNA binding for iphlpapi.dll for ICMP and ARP support under Windows + */ +public interface WinIpHlpDll extends Library { + WinIpHlpDll dll = Loader.load(); + class Loader { + static WinIpHlpDll load() { + try { + return Native.load("iphlpapi", WinIpHlpDll.class); + } + catch (UnsatisfiedLinkError e) { + return Native.load("icmp", WinIpHlpDll.class); + } + } + } + + class AutoOrderedStructure extends Structure { + // this is a requirement of newer JNA, possibly it won't work on some JVM, but probability is quite small + @Override protected List getFieldOrder() { + ArrayList fields = new ArrayList<>(); + for (Field field : getClass().getFields()) { + if (!isStatic(field.getModifiers())) + fields.add(field.getName()); + } + return fields; + } + } + + /** + * Wrapper for Microsoft's IcmpCreateFile + */ + Pointer IcmpCreateFile(); + + /** + * Wrapper for Microsoft's Icmp6CreateFile + */ + Pointer Icmp6CreateFile(); + + /** + * Wrapper for Microsoft's IcmpCloseHandle + */ + void IcmpCloseHandle(Pointer hIcmp); + + /** + * Wrapper for Microsoft's IcmpSendEcho + */ + int IcmpSendEcho( + Pointer hIcmp, + IpAddrByVal destinationAddress, + Pointer requestData, + short requestSize, + IpOptionInformationByRef requestOptions, + Pointer replyBuffer, + int replySize, + int timeout + ); + + /** + * Wrapper for Microsoft's Icmp6SendEcho2 + */ + int Icmp6SendEcho2( + Pointer hIcmp, + Pointer event, + Pointer apcRoutine, + Pointer apcContext, + Ip6SockAddrByRef sourceAddress, + Ip6SockAddrByRef destinationAddress, + Pointer requestData, + short requestSize, + IpOptionInformationByRef requestOptions, + Pointer replyBuffer, + int replySize, + int timeout + ); + + /** + * Wrapper for Microsoft's SendARP + */ + int SendARP( + IpAddrByVal destIP, + int srcIP, + Pointer pMacAddr, + Pointer pPhyAddrLen + ); + + class IpAddr extends AutoOrderedStructure { + public byte[] bytes = new byte[4]; + } + + class IpAddrByVal extends IpAddr implements Structure.ByValue { + } + + class Ip6SockAddr extends AutoOrderedStructure { + public short family = 10; + public short port; + public int flowInfo; + public byte[] bytes = new byte[16]; + public int scopeId; + } + + class Ip6SockAddrByRef extends Ip6SockAddr implements Structure.ByReference { + } + + class IpOptionInformation extends AutoOrderedStructure { + public byte ttl; + public byte tos; + public byte flags; + public byte optionsSize; + public Pointer optionsData; + } + + class IpOptionInformationByVal + extends IpOptionInformation implements Structure.ByValue { + } + + class IpOptionInformationByRef + extends IpOptionInformation implements Structure.ByReference { + } + + class IcmpEchoReply extends AutoOrderedStructure { + public IpAddrByVal address; + public int status; + public int roundTripTime; + public short dataSize; + public short reserved; + public Pointer data; + public IpOptionInformationByVal options; + + public IcmpEchoReply() { + } + + public IcmpEchoReply(Pointer p) { + useMemory(p); + read(); + } + } + + class Icmp6EchoReply extends AutoOrderedStructure { + public short port; + public byte[] flowInfo = new byte[4]; + public final byte[] addressBytes = new byte[16]; + public int scopeId; + public int status; + public int roundTripTime; + + public Icmp6EchoReply() { + } + + public Icmp6EchoReply(Pointer p) { + useMemory(p); + read(); + } + } +} diff --git a/src/main/java/me/replydev/mcping/net/WindowsPinger.java b/src/main/java/me/replydev/mcping/net/WindowsPinger.java new file mode 100644 index 0000000..d4ffd06 --- /dev/null +++ b/src/main/java/me/replydev/mcping/net/WindowsPinger.java @@ -0,0 +1,98 @@ +package me.replydev.mcping.net; + +import com.sun.jna.Memory; +import com.sun.jna.Pointer; + +import java.io.IOException; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.util.Arrays; + +import static java.lang.Thread.currentThread; +import static me.replydev.mcping.net.WinIpHlp.toIp6Addr; +import static me.replydev.mcping.net.WinIpHlp.toIpAddr; +import static me.replydev.mcping.net.WinIpHlpDll.dll; + +class WindowsPinger { + private final int timeout; + private final WinIpHlpDll.Ip6SockAddrByRef anyIp6SourceAddr = new WinIpHlpDll.Ip6SockAddrByRef(); + + public WindowsPinger(int timeout) { + this.timeout = timeout; + } + + public boolean ping(InetAddress address, int count) throws IOException { + if (address instanceof Inet6Address) + return ping6(address, count); + else + return ping4(address, count); + } + + private boolean ping4(InetAddress address, int count) throws IOException { + Pointer handle = dll.IcmpCreateFile(); + if (handle == null) throw new IOException("Unable to create Windows native ICMP handle"); + + int sendDataSize = 32; + int replyDataSize = sendDataSize + (new WinIpHlpDll.IcmpEchoReply().size()) + 10; + Pointer sendData = new Memory(sendDataSize); + sendData.clear(sendDataSize); + Pointer replyData = new Memory(replyDataSize); + + //PingResult result = new PingResult(a, count); + try { + WinIpHlpDll.IpAddrByVal ipaddr = toIpAddr(address); + for (int i = 1; i <= count && !currentThread().isInterrupted(); i++) { + int numReplies = dll.IcmpSendEcho(handle, ipaddr, sendData, (short) sendDataSize, null, replyData, replyDataSize, timeout); + WinIpHlpDll.IcmpEchoReply echoReply = new WinIpHlpDll.IcmpEchoReply(replyData); + if (numReplies > 0 && echoReply.status == 0 && Arrays.equals(echoReply.address.bytes, ipaddr.bytes)) { + return true; + } + } + } + finally { + dll.IcmpCloseHandle(handle); + } + return false; + } + + private boolean ping6(InetAddress address, int count) throws IOException { + Pointer handle = dll.Icmp6CreateFile(); + if (handle == null) throw new IOException("Unable to create Windows native ICMP6 handle"); + + int sendDataSize = 32; + int replyDataSize = sendDataSize + (new WinIpHlpDll.Icmp6EchoReply().size()) + 10; + Pointer sendData = new Memory(sendDataSize); + sendData.clear(sendDataSize); + Pointer replyData = new Memory(replyDataSize); + + //PingResult result = new PingResult(subject.getAddress(), count); + try { + WinIpHlpDll.Ip6SockAddrByRef ipaddr = toIp6Addr(address); + for (int i = 1; i <= count && !currentThread().isInterrupted(); i++) { + int numReplies = dll.Icmp6SendEcho2(handle, null, null, null, anyIp6SourceAddr, toIp6Addr(address), + sendData, (short) sendDataSize, null, replyData, replyDataSize, timeout); + WinIpHlpDll.Icmp6EchoReply echoReply = new WinIpHlpDll.Icmp6EchoReply(replyData); + if (numReplies > 0 && echoReply.status == 0 && Arrays.equals(echoReply.addressBytes, ipaddr.bytes)) { + //result.addReply(echoReply.roundTripTime); + dll.IcmpCloseHandle(handle); + return true; + } + } + } + finally { + dll.IcmpCloseHandle(handle); + } + + return false; + } + + public static void main(String[] args) throws IOException { + //System.out.println(new WindowsPinger(5000).ping(InetAddress.getByName("::1"), 1)); + long now = System.currentTimeMillis(); + System.out.println(new TCPPinger(5000).ping(InetAddress.getByName("164.132.200.78"),1)); + System.out.println(System.currentTimeMillis() - now); + now = System.currentTimeMillis(); + System.out.println(new WindowsPinger(5000).ping(InetAddress.getByName("164.132.200.78"),1)); + System.out.println(System.currentTimeMillis() - now); + } +} \ No newline at end of file diff --git a/src/main/java/me/replydev/mcping/rawData/Description.java b/src/main/java/me/replydev/mcping/rawData/Description.java new file mode 100644 index 0000000..8f3411e --- /dev/null +++ b/src/main/java/me/replydev/mcping/rawData/Description.java @@ -0,0 +1,12 @@ +package me.replydev.mcping.rawData; + +import com.google.gson.annotations.SerializedName; + +public class Description { + @SerializedName("text") + private String text; + + public String getText() { + return this.text; + } +} diff --git a/src/main/java/me/replydev/mcping/rawData/Extra.java b/src/main/java/me/replydev/mcping/rawData/Extra.java new file mode 100644 index 0000000..a9e7358 --- /dev/null +++ b/src/main/java/me/replydev/mcping/rawData/Extra.java @@ -0,0 +1,18 @@ +package me.replydev.mcping.rawData; + +import com.google.gson.annotations.SerializedName; + +class Extra { + + @SerializedName("color") + private String color; + @SerializedName("bold") + private boolean bold; + @SerializedName("text") + private String text; + + public String getText() { + return text; + } + +} diff --git a/src/main/java/me/replydev/mcping/rawData/ExtraDescription.java b/src/main/java/me/replydev/mcping/rawData/ExtraDescription.java new file mode 100644 index 0000000..c9ff30f --- /dev/null +++ b/src/main/java/me/replydev/mcping/rawData/ExtraDescription.java @@ -0,0 +1,19 @@ +package me.replydev.mcping.rawData; + +import com.google.gson.annotations.SerializedName; + +public class ExtraDescription { + + @SuppressWarnings("MismatchedReadAndWriteOfArray") + @SerializedName("extra") + private Extra[] extra; + + + public String getText(){ + StringBuilder s = new StringBuilder(); + for(Extra e : extra){ + s.append(e.getText()); + } + return s.toString(); + } +} diff --git a/src/main/java/me/replydev/mcping/rawData/ForgeData.java b/src/main/java/me/replydev/mcping/rawData/ForgeData.java new file mode 100644 index 0000000..4d84945 --- /dev/null +++ b/src/main/java/me/replydev/mcping/rawData/ForgeData.java @@ -0,0 +1,17 @@ +package me.replydev.mcping.rawData; + +import com.google.gson.annotations.SerializedName; + +public class ForgeData { + + @SerializedName("mods") + private ForgeDataListItem[] modList; + + public int getNMods(){ + return modList.length; + } + + public ForgeDataListItem[] getModList() { + return modList; + } +} \ No newline at end of file diff --git a/src/main/java/me/replydev/mcping/rawData/ForgeDataListItem.java b/src/main/java/me/replydev/mcping/rawData/ForgeDataListItem.java new file mode 100644 index 0000000..b92b53a --- /dev/null +++ b/src/main/java/me/replydev/mcping/rawData/ForgeDataListItem.java @@ -0,0 +1,19 @@ +package me.replydev.mcping.rawData; + +import com.google.gson.annotations.SerializedName; + +public class ForgeDataListItem { + @SerializedName("modId") + private String modid; + + @SerializedName("modmarker") + private String version; + + public String getModid() { + return modid; + } + + public String getVersion() { + return version; + } +} diff --git a/src/main/java/me/replydev/mcping/rawData/ForgeDescriptionTranslate.java b/src/main/java/me/replydev/mcping/rawData/ForgeDescriptionTranslate.java new file mode 100644 index 0000000..b3731ed --- /dev/null +++ b/src/main/java/me/replydev/mcping/rawData/ForgeDescriptionTranslate.java @@ -0,0 +1,12 @@ +package me.replydev.mcping.rawData; + +import com.google.gson.annotations.SerializedName; + +public class ForgeDescriptionTranslate { + @SerializedName("translate") + private String translate; + + public String getTranslate(){ + return translate; + } +} diff --git a/src/main/java/me/replydev/mcping/rawData/ForgeModInfo.java b/src/main/java/me/replydev/mcping/rawData/ForgeModInfo.java new file mode 100644 index 0000000..d8a6eb1 --- /dev/null +++ b/src/main/java/me/replydev/mcping/rawData/ForgeModInfo.java @@ -0,0 +1,20 @@ +package me.replydev.mcping.rawData; + +import com.google.gson.annotations.SerializedName; + +public class ForgeModInfo { + + @SerializedName("type") + private String type; + + @SerializedName("modList") + private ForgeModListItem[] modList; + + public int getNMods(){ + return modList.length; + } + + public ForgeModListItem[] getModList() { + return modList; + } +} diff --git a/src/main/java/me/replydev/mcping/rawData/ForgeModListItem.java b/src/main/java/me/replydev/mcping/rawData/ForgeModListItem.java new file mode 100644 index 0000000..42f16d8 --- /dev/null +++ b/src/main/java/me/replydev/mcping/rawData/ForgeModListItem.java @@ -0,0 +1,20 @@ +package me.replydev.mcping.rawData; + +import com.google.gson.annotations.SerializedName; + +public class ForgeModListItem { + + @SerializedName("modid") + private String modid; + + @SerializedName("version") + private String version; + + public String getModid() { + return modid; + } + + public String getVersion() { + return version; + } +} diff --git a/src/main/java/me/replydev/mcping/rawData/Player.java b/src/main/java/me/replydev/mcping/rawData/Player.java new file mode 100644 index 0000000..d7c2da0 --- /dev/null +++ b/src/main/java/me/replydev/mcping/rawData/Player.java @@ -0,0 +1,19 @@ +package me.replydev.mcping.rawData; + +import com.google.gson.annotations.SerializedName; + +public class Player { + + @SerializedName("name") + private String name; + @SerializedName("id") + private String id; + + public String getName() { + return this.name; + } + + public String getId() { + return this.id; + } +} diff --git a/src/main/java/me/replydev/mcping/rawData/Players.java b/src/main/java/me/replydev/mcping/rawData/Players.java new file mode 100644 index 0000000..246e9a5 --- /dev/null +++ b/src/main/java/me/replydev/mcping/rawData/Players.java @@ -0,0 +1,38 @@ +package me.replydev.mcping.rawData; + +import com.google.gson.annotations.SerializedName; + +import java.util.List; + +public class Players { + @SerializedName("max") + private int max; + @SerializedName("online") + private int online; + @SerializedName("sample") + private List sample; + + public int getMax() { + return this.max; + } + + public int getOnline() { + return this.online; + } + + public void setMax(int max) { + this.max = max; + } + + public void setOnline(int online) { + this.online = online; + } + + public void setSample(List sample) { + this.sample = sample; + } + + public List getSample() { + return this.sample == null ? List.of() : this.sample; + } +} diff --git a/src/main/java/me/replydev/mcping/rawData/Version.java b/src/main/java/me/replydev/mcping/rawData/Version.java new file mode 100644 index 0000000..ee11d53 --- /dev/null +++ b/src/main/java/me/replydev/mcping/rawData/Version.java @@ -0,0 +1,26 @@ +package me.replydev.mcping.rawData; + +import com.google.gson.annotations.SerializedName; + +public class Version { + @SerializedName("name") + private String name; + @SerializedName("protocol") + private int protocol = Integer.MIN_VALUE; // Don't use -1 as this has special meaning + + public void setProtocol(int protocol) { + this.protocol = protocol; + } + + public void setName(String a){ + name = a; + } + + public String getName() { + return this.name; + } + + public int getProtocol() { + return protocol; + } +} diff --git a/src/main/java/me/replydev/qubo/CLI.java b/src/main/java/me/replydev/qubo/CLI.java new file mode 100644 index 0000000..1d56024 --- /dev/null +++ b/src/main/java/me/replydev/qubo/CLI.java @@ -0,0 +1,179 @@ +package me.replydev.qubo; + +import me.replydev.utils.Log; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.nio.file.Path; +import java.util.List; + +public class CLI { + + private static QuboInstance quboInstance; + + public static QuboInstance getQuboInstance() + { + return quboInstance; + } + + static void init(String[] a) + { + printLogo(); + if(!isUTF8Mode()){ + System.out.println("\033[31mThe scanner isn't running in UTF-8 mode!"); + System.out.println("\033[31musage: \"-Dfile.encoding=UTF-8\" -jar [file]"); + System.out.println("\033[31mexample command => \033[33m\"\033[0mjava -Dfile.encoding=UTF-8 -jar qubo.jar -ports 1-110.200-205.300-305.400-405.500-505.600-605.666.700-705.777.800-805.900-905.1000-1005.2000-2020.3000-3005.4000-4005.5000-5005.6666.7000-7005.7777.8000-8010.10000-10020.12345.12346.13338.20000-20020.21000-21020.22000-22020.23000-23005.24000-24005.25000-25020.25500-25630.26000-26020.26666.27000-27020.27777.28000-28005.28888.29000-29005.29999.10000-10020.20000-20020.30000-30020.40000-40020.50000-50020.60000-60020 -th 900 -ti 1500 -c 1 -noping -range \033[0m[IP]\033[33m\""); + GoodBye.quit(-1); + } + + /* controlla se ci sono almeno -range o -rangefile */ + boolean range = false; + boolean rangefile = false; + String fname = "new_scan"; + int c = -1; + for (String b : a){ + c++; + if (b.contains("-rangefile")){ + rangefile = true; + fname = a[c+1]; + }else if (b.contains("-range")){ + range = true; + fname = a[c+1]; + } + } + if (rangefile==false && range==false){ + System.out.println("\033[31mYou must provide either the argument \033[0m-range\033[31m or \033[0m-rangefile"); + GoodBye.quit(-4); + } + fname+=".txt"; + + /* operazioni con la classe Filez */ + Filez.init(); + Path p = Filez.create_file(fname); + Filez.setCurrentPath(p); + Filez.writeToFile(p, "[custom Quboscanner made by www.n0nexist.gq]"); + + /* controlla se dobbiamo usare -range o -rangefile */ + c = -1; + boolean found = false; + for (String b : a){ + c++; + if (b.contains("-rangefile")){ + try{ + System.out.println("\033[31mStarting with \033[0m"+a[c+1]+" \033[31mas range file"); + txtRun(a[c+1],a); + found = true; + }catch(ArrayIndexOutOfBoundsException e){ + System.out.println("\033[31musage\033[0m => \033[33m-rangefile \033[0m(\033[33mfile.txt\033[0m)"); + GoodBye.quit(3); + } + } + } + if (!found){ + System.out.println("\033[0m-rangefile\033[31m option not found, starting a normal scan"); + standardRun(a); + } + + /* comunica all'utente quanti server abbiamo trovato e termina l'esecuzione */ + Log.logln("\nScan terminated \033[33m<=>\033[0m found \033[31m"+Info.serverNotFilteredFound+"\033[0m servers."); + Filez.writeToFile(Filez.getCurrentPath(), "[found "+Info.serverNotFilteredFound+" servers in total]"); + System.out.println("\033[0;0m"); + GoodBye.quit(0); + } + + private static void printLogo() + { + /* logo del programma */ + System.out.println("\033[31m┌─┐ ┬ ┬┌┐ ┌─┐┌─┐┌─┐┌─┐┌┐┌┌┐┌┌─┐┬─┐\n" + + "\033[31m│─┼┐│ │├┴┐│ │└─┐│ ├─┤││││││├┤ ├┬┘\n" + +"\033[33m└─┘└└─┘└─┘└─┘└─┘└─┘┴ ┴┘└┘┘└┘└─┘┴└─\n"); + System.out.println("\033[31mwww.n0nexist.gq \033[0m<=> \033[31mgithub.com/n0nexist"); + } + + private static void standardRun(String[] a) + { + /* run standard con -range */ + InputData i; + try + { + i = new InputData(a); + } + catch (Exception e) + { + System.err.println(e.getMessage()); + return; + } + Info.debugMode = i.isDebugMode(); + quboInstance = new QuboInstance(i); + try{ + quboInstance.run(); + }catch (NumberFormatException e){ + quboInstance.inputData.help(); + } + } + + public static String[] replaceInArray(String[] arr, String to, String with) { + String[] newArr = new String[arr.length]; + for (int i = 0; i < arr.length; i++) { + if (arr[i].equals(to)) { + newArr[i] = with; + } else { + newArr[i] = arr[i]; + } + } + return newArr; + } + + private static void txtRun(String filename,String[] a) + { + /* run con -filerange */ + try + { + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String s; + while ((s = reader.readLine()) != null) + { + if (s.isEmpty()) + { + continue; + } + + try { + + /* rimpiazziamo l'opzione -rangefile negli argomenti con "-range [ilrange]" */ + String[] temp = replaceInArray(a, "-rangefile", "-range"); + temp = replaceInArray(temp, filename, s); + + + System.out.println("\033[33mStarting new scan \033[0m=> \033[33m"+s); + + + /* lanciamo una nuova istanza di qubo con il range corrente */ + QuboInstance qq = new QuboInstance(new InputData(temp)); + quboInstance = qq; + qq.run(); + + } catch (Exception e) { + System.out.println("\033mERROR"); + e.printStackTrace(); + GoodBye.quit(-5); + } + } + reader.close(); + } + catch (IOException e) + { + System.err.println("\033[33mFile \033[0m\""+filename+"\"\033[0m not found"); + GoodBye.quit(-1); + } + } + + private static boolean isUTF8Mode() + { + List arguments = ManagementFactory.getRuntimeMXBean().getInputArguments(); + return arguments.contains("-Dfile.encoding=UTF-8"); + } + +} \ No newline at end of file diff --git a/src/main/java/me/replydev/qubo/Filez.java b/src/main/java/me/replydev/qubo/Filez.java new file mode 100644 index 0000000..8653970 --- /dev/null +++ b/src/main/java/me/replydev/qubo/Filez.java @@ -0,0 +1,82 @@ +package me.replydev.qubo; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Random; +import java.nio.file.StandardOpenOption; + +public class Filez { + + private static Path quboOutputs = Paths.get("qubo_outputs"); + private static Path currentPath; + + + public static String parse(String f) { + return f.replaceAll("[^a-zA-Z0-9]", "_"); + } + + + public static void setCurrentPath(Path p){ + currentPath = p; + } + + public static Path getCurrentPath() { + return currentPath; + } + + + public static void init(){ + /** + * crea la cartella qubo_outputs + * se non esiste già + */ + if (!Files.exists(quboOutputs)) { + try { + Files.createDirectories(quboOutputs); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + public static Path create_file(String fileName) { + /** + * crea il file desiderato + */ + fileName = parse(fileName); + Path newFile = quboOutputs.resolve(fileName); + while (Files.exists(newFile)) { + /* se esiste già, + * creiamo il file con lo stesso nome ma con + * 3 caratteri random aggiunti + */ + String randomChars = ""; + Random rand = new Random(); + for (int i = 0; i < 3; i++) { + char c = (char) (rand.nextInt(26) + 'a'); + randomChars += c; + } + newFile = quboOutputs.resolve(fileName + randomChars); + } + try { + Files.createFile(newFile); + } catch (IOException e) { + e.printStackTrace(); + } + return newFile; + } + + public static void writeToFile(Path newFile,String content){ + /** + * scrivi dentro il file desiderato + * (in append mode) + */ + try { + Files.write(newFile, (content+"\n").getBytes(), StandardOpenOption.APPEND); + } catch (IOException e) { + e.printStackTrace(); + } + } + +} diff --git a/src/main/java/me/replydev/qubo/GoodBye.java b/src/main/java/me/replydev/qubo/GoodBye.java new file mode 100644 index 0000000..dcfea5a --- /dev/null +++ b/src/main/java/me/replydev/qubo/GoodBye.java @@ -0,0 +1,12 @@ +package me.replydev.qubo; + +public class GoodBye { + + public static void quit(int code){ + /* comunica all'utente che stiamo uscendo */ + System.out.println("\033[3;31mEXITING WITH CODE \033[0;0m=>\033[33m "+code); + System.out.println("\033[0;0m"); // resettiamo i colori e lo stile + System.exit(code); + } + +} diff --git a/src/main/java/me/replydev/qubo/Info.java b/src/main/java/me/replydev/qubo/Info.java new file mode 100644 index 0000000..2839f66 --- /dev/null +++ b/src/main/java/me/replydev/qubo/Info.java @@ -0,0 +1,10 @@ +package me.replydev.qubo; + +public class Info { + public static final String version = "0.4"; + public static final String otherVersionInfo = ""; + public static int serverFound = 0; + public static int serverNotFilteredFound = 0; + public static boolean gui; + public static boolean debugMode; +} \ No newline at end of file diff --git a/src/main/java/me/replydev/qubo/InputData.java b/src/main/java/me/replydev/qubo/InputData.java new file mode 100644 index 0000000..b8c590d --- /dev/null +++ b/src/main/java/me/replydev/qubo/InputData.java @@ -0,0 +1,203 @@ +package me.replydev.qubo; + +import org.apache.commons.cli.*; + +import inet.ipaddr.IPAddressSeqRange; +import inet.ipaddr.IPAddressString; +import me.replydev.utils.InvalidRangeException; +import me.replydev.utils.IpList; +import me.replydev.utils.PortList; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Objects; + +public class InputData{ + + private IpList ipList; + private PortList portrange; + + + private CommandLine cmd; + + private boolean ping; + private final String filename = ""; + + private final Options options; + + private Options buildOptions() + { + Option iprange = new Option("range","iprange",true,"REQUIRED IF -rangefile == NULL"); + iprange.setRequired(false); + + Option rangefile = new Option("rangefile",false,"REQUIRED IF -range == NULL"); + rangefile.setRequired(false); + + Option portrange = new Option("ports","portrange",true,"The range of ports that www.n0nexist.gq will work on"); + portrange.setRequired(true); + + Option threads = new Option("th","threads",true,"Maximum number of running async threads"); + threads.setRequired(true); + + Option timeout = new Option("ti","timeout",true,"Server Ping timeout"); + timeout.setRequired(true); + + Option count = new Option("c","pingcount",true,"How many times www.n0nexist.gq will ping a server"); + count.setRequired(false); + + Option noping = new Option("noping",false,"Prevent Qubo from pinging IPs before start scan"); + noping.setRequired(false); + + Option all = new Option("all",false,"Force Qubo to scan broadcast IPs and common ports"); + all.setRequired(false); + + Option filterVersion = new Option("ver","filterversion",true,"Show only hits with given version"); + filterVersion.setRequired(false); + + Option filterMotd = new Option("motd","filtermotd",true,"Show only hits with given motd"); + filterMotd.setRequired(false); + + Option filterOn = new Option("on","minonline",true,"Show only hits with at least players online"); + filterOn.setRequired(false); + + Option debug = new Option("d","debug",false,"Enables debug mode"); + debug.setRequired(false); + + Options options = new Options(); + options.addOption(iprange); + options.addOption(rangefile); + options.addOption(portrange); + options.addOption(threads); + options.addOption(timeout); + options.addOption(count); + options.addOption(noping); + options.addOption(all); + options.addOption(filterVersion); + options.addOption(filterMotd); + options.addOption(filterOn); + options.addOption(debug); + + return options; + } + + public void help(){ + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp("-range/-rangefile -ports -th -ti ",options); + GoodBye.quit(-1); + } + + public InputData(String[] command) throws InvalidRangeException,NumberFormatException { + options = buildOptions(); + CommandLineParser parser = new DefaultParser(); + String ipStart = "",ipEnd = ""; + try + { + cmd = parser.parse(options,command); + try + { + //Check for begin-end range first, first split the string + String[] beginEnd = cmd.getOptionValue("range").split("-"); + + //See if its length is 2 (begin-end) + if (beginEnd.length >= 2) + { + ipStart = cmd.getOptionValue("range").split("-")[0]; + ipEnd = cmd.getOptionValue("range").split("-")[1]; + } + + //Transform hostname into an IP + if(Objects.equals(ipStart, "")) { + try { + ipStart = InetAddress.getByName(cmd.getOptionValue("range")).getHostAddress(); + ipEnd = ipStart; + } catch (UnknownHostException ignored) {} + } + + //Checks if the string split are both IPs. If not, IPAddressString parses them as a CIDR or shorthand range. + if (IpList.isNotIp(ipStart) || IpList.isNotIp(ipEnd)) + { + IPAddressSeqRange range = new IPAddressString(cmd.getOptionValue("range")).getSequentialRange(); + ipStart = range.getLower().toString(); + ipEnd = range.getUpper().toString(); + } + } + catch (NullPointerException | IndexOutOfBoundsException e) + { + /*if(Info.gui) throw new InvalidRangeException(); + else help();*/ + } + + try + { + ipList = new IpList(ipStart,ipEnd); + } + catch (IllegalArgumentException e){ + throw new IllegalArgumentException(e.getMessage()); + } + try{ + portrange = new PortList(cmd.getOptionValue("ports")); + }catch (NumberFormatException e){ + /*if(Info.gui) throw new NumberFormatException(); + help();*/ + } + + ping = !cmd.hasOption("noping"); + } catch (ParseException e) + { + //help(); //help contiene GoodBye.quit + } + } + + public boolean isPing() { + return ping; + } + public boolean isOutput() { + return !cmd.hasOption("nooutput"); + } + public boolean isSkipCommonPorts() { + return !cmd.hasOption("all"); + } + public boolean isFulloutput() { + return cmd.hasOption("fulloutput"); + } + public void setPing(boolean ping){ + this.ping = ping; + } + + public int getCount() { + return Integer.parseInt(cmd.getOptionValue("c","1")); + } + public IpList getIpList() { + return ipList; + } + + public void setIpList(IpList l) { + ipList=l; + } + + public PortList getPortrange() { + return portrange; + } + public int getThreads() throws NumberFormatException{ + return Integer.parseInt(cmd.getOptionValue("th")); + } + public int getTimeout() { + return Integer.parseInt(cmd.getOptionValue("ti")); + } + public String getFilename() { + return filename; + } + + public String getMotd(){ + return cmd.getOptionValue("filtermotd",""); + } + + public String getVersion(){ + return cmd.getOptionValue("filterversion",""); + } + public int getMinPlayer(){ + return Integer.parseInt(cmd.getOptionValue("on","-1")); + } + + public boolean isDebugMode(){ return cmd.hasOption("debug"); } +} diff --git a/src/main/java/me/replydev/qubo/Main.java b/src/main/java/me/replydev/qubo/Main.java new file mode 100644 index 0000000..099de87 --- /dev/null +++ b/src/main/java/me/replydev/qubo/Main.java @@ -0,0 +1,8 @@ +package me.replydev.qubo; + +public class Main { + + public static void main(String[] args) { + CLI.init(args); + } +} \ No newline at end of file diff --git a/src/main/java/me/replydev/qubo/QuboInstance.java b/src/main/java/me/replydev/qubo/QuboInstance.java new file mode 100644 index 0000000..a8d2258 --- /dev/null +++ b/src/main/java/me/replydev/qubo/QuboInstance.java @@ -0,0 +1,152 @@ +package me.replydev.qubo; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import me.replydev.mcping.net.Check; +import me.replydev.mcping.net.SimplePing; +import me.replydev.utils.Log; + +public class QuboInstance +{ + + public final InputData inputData; + private String ip; // current ip + private int port; // current port + private boolean stop; + public final AtomicInteger currentThreads; + private final int[] COMMON_PORTS = { 25, 80, 443, 20, 21, 22, 23, 143, 3306, 3389, 53, 67, 68, 110 }; + private long serverCount = 0; + ExecutorService checkService; + + public QuboInstance(InputData inputData) + { + this.inputData = inputData; + this.currentThreads = new AtomicInteger(); + stop = false; + + if(this.inputData.isDebugMode()){ + Log.logln("Debug mode enabled"); + } + if (this.inputData.getPortrange().size() < 1500) + { + this.inputData.setPing(false); + } + } + + public void run() + { + try + { + checkServersExecutor(); + } + catch (InterruptedException e) + { + checkService.shutdownNow(); + GoodBye.quit(-7); + } + + + } + + private void checkServersExecutor() throws InterruptedException,NumberFormatException { + checkService = Executors.newFixedThreadPool(inputData.getThreads()); + Log.logln("Scanning..."); + + + while (inputData.getIpList().hasNext()) + { + ip = inputData.getIpList().getNext(); + try + { + InetAddress address = InetAddress.getByName(ip); + if (inputData.isPing()) + { + SimplePing simplePing = new SimplePing(address, inputData.getTimeout()); + if (!simplePing.isAlive()) + continue; + } + if (inputData.isSkipCommonPorts() && isLikelyBroadcast(address)) + continue; + } catch (UnknownHostException ignored) {} + + while (inputData.getPortrange().hasNext()) + { + if (stop) + { + checkService.shutdown(); + checkService.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS); + return; + } + + port = inputData.getPortrange().get(); + if (isCommonPort(port)) + { + inputData.getPortrange().next(); + continue; + } + + if (currentThreads.get() < inputData.getThreads()) + { + currentThreads.incrementAndGet(); + checkService.execute( + new Check(ip, port, inputData.getTimeout(), inputData.getCount(), + this, inputData.getVersion(), inputData.getMotd(), inputData.getMinPlayer())); + inputData.getPortrange().next(); // va al successivo + serverCount++; + } + } + inputData.getPortrange().reload(); + } + checkService.shutdown(); + checkService.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS); + } + + public String getCurrent() + { + return "\033[0m"+ip + ":" + port + " - \033[33m" + String.format("%.2f", getPercentage()) + "%"; + } + + public int getThreads() + { + return currentThreads.get(); + } + + public void stop() + { + this.stop = true; + } + + private boolean isCommonPort(int port) + { + if (!inputData.isSkipCommonPorts()) + { + return false; + } + for (int i : COMMON_PORTS) + { + if (i == port) { + return true; + } + } + return false; + } + + public double getPercentage() + { + // 15 : 15000 = x : 100 + double max = inputData.getIpList().getCount() * inputData.getPortrange().size(); + return serverCount * 100 / max; + } + + private boolean isLikelyBroadcast(InetAddress address) + { + byte[] bytes = address.getAddress(); + return bytes[bytes.length - 1] == 0 || bytes[bytes.length - 1] == (byte) 0xFF; + } + +} diff --git a/src/main/java/me/replydev/utils/Confirm.java b/src/main/java/me/replydev/utils/Confirm.java new file mode 100644 index 0000000..3975e5e --- /dev/null +++ b/src/main/java/me/replydev/utils/Confirm.java @@ -0,0 +1,17 @@ +package me.replydev.utils; + +import me.replydev.qubo.Info; + +import javax.swing.*; + +public class Confirm { + + public static boolean getConfirm(String text){ + if(Info.gui){ + return JOptionPane.showConfirmDialog(null, text, "Confirm", JOptionPane.YES_NO_OPTION) == 0; + } + else{ + return Keyboard.s(text + " (y/n): ").equalsIgnoreCase("y"); + } + } +} diff --git a/src/main/java/me/replydev/utils/InvalidRangeException.java b/src/main/java/me/replydev/utils/InvalidRangeException.java new file mode 100644 index 0000000..9625fea --- /dev/null +++ b/src/main/java/me/replydev/utils/InvalidRangeException.java @@ -0,0 +1,11 @@ +package me.replydev.utils; + +public class InvalidRangeException extends Exception { + public InvalidRangeException() + { + } + public InvalidRangeException(String message) + { + super(message); + } +} diff --git a/src/main/java/me/replydev/utils/IpList.java b/src/main/java/me/replydev/utils/IpList.java new file mode 100644 index 0000000..7516439 --- /dev/null +++ b/src/main/java/me/replydev/utils/IpList.java @@ -0,0 +1,77 @@ +package me.replydev.utils; + +import java.util.StringTokenizer; +import java.util.regex.Pattern; + +public class IpList { + + private final long start; + private final long end; + private long index; + + private static final Pattern PATTERN = Pattern.compile( + "^(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])$"); + public static boolean isNotIp(String ip) { + return !PATTERN.matcher(ip).matches(); + } + + public IpList(String _start,String _end){ + if(isNotIp(_start)) throw new IllegalArgumentException(_start + " is not a valid starting ip!"); + if(isNotIp(_end)) throw new IllegalArgumentException(_end + " is not a valid ending ip!"); + + start = host2long(_start); + end = host2long(_end); + index = start; + } + + public boolean hasNext(){ + return index <= end; + } + public long getCount(){ + return end - start; + } + + public String getNext(){ + String data = long2dotted(index); + index++; + return data; + } + + private static long host2long(String host) { + long ip=0; + if (!Character.isDigit(host.charAt(0))) return -1; + int[] addr = ip2intarray(host); + if (addr == null) return -1; + for (int i=0;i 4) return null; + while (tokens.hasMoreTokens()) { + try { + address[i++] = Integer.parseInt(tokens.nextToken()) & 0xFF; + } catch(NumberFormatException nfe) { + return null; + } + } + return address; + } + + private static String long2dotted(long address) { + StringBuilder sb = new StringBuilder(); + for (int i = 0, shift = 24; i < 4; i++, shift -= 8) { + long value = (address >> shift) & 0xff; + sb.append(value); + if (i != 3) { + sb.append('.'); + } + } + return sb.toString(); + } +} diff --git a/src/main/java/me/replydev/utils/Keyboard.java b/src/main/java/me/replydev/utils/Keyboard.java new file mode 100644 index 0000000..837665f --- /dev/null +++ b/src/main/java/me/replydev/utils/Keyboard.java @@ -0,0 +1,24 @@ +package me.replydev.utils; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +public class Keyboard { + + private static final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + + public static String s(){ + try { + return reader.readLine(); + } catch (IOException e) { + + } + return null; + } + + public static String s(String message){ + Log.log(message); + return s(); + } +} diff --git a/src/main/java/me/replydev/utils/Log.java b/src/main/java/me/replydev/utils/Log.java new file mode 100644 index 0000000..6d97372 --- /dev/null +++ b/src/main/java/me/replydev/utils/Log.java @@ -0,0 +1,21 @@ +package me.replydev.utils; + +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.Locale; + +import me.replydev.qubo.Info; + +public class Log { + public static void logln(String s){ + log(s + "\n"); + } + static void log(String s){ + if(Info.gui) return; + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss", Locale.ITALIAN); + LocalTime time = LocalTime.now(); + String f = formatter.format(time); + System.out.print("\u001b[33m[\u001b[0m" + f + "\u001b[33m]\u001b[0m - " + s); + } + +} diff --git a/src/main/java/me/replydev/utils/PortList.java b/src/main/java/me/replydev/utils/PortList.java new file mode 100644 index 0000000..d3faa05 --- /dev/null +++ b/src/main/java/me/replydev/utils/PortList.java @@ -0,0 +1,91 @@ +package me.replydev.utils; + +import java.util.Iterator; + +public final class PortList implements Iterator, Cloneable { + + private int[] portRangeStart; + private int[] portRangeEnd; + + private int rangeCountMinus1; + private int rangeIndex; + private int currentPort; + + private boolean hasNext; + + private String[] portRanges; + + + public PortList(String portString) throws NumberFormatException{ + if (portString != null && (portString = portString.trim()).length() > 0) { + portRanges = portString.split("[\\s\t\n\r,.;]+"); + + // initialize storage + portRangeStart = new int[portRanges.length+1]; // +1 for optimization of 'next' method, prevents ArrayIndexOutOfBoundsException + portRangeEnd = new int[portRanges.length]; + + // parse ints + for (int i = 0; i < portRanges.length; i++) { + String range = portRanges[i]; + int dashPos = range.indexOf('-') + 1; + int endPort = Integer.parseInt(range.substring(dashPos)); + portRangeEnd[i] = endPort; + portRangeStart[i] = dashPos == 0 ? endPort : Integer.parseInt(range.substring(0, dashPos-1)); + if (endPort <= 0 || endPort >= 65536) { + throw new NumberFormatException(endPort + " port is out of range"); + } + } + reload(); + } + } + + public boolean hasNext() { + return hasNext; + } + + public Integer next() { + int returnPort = currentPort++; + + if (currentPort > portRangeEnd[rangeIndex]) { + hasNext = rangeIndex < rangeCountMinus1; + rangeIndex++; + currentPort = portRangeStart[rangeIndex]; + } + + return returnPort; + } + public Integer get(){ + return currentPort; + } + + public int size() { + int size = 0; + if (portRangeStart != null) { + for (int i = 0; i <= rangeCountMinus1; i++) { + size += portRangeEnd[i] - portRangeStart[i] + 1; + } + } + return size; + } + + public PortList copy() { + try { + return (PortList) super.clone(); + } + catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + public void reload(){ + currentPort = portRangeStart[0]; + rangeCountMinus1 = portRanges.length - 1; + rangeIndex = 0; + hasNext = rangeCountMinus1 >= 0; + } + +} \ No newline at end of file diff --git a/src/main/java/me/replydev/utils/Statuz.java b/src/main/java/me/replydev/utils/Statuz.java new file mode 100644 index 0000000..bdb382a --- /dev/null +++ b/src/main/java/me/replydev/utils/Statuz.java @@ -0,0 +1,14 @@ +package me.replydev.utils; + +import me.replydev.qubo.CLI; +import me.replydev.qubo.GoodBye; + +public class Statuz { + + public static void status(){ + try{ + System.out.print("\033[31m[\033[0mTHREADS: \033[33m"+CLI.getQuboInstance().getThreads()+"\033[31m]-[\033[0mCURRENT: \033[33m"+CLI.getQuboInstance().getCurrent()+"\033[31m] \r"); + }catch(Exception a){a.printStackTrace();GoodBye.quit(7);} + } + +} diff --git a/src/main/java/me/replydev/utils/SystemSpecs.java b/src/main/java/me/replydev/utils/SystemSpecs.java new file mode 100644 index 0000000..b77a2e6 --- /dev/null +++ b/src/main/java/me/replydev/utils/SystemSpecs.java @@ -0,0 +1,8 @@ +package me.replydev.utils; + +public class SystemSpecs { + public String getOperatingSystem() { + // System.out.println("Using System Property: " + os); + return System.getProperty("os.name"); + } +}