View Javadoc

1   package net.sourceforge.rconed;
2   
3   import java.io.BufferedReader;
4   import java.io.IOException;
5   import java.io.InputStream;
6   import java.io.InputStreamReader;
7   import java.io.OutputStream;
8   import java.net.InetAddress;
9   import java.net.InetSocketAddress;
10  import java.net.Socket;
11  import java.net.SocketTimeoutException;
12  import java.net.UnknownHostException;
13  import java.security.MessageDigest;
14  import java.security.NoSuchAlgorithmException;
15  
16  import net.sourceforge.rconed.exception.BadRcon;
17  import net.sourceforge.rconed.exception.ResponseEmpty;
18  
19  /**
20   * BF2Rcon is a simple Java library for issuing RCON commands to BF2 game servers.
21   * <p/>
22   * This has been used with default and BF2CC managed servers.
23   * <p/>
24   * Example:
25   * <p/>
26   * String response = BF2Rcon.send("127.0.0.1", 6711, "admin", "game.sayAll \"ploppers\"");
27   * <p/>
28   * @author DeadEd
29   */
30  public class BF2Rcon {
31  
32      final static int RESPONSE_TIMEOUT = 2000;
33  
34      static Socket rconSocket = null;
35      static InputStream in = null;
36      static OutputStream out = null;
37  
38  
39      /**
40       * Send the RCON command to the game server
41       *
42       * @param ipStr     The IP (as a String) of the machine where the RCON command will go.
43       * @param port      The port of the machine where the RCON command will go.
44       * @param password  The RCON password.
45       * @param command   The RCON command (without the rcon prefix).
46       * @return The reponse text from the server after trying the RCON command.
47       * @throws SocketTimeoutException when there is any problem communicating with the server.
48       * @throws BadRcon when authentication fails
49       * @throws ResponseEmpty when the response is empty
50       */
51      public static String send(String ipStr, int port, String password, String command) throws SocketTimeoutException, BadRcon, ResponseEmpty {
52          return send(ipStr, port, password, command, 0);
53      }
54      
55      /**
56       * Send the RCON command to the game server (must have been previously authed with the correct rcon_password)
57       *
58       * @param ipStr     The IP (as a String) of the machine where the RCON command will go.
59       * @param port      The port of the machine where the RCON command will go.
60       * @param password  The RCON password.
61       * @param command   The RCON command (without the rcon prefix).
62       * @param localPort The port of the local machine to use for sending out the RCON request.
63       * @return The reponse text from the server after trying the RCON command.
64       * @throws SocketTimeoutException when there is any problem communicating with the server.
65       * @throws BadRcon when authentication fails
66       * @throws ResponseEmpty when the response is empty
67       */
68      public static String send(String ipStr, int port, String password, String command, int localPort) throws SocketTimeoutException, BadRcon, ResponseEmpty {
69          return send(ipStr, port, password, command, null, localPort);
70      }
71      
72      /**
73       * Send the RCON command to the game server (must have been previously authed with the correct rcon_password)
74       *
75       * @param ipStr     The IP (as a String) of the machine where the RCON command will go.
76       * @param port      The port of the machine where the RCON command will go.
77       * @param password  The RCON password.
78       * @param command   The RCON command (without the rcon prefix).
79       * @param localhost The IP of the local machine to use for sending out the RCON request.
80       * @param localPort The port of the local machine to use for sending out the RCON request.
81       * @return The reponse text from the server after trying the RCON command.
82       * @throws SocketTimeoutException when there is any problem communicating with the server.
83       * @throws BadRcon when authentication fails
84       * @throws ResponseEmpty when the response is empty
85       */
86      public static String send(String ipStr, int port, String password, String command, InetAddress localhost, int localPort) throws SocketTimeoutException, BadRcon, ResponseEmpty {
87          StringBuffer response = new StringBuffer();
88  
89          try {
90              rconSocket = new Socket();
91  
92              //InetAddress addr = InetAddress.getLocalHost();
93              //byte[] ipAddr = addr.getAddress();
94              //InetAddress inetLocal = InetAddress.getByAddress(ipAddr);
95              
96              //rconSocket.bind(new InetSocketAddress(inetLocal, localPort));
97              rconSocket.bind(new InetSocketAddress(localhost, localPort));
98              rconSocket.connect(new InetSocketAddress(ipStr, port), RESPONSE_TIMEOUT);
99  
100             out = rconSocket.getOutputStream();
101             in = rconSocket.getInputStream();
102             BufferedReader buffRead = new BufferedReader(new InputStreamReader(in));
103 
104             rconSocket.setSoTimeout(RESPONSE_TIMEOUT);
105 
106             String digestSeed = "";
107             boolean loggedIn = false;
108             boolean keepGoing = true;
109             while(keepGoing) {
110                 String receivedContent = buffRead.readLine();
111                 if(receivedContent.startsWith("### Digest seed: ")) {
112                     digestSeed = receivedContent.substring(17, receivedContent.length());
113                     try {
114                         MessageDigest md5 = MessageDigest.getInstance("MD5");
115                         md5.update(digestSeed.getBytes());
116                         md5.update(password.getBytes());
117                         String digestStr = "login "+ digestedToHex(md5.digest()) +"\n";
118                         out.write(digestStr.getBytes());
119                     } catch (NoSuchAlgorithmException e1) {
120                         response.append("MD5 algorithm not available - unable to complete RCON request.");
121                         keepGoing = false;
122                     }
123                 } else if(receivedContent.startsWith("error: not authenticated: you can only invoke 'login'")) {
124                     throw new BadRcon();
125                 } else if(receivedContent.startsWith("Authentication failed.")) {
126                     throw new BadRcon();
127                 } else if(receivedContent.startsWith("Authentication successful, rcon ready.")) {
128                     keepGoing = false;
129                     loggedIn = true;
130                 }
131             }
132             if(loggedIn) {
133                 // logged in, now we can execute the command
134                 String cmd = "\u0002exec "+ command +"\n";
135                 out.write(cmd.getBytes());
136 
137                 readResponse(buffRead, response);
138                 if(response.length() == 0) {
139                     throw new ResponseEmpty();
140                 }
141             }
142 
143         } catch (SocketTimeoutException timeout) {
144             throw timeout;
145         } catch (UnknownHostException e) {
146             response.append("UnknownHostException: "+ e.getMessage());
147         } catch (IOException e) {
148             response.append("Couldn't get I/O for the connection: "+ e.getMessage());
149             e.printStackTrace();
150         } finally {
151             try {
152                 if(out != null) {
153                     out.close();
154                 }
155                 if(in != null) {
156                     in.close();
157                 }
158                 if(rconSocket != null) {
159                     rconSocket.close();
160                 }
161             } catch (IOException e1) {
162                 // eat ... messed up at this point anyway 
163             } 
164         }
165 
166         return response.toString();
167     }
168 
169     /**
170      * @param buffRead where to read from
171      * @param sb where to put read data
172      * @throws IOException
173      */
174     private static void readResponse(BufferedReader buffRead, StringBuffer sb) throws IOException {
175         int ch;
176         while(true) {
177             ch = buffRead.read();
178             if(ch == -1 || ch == 4) {
179                 return;
180             }
181             sb.append((char)ch);
182         }
183     }
184 
185     private static String digestedToHex(byte digest[]) {
186         StringBuffer store = new StringBuffer();
187         for(int x=0; x < digest.length; x++) {
188             byte bite = digest[x];
189             String val = Integer.toHexString(bite & 255);
190             if(val.length() == 1) {
191                 store.append("0");
192             }
193             store.append(val);
194         }
195         return store.toString();
196     }
197 }