mirror of https://github.com/OISF/suricata
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
655 lines
18 KiB
C
655 lines
18 KiB
C
/* 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>
|
|
*
|
|
* App-layer parser for ENIP protocol
|
|
*
|
|
*/
|
|
|
|
#include "suricata-common.h"
|
|
|
|
#include "util-debug.h"
|
|
#include "util-byte.h"
|
|
#include "util-enum.h"
|
|
#include "util-mem.h"
|
|
#include "util-misc.h"
|
|
|
|
#include "stream.h"
|
|
|
|
#include "app-layer-protos.h"
|
|
#include "app-layer-parser.h"
|
|
#include "app-layer-enip.h"
|
|
#include "app-layer-enip-common.h"
|
|
|
|
#include "app-layer-detect-proto.h"
|
|
|
|
#include "conf.h"
|
|
#include "decode.h"
|
|
|
|
#include "detect-parse.h"
|
|
#include "detect-engine.h"
|
|
#include "util-byte.h"
|
|
#include "util-unittest.h"
|
|
#include "util-unittest-helper.h"
|
|
#include "pkt-var.h"
|
|
#include "util-profiling.h"
|
|
|
|
|
|
SCEnumCharMap enip_decoder_event_table[ ] = {
|
|
{ NULL, -1 },
|
|
};
|
|
|
|
/** \brief get value for 'complete' status in ENIP
|
|
*
|
|
* For ENIP we use a simple bool.
|
|
*/
|
|
static int ENIPGetAlstateProgress(void *tx, uint8_t direction)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
/** \brief get value for 'complete' status in ENIP
|
|
*
|
|
* For ENIP we use a simple bool.
|
|
*/
|
|
static int ENIPGetAlstateProgressCompletionStatus(uint8_t direction)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static DetectEngineState *ENIPGetTxDetectState(void *vtx)
|
|
{
|
|
ENIPTransaction *tx = (ENIPTransaction *)vtx;
|
|
return tx->de_state;
|
|
}
|
|
|
|
static int ENIPSetTxDetectState(void *vtx, DetectEngineState *s)
|
|
{
|
|
ENIPTransaction *tx = (ENIPTransaction *)vtx;
|
|
tx->de_state = s;
|
|
return 0;
|
|
}
|
|
|
|
static AppLayerTxData *ENIPGetTxData(void *vtx)
|
|
{
|
|
ENIPTransaction *tx = (ENIPTransaction *)vtx;
|
|
return &tx->tx_data;
|
|
}
|
|
|
|
static void *ENIPGetTx(void *alstate, uint64_t tx_id)
|
|
{
|
|
ENIPState *enip = (ENIPState *) alstate;
|
|
ENIPTransaction *tx = NULL;
|
|
|
|
if (enip->curr && enip->curr->tx_num == tx_id + 1)
|
|
return enip->curr;
|
|
|
|
TAILQ_FOREACH(tx, &enip->tx_list, next) {
|
|
if (tx->tx_num != (tx_id+1))
|
|
continue;
|
|
|
|
SCLogDebug("returning tx %p", tx);
|
|
return tx;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static uint64_t ENIPGetTxCnt(void *alstate)
|
|
{
|
|
return ((uint64_t) ((ENIPState *) alstate)->transaction_max);
|
|
}
|
|
|
|
static AppLayerDecoderEvents *ENIPGetEvents(void *tx)
|
|
{
|
|
return ((ENIPTransaction *)tx)->decoder_events;
|
|
}
|
|
|
|
static int ENIPStateGetEventInfo(const char *event_name, int *event_id, AppLayerEventType *event_type)
|
|
{
|
|
*event_id = SCMapEnumNameToValue(event_name, enip_decoder_event_table);
|
|
|
|
if (*event_id == -1) {
|
|
SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%s\" not present in "
|
|
"enip's enum map table.", event_name);
|
|
/* yes this is fatal */
|
|
return -1;
|
|
}
|
|
|
|
*event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ENIPStateGetEventInfoById(int event_id, const char **event_name,
|
|
AppLayerEventType *event_type)
|
|
{
|
|
*event_name = SCMapEnumValueToName(event_id, enip_decoder_event_table);
|
|
if (*event_name == NULL) {
|
|
SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%d\" not present in "
|
|
"enip's enum map table.", event_id);
|
|
/* yes this is fatal */
|
|
return -1;
|
|
}
|
|
|
|
*event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/** \brief Allocate enip state
|
|
*
|
|
* return state
|
|
*/
|
|
static void *ENIPStateAlloc(void)
|
|
{
|
|
SCLogDebug("ENIPStateAlloc");
|
|
void *s = SCMalloc(sizeof(ENIPState));
|
|
if (unlikely(s == NULL))
|
|
return NULL;
|
|
|
|
memset(s, 0, sizeof(ENIPState));
|
|
|
|
ENIPState *enip_state = (ENIPState *) s;
|
|
|
|
TAILQ_INIT(&enip_state->tx_list);
|
|
return s;
|
|
}
|
|
|
|
/** \internal
|
|
* \brief Free a ENIP TX
|
|
* \param tx ENIP TX to free */
|
|
static void ENIPTransactionFree(ENIPTransaction *tx, ENIPState *state)
|
|
{
|
|
SCEnter();
|
|
SCLogDebug("ENIPTransactionFree");
|
|
CIPServiceEntry *svc = NULL;
|
|
while ((svc = TAILQ_FIRST(&tx->service_list)))
|
|
{
|
|
TAILQ_REMOVE(&tx->service_list, svc, next);
|
|
|
|
SegmentEntry *seg = NULL;
|
|
while ((seg = TAILQ_FIRST(&svc->segment_list)))
|
|
{
|
|
TAILQ_REMOVE(&svc->segment_list, seg, next);
|
|
SCFree(seg);
|
|
}
|
|
|
|
AttributeEntry *attr = NULL;
|
|
while ((attr = TAILQ_FIRST(&svc->attrib_list)))
|
|
{
|
|
TAILQ_REMOVE(&svc->attrib_list, attr, next);
|
|
SCFree(attr);
|
|
}
|
|
|
|
SCFree(svc);
|
|
}
|
|
|
|
AppLayerDecoderEventsFreeEvents(&tx->decoder_events);
|
|
|
|
if (tx->de_state != NULL)
|
|
{
|
|
DetectEngineStateFree(tx->de_state);
|
|
|
|
state->tx_with_detect_state_cnt--;
|
|
}
|
|
|
|
if (state->iter == tx)
|
|
state->iter = NULL;
|
|
|
|
SCFree(tx);
|
|
SCReturn;
|
|
}
|
|
|
|
/** \brief Free enip state
|
|
*
|
|
*/
|
|
static void ENIPStateFree(void *s)
|
|
{
|
|
SCEnter();
|
|
SCLogDebug("ENIPStateFree");
|
|
if (s)
|
|
{
|
|
ENIPState *enip_state = (ENIPState *) s;
|
|
|
|
ENIPTransaction *tx = NULL;
|
|
while ((tx = TAILQ_FIRST(&enip_state->tx_list)))
|
|
{
|
|
TAILQ_REMOVE(&enip_state->tx_list, tx, next);
|
|
ENIPTransactionFree(tx, enip_state);
|
|
}
|
|
|
|
if (enip_state->buffer != NULL)
|
|
{
|
|
SCFree(enip_state->buffer);
|
|
}
|
|
|
|
SCFree(s);
|
|
}
|
|
SCReturn;
|
|
}
|
|
|
|
/** \internal
|
|
* \brief Allocate a ENIP TX
|
|
* \retval tx or NULL */
|
|
static ENIPTransaction *ENIPTransactionAlloc(ENIPState *state)
|
|
{
|
|
SCLogDebug("ENIPStateTransactionAlloc");
|
|
ENIPTransaction *tx = (ENIPTransaction *) SCCalloc(1,
|
|
sizeof(ENIPTransaction));
|
|
if (unlikely(tx == NULL))
|
|
return NULL;
|
|
|
|
state->curr = tx;
|
|
state->transaction_max++;
|
|
|
|
memset(tx, 0x00, sizeof(ENIPTransaction));
|
|
TAILQ_INIT(&tx->service_list);
|
|
|
|
tx->enip = state;
|
|
tx->tx_num = state->transaction_max;
|
|
tx->service_count = 0;
|
|
|
|
TAILQ_INSERT_TAIL(&state->tx_list, tx, next);
|
|
|
|
return tx;
|
|
}
|
|
|
|
/**
|
|
* \brief enip transaction cleanup callback
|
|
*/
|
|
static void ENIPStateTransactionFree(void *state, uint64_t tx_id)
|
|
{
|
|
SCEnter();
|
|
SCLogDebug("ENIPStateTransactionFree");
|
|
ENIPState *enip_state = state;
|
|
ENIPTransaction *tx = NULL;
|
|
TAILQ_FOREACH(tx, &enip_state->tx_list, next)
|
|
{
|
|
|
|
if ((tx_id+1) < tx->tx_num)
|
|
break;
|
|
else if ((tx_id+1) > tx->tx_num)
|
|
continue;
|
|
|
|
if (tx == enip_state->curr)
|
|
enip_state->curr = NULL;
|
|
|
|
if (tx->decoder_events != NULL)
|
|
{
|
|
if (tx->decoder_events->cnt <= enip_state->events)
|
|
enip_state->events -= tx->decoder_events->cnt;
|
|
else
|
|
enip_state->events = 0;
|
|
}
|
|
|
|
TAILQ_REMOVE(&enip_state->tx_list, tx, next);
|
|
ENIPTransactionFree(tx, state);
|
|
break;
|
|
}
|
|
SCReturn;
|
|
}
|
|
|
|
/** \internal
|
|
*
|
|
* \brief This function is called to retrieve a ENIP
|
|
*
|
|
* \param state ENIP state structure for the parser
|
|
* \param input Input line of the command
|
|
* \param input_len Length of the request
|
|
*
|
|
* \retval 1 when the command is parsed, 0 otherwise
|
|
*/
|
|
static AppLayerResult ENIPParse(Flow *f, void *state, AppLayerParserState *pstate,
|
|
const uint8_t *input, uint32_t input_len, void *local_data,
|
|
const uint8_t flags)
|
|
{
|
|
SCEnter();
|
|
ENIPState *enip = (ENIPState *) state;
|
|
ENIPTransaction *tx;
|
|
|
|
if (input == NULL && AppLayerParserStateIssetFlag(pstate,
|
|
APP_LAYER_PARSER_EOF_TS|APP_LAYER_PARSER_EOF_TC))
|
|
{
|
|
SCReturnStruct(APP_LAYER_OK);
|
|
} else if (input == NULL && input_len != 0) {
|
|
// GAP
|
|
SCReturnStruct(APP_LAYER_OK);
|
|
} else if (input == NULL || input_len == 0)
|
|
{
|
|
SCReturnStruct(APP_LAYER_ERROR);
|
|
}
|
|
|
|
while (input_len > 0)
|
|
{
|
|
tx = ENIPTransactionAlloc(enip);
|
|
if (tx == NULL)
|
|
SCReturnStruct(APP_LAYER_OK);
|
|
|
|
SCLogDebug("ENIPParse input len %d", input_len);
|
|
DecodeENIPPDU(input, input_len, tx);
|
|
uint32_t pkt_len = tx->header.length + sizeof(ENIPEncapHdr);
|
|
SCLogDebug("ENIPParse packet len %d", pkt_len);
|
|
if (pkt_len > input_len)
|
|
{
|
|
SCLogDebug("Invalid packet length");
|
|
break;
|
|
}
|
|
|
|
input += pkt_len;
|
|
input_len -= pkt_len;
|
|
//SCLogDebug("remaining %d", input_len);
|
|
|
|
if (input_len < sizeof(ENIPEncapHdr))
|
|
{
|
|
//SCLogDebug("Not enough data"); //not enough data for ENIP
|
|
break;
|
|
}
|
|
}
|
|
|
|
SCReturnStruct(APP_LAYER_OK);
|
|
}
|
|
|
|
|
|
|
|
static uint16_t ENIPProbingParser(Flow *f, uint8_t direction,
|
|
const uint8_t *input, uint32_t input_len, uint8_t *rdir)
|
|
{
|
|
// SCLogDebug("ENIPProbingParser %d", input_len);
|
|
if (input_len < sizeof(ENIPEncapHdr))
|
|
{
|
|
SCLogDebug("length too small to be a ENIP header");
|
|
return ALPROTO_UNKNOWN;
|
|
}
|
|
uint16_t cmd;
|
|
uint32_t status;
|
|
int ret = ByteExtractUint16(&cmd, BYTE_LITTLE_ENDIAN, sizeof(uint16_t),
|
|
(const uint8_t *) (input));
|
|
if(ret < 0) {
|
|
return ALPROTO_FAILED;
|
|
}
|
|
//ok for all the known commands
|
|
switch(cmd) {
|
|
case NOP:
|
|
case LIST_SERVICES:
|
|
case LIST_IDENTITY:
|
|
case LIST_INTERFACES:
|
|
case REGISTER_SESSION:
|
|
case UNREGISTER_SESSION:
|
|
case SEND_RR_DATA:
|
|
case SEND_UNIT_DATA:
|
|
case INDICATE_STATUS:
|
|
case CANCEL:
|
|
ret = ByteExtractUint32(&status, BYTE_LITTLE_ENDIAN,
|
|
sizeof(uint32_t),
|
|
(const uint8_t *) (input + 8));
|
|
if(ret < 0) {
|
|
return ALPROTO_FAILED;
|
|
}
|
|
switch(status) {
|
|
case SUCCESS:
|
|
case INVALID_CMD:
|
|
case NO_RESOURCES:
|
|
case INCORRECT_DATA:
|
|
case INVALID_SESSION:
|
|
case INVALID_LENGTH:
|
|
case UNSUPPORTED_PROT_REV:
|
|
case ENCAP_HEADER_ERROR:
|
|
return ALPROTO_ENIP;
|
|
}
|
|
}
|
|
return ALPROTO_FAILED;
|
|
}
|
|
|
|
/**
|
|
* \brief Function to register the ENIP protocol parsers and other functions
|
|
*/
|
|
void RegisterENIPUDPParsers(void)
|
|
{
|
|
SCEnter();
|
|
const char *proto_name = "enip";
|
|
|
|
if (AppLayerProtoDetectConfProtoDetectionEnabled("udp", proto_name))
|
|
{
|
|
AppLayerProtoDetectRegisterProtocol(ALPROTO_ENIP, proto_name);
|
|
|
|
if (RunmodeIsUnittests())
|
|
{
|
|
AppLayerProtoDetectPPRegister(IPPROTO_UDP, "44818", ALPROTO_ENIP,
|
|
0, sizeof(ENIPEncapHdr), STREAM_TOSERVER, ENIPProbingParser, NULL);
|
|
|
|
AppLayerProtoDetectPPRegister(IPPROTO_UDP, "44818", ALPROTO_ENIP,
|
|
0, sizeof(ENIPEncapHdr), STREAM_TOCLIENT, ENIPProbingParser, NULL);
|
|
|
|
} else
|
|
{
|
|
if (!AppLayerProtoDetectPPParseConfPorts("udp", IPPROTO_UDP,
|
|
proto_name, ALPROTO_ENIP, 0, sizeof(ENIPEncapHdr),
|
|
ENIPProbingParser, ENIPProbingParser))
|
|
{
|
|
SCLogDebug(
|
|
"no ENIP UDP config found enabling ENIP detection on port 44818.");
|
|
|
|
AppLayerProtoDetectPPRegister(IPPROTO_UDP, "44818",
|
|
ALPROTO_ENIP, 0, sizeof(ENIPEncapHdr), STREAM_TOSERVER,
|
|
ENIPProbingParser, NULL);
|
|
|
|
AppLayerProtoDetectPPRegister(IPPROTO_UDP, "44818",
|
|
ALPROTO_ENIP, 0, sizeof(ENIPEncapHdr), STREAM_TOCLIENT,
|
|
ENIPProbingParser, NULL);
|
|
}
|
|
}
|
|
|
|
} else
|
|
{
|
|
SCLogConfig("Protocol detection and parser disabled for %s protocol.",
|
|
proto_name);
|
|
return;
|
|
}
|
|
|
|
if (AppLayerParserConfParserEnabled("udp", proto_name))
|
|
{
|
|
AppLayerParserRegisterParser(IPPROTO_UDP, ALPROTO_ENIP,
|
|
STREAM_TOSERVER, ENIPParse);
|
|
AppLayerParserRegisterParser(IPPROTO_UDP, ALPROTO_ENIP,
|
|
STREAM_TOCLIENT, ENIPParse);
|
|
|
|
AppLayerParserRegisterStateFuncs(IPPROTO_UDP, ALPROTO_ENIP,
|
|
ENIPStateAlloc, ENIPStateFree);
|
|
|
|
AppLayerParserRegisterGetEventsFunc(IPPROTO_UDP, ALPROTO_ENIP, ENIPGetEvents);
|
|
|
|
AppLayerParserRegisterDetectStateFuncs(IPPROTO_UDP, ALPROTO_ENIP,
|
|
ENIPGetTxDetectState, ENIPSetTxDetectState);
|
|
|
|
AppLayerParserRegisterGetTx(IPPROTO_UDP, ALPROTO_ENIP, ENIPGetTx);
|
|
AppLayerParserRegisterTxDataFunc(IPPROTO_UDP, ALPROTO_ENIP, ENIPGetTxData);
|
|
AppLayerParserRegisterGetTxCnt(IPPROTO_UDP, ALPROTO_ENIP, ENIPGetTxCnt);
|
|
AppLayerParserRegisterTxFreeFunc(IPPROTO_UDP, ALPROTO_ENIP, ENIPStateTransactionFree);
|
|
|
|
AppLayerParserRegisterGetStateProgressFunc(IPPROTO_UDP, ALPROTO_ENIP, ENIPGetAlstateProgress);
|
|
AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_ENIP, ENIPGetAlstateProgressCompletionStatus);
|
|
|
|
AppLayerParserRegisterGetEventInfo(IPPROTO_UDP, ALPROTO_ENIP, ENIPStateGetEventInfo);
|
|
AppLayerParserRegisterGetEventInfoById(IPPROTO_UDP, ALPROTO_ENIP, ENIPStateGetEventInfoById);
|
|
|
|
AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_UDP,
|
|
ALPROTO_ENIP, STREAM_TOSERVER | STREAM_TOCLIENT);
|
|
|
|
} else
|
|
{
|
|
SCLogInfo(
|
|
"Parsed disabled for %s protocol. Protocol detection" "still on.",
|
|
proto_name);
|
|
}
|
|
|
|
#ifdef UNITTESTS
|
|
AppLayerParserRegisterProtocolUnittests(IPPROTO_UDP, ALPROTO_ENIP, ENIPParserRegisterTests);
|
|
#endif
|
|
|
|
SCReturn;
|
|
}
|
|
|
|
/**
|
|
* \brief Function to register the ENIP protocol parsers and other functions
|
|
*/
|
|
void RegisterENIPTCPParsers(void)
|
|
{
|
|
SCEnter();
|
|
const char *proto_name = "enip";
|
|
|
|
if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name))
|
|
{
|
|
AppLayerProtoDetectRegisterProtocol(ALPROTO_ENIP, proto_name);
|
|
|
|
if (RunmodeIsUnittests())
|
|
{
|
|
AppLayerProtoDetectPPRegister(IPPROTO_TCP, "44818", ALPROTO_ENIP,
|
|
0, sizeof(ENIPEncapHdr), STREAM_TOSERVER, ENIPProbingParser, NULL);
|
|
|
|
AppLayerProtoDetectPPRegister(IPPROTO_TCP, "44818", ALPROTO_ENIP,
|
|
0, sizeof(ENIPEncapHdr), STREAM_TOCLIENT, ENIPProbingParser, NULL);
|
|
|
|
} else
|
|
{
|
|
if (!AppLayerProtoDetectPPParseConfPorts("tcp", IPPROTO_TCP,
|
|
proto_name, ALPROTO_ENIP, 0, sizeof(ENIPEncapHdr),
|
|
ENIPProbingParser, ENIPProbingParser))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
} else
|
|
{
|
|
SCLogDebug("Protocol detection and parser disabled for %s protocol.",
|
|
proto_name);
|
|
return;
|
|
}
|
|
|
|
if (AppLayerParserConfParserEnabled("tcp", proto_name))
|
|
{
|
|
AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_ENIP,
|
|
STREAM_TOSERVER, ENIPParse);
|
|
AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_ENIP,
|
|
STREAM_TOCLIENT, ENIPParse);
|
|
AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_ENIP,
|
|
ENIPStateAlloc, ENIPStateFree);
|
|
|
|
AppLayerParserRegisterGetEventsFunc(IPPROTO_TCP, ALPROTO_ENIP, ENIPGetEvents);
|
|
|
|
AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP, ALPROTO_ENIP,
|
|
ENIPGetTxDetectState, ENIPSetTxDetectState);
|
|
|
|
AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_ENIP, ENIPGetTx);
|
|
AppLayerParserRegisterTxDataFunc(IPPROTO_TCP, ALPROTO_ENIP, ENIPGetTxData);
|
|
AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_ENIP, ENIPGetTxCnt);
|
|
AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_ENIP, ENIPStateTransactionFree);
|
|
|
|
AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_ENIP, ENIPGetAlstateProgress);
|
|
AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_ENIP, ENIPGetAlstateProgressCompletionStatus);
|
|
|
|
AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_ENIP, ENIPStateGetEventInfo);
|
|
|
|
AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP,
|
|
ALPROTO_ENIP, STREAM_TOSERVER | STREAM_TOCLIENT);
|
|
|
|
/* This parser accepts gaps. */
|
|
AppLayerParserRegisterOptionFlags(IPPROTO_TCP, ALPROTO_ENIP,
|
|
APP_LAYER_PARSER_OPT_ACCEPT_GAPS);
|
|
|
|
} else
|
|
{
|
|
SCLogConfig("Parser disabled for %s protocol. Protocol detection still on.",
|
|
proto_name);
|
|
}
|
|
|
|
#ifdef UNITTESTS
|
|
AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_ENIP, ENIPParserRegisterTests);
|
|
#endif
|
|
|
|
SCReturn;
|
|
}
|
|
|
|
/* UNITTESTS */
|
|
#ifdef UNITTESTS
|
|
#include "app-layer-parser.h"
|
|
#include "detect-parse.h"
|
|
#include "detect-engine.h"
|
|
#include "flow-util.h"
|
|
#include "stream-tcp.h"
|
|
#include "util-unittest.h"
|
|
#include "util-unittest-helper.h"
|
|
|
|
static uint8_t listIdentity[] = {/* List ID */ 0x63, 0x00,
|
|
/* Length */ 0x00, 0x00,
|
|
/* Session */ 0x00, 0x00, 0x00, 0x00,
|
|
/* Status */ 0x00, 0x00, 0x00, 0x00,
|
|
/* Delay*/ 0x00,
|
|
/* Context */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
/* Quantity of coils */ 0x00, 0x00, 0x00, 0x00, 0x00};
|
|
|
|
/**
|
|
* \brief Test if ENIP Packet matches signature
|
|
*/
|
|
static int ALDecodeENIPTest(void)
|
|
{
|
|
AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
|
|
Flow f;
|
|
TcpSession ssn;
|
|
|
|
memset(&f, 0, sizeof(f));
|
|
memset(&ssn, 0, sizeof(ssn));
|
|
|
|
f.protoctx = (void *)&ssn;
|
|
f.proto = IPPROTO_TCP;
|
|
f.alproto = ALPROTO_ENIP;
|
|
|
|
StreamTcpInitConfig(TRUE);
|
|
|
|
int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_ENIP, STREAM_TOSERVER,
|
|
listIdentity, sizeof(listIdentity));
|
|
FAIL_IF(r != 0);
|
|
|
|
ENIPState *enip_state = f.alstate;
|
|
FAIL_IF_NULL(enip_state);
|
|
|
|
ENIPTransaction *tx = ENIPGetTx(enip_state, 0);
|
|
FAIL_IF_NULL(tx);
|
|
|
|
FAIL_IF(tx->header.command != 99);
|
|
|
|
AppLayerParserThreadCtxFree(alp_tctx);
|
|
StreamTcpFreeConfig(TRUE);
|
|
FLOW_DESTROY(&f);
|
|
|
|
PASS;
|
|
}
|
|
|
|
#endif /* UNITTESTS */
|
|
|
|
void ENIPParserRegisterTests(void)
|
|
{
|
|
#ifdef UNITTESTS
|
|
UtRegisterTest("ALDecodeENIPTest", ALDecodeENIPTest);
|
|
#endif /* UNITTESTS */
|
|
}
|