/* Copyright (C) 2015 Open Information Security Foundation
 *
 * You can copy, redistribute or modify this Program under the terms of
 * the GNU General Public License version 2 as published by the Free
 * Software Foundation.
 *
 * 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
 * version 2 along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

/**
 * \file
 *
 * DNP3 application layer protocol header file
 */

#ifndef __APP_LAYER_DNP3_H__
#define __APP_LAYER_DNP3_H__

#include "rust.h"
#if __BYTE_ORDER == __BIG_ENDIAN
#include "util-byte.h"
#endif

/* DNP3 application request function codes. */
#define DNP3_APP_FC_CONFIRM                0x00
#define DNP3_APP_FC_READ                   0x01
#define DNP3_APP_FC_WRITE                  0x02
#define DNP3_APP_FC_SELECT                 0x03
#define DNP3_APP_FC_OPERATE                0x04
#define DNP3_APP_FC_DIR_OPERATE            0x05
#define DNP3_APP_FC_DIR_OPERATE_NR         0x06
#define DNP3_APP_FC_FREEZE                 0x07
#define DNP3_APP_FC_FREEZE_NR              0x08
#define DNP3_APP_FC_FREEZE_CLEAR           0x09
#define DNP3_APP_FC_FREEZE_CLEAR_NR        0x0a
#define DNP3_APP_FC_FREEZE_AT_TIME         0x0b
#define DNP3_APP_FC_FREEZE_AT_TIME_NR      0x0c
#define DNP3_APP_FC_COLD_RESTART           0x0d
#define DNP3_APP_FC_WARM_RESTART           0x0e
#define DNP3_APP_FC_INITIALIZE_DATA        0x0f
#define DNP3_APP_FC_INITIALIZE_APPLICATION 0x10
#define DNP3_APP_FC_START_APPLICATION      0x11
#define DNP3_APP_FC_STOP_APPLICATION       0x12
#define DNP3_APP_FC_SAVE_CONFIGURATION     0x13
#define DNP3_APP_FC_ENABLE_UNSOLICITED     0x14
#define DNP3_APP_FC_DISABLE_UNSOLICITED    0x15
#define DNP3_APP_FC_ASSIGN_CLASS           0x16
#define DNP3_APP_FC_DELAY_MEASUREMENT      0x17
#define DNP3_APP_FC_RECORD_CURRENT_TIME    0x18
#define DNP3_APP_FC_OPEN_TIME              0x19
#define DNP3_APP_FC_CLOSE_FILE             0x1a
#define DNP3_APP_FC_DELETE_FILE            0x1b
#define DNP3_APP_FC_GET_FILE_INFO          0x1c
#define DNP3_APP_FC_AUTHENTICATE_FILE      0x1d
#define DNP3_APP_FC_ABORT_FILE             0x1e
#define DNP3_APP_FC_ACTIVATE_CONFIG        0x1f
#define DNP3_APP_FC_AUTH_REQ               0x20
#define DNP3_APP_FC_AUTH_REQ_NR            0x21

/* DNP3 application response function codes. */
#define DNP3_APP_FC_RESPONSE               0x81
#define DNP3_APP_FC_UNSOLICITED_RESP       0x82
#define DNP3_APP_FC_AUTH_RESP              0x83

/* Extract fields from the link control octet. */
#define DNP3_LINK_DIR(control) (control & 0x80)
#define DNP3_LINK_PRI(control) (control & 0x40)
#define DNP3_LINK_FCB(control) (control & 0x20)
#define DNP3_LINK_FCV(control) (control & 0x10)
#define DNP3_LINK_FC(control)  (control & 0x0f)

/* Extract fields from transport layer header octet. */
#define DNP3_TH_FIN(x) (x & 0x80)
#define DNP3_TH_FIR(x) (x & 0x40)
#define DNP3_TH_SEQ(x) (x & 0x3f)

/* Extract fields from the application control octet. */
#define DNP3_APP_FIR(x) (x & 0x80)
#define DNP3_APP_FIN(x) (x & 0x40)
#define DNP3_APP_CON(x) (x & 0x20)
#define DNP3_APP_UNS(x) (x & 0x10)
#define DNP3_APP_SEQ(x) (x & 0x0f)

/* DNP3 values are stored in little endian on the wire, so swapping will be
 * needed on big endian architectures. */
#if __BYTE_ORDER == __BIG_ENDIAN
#define DNP3_SWAP16(x) SCByteSwap16(x)
#define DNP3_SWAP32(x) SCByteSwap32(x)
#define DNP3_SWAP64(x) SCByteSwap64(x)
#elif __BYTE_ORDER == __LITTLE_ENDIAN
#define DNP3_SWAP16(x) x
#define DNP3_SWAP32(x) x
#define DNP3_SWAP64(x) x
#endif

/* DNP3 decoder events. */
enum {
    DNP3_DECODER_EVENT_FLOODED = 1,
    DNP3_DECODER_EVENT_LEN_TOO_SMALL,
    DNP3_DECODER_EVENT_BAD_LINK_CRC,
    DNP3_DECODER_EVENT_BAD_TRANSPORT_CRC,
    DNP3_DECODER_EVENT_MALFORMED,
    DNP3_DECODER_EVENT_UNKNOWN_OBJECT,
};

