PageRenderTime 59ms CodeModel.GetById 31ms RepoModel.GetById 1ms app.codeStats 0ms

/minecraft/net/minecraft/src/RConThreadQuery.java

https://bitbucket.org/socialsecuritynumber/nahreliant
Java | 408 lines | 315 code | 46 blank | 47 comment | 30 complexity | 194871c027b7c816ef89e733fb280820 MD5 | raw file
  1. package net.minecraft.src;
  2. import java.io.IOException;
  3. import java.net.DatagramPacket;
  4. import java.net.DatagramSocket;
  5. import java.net.InetAddress;
  6. import java.net.PortUnreachableException;
  7. import java.net.SocketAddress;
  8. import java.net.SocketException;
  9. import java.net.SocketTimeoutException;
  10. import java.net.UnknownHostException;
  11. import java.util.Date;
  12. import java.util.HashMap;
  13. import java.util.Iterator;
  14. import java.util.Map;
  15. import java.util.Map.Entry;
  16. public class RConThreadQuery extends RConThreadBase
  17. {
  18. /** The time of the last client auth check */
  19. private long lastAuthCheckTime;
  20. /** The RCon query port */
  21. private int queryPort;
  22. /** Port the server is running on */
  23. private int serverPort;
  24. /** The maximum number of players allowed on the server */
  25. private int maxPlayers;
  26. /** The current server message of the day */
  27. private String serverMotd;
  28. /** The name of the currently loaded world */
  29. private String worldName;
  30. /** The remote socket querying the server */
  31. private DatagramSocket querySocket = null;
  32. /** A buffer for incoming DatagramPackets */
  33. private byte[] buffer = new byte[1460];
  34. /** Storage for incoming DatagramPackets */
  35. private DatagramPacket incomingPacket = null;
  36. private Map field_72644_p;
  37. /** The hostname of this query server */
  38. private String queryHostname;
  39. /** The hostname of the running server */
  40. private String serverHostname;
  41. /** A map of SocketAddress objects to RConThreadQueryAuth objects */
  42. private Map queryClients;
  43. /**
  44. * The time that this RConThreadQuery was constructed, from (new Date()).getTime()
  45. */
  46. private long time;
  47. /** The RConQuery output stream */
  48. private RConOutputStream output;
  49. /** The time of the last query response sent */
  50. private long lastQueryResponseTime;
  51. public RConThreadQuery(IServer par1IServer)
  52. {
  53. super(par1IServer);
  54. this.queryPort = par1IServer.getIntProperty("query.port", 0);
  55. this.serverHostname = par1IServer.getHostname();
  56. this.serverPort = par1IServer.getPort();
  57. this.serverMotd = par1IServer.getServerMOTD();
  58. this.maxPlayers = par1IServer.getMaxPlayers();
  59. this.worldName = par1IServer.getFolderName();
  60. this.lastQueryResponseTime = 0L;
  61. this.queryHostname = "0.0.0.0";
  62. if (0 != this.serverHostname.length() && !this.queryHostname.equals(this.serverHostname))
  63. {
  64. this.queryHostname = this.serverHostname;
  65. }
  66. else
  67. {
  68. this.serverHostname = "0.0.0.0";
  69. try
  70. {
  71. InetAddress var2 = InetAddress.getLocalHost();
  72. this.queryHostname = var2.getHostAddress();
  73. }
  74. catch (UnknownHostException var3)
  75. {
  76. this.logWarning("Unable to determine local host IP, please set server-ip in \'" + par1IServer.getSettingsFilename() + "\' : " + var3.getMessage());
  77. }
  78. }
  79. if (0 == this.queryPort)
  80. {
  81. this.queryPort = this.serverPort;
  82. this.logInfo("Setting default query port to " + this.queryPort);
  83. par1IServer.setProperty("query.port", Integer.valueOf(this.queryPort));
  84. par1IServer.setProperty("debug", Boolean.valueOf(false));
  85. par1IServer.saveProperties();
  86. }
  87. this.field_72644_p = new HashMap();
  88. this.output = new RConOutputStream(1460);
  89. this.queryClients = new HashMap();
  90. this.time = (new Date()).getTime();
  91. }
  92. /**
  93. * Sends a byte array as a DatagramPacket response to the client who sent the given DatagramPacket
  94. */
  95. private void sendResponsePacket(byte[] par1ArrayOfByte, DatagramPacket par2DatagramPacket) throws IOException
  96. {
  97. this.querySocket.send(new DatagramPacket(par1ArrayOfByte, par1ArrayOfByte.length, par2DatagramPacket.getSocketAddress()));
  98. }
  99. /**
  100. * Parses an incoming DatagramPacket, returning true if the packet was valid
  101. */
  102. private boolean parseIncomingPacket(DatagramPacket par1DatagramPacket) throws IOException
  103. {
  104. byte[] var2 = par1DatagramPacket.getData();
  105. int var3 = par1DatagramPacket.getLength();
  106. SocketAddress var4 = par1DatagramPacket.getSocketAddress();
  107. this.logDebug("Packet len " + var3 + " [" + var4 + "]");
  108. if (3 <= var3 && -2 == var2[0] && -3 == var2[1])
  109. {
  110. this.logDebug("Packet \'" + RConUtils.getByteAsHexString(var2[2]) + "\' [" + var4 + "]");
  111. switch (var2[2])
  112. {
  113. case 0:
  114. if (!this.verifyClientAuth(par1DatagramPacket).booleanValue())
  115. {
  116. this.logDebug("Invalid challenge [" + var4 + "]");
  117. return false;
  118. }
  119. else if (15 == var3)
  120. {
  121. this.sendResponsePacket(this.createQueryResponse(par1DatagramPacket), par1DatagramPacket);
  122. this.logDebug("Rules [" + var4 + "]");
  123. }
  124. else
  125. {
  126. RConOutputStream var5 = new RConOutputStream(1460);
  127. var5.writeInt(0);
  128. var5.writeByteArray(this.getRequestID(par1DatagramPacket.getSocketAddress()));
  129. var5.writeString(this.serverMotd);
  130. var5.writeString("SMP");
  131. var5.writeString(this.worldName);
  132. var5.writeString(Integer.toString(this.getNumberOfPlayers()));
  133. var5.writeString(Integer.toString(this.maxPlayers));
  134. var5.writeShort((short)this.serverPort);
  135. var5.writeString(this.queryHostname);
  136. this.sendResponsePacket(var5.toByteArray(), par1DatagramPacket);
  137. this.logDebug("Status [" + var4 + "]");
  138. }
  139. case 9:
  140. this.sendAuthChallenge(par1DatagramPacket);
  141. this.logDebug("Challenge [" + var4 + "]");
  142. return true;
  143. default:
  144. return true;
  145. }
  146. }
  147. else
  148. {
  149. this.logDebug("Invalid packet [" + var4 + "]");
  150. return false;
  151. }
  152. }
  153. /**
  154. * Creates a query response as a byte array for the specified query DatagramPacket
  155. */
  156. private byte[] createQueryResponse(DatagramPacket par1DatagramPacket) throws IOException
  157. {
  158. long var2 = System.currentTimeMillis();
  159. if (var2 < this.lastQueryResponseTime + 5000L)
  160. {
  161. byte[] var7 = this.output.toByteArray();
  162. byte[] var8 = this.getRequestID(par1DatagramPacket.getSocketAddress());
  163. var7[1] = var8[0];
  164. var7[2] = var8[1];
  165. var7[3] = var8[2];
  166. var7[4] = var8[3];
  167. return var7;
  168. }
  169. else
  170. {
  171. this.lastQueryResponseTime = var2;
  172. this.output.reset();
  173. this.output.writeInt(0);
  174. this.output.writeByteArray(this.getRequestID(par1DatagramPacket.getSocketAddress()));
  175. this.output.writeString("splitnum");
  176. this.output.writeInt(128);
  177. this.output.writeInt(0);
  178. this.output.writeString("hostname");
  179. this.output.writeString(this.serverMotd);
  180. this.output.writeString("gametype");
  181. this.output.writeString("SMP");
  182. this.output.writeString("game_id");
  183. this.output.writeString("MINECRAFT");
  184. this.output.writeString("version");
  185. this.output.writeString(this.server.getMinecraftVersion());
  186. this.output.writeString("plugins");
  187. this.output.writeString(this.server.getPlugins());
  188. this.output.writeString("map");
  189. this.output.writeString(this.worldName);
  190. this.output.writeString("numplayers");
  191. this.output.writeString("" + this.getNumberOfPlayers());
  192. this.output.writeString("maxplayers");
  193. this.output.writeString("" + this.maxPlayers);
  194. this.output.writeString("hostport");
  195. this.output.writeString("" + this.serverPort);
  196. this.output.writeString("hostip");
  197. this.output.writeString(this.queryHostname);
  198. this.output.writeInt(0);
  199. this.output.writeInt(1);
  200. this.output.writeString("player_");
  201. this.output.writeInt(0);
  202. String[] var4 = this.server.getAllUsernames();
  203. byte var5 = (byte)var4.length;
  204. for (byte var6 = (byte)(var5 - 1); var6 >= 0; --var6)
  205. {
  206. this.output.writeString(var4[var6]);
  207. }
  208. this.output.writeInt(0);
  209. return this.output.toByteArray();
  210. }
  211. }
  212. /**
  213. * Returns the request ID provided by the authorized client
  214. */
  215. private byte[] getRequestID(SocketAddress par1SocketAddress)
  216. {
  217. return ((RConThreadQueryAuth)this.queryClients.get(par1SocketAddress)).getRequestId();
  218. }
  219. /**
  220. * Returns true if the client has a valid auth, otherwise false
  221. */
  222. private Boolean verifyClientAuth(DatagramPacket par1DatagramPacket)
  223. {
  224. SocketAddress var2 = par1DatagramPacket.getSocketAddress();
  225. if (!this.queryClients.containsKey(var2))
  226. {
  227. return Boolean.valueOf(false);
  228. }
  229. else
  230. {
  231. byte[] var3 = par1DatagramPacket.getData();
  232. return ((RConThreadQueryAuth)this.queryClients.get(var2)).getRandomChallenge() != RConUtils.getBytesAsBEint(var3, 7, par1DatagramPacket.getLength()) ? Boolean.valueOf(false) : Boolean.valueOf(true);
  233. }
  234. }
  235. /**
  236. * Sends an auth challenge DatagramPacket to the client and adds the client to the queryClients map
  237. */
  238. private void sendAuthChallenge(DatagramPacket par1DatagramPacket) throws IOException
  239. {
  240. RConThreadQueryAuth var2 = new RConThreadQueryAuth(this, par1DatagramPacket);
  241. this.queryClients.put(par1DatagramPacket.getSocketAddress(), var2);
  242. this.sendResponsePacket(var2.getChallengeValue(), par1DatagramPacket);
  243. }
  244. /**
  245. * Removes all clients whose auth is no longer valid
  246. */
  247. private void cleanQueryClientsMap()
  248. {
  249. if (this.running)
  250. {
  251. long var1 = System.currentTimeMillis();
  252. if (var1 >= this.lastAuthCheckTime + 30000L)
  253. {
  254. this.lastAuthCheckTime = var1;
  255. Iterator var3 = this.queryClients.entrySet().iterator();
  256. while (var3.hasNext())
  257. {
  258. Entry var4 = (Entry)var3.next();
  259. if (((RConThreadQueryAuth)var4.getValue()).hasExpired(var1).booleanValue())
  260. {
  261. var3.remove();
  262. }
  263. }
  264. }
  265. }
  266. }
  267. public void run()
  268. {
  269. this.logInfo("Query running on " + this.serverHostname + ":" + this.queryPort);
  270. this.lastAuthCheckTime = System.currentTimeMillis();
  271. this.incomingPacket = new DatagramPacket(this.buffer, this.buffer.length);
  272. try
  273. {
  274. while (this.running)
  275. {
  276. try
  277. {
  278. this.querySocket.receive(this.incomingPacket);
  279. this.cleanQueryClientsMap();
  280. this.parseIncomingPacket(this.incomingPacket);
  281. }
  282. catch (SocketTimeoutException var7)
  283. {
  284. this.cleanQueryClientsMap();
  285. }
  286. catch (PortUnreachableException var8)
  287. {
  288. ;
  289. }
  290. catch (IOException var9)
  291. {
  292. this.stopWithException(var9);
  293. }
  294. }
  295. }
  296. finally
  297. {
  298. this.closeAllSockets();
  299. }
  300. }
  301. /**
  302. * Creates a new Thread object from this class and starts running
  303. */
  304. public void startThread()
  305. {
  306. if (!this.running)
  307. {
  308. if (0 < this.queryPort && 65535 >= this.queryPort)
  309. {
  310. if (this.initQuerySystem())
  311. {
  312. super.startThread();
  313. }
  314. }
  315. else
  316. {
  317. this.logWarning("Invalid query port " + this.queryPort + " found in \'" + this.server.getSettingsFilename() + "\' (queries disabled)");
  318. }
  319. }
  320. }
  321. /**
  322. * Stops the query server and reports the given Exception
  323. */
  324. private void stopWithException(Exception par1Exception)
  325. {
  326. if (this.running)
  327. {
  328. this.logWarning("Unexpected exception, buggy JRE? (" + par1Exception.toString() + ")");
  329. if (!this.initQuerySystem())
  330. {
  331. this.logSevere("Failed to recover from buggy JRE, shutting down!");
  332. this.running = false;
  333. }
  334. }
  335. }
  336. /**
  337. * Initializes the query system by binding it to a port
  338. */
  339. private boolean initQuerySystem()
  340. {
  341. try
  342. {
  343. this.querySocket = new DatagramSocket(this.queryPort, InetAddress.getByName(this.serverHostname));
  344. this.registerSocket(this.querySocket);
  345. this.querySocket.setSoTimeout(500);
  346. return true;
  347. }
  348. catch (SocketException var2)
  349. {
  350. this.logWarning("Unable to initialise query system on " + this.serverHostname + ":" + this.queryPort + " (Socket): " + var2.getMessage());
  351. }
  352. catch (UnknownHostException var3)
  353. {
  354. this.logWarning("Unable to initialise query system on " + this.serverHostname + ":" + this.queryPort + " (Unknown Host): " + var3.getMessage());
  355. }
  356. catch (Exception var4)
  357. {
  358. this.logWarning("Unable to initialise query system on " + this.serverHostname + ":" + this.queryPort + " (E): " + var4.getMessage());
  359. }
  360. return false;
  361. }
  362. }