I'm trying to connect to the RCON of a Steam game-server which uses the Steam RCON Protocol. I googled a bit and found the following code that someone did a while back for this exact purpose. After examining the code for a while, it really seems like it should work, but for some reason it doesn't.
Here is the code:
class RconSteam {
/*
* The constants are the packet types.
*/
static final int EXECUTE_COMMAND_PACKET = 2;
static final int AUTHORIZATION_PACKET = 3;
static final int AUTHORIZATION_RESPONSE = 2;
static final SocketAddress serverAddress = new InetSocketAddress("IP ADDRESS", PORT);
static final String rconPassword = "password";
static InputStream inputStream;
static OutputStream outputStream;
/**
* Starts the application.
*
* @param args
* command-line arguments
* @throws IOException
*/
public static void main(String[] args) {
try {
/*
* Prepare the socket and retrieve the streams.
*/
Socket socket = new Socket();
socket.bind(new InetSocketAddress(InetAddress.getLocalHost(), 0));
socket.connect(serverAddress);
inputStream = socket.getInputStream();
outputStream = socket.getOutputStream();
/*
* Authorize the user and then send commands. Multiple commands can
* be sent, but the user may need to be reauthorized after some
* time.
*/
boolean auth = sendAuthorizationPacket();
if (auth) {
if(sendCommand("serverchat HELLO"))
{
System.out.println("WINNER");
} else {
System.out.println("FAILED");
}
} else {
System.out.println("OH NOES");
}
/*
* Close the socket once its done being used.
*/
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Sends a command to the server. The user must be authorized for the server
* to execute the commands.
*
* @param command
* the command
* @throws IOException
*/
static boolean sendCommand(String command) throws IOException {
byte[] packet = createPacket(2000, EXECUTE_COMMAND_PACKET, command);
outputStream.write(packet);
/*
* The server responds to command execution packets as well, but those
* responses are not very important. However, it may be worthwhile to
* check if the server actually responded with text. If a response
* contains no textual message, that usually means that the user needs
* to be reauthorized with the server. Another way to check if the user
* needs to be reauthorized is simply by joining the server and seeing
* if the commands sent have any effect.
*/
ByteBuffer response = parsePacket();
int type = response.getInt(8);
int id = response.getInt(4);
System.out.println(type);
System.out.println(id);
return ((type == EXECUTE_COMMAND_PACKET) && (id) == 2000);
}
/**
* Sends an authorization packet to the server.
*
* @return whether or not the user was authorized by the server
* @throws IOException
*/
static boolean sendAuthorizationPacket() throws IOException {
/*
* Create the authorization packet and send it.
*/
byte[] packet = createPacket(1000, AUTHORIZATION_PACKET, rconPassword);
outputStream.write(packet);
/*
* Read the response (the first packet is a junk one) and check if the
* server authorized the user. The user is authorized if the server
* responds with the same packet ID as the one sent, which is 1000 in
* this case.
*/
ByteBuffer response = parsePacket();
return (response.getInt(8) == AUTHORIZATION_RESPONSE) && (response.getInt(4) == 1000);
}
/**
* Creates an RCON packet.
*
* @param id
* the packet ID (not an opcode)
* @param type
* the type
* @param command
* the command
* @return the bytes representing the packet
*/
static byte[] createPacket(int id, int type, String command) {
ByteBuffer packet = ByteBuffer.allocate(command.length() + 16);
packet.order(LITTLE_ENDIAN);
packet.putInt(command.length() + 12).putInt(id).putInt(type).put(command.getBytes()).putInt(0);
return packet.array();
}
/**
* Parses the next packet from the socket's input stream.
*
* @return the next packet
* @throws IOException
*/
static ByteBuffer parsePacket() throws IOException {
/*
* Read the length of the packet.
*/
byte[] length = new byte[4];
inputStream.read(length);
/*
* Create a buffer to contain the packet's payload.
*/
ByteBuffer packet = ByteBuffer.allocate(4120);
packet.order(LITTLE_ENDIAN);
packet.put(length);
/*
* Read the payload.
*/
for (int i = 0; i < packet.getInt(0); i++) {
packet.put((byte) inputStream.read());
}
return packet;
}
}
The program runs through the program without errors, however it seems to only get a response from the server the first time it sends a packet. I can successfully authenticate to the server and get the reponse for that from the server, but the second command doesn't seem to get properly responded to and closes the connection(?).
I tried testing to skip to the serverchat HELLO command right away, and I do get a reponse from the server that I'm not authenticated.
I thought I'd look whats going on with Wireshark, but I'm not sure what to make of this. My limited wireshark experience tells me that there is something wrong after sending the 2nd packet. 
At this point, I am not entirely sure if the problem lies within this code, or if it's server or network related (if so, sorry for posting on SO). Other tools seem to work fine though, so I'm guessing it's code-related.
Any help appreciated!
EDIT: Link to the screenshot if it's too small: https://i.sstatic.net/DomzM.png
EDIT 2: Code after update:
class RconSteam {
/*
* The constants are the packet types.
*/
static final int EXECUTE_COMMAND_PACKET = 2;
static final int AUTHORIZATION_PACKET = 3;
static final int AUTHORIZATION_RESPONSE = 2;
static final SocketAddress serverAddress = new InetSocketAddress("IP HERE", PORT);
static final String rconPassword = "password\0";
static InputStream inputStream;
static OutputStream outputStream;
/**
* Starts the application.
*
* @param args
* command-line arguments
* @throws IOException
*/
public static void main(String[] args) {
try {
/*
* Prepare the socket and retrieve the streams.
*/
Socket socket = new Socket();
socket.bind(new InetSocketAddress(InetAddress.getLocalHost(), 0));
socket.connect(serverAddress);
inputStream = socket.getInputStream();
outputStream = socket.getOutputStream();
/*
* Authorize the user and then send commands. Multiple commands can
* be sent, but the user may need to be reauthorized after some
* time.
*/
boolean auth = sendAuthorizationPacket();
if (auth) {
if(sendCommand("serverchat HELLO"))
{
System.out.println("WINNER");
} else {
System.out.println("FAILED");
}
} else {
System.out.println("OH NOES");
}
/*
* Close the socket once its done being used.
*/
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Sends a command to the server. The user must be authorized for the server
* to execute the commands.
*
* @param command
* the command
* @throws IOException
*/
static boolean sendCommand(String command) throws IOException {
byte[] packet = createPacket(2000, EXECUTE_COMMAND_PACKET, command+'\0');
outputStream.write(packet);
/*
* The server responds to command execution packets as well, but those
* responses are not very important. However, it may be worthwhile to
* check if the server actually responded with text. If a response
* contains no textual message, that usually means that the user needs
* to be reauthorized with the server. Another way to check if the user
* needs to be reauthorized is simply by joining the server and seeing
* if the commands sent have any effect.
*/
ByteBuffer response = parsePacket();
int type = response.getInt(8);
int id = response.getInt(4);
System.out.println(type);
System.out.println(id);
return ((type == EXECUTE_COMMAND_PACKET) && (id) == 2000);
}
/**
* Sends an authorization packet to the server.
*
* @return whether or not the user was authorized by the server
* @throws IOException
*/
static boolean sendAuthorizationPacket() throws IOException {
/*
* Create the authorization packet and send it.
*/
byte[] packet = createPacket(1000, AUTHORIZATION_PACKET, rconPassword);
outputStream.write(packet);
/*
* Read the response (the first packet is a junk one) and check if the
* server authorized the user. The user is authorized if the server
* responds with the same packet ID as the one sent, which is 1000 in
* this case.
*/
ByteBuffer response = parsePacket();
return (response.getInt(8) == AUTHORIZATION_RESPONSE) && (response.getInt(4) == 1000);
}
/**
* Creates an RCON packet.
*
* @param id
* the packet ID (not an opcode)
* @param type
* the type
* @param command
* the command
* @return the bytes representing the packet
*/
static byte[] createPacket(int id, int type, String command) {
ByteBuffer packet = ByteBuffer.allocate(command.length() + 16);
packet.order(LITTLE_ENDIAN);
//packet.putInt(command.length() + 12).putInt(id).putInt(type).put(command.getBytes()).putInt(0);
packet.putInt(command.length() + 14).putInt(id).putInt(type).put(command.getBytes()).put((byte)0).put((byte)0);
return packet.array();
}
/**
* Parses the next packet from the socket's input stream.
*
* @return the next packet
* @throws IOException
*/
static ByteBuffer parsePacket() throws IOException {
/*
* Read the length of the packet.
*/
byte[] length = new byte[4];
inputStream.read(length);
/*
* Create a buffer to contain the packet's payload.
*/
ByteBuffer packet = ByteBuffer.allocate(4120);
packet.order(LITTLE_ENDIAN);
packet.put(length);
/*
* Read the payload.
*/
for (int i = 0; i < packet.getInt(0); i++) {
packet.put((byte) inputStream.read());
}
return packet;
}
}