/**
 * \brief DNP3 link header.
 */
typedef struct DNP3LinkHeader_ {
    uint8_t  start_byte0; /**< First check byte. */
    uint8_t  start_byte1; /**< Second check byte. */
    uint8_t  len;         /**< Length of PDU without CRCs. */
    uint8_t  control;     /**< Control flags. */
    uint16_t dst;         /**< DNP3 destination address. */
    uint16_t src;         /**< DNP3 source address. */
    uint16_t crc;         /**< Link header CRC. */
} __attribute__((__packed__)) DNP3LinkHeader;

/**
 * \brief DNP3 transport header.
 */
typedef uint8_t DNP3TransportHeader;

/**
 * \brief DNP3 application header.
 */
typedef struct DNP3ApplicationHeader_ {
    uint8_t control;        /**< Control flags. */
    uint8_t function_code;  /**< Application function code. */
} __attribute__((__packed__)) DNP3ApplicationHeader;

/**
 * \brief DNP3 internal indicators.
 *
 * Part of the application header for responses only.
 */
typedef struct DNP3InternalInd_ {
    uint8_t iin1;
    uint8_t iin2;
} __attribute__((__packed__)) DNP3InternalInd;

/**
 * \brief A struct used for buffering incoming data prior to reassembly.
 */
typedef struct DNP3Buffer_ {
    uint8_t *buffer;
    size_t   size;
    int      len;
    int      offset;
} DNP3Buffer;

/**
 * \brief DNP3 application object header.
 */
typedef struct DNP3ObjHeader_ {
    uint8_t group;
    uint8_t variation;
    uint8_t qualifier;
} __attribute__((packed)) DNP3ObjHeader;

/**
 * \brief DNP3 object point.
 *
 * Each DNP3 object can have 0 or more points representing the values
 * of the object.
 */
typedef struct DNP3Point_ {
    uint32_t prefix;  /**< Prefix value for point. */
    uint32_t index;   /**< Index of point. If the object is prefixed
                       * with an index then this will be that
                       * value. Otherwise this is the place the point
                       * was in the list of points (starting at 0). */
    uint32_t size;    /**< Size of point if the object prefix was a
                       * size. */
    void *data;       /**< Data for this point. */
    TAILQ_ENTRY(DNP3Point_) next;
} DNP3Point;

typedef TAILQ_HEAD(DNP3PointList_, DNP3Point_) DNP3PointList;

/**
 * \brief Struct to hold the list of decoded objects.
 */
typedef struct DNP3Object_ {
    uint8_t   group;
    uint8_t   variation;
    uint8_t   qualifier;
    uint8_t   prefix_code;
    uint8_t   range_code;
    uint32_t  start;
    uint32_t  stop;
    uint32_t  count;
    DNP3PointList *points; /**< List of points for this object. */

    TAILQ_ENTRY(DNP3Object_) next;
} DNP3Object;

typedef TAILQ_HEAD(DNP3ObjectList_, DNP3Object_) DNP3ObjectList;

/**
 * \brief DNP3 transaction.
 */
typedef struct DNP3Transaction_ {
    AppLayerTxData         tx_data;

    uint64_t tx_num; /**< Internal transaction ID. */
    bool is_request; /**< Is this tx a request? */

    struct DNP3State_ *dnp3;

    uint8_t *buffer; /**< Reassembled request buffer. */
    uint32_t buffer_len;
    DNP3ObjectList objects;
    DNP3LinkHeader lh;
    DNP3TransportHeader th;
    DNP3ApplicationHeader ah;
    DNP3InternalInd iin;
    uint8_t done;
    uint8_t complete; /**< Was the decode complete.  It will not be
                         complete if we hit objects we do not know. */

    TAILQ_ENTRY(DNP3Transaction_) next;
} DNP3Transaction;

TAILQ_HEAD(TxListHead, DNP3Transaction_);

/**
 * \brief Per flow DNP3 state.
 */
typedef struct DNP3State_ {
    AppLayerStateData state_data;
    TAILQ_HEAD(, DNP3Transaction_) tx_list;
    DNP3Transaction *curr;     /**< Current transaction. */
    uint64_t transaction_max;
    uint16_t events;
    uint32_t unreplied;        /**< Number of unreplied requests. */
    uint8_t flooded;           /**< Flag indicating flood. */

    DNP3Buffer request_buffer;  /**< Request buffer for buffering
                                 * incomplete request PDUs received
                                 * over TCP. */
    DNP3Buffer response_buffer; /**< Response buffer for buffering
                                 * incomplete response PDUs received
                                 * over TCP. */

} DNP3State;

void RegisterDNP3Parsers(void);
void DNP3ParserRegisterTests(void);
int DNP3PrefixIsSize(uint8_t);

#endif /* __APP_LAYER_DNP3_H__ */
