Package net.ceidwarfare.coyote.classes

Source Code of net.ceidwarfare.coyote.classes.RconPacket

/*******************************************************************************
* =============================================================================
* Filename:      RconPacket.java
* Project:       Coyote
* Creation Date: 2013-03-10
* Author:        Dimitris Zarras (feugatos) <zarras.dim@gmail.com>
* =============================================================================
* =============================================================================
* Copyright 2013 Dimitris Zarras
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =============================================================================
* =============================================================================
* Contributors:
*      * Dimitris Zarras (feugatos) <zarras.dim@gmail.com>
* =============================================================================
******************************************************************************/
package net.ceidwarfare.coyote.classes;

import net.ceidwarfare.coyote.exceptions.UnexpectedValueException;
import net.ceidwarfare.helpers.ByteConversions;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.util.Arrays;

/**
* This class is used to create the actual packets that will be sent to the server. The static methods
* createPacket(int, int, String) and createPacket(byte[]) can be used to create instances of this class.
*/
public class RconPacket {

    /**
     * The packet type of an authentication packet, currently 3
     */
    public static final int SERVERDATA_AUTH = 3;

    /**
     * The packet type of an authentication response packet, currently 2
     */
    public static final int SERVERDATA_AUTH_RESPONSE = 2;

    /**
     * The packet type of an exec command packet, currently 2
     */
    public static final int SERVERDATA_EXECCOMMAND = 2;

    /**
     * The packet type of a response packet, currently 0
     */
    public static final int SERVERDATA_RESPONSE_VALUE = 0;

    /**
     * The minimum size of a packet excluding the packetSize variable, currently 10
     */
    public static final int MIN_SIZE = 10;

    /**
     * The maximum size of a packet excluding the packetSize variable, currently 4096
     */
    public static final int MAX_SIZE = 4096;

    /**
     * The minimum size of a packet including the packetSize variable, currently 14
     */
    public static final int REAL_MIN_SIZE = 14;

    /**
     * The maximum size of a packet including the packetSize variable, currently 4100
     */
    public static final int REAL_MAX_SIZE = 4100;

    /**
     * The maximum length for the body string of the packet excluding the '\0' character at the end, currently 4086
     */
    public static final int MAX_BODY_SIZE = 4086;


    //Private member fields
    private int size;
    private int id;
    private int type;
    private String body;
    private String emptyBody;

    /**
     * Constructs a new RconPacket given it's id, type and body. This constructor doesn't do any input validation so all
     * data passed to this constructor must be validated beforehand.
     *
     * @param id   the id of the packet
     * @param type the type of the packet
     * @param body the body of the packet
     */
    private RconPacket(int id, int type, String body) {

        this.size = 4 //for id
                + 4 // for type
                + body.length() + 1 //the body length plus the null termination char
                + 1; //1 for the null termination char of the emptyBody
        this.id = id;
        this.type = type;
        this.body = body;
        this.emptyBody = "";

    }

    /**
     * Returns the packet size (excluding the packetSize variable itself).
     *
     * @return the size of the packet
     */
    public int getSize() {
        return size;
    }

    /**
     * Returns the size of the byte[] array that will be sent to the server. This includes the packetSize variable
     * as well.
     *
     * @return the size of the packet including the packetSize variable itself
     */
    public int getRealSize() {
        return size + 4; //4 more bytes for the size variable itself
    }

    /**
     * Returns the id of the current packet.
     *
     * @return the packet id
     */
    public int getId() {
        return id;
    }

    /**
     * Returns the numeric representation of the type of the current packet. Check the class constants
     * SERVERDATA_AUTH, SERVERDATA_AUTH_RESPONSE,SERVERDATA_EXECCOMMAND and SERVERDATA_RESPONSE_VALUE as well.
     *
     * @return the packet type as an integer
     */
    public int getType() {
        return type;
    }

    /**
     * Returns the body string of the current packet. This is the command to be sent for a SERVERDATA_EXECCOMMAND
     * packet, the response from the server for a SERVERDATA_RESPONSE_VALUE packet, the rcon password for a SERVER_DATA_AUTH
     * packet and an empty string for a SERVER_DATA_AUTH_RESPONSE packet.
     *
     * @return the body of the packet
     */
    public String getBody() {
        return body;
    }

