import java.net.Socket; import java.io.OutputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.lang.Math; import java.lang.Exception; public class RconFail { static String host = ""; static short port = 25575; static String password = ""; Socket sock; int nextId; OutputStream sockStream; ByteArrayOutputStream baStream; DataOutputStream dataStream; RconFail(String host, short port, String password, boolean nodelay) throws Exception { sock = new Socket(host, port); sock.setTcpNoDelay(nodelay); sockStream = sock.getOutputStream(); baStream = new ByteArrayOutputStream(); dataStream = new DataOutputStream(baStream); enqueueMessage(3, password); sendBulk(); } void sendBytes(byte[] buffer, int offset, int length) throws Exception { System.out.print("Sending bytes:"); for (int i = 0; i < length; i++) System.out.format(" %02X", new Integer(buffer[offset + i])); System.out.println(""); sockStream.write(buffer, offset, length); Thread.sleep(500); } void enqueueMessage(int type, String message) throws Exception { System.out.println("Enqueuing " + type + " " + message); byte[] utf = message.getBytes("UTF-8"); dataStream.writeInt(Integer.reverseBytes(utf.length + 10)); dataStream.writeInt(Integer.reverseBytes(nextId)); dataStream.writeInt(Integer.reverseBytes(type)); dataStream.write(utf); dataStream.write(0); dataStream.write(0); nextId++; } void enqueueCommand(String command) throws Exception { enqueueMessage(2, command); } void sendBulk() throws Exception { System.out.println("Sending at once"); byte[] ba = baStream.toByteArray(); sendBytes(ba, 0, ba.length); baStream.reset(); } void sendFragmented(int fragmentSize) throws Exception { System.out.println("Sending fragmented"); byte[] ba = baStream.toByteArray(); for (int cursor = 0; cursor < ba.length; cursor += fragmentSize) { sendBytes(ba, cursor, Math.min(ba.length - cursor, fragmentSize)); } baStream.reset(); } void sendImmediately() throws Exception { baStream.writeTo(sockStream); baStream.reset(); } // An attempt to be deterministic. static void artificial() throws Exception { for (int which = 0; which < 2; which++) { try { System.out.println("Testing artificial case " + which); RconFail rcon = new RconFail(host, port, password, true); rcon.enqueueCommand("say 1"); rcon.sendBulk(); // Okay. rcon.enqueueCommand("say 2"); rcon.sendBulk(); // Okay. switch (which) { case 0: rcon.enqueueCommand("say 3"); rcon.enqueueCommand("say 4"); rcon.sendBulk(); // Server thinks these are one message that's too large. // Socket is closed. The next thing we send will send an RST our way. // The thing we send after that (7) will throw with a broken pipe. break; case 1: rcon.enqueueCommand("say 5"); rcon.sendFragmented(10); // Partial message is too short. // Socket is closed. The second fragment will send an RST our way. // Therefore "say 6" will throw already. break; } rcon.enqueueCommand("say 6"); rcon.sendBulk(); rcon.enqueueCommand("say 7"); rcon.sendBulk(); } catch (Exception e) { e.printStackTrace(); } } } // Something you might do. Normally you'd read the server's output // to avoid filling up the TCP window (or, you know, do something // with it), but it's fine for testing relatively small volumes. static void highVolume() throws Exception { System.out.println("Testing high volume, not dumping all bytes"); RconFail rcon = new RconFail(host, port, password, false); for (int i = 0; i < 100; i++) { rcon.enqueueCommand("say " + i); rcon.sendImmediately(); // Boom! } } public static void main(String[] args) throws Exception { artificial(); highVolume(); } }