Coverage Report - net.sourceforge.rconed.SourceRcon
 
Classes in this File Line Coverage Branch Coverage Complexity
SourceRcon
0 %
0/69
0 %
0/18
3.143
 
 1  
 package net.sourceforge.rconed;
 2  
 
 3  
 import net.sourceforge.rconed.exception.BadRcon;
 4  
 import net.sourceforge.rconed.exception.ResponseEmpty;
 5  
 
 6  
 import java.io.*;
 7  
 import java.net.*;
 8  
 import java.nio.ByteBuffer;
 9  
 import java.nio.ByteOrder;
 10  
 
 11  
 /**
 12  
  * User: oscahie (aka PiTaGoRaS)<br/>
 13  
  * Date: 03-jan-2005<br/>
 14  
  * Time: 19:11:40<br/>
 15  
  * version: 0.4<br/>
 16  
  * Rcon library for Source Engine based games<br/> 
 17  
  */
 18  0
 public class SourceRcon {
 19  
 
 20  
     final static int SERVERDATA_EXECCOMMAND = 2;
 21  
     final static int SERVERDATA_AUTH = 3;
 22  
     final static int SERVERDATA_RESPONSE_VALUE = 0;
 23  
     final static int SERVERDATA_AUTH_RESPONSE = 2;
 24  
 
 25  
     final static int RESPONSE_TIMEOUT = 2000;
 26  
     final static int MULTIPLE_PACKETS_TIMEOUT = 300;
 27  
 
 28  0
     static Socket rconSocket = null;
 29  0
     static InputStream in = null;
 30  0
     static OutputStream out = null;
 31  
 
 32  
 
 33  
     /**
 34  
      * Send the RCON command to the game server (must have been previously authed with the correct rcon_password)
 35  
      *
 36  
      * @param ipStr     The IP (as a String) of the machine where the RCON command will go.
 37  
      * @param port      The port of the machine where the RCON command will go.
 38  
      * @param password  The RCON password.
 39  
      * @param command   The RCON command (without the rcon prefix).
 40  
      * @return The reponse text from the server after trying the RCON command.
 41  
      * @throws IOException  if there was an input/output problem
 42  
      */
 43  
     public static String send(String ipStr, int port, String password, String command) throws BadRcon, ResponseEmpty, IOException {
 44  0
         return send(ipStr, port, password, command, 0);
 45  
     }
 46  
     
 47  
     /**
 48  
      * Send the RCON command to the game server (must have been previously authed with the correct rcon_password)
 49  
      *
 50  
      * @param ipStr     The IP (as a String) of the machine where the RCON command will go.
 51  
      * @param port      The port of the machine where the RCON command will go.
 52  
      * @param password  The RCON password.
 53  
      * @param command   The RCON command (without the rcon prefix).
 54  
      * @param localPort The port of the local machine to use for sending out the RCON request.
 55  
      * @return The reponse text from the server after trying the RCON command.
 56  
      * @throws IOException  if there was an input/output problem
 57  
      */
 58  
     public static String send(String ipStr, int port, String password, String command, int localPort) throws BadRcon, ResponseEmpty, IOException {
 59  0
         String response = "";
 60  
 
 61  0
             rconSocket = new Socket();
 62  
 
 63  0
             InetAddress addr = InetAddress.getLocalHost();
 64  0
             byte[] ipAddr = addr.getAddress();
 65  0
             InetAddress inetLocal = InetAddress.getByAddress(ipAddr);
 66  
             
 67  0
             rconSocket.bind(new InetSocketAddress(inetLocal, localPort));
 68  0
             rconSocket.connect(new InetSocketAddress(ipStr, port), 1000);
 69  
 
 70  0
             out = rconSocket.getOutputStream();
 71  0
             in = rconSocket.getInputStream();
 72  
 
 73  0
             rconSocket.setSoTimeout(RESPONSE_TIMEOUT);
 74  
 
 75  0
             if (rcon_auth(password)) {
 76  
                 // We are now authed
 77  0
                 ByteBuffer[] resp = sendCommand(command);
 78  
                 // Close socket handlers, we don't need them more
 79  0
                 out.close(); in.close(); rconSocket.close();
 80  0
                 if (resp != null) {
 81  0
                     response = assemblePackets(resp);
 82  0
                     if (response.length() == 0) {
 83  0
                         throw new ResponseEmpty();
 84  
                     }
 85  
                 }
 86  0
             }
 87  
             else {
 88  0
                 throw new BadRcon();
 89  
             }
 90  
 
 91  0
         return response;
 92  
     }
 93  
 
 94  
 
 95  
     private static ByteBuffer[] sendCommand(String command) throws IOException {
 96  
 
 97  0
         byte[] request = constructPacket(2, SERVERDATA_EXECCOMMAND, command);
 98  
 
 99  0
         ByteBuffer[] resp = new ByteBuffer[128];
 100  0
         int i = 0;
 101  0
             out.write(request);
 102  0
             resp[i] = receivePacket();  // First and maybe the unique response packet
 103  
             try {
 104  
                 // We don't know how many packets will return in response, so we'll
 105  
                 // read() the socket until TimeoutException occurs.
 106  0
                 rconSocket.setSoTimeout(MULTIPLE_PACKETS_TIMEOUT);
 107  
                 while (true) {
 108  0
                     resp[++i] = receivePacket();
 109  
                 }
 110  0
             } catch (SocketTimeoutException e) {
 111  
                 // No more packets in the response, go on
 112  0
                 return resp;
 113  
             }
 114  
     }
 115  
 
 116  
 
 117  
     private static byte[] constructPacket(int id, int cmdtype, String s1) {
 118  
 
 119  0
         ByteBuffer p = ByteBuffer.allocate(s1.length() + 16);
 120  0
         p.order(ByteOrder.LITTLE_ENDIAN);
 121  
 
 122  
         // length of the packet
 123  0
         p.putInt(s1.length() + 12);
 124  
         // request id
 125  0
         p.putInt(id);
 126  
         // type of command
 127  0
         p.putInt(cmdtype);
 128  
         // the command itself
 129  0
         p.put(s1.getBytes());
 130  
         // two null bytes at the end
 131  0
         p.put((byte) 0x00);
 132  0
         p.put((byte) 0x00);
 133  
         // null string2 (see Source protocol)
 134  0
         p.put((byte) 0x00);
 135  0
         p.put((byte) 0x00);
 136  
 
 137  0
         return p.array();
 138  
     }
 139  
 
 140  
     private static ByteBuffer receivePacket() throws IOException {
 141  
 
 142  0
         ByteBuffer p = ByteBuffer.allocate(4120);
 143  0
         p.order(ByteOrder.LITTLE_ENDIAN);
 144  
 
 145  0
         byte[] length = new byte[4];
 146  
 
 147  0
         if (in.read(length, 0, 4) != 4) {
 148  0
                 return null;
 149  
         }
 150  
         
 151  
         // Now we've the length of the packet, let's go read the bytes
 152  0
         p.put(length);
 153  0
         int i = 0;
 154  0
         while (i < p.getInt(0)) {
 155  0
             p.put((byte) in.read());
 156  0
             i++;
 157  
         }
 158  0
         return p;
 159  
     }
 160  
 
 161  
 
 162  
     private static String assemblePackets(ByteBuffer[] packets) {
 163  
     // Return the text from all the response packets together
 164  
 
 165  0
         String response = "";
 166  
 
 167  0
         for (int i = 0; i < packets.length; i++) {
 168  0
             if (packets[i] != null) {
 169  0
                 response = response.concat(new String(packets[i].array(), 12, packets[i].position()-14));
 170  
             }
 171  
         }
 172  0
         return response;
 173  
     }
 174  
 
 175  
 
 176  
     private static boolean rcon_auth(String rcon_password) throws IOException {
 177  
 
 178  0
         byte[] authRequest = constructPacket(1337, SERVERDATA_AUTH, rcon_password);
 179  
 
 180  0
         ByteBuffer response = ByteBuffer.allocate(64);
 181  0
             out.write(authRequest);
 182  0
             response = receivePacket(); // junk response packet
 183  0
             response = receivePacket();
 184  
 
 185  
             // Lets see if the received request_id is leet enougth ;)
 186  0
             if ((response.getInt(4) == 1337) && (response.getInt(8) == SERVERDATA_AUTH_RESPONSE)) {
 187  0
                 return true;
 188  
             }
 189  
 
 190  0
         return false;
 191  
     }
 192  
 
 193  
 }