    /**
     * Returns the byte[] array representation of the current packet which can be directly use with OutputStream.write()
     * in order to send the packet to the server.
     *
     * @return the byte[] array representation of the current packet
     */
    public byte[] getBytes() {
        ByteBuffer buffer = ByteBuffer.allocate(this.getRealSize());
        buffer.order(ByteOrder.LITTLE_ENDIAN);

        buffer.putInt(size);
        buffer.putInt(id);
        buffer.putInt(type);
        buffer.put((body + '\0').getBytes(Charset.forName("US-ASCII")));
        buffer.put((emptyBody + '\0').getBytes(Charset.forName("US-ASCII")));

        return buffer.array();
    }

    /**
     * Create a new RconPacket given it's id, type and body. This method is typically used to create an authentication
     * packet or a command packet that will be sent to the server.
     *
     * @param id   the id of the packet. must be greater than -1.
     * @param type the type of the packet. check SERVERDATA_* class variables for more.
     * @param body the body of the packet. can't be larger than MAX_BODY_SIZE characters.
     * @return the corresponding RconPacket.
     * @throws IllegalArgumentException if id is less that 0 , or if type is not equal to one of the SERVERDATA_* class
     *                                  variables or if body exceeds MAX_BODY_SIZE characters.
     */
    public static RconPacket createPacket(int id, int type, String body) {

        //check if id is less than 0
        if (id < 0) {
            throw new IllegalArgumentException("the packetId must be an integer greater or equal to 0");
        }

        //check if type is one of the expected ones
        if ((type != SERVERDATA_AUTH) && (type != SERVERDATA_AUTH_RESPONSE) && (type != SERVERDATA_EXECCOMMAND) &&
                (type != SERVERDATA_RESPONSE_VALUE)) {
            throw new IllegalArgumentException("the packetType must be the value of one of the SERVERDATA_* values");
        }

        if (body == null) {
            body = "";
        } else {
            //check if the length of the body doesn't exceed MAX_BODY_SIZE
            if (body.length() > MAX_BODY_SIZE) {
                throw new IllegalArgumentException("The packet body cannot be greater than " + MAX_BODY_SIZE + " bytes");
            }
        }

        //return the newly constructed RconPacket
        return new RconPacket(id, type, body);
    }

    /**
     * Create a packet given its little endian byte[] array representation. Typically this method is used to construct
     * incoming packets which are read, as a byte[] array, from the server's InputStream using read().
     *
     * @param bytes the little endian byte[] array to be converted to an RconPacket
     * @return the RconPacket constructed from the given bytes
     * @throws IllegalArgumentException if bytes is null or bytes.length is not between MIN_SIZE and MAX_SIZE
     * @throws UnexpectedValueException if the packetId or packetType don't have an expected value, eg packetId being -19
     *                                  or packetType being 144
     */
    public static RconPacket createPacket(byte[] bytes) {
        //check if bytes is null
        if (bytes == null) {
            throw new IllegalArgumentException("bytes cannot be a null reference");
        }

        //check if bytes is at least MIN_SIZE and at most MAX_SIZE bytes long
        if ((bytes.length < MIN_SIZE) || (bytes.length > MAX_SIZE)) {
            throw new IllegalArgumentException("bytes be at least MIN_SIZE and at most MAX_SIZE bytes long");
        }

        //check if the converted from bytes id is greater than -1
        //-1 is only used if the authentication is not successful in the id field of the SERVERDATA_AUTH_RESPONSE packet
        int packetId = ByteConversions.littleEndianToInt(Arrays.copyOfRange(bytes, 0, 4));
        if (packetId < -1) {
            throw new UnexpectedValueException("the packetId must be an integer greater than -1.");
        }

        //check if the converted from bytes type is one of the expected.
        int packetType = ByteConversions.littleEndianToInt(Arrays.copyOfRange(bytes, 4, 8));
        if ((packetType != SERVERDATA_AUTH) && (packetType != SERVERDATA_EXECCOMMAND) &&
                (packetType != SERVERDATA_RESPONSE_VALUE) && (packetType != SERVERDATA_AUTH_RESPONSE)) {
            throw new UnexpectedValueException("the packetType must be the value of one of the SERVERDATA_* values");
        }

        //construct the packet body. we exclude the final 2 bytes as we already now they should be '\0'.
        String response = new String(Arrays.copyOfRange(bytes, 8, bytes.length - 2));

        //return the newly created packet
        return new RconPacket(packetId, packetType, response);
    }
}
TOP

Related Classes of net.ceidwarfare.coyote.classes.RconPacket

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.