mirror of https://github.com/OISF/suricata
app-layer: template for application layer parser
parent
bcfb3fd5d5
commit
c1b921265f
@ -0,0 +1,533 @@
|
||||
/* 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 Template application layer detector and parser for learning and
|
||||
* template pruposes.
|
||||
*
|
||||
* This template implements a simple application layer for something
|
||||
* like the echo protocol running on port 7.
|
||||
*/
|
||||
|
||||
#include "suricata-common.h"
|
||||
#include "stream.h"
|
||||
|
||||
#include "util-unittest.h"
|
||||
|
||||
#include "app-layer-detect-proto.h"
|
||||
#include "app-layer-parser.h"
|
||||
|
||||
#include "app-layer-template.h"
|
||||
|
||||
/* The default port to probe for echo traffic if not provided in the
|
||||
* configuration file. */
|
||||
#define TEMPLATE_DEFAULT_PORT "7"
|
||||
|
||||
/* The minimum size for an echo message. For some protocols this might
|
||||
* be the size of a header. */
|
||||
#define TEMPLATE_MIN_FRAME_LEN 1
|
||||
|
||||
/* Enum of app-layer events for an echo protocol. Normally you might
|
||||
* have events for errors in parsing data, like unexpected data being
|
||||
* received. For echo we'll make something up, and log an app-layer
|
||||
* level alert if an empty message is received.
|
||||
*
|
||||
* Example rule:
|
||||
*
|
||||
* alert template any any -> any any (msg:"SURCATA Template empty message"; \
|
||||
* app-layer-event:template.empty_message; sid:X; rev:Y;)
|
||||
*/
|
||||
enum {
|
||||
TEMPLATE_DECODER_EVENT_EMPTY_MESSAGE,
|
||||
};
|
||||
|
||||
SCEnumCharMap template_decoder_event_table[] = {
|
||||
{"EMPTY_MESSAGE", TEMPLATE_DECODER_EVENT_EMPTY_MESSAGE},
|
||||
};
|
||||
|
||||
static TemplateTransaction *TemplateTxAlloc(TemplateState *echo)
|
||||
{
|
||||
TemplateTransaction *tx = SCCalloc(1, sizeof(TemplateTransaction));
|
||||
if (unlikely(tx == NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Increment the transaction ID on the state each time one is
|
||||
* allocated. */
|
||||
tx->tx_id = echo->transaction_max++;
|
||||
|
||||
TAILQ_INSERT_TAIL(&echo->tx_list, tx, next);
|
||||
|
||||
return tx;
|
||||
}
|
||||
|
||||
static void TemplateTxFree(void *tx)
|
||||
{
|
||||
TemplateTransaction *templatetx = tx;
|
||||
|
||||
if (templatetx->request_buffer != NULL) {
|
||||
SCFree(templatetx->request_buffer);
|
||||
}
|
||||
|
||||
if (templatetx->response_buffer != NULL) {
|
||||
SCFree(templatetx->response_buffer);
|
||||
}
|
||||
|
||||
AppLayerDecoderEventsFreeEvents(&templatetx->decoder_events);
|
||||
|
||||
SCFree(tx);
|
||||
}
|
||||
|
||||
static void *TemplateStateAlloc(void)
|
||||
{
|
||||
SCLogNotice("Allocating template state.");
|
||||
TemplateState *state = SCCalloc(1, sizeof(TemplateState));
|
||||
if (unlikely(state == NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
TAILQ_INIT(&state->tx_list);
|
||||
return state;
|
||||
}
|
||||
|
||||
static void TemplateStateFree(void *state)
|
||||
{
|
||||
TemplateState *template_state = state;
|
||||
TemplateTransaction *tx;
|
||||
SCLogNotice("Freeing template state.");
|
||||
while ((tx = TAILQ_FIRST(&template_state->tx_list)) != NULL) {
|
||||
TAILQ_REMOVE(&template_state->tx_list, tx, next);
|
||||
TemplateTxFree(tx);
|
||||
}
|
||||
SCFree(template_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Callback from the application layer to have a transaction freed.
|
||||
*
|
||||
* \param state a void pointer to the TemplateState object.
|
||||
* \param tx_id the transaction ID to free.
|
||||
*/
|
||||
static void TemplateStateTxFree(void *state, uint64_t tx_id)
|
||||
{
|
||||
TemplateState *echo = state;
|
||||
TemplateTransaction *tx = NULL, *ttx;
|
||||
|
||||
SCLogNotice("Freeing transaction %"PRIu64, tx_id);
|
||||
|
||||
TAILQ_FOREACH_SAFE(tx, &echo->tx_list, next, ttx) {
|
||||
|
||||
/* Continue if this is not the transaction we are looking
|
||||
* for. */
|
||||
if (tx->tx_id != tx_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Remove and free the transaction. */
|
||||
TAILQ_REMOVE(&echo->tx_list, tx, next);
|
||||
TemplateTxFree(tx);
|
||||
return;
|
||||
}
|
||||
|
||||
SCLogNotice("Transaction %"PRIu64" not found.", tx_id);
|
||||
}
|
||||
|
||||
static int TemplateStateGetEventInfo(const char *event_name, int *event_id,
|
||||
AppLayerEventType *event_type)
|
||||
{
|
||||
*event_id = SCMapEnumNameToValue(event_name, template_decoder_event_table);
|
||||
if (*event_id == -1) {
|
||||
SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%s\" not present in "
|
||||
"template enum map table.", event_name);
|
||||
/* This should be treated as fatal. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
*event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static AppLayerDecoderEvents *TemplateGetEvents(void *state, uint64_t tx_id)
|
||||
{
|
||||
TemplateState *template_state = state;
|
||||
TemplateTransaction *tx;
|
||||
|
||||
TAILQ_FOREACH(tx, &template_state->tx_list, next) {
|
||||
if (tx->tx_id == tx_id) {
|
||||
return tx->decoder_events;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int TemplateHasEvents(void *state)
|
||||
{
|
||||
TemplateState *echo = state;
|
||||
return echo->events;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Probe the input to see if it looks like echo.
|
||||
*
|
||||
* \retval ALPROTO_TEMPLATE if it looks like echo, otherwise
|
||||
* ALPROTO_UNKNOWN.
|
||||
*/
|
||||
static AppProto TemplateProbingParser(uint8_t *input, uint32_t input_len,
|
||||
uint32_t *offset)
|
||||
{
|
||||
/* Very simple test - if there is input, this is echo. */
|
||||
if (input_len >= TEMPLATE_MIN_FRAME_LEN) {
|
||||
SCLogNotice("Detected as ALPROTO_TEMPLATE.");
|
||||
return ALPROTO_TEMPLATE;
|
||||
}
|
||||
|
||||
SCLogNotice("Protocol not detected as ALPROTO_TEMPLATE.");
|
||||
return ALPROTO_UNKNOWN;
|
||||
}
|
||||
|
||||
static int TemplateParseRequest(Flow *f, void *state,
|
||||
AppLayerParserState *pstate, uint8_t *input, uint32_t input_len,
|
||||
void *local_data)
|
||||
{
|
||||
TemplateState *echo = state;
|
||||
|
||||
SCLogNotice("Parsing echo request: len=%"PRIu32, input_len);
|
||||
|
||||
/* Likely connection closed, we can just return here. */
|
||||
if ((input == NULL || input_len == 0) &&
|
||||
AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Probably don't want to create a transaction in this case
|
||||
* either. */
|
||||
if (input == NULL || input_len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Normally you would parse out data here and store it in the
|
||||
* transaction object, but as this is echo, we'll just record the
|
||||
* request data. */
|
||||
|
||||
/* Also, if this protocol may have a "protocol data unit" span
|
||||
* multiple chunks of data, which is always a possibility with
|
||||
* TCP, you may need to do some buffering here.
|
||||
*
|
||||
* For the sake of simplicity, buffering is left out here, but
|
||||
* even for an echo protocol we may want to buffer until a new
|
||||
* line is seen, assuming its text based.
|
||||
*/
|
||||
|
||||
/* Allocate a transaction.
|
||||
*
|
||||
* But note that if a "protocol data unit" is not received in one
|
||||
* chunk of data, and the buffering is done on the transaction, we
|
||||
* may need to look for the transaction that this newly recieved
|
||||
* data belongs to.
|
||||
*/
|
||||
TemplateTransaction *tx = TemplateTxAlloc(echo);
|
||||
if (unlikely(tx == NULL)) {
|
||||
SCLogNotice("Failed to allocate new Template tx.");
|
||||
goto end;
|
||||
}
|
||||
SCLogNotice("Allocated Template tx %"PRIu64".", tx->tx_id);
|
||||
|
||||
/* Make a copy of the request. */
|
||||
tx->request_buffer = SCCalloc(1, input_len);
|
||||
if (unlikely(tx->request_buffer == NULL)) {
|
||||
goto end;
|
||||
}
|
||||
memcpy(tx->request_buffer, input, input_len);
|
||||
tx->request_buffer_len = input_len;
|
||||
|
||||
/* Here we check for an empty message and create an app-layer
|
||||
* event. */
|
||||
if ((input_len == 1 && tx->request_buffer[0] == '\n') ||
|
||||
(input_len == 2 && tx->request_buffer[0] == '\r')) {
|
||||
SCLogNotice("Creating event for empty message.");
|
||||
AppLayerDecoderEventsSetEventRaw(&tx->decoder_events,
|
||||
TEMPLATE_DECODER_EVENT_EMPTY_MESSAGE);
|
||||
echo->events++;
|
||||
}
|
||||
|
||||
end:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int TemplateParseResponse(Flow *f, void *state, AppLayerParserState *pstate,
|
||||
uint8_t *input, uint32_t input_len, void *local_data)
|
||||
{
|
||||
TemplateState *echo = state;
|
||||
TemplateTransaction *tx = NULL, *ttx;;
|
||||
|
||||
SCLogNotice("Parsing Template response.");
|
||||
|
||||
/* Likely connection closed, we can just return here. */
|
||||
if ((input == NULL || input_len == 0) &&
|
||||
AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Probably don't want to create a transaction in this case
|
||||
* either. */
|
||||
if (input == NULL || input_len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Look up the existing transaction for this response. In the case
|
||||
* of echo, it will be the most recent transaction on the
|
||||
* TemplateState object. */
|
||||
|
||||
/* We should just grab the last transaction, but this is to
|
||||
* illustrate how you might traverse the transaction list to find
|
||||
* the transaction associated with this response. */
|
||||
TAILQ_FOREACH(ttx, &echo->tx_list, next) {
|
||||
tx = ttx;
|
||||
}
|
||||
|
||||
if (tx == NULL) {
|
||||
SCLogNotice("Failed to find transaction for response on echo state %p.",
|
||||
echo);
|
||||
goto end;
|
||||
}
|
||||
|
||||
SCLogNotice("Found transaction %"PRIu64" for response on echo state %p.",
|
||||
tx->tx_id, echo);
|
||||
|
||||
/* If the protocol requires multiple chunks of data to complete, you may
|
||||
* run into the case where you have existing response data.
|
||||
*
|
||||
* In this case, we just log that there is existing data and free it. But
|
||||
* you might want to realloc the buffer and append the data.
|
||||
*/
|
||||
if (tx->response_buffer != NULL) {
|
||||
SCLogNotice("WARNING: Transaction already has response data, "
|
||||
"existing data will be overwritten.");
|
||||
SCFree(tx->response_buffer);
|
||||
}
|
||||
|
||||
/* Make a copy of the response. */
|
||||
tx->response_buffer = SCCalloc(1, input_len);
|
||||
if (unlikely(tx->response_buffer == NULL)) {
|
||||
goto end;
|
||||
}
|
||||
memcpy(tx->response_buffer, input, input_len);
|
||||
tx->response_buffer_len = input_len;
|
||||
|
||||
/* Set the response_done flag for transaction state checking in
|
||||
* TemplateGetStateProgress(). */
|
||||
tx->response_done = 1;
|
||||
|
||||
end:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint64_t TemplateGetTxCnt(void *state)
|
||||
{
|
||||
TemplateState *echo = state;
|
||||
SCLogNotice("Current tx count is %"PRIu64".", echo->transaction_max);
|
||||
return echo->transaction_max;
|
||||
}
|
||||
|
||||
static void *TemplateGetTx(void *state, uint64_t tx_id)
|
||||
{
|
||||
TemplateState *echo = state;
|
||||
TemplateTransaction *tx;
|
||||
|
||||
SCLogNotice("Requested tx ID %"PRIu64".", tx_id);
|
||||
|
||||
TAILQ_FOREACH(tx, &echo->tx_list, next) {
|
||||
if (tx->tx_id == tx_id) {
|
||||
SCLogNotice("Transaction %"PRIu64" found, returning tx object %p.",
|
||||
tx_id, tx);
|
||||
return tx;
|
||||
}
|
||||
}
|
||||
|
||||
SCLogNotice("Transaction ID %"PRIu64" not found.", tx_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Called by the application layer.
|
||||
*
|
||||
* In most cases 1 can be returned here.
|
||||
*/
|
||||
static int TemplateGetAlstateProgressCompletionStatus(uint8_t direction) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the state of a transaction in a given direction.
|
||||
*
|
||||
* In the case of the echo protocol, the existence of a transaction
|
||||
* means that the request is done. However, some protocols that may
|
||||
* need multiple chunks of data to complete the request may need more
|
||||
* than just the existence of a transaction for the request to be
|
||||
* considered complete.
|
||||
*
|
||||
* For the response to be considered done, the response for a request
|
||||
* needs to be seen. The response_done flag is set on response for
|
||||
* checking here.
|
||||
*/
|
||||
static int TemplateGetStateProgress(void *tx, uint8_t direction)
|
||||
{
|
||||
TemplateTransaction *echotx = tx;
|
||||
|
||||
SCLogNotice("Transaction progress requested for tx ID %"PRIu64
|
||||
", direction=0x%02x", echotx->tx_id, direction);
|
||||
|
||||
if (direction & STREAM_TOCLIENT && echotx->response_done) {
|
||||
return 1;
|
||||
}
|
||||
else if (direction & STREAM_TOSERVER) {
|
||||
/* For echo, just the existence of the transaction means the
|
||||
* request is done. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief ???
|
||||
*/
|
||||
static DetectEngineState *TemplateGetTxDetectState(void *vtx)
|
||||
{
|
||||
TemplateTransaction *tx = vtx;
|
||||
return tx->de_state;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief ???
|
||||
*/
|
||||
static int TemplateSetTxDetectState(void *state, void *vtx,
|
||||
DetectEngineState *s)
|
||||
{
|
||||
TemplateTransaction *tx = vtx;
|
||||
tx->de_state = s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RegisterTemplateParsers(void)
|
||||
{
|
||||
char *proto_name = "template";
|
||||
|
||||
/* Check if Template TCP detection is enabled. If it does not exist in
|
||||
* the configuration file then it will be enabled by default. */
|
||||
if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
|
||||
|
||||
SCLogNotice("Template TCP protocol detection enabled.");
|
||||
|
||||
AppLayerProtoDetectRegisterProtocol(ALPROTO_TEMPLATE, proto_name);
|
||||
|
||||
if (RunmodeIsUnittests()) {
|
||||
|
||||
SCLogNotice("Unittest mode, registeringd default configuration.");
|
||||
AppLayerProtoDetectPPRegister(IPPROTO_TCP, TEMPLATE_DEFAULT_PORT,
|
||||
ALPROTO_TEMPLATE, 0, TEMPLATE_MIN_FRAME_LEN, STREAM_TOSERVER,
|
||||
TemplateProbingParser);
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
if (!AppLayerProtoDetectPPParseConfPorts("tcp", IPPROTO_TCP,
|
||||
proto_name, ALPROTO_TEMPLATE, 0, TEMPLATE_MIN_FRAME_LEN,
|
||||
TemplateProbingParser)) {
|
||||
SCLogNotice("No echo app-layer configuration, enabling echo"
|
||||
" detection TCP detection on port %s.",
|
||||
TEMPLATE_DEFAULT_PORT);
|
||||
AppLayerProtoDetectPPRegister(IPPROTO_TCP,
|
||||
TEMPLATE_DEFAULT_PORT, ALPROTO_TEMPLATE, 0,
|
||||
TEMPLATE_MIN_FRAME_LEN, STREAM_TOSERVER,
|
||||
TemplateProbingParser);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else {
|
||||
SCLogNotice("Protocol detecter and parser disabled for Template.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (AppLayerParserConfParserEnabled("udp", proto_name)) {
|
||||
|
||||
SCLogNotice("Registering Template protocol parser.");
|
||||
|
||||
/* Register functions for state allocation and freeing. A
|
||||
* state is allocated for every new Template flow. */
|
||||
AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_TEMPLATE,
|
||||
TemplateStateAlloc, TemplateStateFree);
|
||||
|
||||
/* Register request parser for parsing frame from server to client. */
|
||||
AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_TEMPLATE,
|
||||
STREAM_TOSERVER, TemplateParseRequest);
|
||||
|
||||
/* Register response parser for parsing frames from server to client. */
|
||||
AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_TEMPLATE, STREAM_TOCLIENT,
|
||||
TemplateParseResponse);
|
||||
|
||||
/* Register a function to be called by the application layer
|
||||
* when a transaction is to be freed. */
|
||||
AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_TEMPLATE,
|
||||
TemplateStateTxFree);
|
||||
|
||||
/* Register a function to return the current transaction count. */
|
||||
AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_TEMPLATE, TemplateGetTxCnt);
|
||||
|
||||
/* Transaction handling. */
|
||||
AppLayerParserRegisterGetStateProgressCompletionStatus(IPPROTO_TCP,
|
||||
ALPROTO_TEMPLATE, TemplateGetAlstateProgressCompletionStatus);
|
||||
AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP,
|
||||
ALPROTO_TEMPLATE, TemplateGetStateProgress);
|
||||
AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_TEMPLATE,
|
||||
TemplateGetTx);
|
||||
|
||||
/* Application layer event handling. */
|
||||
AppLayerParserRegisterHasEventsFunc(IPPROTO_TCP, ALPROTO_TEMPLATE,
|
||||
TemplateHasEvents);
|
||||
|
||||
/* What is this being registered for? */
|
||||
AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP, ALPROTO_TEMPLATE,
|
||||
NULL, TemplateGetTxDetectState, TemplateSetTxDetectState);
|
||||
|
||||
AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_TEMPLATE,
|
||||
TemplateStateGetEventInfo);
|
||||
AppLayerParserRegisterGetEventsFunc(IPPROTO_TCP, ALPROTO_TEMPLATE,
|
||||
TemplateGetEvents);
|
||||
}
|
||||
else {
|
||||
SCLogNotice("Template protocol parsing disabled.");
|
||||
}
|
||||
|
||||
#ifdef UNITTESTS
|
||||
AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_TEMPLATE,
|
||||
TemplateParserRegisterTests);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef UNITTESTS
|
||||
#endif
|
||||
|
||||
void TemplateParserRegisterTests(void)
|
||||
{
|
||||
#ifdef UNITTESTS
|
||||
#endif
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#ifndef __APP_LAYER_TEMPLATE_H__
|
||||
#define __APP_LAYER_TEMPLATE_H__
|
||||
|
||||
#include "detect-engine-state.h"
|
||||
|
||||
#include "queue.h"
|
||||
|
||||
void RegisterTemplateParsers(void);
|
||||
void TemplateParserRegisterTests(void);
|
||||
|
||||
typedef struct TemplateTransaction_ {
|
||||
|
||||
uint64_t tx_id; /*<< Internal transaction ID. */
|
||||
|
||||
AppLayerDecoderEvents *decoder_events; /*<< Application layer
|
||||
* events that occurred
|
||||
* while parsing this
|
||||
* transaction. */
|
||||
|
||||
uint8_t *request_buffer;
|
||||
uint32_t request_buffer_len;
|
||||
|
||||
uint8_t *response_buffer;
|
||||
uint32_t response_buffer_len;
|
||||
|
||||
uint8_t response_done; /*<< Flag to be set when the response is
|
||||
* seen. */
|
||||
|
||||
DetectEngineState *de_state;
|
||||
|
||||
TAILQ_ENTRY(TemplateTransaction_) next;
|
||||
|
||||
} TemplateTransaction;
|
||||
|
||||
typedef struct TemplateState_ {
|
||||
|
||||
TAILQ_HEAD(, TemplateTransaction_) tx_list; /**< List of Template transactions
|
||||
* associated with this
|
||||
* state. */
|
||||
|
||||
uint64_t transaction_max; /**< A count of the number of
|
||||
* transactions created. The
|
||||
* transaction ID for each transaction
|
||||
* is allocted by incrementing this
|
||||
* value. */
|
||||
|
||||
uint16_t events; /**< Number of application layer events created
|
||||
* for this state. */
|
||||
|
||||
} TemplateState;
|
||||
|
||||
#endif /* __APP_LAYER_TEMPLATE_H__ */
|
||||
Loading…
Reference in New Issue