/* 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
 *
 * \author Kevin Wong <kwong@solananetworks.com>
 */

#ifndef SURICATA_APP_LAYER_ENIP_COMMON_H
#define SURICATA_APP_LAYER_ENIP_COMMON_H

#include "rust.h"

// EtherNet/IP commands
#define NOP                0x0000
#define LIST_SERVICES      0x0004
#define LIST_IDENTITY      0x0063
#define LIST_INTERFACES    0x0064
#define REGISTER_SESSION   0x0065
#define UNREGISTER_SESSION 0x0066
#define SEND_RR_DATA       0x006F
#define SEND_UNIT_DATA     0x0070
#define INDICATE_STATUS    0x0072
#define CANCEL             0x0073

//Common Packet Format Types
#define NULL_ADDR               0x0000
#define CONNECTION_BASED        0x00a1
#define CONNECTED_DATA_ITEM     0x00b1
#define UNCONNECTED_DATA_ITEM   0x00b2
#define SEQUENCE_ADDR_ITEM      0xB002

//status codes
#define SUCCESS               0x0000
#define INVALID_CMD           0x0001
#define NO_RESOURCES          0x0002
#define INCORRECT_DATA        0x0003
#define INVALID_SESSION       0x0064
#define INVALID_LENGTH        0x0065
#define UNSUPPORTED_PROT_REV  0x0069
//Found in wireshark
#define ENCAP_HEADER_ERROR    0x006A

#define MAX_CIP_SERVICE     127
#define MAX_CIP_CLASS       65535
#define MAX_CIP_ATTRIBUTE   65535

// CIP service codes
#define CIP_RESERVED        0x00
#define CIP_GET_ATTR_ALL    0x01
#define CIP_GET_ATTR_LIST   0x03
#define CIP_SET_ATTR_LIST   0x04
#define CIP_RESET           0x05
#define CIP_START           0x06
#define CIP_STOP            0x07
#define CIP_CREATE          0x08
#define CIP_DELETE          0x09
#define CIP_MSP             0x0a
#define CIP_APPLY_ATTR      0x0d
#define CIP_GET_ATTR_SINGLE 0x0e
#define CIP_SET_ATTR_SINGLE 0x10
#define CIP_KICK_TIMER      0x4b
#define CIP_OPEN_CONNECTION 0x4c
#define CIP_CHANGE_START    0x4f
#define CIP_GET_STATUS      0x50

//PATH sizing codes
#define PATH_CLASS_8BIT         0x20
#define PATH_CLASS_16BIT        0x21
#define PATH_INSTANCE_8BIT      0x24
#define PATH_INSTANCE_16BIT     0x25
#define PATH_ATTR_8BIT          0x30
#define PATH_ATTR_16BIT         0x31 //possible value

/**
 * ENIP encapsulation header
 */
typedef struct ENIPEncapHdr_
{
    uint64_t context;
    uint32_t session;
    uint32_t status;
    uint32_t option;
    uint16_t command;
    uint16_t length;
} ENIPEncapHdr;

/**
 * ENIP encapsulation data header
 */
typedef struct ENIPEncapDataHdr_
{
    uint32_t interface_handle;
    uint16_t timeout;
    uint16_t item_count;
} ENIPEncapDataHdr;

/**
 * ENIP encapsulation address item
 */
typedef struct ENIPEncapAddressItem_ {
    uint16_t type;
    uint16_t length;
    uint32_t conn_id;
    uint32_t sequence_num;
} ENIPEncapAddressItem;

/**
 * ENIP encapsulation data item
 */
typedef struct ENIPEncapDataItem_
{
    uint16_t type;
    uint16_t length;
    uint16_t sequence_count;
} ENIPEncapDataItem;

/**
 * CIP Request Header
 */
typedef struct CIPReqHdr_
{
    uint8_t service;
    uint8_t path_size;
} CIPReqHdr;

/**
 * CIP Response Header
 */
typedef struct CIPRespHdr_
{
    uint8_t service;
    uint8_t pad;
    uint8_t status;
    uint8_t status_size;
} CIPRespHdr;

typedef struct SegmentEntry_
{
    uint16_t segment;   /**< segment type */
    uint16_t value;     /**< segment value (class or attribute) */

    TAILQ_ENTRY(SegmentEntry_) next;
} SegmentEntry;

typedef struct AttributeEntry_
{
    uint16_t attribute; /**< segment class */

    TAILQ_ENTRY(AttributeEntry_) next;
} AttributeEntry;

typedef struct CIPServiceEntry_
{
    uint8_t service;                            /**< cip service */
    uint8_t direction;
    union
    {
        struct
        {
            uint8_t path_size;                  /**< cip path size */
            uint16_t path_offset;               /**< offset to cip path */
        } request;
        struct
        {
            uint16_t status;
        } response;
    };

    TAILQ_HEAD(, SegmentEntry_) segment_list;   /**< list for CIP segment */
    TAILQ_HEAD(, AttributeEntry_) attrib_list;  /**< list for CIP segment */

    TAILQ_ENTRY(CIPServiceEntry_) next;
} CIPServiceEntry;

typedef struct ENIPTransaction_
{
    struct ENIPState_ *enip;
    uint64_t tx_num;                            /**< internal: id */
    uint16_t tx_id;                             /**< transaction id */
    uint16_t service_count;

    ENIPEncapHdr header;                        /**< encapsulation header */
    ENIPEncapDataHdr encap_data_header;         /**< encapsulation data header */
    ENIPEncapAddressItem encap_addr_item;       /**< encapsulated address item */
    ENIPEncapDataItem encap_data_item;          /**< encapsulated data item */

    TAILQ_HEAD(, CIPServiceEntry_) service_list; /**< list for CIP  */

    TAILQ_ENTRY(ENIPTransaction_) next;
    AppLayerTxData tx_data;
} ENIPTransaction;

/** \brief Per flow ENIP state container */
typedef struct ENIPState_
{
    AppLayerStateData state_data;
    TAILQ_HEAD(, ENIPTransaction_) tx_list; /**< transaction list */
    ENIPTransaction *curr;                  /**< ptr to current tx */
    ENIPTransaction *iter;
    uint64_t transaction_max;
    uint64_t tx_with_detect_state_cnt;

    uint16_t events;
    uint16_t givenup;

    /* used by TCP only */
    uint16_t offset;
    uint16_t record_len;
    uint8_t *buffer;
} ENIPState;

int DecodeENIPPDU(const uint8_t *input, uint32_t input_len,
        ENIPTransaction *enip_data);
int DecodeCommonPacketFormatPDU(const uint8_t *input, uint32_t input_len,
        ENIPTransaction *enip_data, uint16_t offset);
int DecodeCIPPDU(const uint8_t *input, uint32_t input_len,
        ENIPTransaction *enip_data, uint16_t offset);
int DecodeCIPRequestPDU(const uint8_t *input, uint32_t input_len,
        ENIPTransaction *enip_data, uint16_t offset);
int DecodeCIPResponsePDU(const uint8_t *input, uint32_t input_len,
        ENIPTransaction *enip_data, uint16_t offset);
int DecodeCIPRequestPathPDU(const uint8_t *input, uint32_t input_len,
        CIPServiceEntry *node, uint16_t offset);
int DecodeCIPRequestMSPPDU(const uint8_t *input, uint32_t input_len,
        ENIPTransaction *enip_data, uint16_t offset);
int DecodeCIPResponseMSPPDU(const uint8_t *input, uint32_t input_len,
        ENIPTransaction *enip_data, uint16_t offset);

#endif /* SURICATA_APP_LAYER_ENIP_COMMON_H */
