]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
app-layer: template for application layer parser
authorJason Ish <ish@unx.ca>
Tue, 1 Sep 2015 18:04:34 +0000 (12:04 -0600)
committerJason Ish <ish@unx.ca>
Fri, 11 Sep 2015 05:55:02 +0000 (23:55 -0600)
src/Makefile.am
src/app-layer-detect-proto.c
src/app-layer-parser.c
src/app-layer-protos.c
src/app-layer-protos.h
src/app-layer-template.c [new file with mode: 0644]
src/app-layer-template.h [new file with mode: 0644]

index de99d312f929fc57ea7e054cfad64e30c9b1f82b..97a3a993633fb0d0dc5a9d0fa1e902cea627da42 100644 (file)
@@ -33,6 +33,7 @@ app-layer-protos.c app-layer-protos.h \
 app-layer-smb2.c app-layer-smb2.h \
 app-layer-smb.c app-layer-smb.h \
 app-layer-smtp.c app-layer-smtp.h \
+app-layer-template.c app-layer-template.h \
 app-layer-ssh.c app-layer-ssh.h \
 app-layer-ssl.c app-layer-ssl.h \
 app-layer-tls-handshake.c app-layer-tls-handshake.h \
index ed029e5e6b8b2711f6c348a1a9d9a035a64d3fe2..221b50ffc801e5eac40da4a20d336bdf2b4a6cbf 100644 (file)
@@ -687,6 +687,8 @@ void AppLayerProtoDetectPrintProbingParsers(AppLayerProtoDetectProbingParser *pp
                         printf("            alproto: ALPROTO_DNS\n");
                     else if (pp_pe->alproto == ALPROTO_MODBUS)
                         printf("            alproto: ALPROTO_MODBUS\n");
+                    else if (pp_pe->alproto == ALPROTO_TEMPLATE)
+                        printf("            alproto: ALPROTO_TEMPLATE\n");
                     else
                         printf("impossible\n");
 
@@ -738,6 +740,8 @@ void AppLayerProtoDetectPrintProbingParsers(AppLayerProtoDetectProbingParser *pp
                     printf("            alproto: ALPROTO_DNS\n");
                 else if (pp_pe->alproto == ALPROTO_MODBUS)
                     printf("            alproto: ALPROTO_MODBUS\n");
+                else if (pp_pe->alproto == ALPROTO_TEMPLATE)
+                    printf("            alproto: ALPROTO_TEMPLATE\n");
                 else
                     printf("impossible\n");
 
index 134f99095c1ce1a65a1783570b2359c047a62ddf..2650863e2082a4b2ce73015e5847bf8b2a8aba0c 100644 (file)
@@ -58,6 +58,7 @@
 #include "app-layer-dns-udp.h"
 #include "app-layer-dns-tcp.h"
 #include "app-layer-modbus.h"
+#include "app-layer-template.h"
 
 #include "conf.h"
 #include "util-spm.h"
@@ -1106,6 +1107,7 @@ void AppLayerParserRegisterProtocolParsers(void)
     RegisterDNSUDPParsers();
     RegisterDNSTCPParsers();
     RegisterModbusParsers();
+    RegisterTemplateParsers();
 
     /** IMAP */
     AppLayerProtoDetectRegisterProtocol(ALPROTO_IMAP, "imap");
index 0b8ed17bee7d4c518ddbb8ac4959f9d68580f5b3..e8875643f8793ae4b28a9bc6dd2df382314f4faa 100644 (file)
@@ -75,6 +75,9 @@ const char *AppProtoToString(AppProto alproto)
         case ALPROTO_MODBUS:
             proto_name = "modbus";
             break;
+        case ALPROTO_TEMPLATE:
+            proto_name = "template";
+            break;
         case ALPROTO_FAILED:
 #ifdef UNITTESTS
         case ALPROTO_TEST:
index 79973661efc91f707751675b6bad7cabd0cd3342..aff90e9bbaf74e45c245d26d01e8d1b35a75e511 100644 (file)
@@ -42,6 +42,7 @@ enum AppProtoEnum {
 
     ALPROTO_DNS,
     ALPROTO_MODBUS,
+    ALPROTO_TEMPLATE,
 
     /* used by the probing parser when alproto detection fails
      * permanently for that particular stream */
diff --git a/src/app-layer-template.c b/src/app-layer-template.c
new file mode 100644 (file)
index 0000000..87b64b7
--- /dev/null
@@ -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
+}
diff --git a/src/app-layer-template.h b/src/app-layer-template.h
new file mode 100644 (file)
index 0000000..4e58fa8
--- /dev/null
@@ -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__ */