package cn.nukkit.network.query; import cn.nukkit.Server; import cn.nukkit.event.server.QueryRegenerateEvent; import cn.nukkit.utils.Binary; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Random; /** * author: MagicDroidX * Nukkit Project */ public class QueryHandler { public static final byte HANDSHAKE = 0x09; public static final byte STATISTICS = 0x00; private final Server server; private byte[] lastToken; private byte[] token; private byte[] longData; private byte[] shortData; private long timeout; public QueryHandler() { this.server = Server.getInstance(); this.server.getLogger().info(this.server.getLanguage().translateString("nukkit.server.query.start")); String ip = this.server.getIp(); String addr = (!ip.isEmpty()) ? ip : "0.0.0.0"; int port = this.server.getPort(); this.server.getLogger().info(this.server.getLanguage().translateString("nukkit.server.query.info", String.valueOf(port))); this.regenerateToken(); this.lastToken = this.token; this.regenerateInfo(); this.server.getLogger().info(this.server.getLanguage().translateString("nukkit.server.query.running", new String[]{addr, String.valueOf(port)})); } public void regenerateInfo() { QueryRegenerateEvent ev = this.server.getQueryInformation(); this.longData = ev.getLongQuery(this.longData); this.shortData = ev.getShortQuery(this.shortData); this.timeout = System.currentTimeMillis() + ev.getTimeout(); } public void regenerateToken() { this.lastToken = this.token; byte[] token = new byte[16]; for (int i = 0; i < 16; i++) { token[i] = (byte) new Random().nextInt(255); } this.token = token; } public static String getTokenString(byte[] token, String salt) { return getTokenString(new String(token), salt); } public static String getTokenString(String token, String salt) { try { return String.valueOf(Binary.readInt(Binary.subBytes(MessageDigest.getInstance("SHA-512").digest((salt + ":" + token).getBytes()), 7, 4))); } catch (NoSuchAlgorithmException e) { return String.valueOf(new Random().nextInt()); } } public void handle(String address, int port, byte[] packet) { int offset = 2; //skip MAGIC byte packetType = packet[offset++]; int sessionID = Binary.readInt(Binary.subBytes(packet, offset, 4)); offset += 4; byte[] payload = Binary.subBytes(packet, offset); switch (packetType) { case HANDSHAKE: byte[] reply = Binary.appendBytes( HANDSHAKE, Binary.writeInt(sessionID), getTokenString(this.token, address).getBytes(), new byte[]{0x00} ); this.server.getNetwork().sendPacket(address, port, reply); break; case STATISTICS: String token = String.valueOf(Binary.readInt(Binary.subBytes(payload, 0, 4))); if (!token.equals(getTokenString(this.token, address)) && !token.equals(getTokenString(this.lastToken, address))) { break; } if (this.timeout < System.currentTimeMillis()) { this.regenerateInfo(); } reply = Binary.appendBytes( STATISTICS, Binary.writeInt(sessionID), payload.length == 8 ? this.longData : this.shortData ); this.server.getNetwork().sendPacket(address, port, reply); break; } } }