/*
* Copyright (C) 2016 Matteo Morena
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package mamo.vanillaVotifier;
import mamo.vanillaVotifier.exception.BrokenPipeException;
import mamo.vanillaVotifier.exception.InvalidRconPasswordException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketOptions;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.SecureRandom;
public class RconConnection {
@NotNull protected InetSocketAddress inetSocketAddress;
@NotNull protected String password;
protected int requestId;
@Nullable protected Socket socket;
public RconConnection(@NotNull InetSocketAddress inetSocketAddress, @NotNull String password) {
this.inetSocketAddress = inetSocketAddress;
this.password = password;
SecureRandom random = new SecureRandom();
while (true) {
requestId = random.nextInt();
if (requestId != -1) {
break;
}
}
}
@NotNull
public InetSocketAddress getInetSocketAddress() {
return inetSocketAddress;
}
@NotNull
public String getPassword() {
return password;
}
public int getRequestId() {
return requestId;
}
public synchronized VotifierPacket logIn() throws IOException, InvalidRconPasswordException {
return sendRequest(new VotifierPacket(getRequestId(), VotifierPacket.Type.LOG_IN, getPassword()));
}
@NotNull
public synchronized VotifierPacket sendCommand(@NotNull String command) throws IOException {
try {
return sendRequest(new VotifierPacket(getRequestId(), VotifierPacket.Type.COMMAND, command));
} catch (InvalidRconPasswordException e) {
// Can't happen.
}
return null;
}
@NotNull
public synchronized VotifierPacket sendRequest(@NotNull VotifierPacket request) throws IOException, InvalidRconPasswordException {
if (socket == null) {
socket = new Socket(inetSocketAddress.getAddress(), inetSocketAddress.getPort());
socket.setSoTimeout(SocketOptions.SO_TIMEOUT);
}
byte[] requestBytes = new byte[request.getLength() + Integer.SIZE / 8];
ByteBuffer requestBuffer = ByteBuffer.wrap(requestBytes);
requestBuffer.order(ByteOrder.LITTLE_ENDIAN);
requestBuffer.putInt(request.getLength());
requestBuffer.putInt(getRequestId());
requestBuffer.putInt(request.getType().toInt());
requestBuffer.put(request.getPayload().getBytes());
requestBuffer.put((byte) 0);
requestBuffer.put((byte) 0);
try {
socket.getOutputStream().write(requestBytes);
socket.getOutputStream().flush();
} catch (SocketException e) {
if (e.getMessage().toLowerCase().contains("broken pipe")) {
throw new BrokenPipeException();
} else {
throw e;
}
}
byte[] responseBytes = new byte[Integer.SIZE / 8];
if (socket.getInputStream().read(responseBytes) == -1) {
throw new BrokenPipeException();
}
ByteBuffer responseBuffer = ByteBuffer.wrap(responseBytes);
responseBuffer.order(ByteOrder.LITTLE_ENDIAN);
int responseLength = responseBuffer.getInt();
responseBytes = new byte[responseLength];
if (socket.getInputStream().read(responseBytes) == -1) {
throw new BrokenPipeException();
}
responseBuffer = ByteBuffer.wrap(responseBytes);
responseBuffer.order(ByteOrder.LITTLE_ENDIAN);
int responseRequestId = responseBuffer.getInt();
VotifierPacket.Type responseType = VotifierPacket.Type.fromInt(responseBuffer.getInt());
byte[] responsePayload = new byte[responseLength - Integer.SIZE / 8 - Integer.SIZE / 8 - Byte.SIZE / 8 * 2];
responseBuffer.get(responsePayload);
responseBuffer.get();
responseBuffer.get();
if (request.getType() == VotifierPacket.Type.LOG_IN) {
if (responseRequestId != getRequestId()) {
throw new InvalidRconPasswordException();
}
}
return new VotifierPacket(responseLength, responseRequestId, responseType, new String(responsePayload));
}
}