]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
dnp3: fixups to work with unified json tx logger 8101/head
authorJason Ish <jason.ish@oisf.net>
Thu, 13 Oct 2022 21:51:10 +0000 (15:51 -0600)
committerJason Ish <jason.ish@oisf.net>
Thu, 27 Oct 2022 15:54:30 +0000 (09:54 -0600)
Update DNP3 to work with a single TX logger, and just register one
logger instead of 2.

This primarily creates a TX per message instead of correlating replies
to requests, which fits the DNP3 model better, but we didn't really have
this concept nailed down when DNP3 was written.

src/app-layer-dnp3.c
src/app-layer-dnp3.h
src/detect-dnp3.c
src/output-json-alert.c
src/output-json-dnp3.c
src/suricata-common.h
src/util-lua-dnp3.c
src/util-profiling.c

index b985e57616bf66fd2be730acb0fe212ac7cc7dfe..06b4c649608546fa8fa1128c06dc713b753f20ba 100644 (file)
@@ -490,7 +490,7 @@ static void DNP3SetEventTx(DNP3Transaction *tx, uint8_t event)
 /**
  * \brief Allocation a DNP3 transaction.
  */
-static DNP3Transaction *DNP3TxAlloc(DNP3State *dnp3)
+static DNP3Transaction *DNP3TxAlloc(DNP3State *dnp3, bool request)
 {
     DNP3Transaction *tx = SCCalloc(1, sizeof(DNP3Transaction));
     if (unlikely(tx == NULL)) {
@@ -501,8 +501,8 @@ static DNP3Transaction *DNP3TxAlloc(DNP3State *dnp3)
     dnp3->curr = tx;
     tx->dnp3 = dnp3;
     tx->tx_num = dnp3->transaction_max;
-    TAILQ_INIT(&tx->request_objects);
-    TAILQ_INIT(&tx->response_objects);
+    tx->is_request = request;
+    TAILQ_INIT(&tx->objects);
     TAILQ_INSERT_TAIL(&dnp3->tx_list, tx, next);
 
     /* Check for flood state. */
@@ -872,12 +872,8 @@ static void DNP3HandleUserDataRequest(DNP3State *dnp3, const uint8_t *input,
 
     if (!DNP3_TH_FIR(th)) {
         TAILQ_FOREACH(ttx, &dnp3->tx_list, next) {
-            if (ttx->request_lh.src == lh->src &&
-                ttx->request_lh.dst == lh->dst &&
-                ttx->has_request &&
-                !ttx->request_done &&
-                NEXT_TH_SEQNO(DNP3_TH_SEQ(ttx->request_th)) == DNP3_TH_SEQ(th))
-            {
+            if (ttx->lh.src == lh->src && ttx->lh.dst == lh->dst && ttx->is_request && !ttx->done &&
+                    NEXT_TH_SEQNO(DNP3_TH_SEQ(ttx->th)) == DNP3_TH_SEQ(th)) {
                 tx = ttx;
                 break;
             }
@@ -889,7 +885,7 @@ static void DNP3HandleUserDataRequest(DNP3State *dnp3, const uint8_t *input,
 
         /* Update the saved transport header so subsequent segments
          * will be matched to this sequence number. */
-        tx->response_th = th;
+        tx->th = th;
     }
     else {
         ah = (DNP3ApplicationHeader *)(input + sizeof(DNP3LinkHeader) +
@@ -901,24 +897,21 @@ static void DNP3HandleUserDataRequest(DNP3State *dnp3, const uint8_t *input,
         }
 
         /* Create a transaction. */
-        tx = DNP3TxAlloc(dnp3);
+        tx = DNP3TxAlloc(dnp3, true);
         if (unlikely(tx == NULL)) {
             return;
         }
-        tx->request_lh = *lh;
-        tx->request_th = th;
-        tx->request_ah = *ah;
-        tx->has_request = 1;
-
+        tx->lh = *lh;
+        tx->th = th;
+        tx->ah = *ah;
     }
 
     if (!DNP3ReassembleApplicationLayer(input + sizeof(DNP3LinkHeader),
-            input_len - sizeof(DNP3LinkHeader),
-            &tx->request_buffer, &tx->request_buffer_len)) {
+                input_len - sizeof(DNP3LinkHeader), &tx->buffer, &tx->buffer_len)) {
 
         /* Malformed, set event and mark as done. */
         DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_MALFORMED);
-        tx->request_done = 1;
+        tx->done = 1;
         return;
     }
 
@@ -927,26 +920,11 @@ static void DNP3HandleUserDataRequest(DNP3State *dnp3, const uint8_t *input,
         return;
     }
 
-    tx->request_done = 1;
-
-    /* Some function codes do not expect a reply. */
-    switch (tx->request_ah.function_code) {
-        case DNP3_APP_FC_CONFIRM:
-        case DNP3_APP_FC_DIR_OPERATE_NR:
-        case DNP3_APP_FC_FREEZE_NR:
-        case DNP3_APP_FC_FREEZE_CLEAR_NR:
-        case DNP3_APP_FC_FREEZE_AT_TIME_NR:
-        case DNP3_APP_FC_AUTH_REQ_NR:
-            tx->response_done = 1;
-        default:
-            break;
-    }
+    tx->done = 1;
 
-    if (DNP3DecodeApplicationObjects(
-            tx, tx->request_buffer + sizeof(DNP3ApplicationHeader),
-                tx->request_buffer_len - sizeof(DNP3ApplicationHeader),
-                &tx->request_objects)) {
-        tx->request_complete = 1;
+    if (DNP3DecodeApplicationObjects(tx, tx->buffer + sizeof(DNP3ApplicationHeader),
+                tx->buffer_len - sizeof(DNP3ApplicationHeader), &tx->objects)) {
+        tx->complete = 1;
     }
 }
 
@@ -971,11 +949,8 @@ static void DNP3HandleUserDataResponse(DNP3State *dnp3, const uint8_t *input,
 
     if (!DNP3_TH_FIR(th)) {
         TAILQ_FOREACH(ttx, &dnp3->tx_list, next) {
-            if (ttx->response_lh.src == lh->src &&
-                ttx->response_lh.dst == lh->dst &&
-                ttx->has_response && !ttx->response_done &&
-                NEXT_TH_SEQNO(DNP3_TH_SEQ(ttx->response_th)) == DNP3_TH_SEQ(th))
-            {
+            if (ttx->lh.src == lh->src && ttx->lh.dst == lh->dst && !ttx->is_request &&
+                    !ttx->done && NEXT_TH_SEQNO(DNP3_TH_SEQ(ttx->th)) == DNP3_TH_SEQ(th)) {
                 tx = ttx;
                 break;
             }
@@ -987,53 +962,27 @@ static void DNP3HandleUserDataResponse(DNP3State *dnp3, const uint8_t *input,
 
         /* Replace the transport header in the transaction with this
          * one in case there are more frames. */
-        tx->response_th = th;
+        tx->th = th;
     }
     else {
         ah = (DNP3ApplicationHeader *)(input + offset);
         offset += sizeof(DNP3ApplicationHeader);
         iin = (DNP3InternalInd *)(input + offset);
 
-        if (ah->function_code == DNP3_APP_FC_UNSOLICITED_RESP) {
-            tx = DNP3TxAlloc(dnp3);
-            if (unlikely(tx == NULL)) {
-                return;
-            }
-
-            /* There is no request associated with an unsolicited
-             * response, so mark the request done as far as
-             * transaction state handling is concerned. */
-            tx->request_done = 1;
-        }
-        else {
-            /* Find transaction. */
-            TAILQ_FOREACH(ttx, &dnp3->tx_list, next) {
-                if (ttx->has_request &&
-                    ttx->request_done &&
-                    ttx->request_lh.src == lh->dst &&
-                    ttx->request_lh.dst == lh->src &&
-                    !ttx->has_response &&
-                    !ttx->response_done &&
-                    DNP3_APP_SEQ(ttx->request_ah.control) == DNP3_APP_SEQ(ah->control)) {
-                    tx = ttx;
-                    break;
-                }
-            }
-            if (tx == NULL) {
-                return;
-            }
+        tx = DNP3TxAlloc(dnp3, false);
+        if (unlikely(tx == NULL)) {
+            return;
         }
-
-        tx->has_response = 1;
-        tx->response_lh = *lh;
-        tx->response_th = th;
-        tx->response_ah = *ah;
-        tx->response_iin = *iin;
+        tx->lh = *lh;
+        tx->th = th;
+        tx->ah = *ah;
+        tx->iin = *iin;
     }
 
+    BUG_ON(tx->is_request);
+
     if (!DNP3ReassembleApplicationLayer(input + sizeof(DNP3LinkHeader),
-            input_len - sizeof(DNP3LinkHeader),
-            &tx->response_buffer, &tx->response_buffer_len)) {
+                input_len - sizeof(DNP3LinkHeader), &tx->buffer, &tx->buffer_len)) {
         DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_MALFORMED);
         return;
     }
@@ -1042,13 +991,12 @@ static void DNP3HandleUserDataResponse(DNP3State *dnp3, const uint8_t *input,
         return;
     }
 
-    tx->response_done = 1;
+    tx->done = 1;
 
     offset = sizeof(DNP3ApplicationHeader) + sizeof(DNP3InternalInd);
-    if (DNP3DecodeApplicationObjects(tx, tx->response_buffer + offset,
-            tx->response_buffer_len - offset,
-            &tx->response_objects)) {
-        tx->response_complete = 1;
+    if (DNP3DecodeApplicationObjects(
+                tx, tx->buffer + offset, tx->buffer_len - offset, &tx->objects)) {
+        tx->complete = 1;
     }
 }
 
@@ -1385,12 +1333,8 @@ static void DNP3TxFree(DNP3Transaction *tx)
 {
     SCEnter();
 
-    if (tx->request_buffer != NULL) {
-        SCFree(tx->request_buffer);
-    }
-
-    if (tx->response_buffer != NULL) {
-        SCFree(tx->response_buffer);
+    if (tx->buffer != NULL) {
+        SCFree(tx->buffer);
     }
 
     AppLayerDecoderEventsFreeEvents(&tx->tx_data.events);
@@ -1399,8 +1343,7 @@ static void DNP3TxFree(DNP3Transaction *tx)
         DetectEngineStateFree(tx->tx_data.de_state);
     }
 
-    DNP3TxFreeObjectList(&tx->request_objects);
-    DNP3TxFreeObjectList(&tx->response_objects);
+    DNP3TxFreeObjectList(&tx->objects);
 
     SCFree(tx);
     SCReturn;
@@ -1491,11 +1434,30 @@ static int DNP3GetAlstateProgress(void *tx, uint8_t direction)
         SCReturnInt(1);
     }
 
-    if (direction & STREAM_TOCLIENT && dnp3tx->response_done) {
-        retval = 1;
-    }
-    else if (direction & STREAM_TOSERVER && dnp3tx->request_done) {
-        retval = 1;
+    /* This is a unidirectional protocol.
+     *
+     * If progress is being checked in the TOSERVER (request)
+     * direction, always return complete if the message is not a
+     * request, as there will never be replies on transactions created
+     * in the TOSERVER direction.
+     *
+     * Like wise, if progress is being checked in the TOCLIENT
+     * direction, requests will never be seen. So always return
+     * complete if the transaction is not a reply.
+     *
+     * Otherwise, if TOSERVER and transaction is a request, return
+     * complete if the transaction is complete. And if TOCLIENT and
+     * transaction is a response, return complete if the transaction
+     * is complete.
+     */
+    if (direction & STREAM_TOSERVER) {
+        if (!dnp3tx->is_request || dnp3tx->complete) {
+            retval = 1;
+        }
+    } else if (direction & STREAM_TOCLIENT) {
+        if (dnp3tx->is_request || dnp3tx->complete) {
+            retval = 1;
+        }
     }
 
     SCReturnInt(retval);
@@ -1626,6 +1588,12 @@ void RegisterDNP3Parsers(void)
         AppLayerParserRegisterTxDataFunc(IPPROTO_TCP, ALPROTO_DNP3,
             DNP3GetTxData);
         AppLayerParserRegisterStateDataFunc(IPPROTO_TCP, ALPROTO_DNP3, DNP3GetStateData);
+#if 0
+        /* While this parser is now fully unidirectional. setting this
+         * flag breaks detection at this time. */
+        AppLayerParserRegisterOptionFlags(
+                IPPROTO_TCP, ALPROTO_DNP3, APP_LAYER_PARSER_OPT_UNIDIR_TXS);
+#endif
     }
     else {
         SCLogConfig("Parser disabled for protocol %s. "
@@ -2130,17 +2098,19 @@ static int DNP3ParserTestRequestResponse(void)
     FAIL_IF(tx == NULL);
     FAIL_IF(tx->tx_num != 1);
     FAIL_IF(tx != state->curr);
-    FAIL_IF(tx->request_buffer == NULL);
-    FAIL_IF(tx->request_buffer_len != 20);
-    FAIL_IF(tx->request_ah.function_code != DNP3_APP_FC_DIR_OPERATE);
+    FAIL_IF(tx->buffer == NULL);
+    FAIL_IF(tx->buffer_len != 20);
+    FAIL_IF(tx->ah.function_code != DNP3_APP_FC_DIR_OPERATE);
 
     SCMutexLock(&flow.m);
     FAIL_IF(AppLayerParserParse(NULL, alp_tctx, &flow, ALPROTO_DNP3,
             STREAM_TOCLIENT, response, sizeof(response)));
     SCMutexUnlock(&flow.m);
-    FAIL_IF(DNP3GetTx(state, 0) != tx);
-    FAIL_IF(!tx->response_done);
-    FAIL_IF(tx->response_buffer == NULL);
+    DNP3Transaction *tx0 = DNP3GetTx(state, 1);
+    FAIL_IF(tx0 == NULL);
+    FAIL_IF(tx0 == tx);
+    FAIL_IF(!tx0->done);
+    FAIL_IF(tx0->buffer == NULL);
 
     AppLayerParserThreadCtxFree(alp_tctx);
     StreamTcpFreeConfig(true);
@@ -2197,19 +2167,19 @@ static int DNP3ParserTestUnsolicitedResponseConfirm(void)
     FAIL_IF(tx == NULL);
     FAIL_IF(tx->tx_num != 1);
     FAIL_IF(tx != state->curr);
-    FAIL_IF(tx->request_buffer != NULL);
-    FAIL_IF(tx->response_buffer == NULL);
-    FAIL_IF(!tx->response_done);
-    FAIL_IF(tx->response_ah.function_code != DNP3_APP_FC_UNSOLICITED_RESP);
+    FAIL_IF(!tx->done);
+    FAIL_IF(tx->ah.function_code != DNP3_APP_FC_UNSOLICITED_RESP);
 
     SCMutexLock(&flow.m);
     FAIL_IF(AppLayerParserParse(NULL, alp_tctx, &flow, ALPROTO_DNP3,
             STREAM_TOSERVER, confirm, sizeof(confirm)));
     SCMutexUnlock(&flow.m);
-    FAIL_IF(DNP3GetTx(state, 0) != tx);
-    FAIL_IF(!tx->response_done);
-    FAIL_IF(tx->response_buffer == NULL);
-    /* FAIL_IF(tx->iin1 != 0 || tx->iin2 != 0); */
+
+    /* Confirms are ignored currently.  With the move to
+       unidirectional transactions it might be easy to support these
+       now. */
+    DNP3Transaction *resptx = DNP3GetTx(state, 1);
+    FAIL_IF(resptx);
 
     AppLayerParserThreadCtxFree(alp_tctx);
     StreamTcpFreeConfig(true);
@@ -2219,6 +2189,9 @@ static int DNP3ParserTestUnsolicitedResponseConfirm(void)
 
 /**
  * \test Test flood state.
+ *
+ * Note that flood state needs to revisited with the modification to a
+ * unidirectional protocol.
  */
 static int DNP3ParserTestFlooded(void)
 {
@@ -2263,10 +2236,11 @@ static int DNP3ParserTestFlooded(void)
     FAIL_IF(tx == NULL);
     FAIL_IF(tx->tx_num != 1);
     FAIL_IF(tx != state->curr);
-    FAIL_IF(tx->request_buffer == NULL);
-    FAIL_IF(tx->request_buffer_len != 20);
-    /* FAIL_IF(tx->app_function_code != DNP3_APP_FC_DIR_OPERATE); */
-    FAIL_IF(tx->response_done);
+    FAIL_IF(tx->buffer == NULL);
+    FAIL_IF(tx->buffer_len != 20);
+    FAIL_IF(tx->ah.function_code != DNP3_APP_FC_DIR_OPERATE);
+    FAIL_IF_NOT(tx->done);
+    FAIL_IF_NOT(DNP3GetAlstateProgress(tx, STREAM_TOSERVER));
 
     for (int i = 0; i < DNP3_DEFAULT_REQ_FLOOD_COUNT - 1; i++) {
         SCMutexLock(&flow.m);
@@ -2275,7 +2249,7 @@ static int DNP3ParserTestFlooded(void)
         SCMutexUnlock(&flow.m);
     }
     FAIL_IF(state->flooded);
-    FAIL_IF(DNP3GetAlstateProgress(tx, 0));
+    FAIL_IF_NOT(DNP3GetAlstateProgress(tx, STREAM_TOSERVER));
 
     /* One more request should trip us into flooded state. */
     SCMutexLock(&flow.m);
@@ -2287,9 +2261,6 @@ static int DNP3ParserTestFlooded(void)
     /* Progress for the oldest tx should return 1. */
     FAIL_IF(!DNP3GetAlstateProgress(tx, 0));
 
-    /* But progress for the current state should still return 0. */
-    FAIL_IF(DNP3GetAlstateProgress(state->curr, 0));
-
     AppLayerParserThreadCtxFree(alp_tctx);
     StreamTcpFreeConfig(true);
     FLOW_DESTROY(&flow);
@@ -2393,9 +2364,9 @@ static int DNP3ParserTestPartialFrame(void)
     FAIL_IF(tx == NULL);
     FAIL_IF(tx->tx_num != 1);
     FAIL_IF(tx != state->curr);
-    FAIL_IF(tx->request_buffer == NULL);
-    FAIL_IF(tx->request_buffer_len != 20);
-    FAIL_IF(tx->request_ah.function_code != DNP3_APP_FC_DIR_OPERATE);
+    FAIL_IF(tx->buffer == NULL);
+    FAIL_IF(tx->buffer_len != 20);
+    FAIL_IF(tx->ah.function_code != DNP3_APP_FC_DIR_OPERATE);
 
     /* Send partial response. */
     SCMutexLock(&flow.m);
@@ -2405,7 +2376,8 @@ static int DNP3ParserTestPartialFrame(void)
     FAIL_IF(r != 0);
     FAIL_IF(state->response_buffer.len != sizeof(response_partial1));
     FAIL_IF(state->response_buffer.offset != 0);
-    FAIL_IF(tx->response_done);
+    tx = DNP3GetTx(state, 1);
+    FAIL_IF_NOT_NULL(tx);
 
     /* Send rest of response. */
     SCMutexLock(&flow.m);
@@ -2418,10 +2390,11 @@ static int DNP3ParserTestPartialFrame(void)
     FAIL_IF(state->response_buffer.len != 0);
     FAIL_IF(state->response_buffer.offset != 0);
 
-    /* Transaction should be replied to now. */
-    FAIL_IF(!tx->response_done);
-    FAIL_IF(tx->response_buffer == NULL);
-    FAIL_IF(tx->response_buffer_len == 0);
+    /* There should now be a response transaction. */
+    tx = DNP3GetTx(state, 1);
+    FAIL_IF_NULL(tx);
+    FAIL_IF(tx->buffer == NULL);
+    FAIL_IF(tx->buffer_len == 0);
 
     AppLayerParserThreadCtxFree(alp_tctx);
     StreamTcpFreeConfig(true);
@@ -2508,9 +2481,9 @@ static int DNP3ParserTestParsePDU01(void)
     FAIL_IF(pdus < 1);
     DNP3Transaction *dnp3tx = DNP3GetTx(dnp3state, 0);
     FAIL_IF_NULL(dnp3tx);
-    FAIL_IF(!dnp3tx->has_request);
-    FAIL_IF(TAILQ_EMPTY(&dnp3tx->request_objects));
-    DNP3Object *object = TAILQ_FIRST(&dnp3tx->request_objects);
+    FAIL_IF(!dnp3tx->is_request);
+    FAIL_IF(TAILQ_EMPTY(&dnp3tx->objects));
+    DNP3Object *object = TAILQ_FIRST(&dnp3tx->objects);
     FAIL_IF(object->group != 1 || object->variation != 0);
     FAIL_IF(object->count != 0);
 
@@ -2548,8 +2521,8 @@ static int DNP3ParserDecodeG70V3Test(void)
     FAIL_IF(bytes != sizeof(pkt));
     DNP3Transaction *tx = DNP3GetTx(dnp3state, 0);
     FAIL_IF_NULL(tx);
-    FAIL_IF_NOT(tx->has_request);
-    DNP3Object *obj = TAILQ_FIRST(&tx->request_objects);
+    FAIL_IF_NOT(tx->is_request);
+    DNP3Object *obj = TAILQ_FIRST(&tx->objects);
     FAIL_IF_NULL(obj);
     FAIL_IF_NOT(obj->group == 70);
     FAIL_IF_NOT(obj->variation == 3);
index 94220c91c39641524a2b07606a205dabce1ddac5..5b395dc9bddfbe5e2583a5cf9130f37d0fe20341 100644 (file)
@@ -208,37 +208,20 @@ 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                has_request;
-    uint8_t                request_done;
-    DNP3LinkHeader         request_lh;
-    DNP3TransportHeader    request_th;
-    DNP3ApplicationHeader  request_ah;
-    uint8_t               *request_buffer; /**< Reassembled request
-                                            * buffer. */
-    uint32_t               request_buffer_len;
-    uint8_t                request_complete; /**< Was the decode
-                                        * complete.  It will not be
-                                        * complete if we hit objects
-                                        * we do not know. */
-    DNP3ObjectList         request_objects;
-
-    uint8_t                has_response;
-    uint8_t                response_done;
-    DNP3LinkHeader         response_lh;
-    DNP3TransportHeader    response_th;
-    DNP3ApplicationHeader  response_ah;
-    DNP3InternalInd        response_iin;
-    uint8_t               *response_buffer; /**< Reassembed response
-                                             * buffer. */
-    uint32_t               response_buffer_len;
-    uint8_t                response_complete; /**< Was the decode
-                                         * complete.  It will not be
-                                         * complete if we hit objects
-                                         * we do not know. */
-    DNP3ObjectList         response_objects;
+    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;
index f3d64c3b326ed3079a0dfdbca7ec9f75b4a6b530..8099fea4c447c4e2849b5edf32773c06d348c531 100644 (file)
@@ -156,21 +156,17 @@ static InspectionBuffer *GetDNP3Data(DetectEngineThreadCtx *det_ctx,
         DNP3Transaction *tx = (DNP3Transaction *)txv;
         SCLogDebug("tx %p", tx);
 
-        const uint8_t *data = NULL;
-        uint32_t data_len = 0;
-
-        if (flow_flags & STREAM_TOSERVER) {
-            data = tx->request_buffer;
-            data_len = tx->request_buffer_len;
-        } else if (flow_flags & STREAM_TOCLIENT) {
-            data = tx->response_buffer;
-            data_len = tx->response_buffer_len;
+        if ((flow_flags & STREAM_TOSERVER && !tx->is_request) ||
+                (flow_flags & STREAM_TOCLIENT && tx->is_request)) {
+            return NULL;
         }
-        if (data == NULL || data_len == 0)
+
+        if (tx->buffer == NULL || tx->buffer_len == 0) {
             return NULL;
+        }
 
-        SCLogDebug("tx %p data %p data_len %u", tx, data, data_len);
-        InspectionBufferSetup(det_ctx, list_id, buffer, data, data_len);
+        SCLogDebug("tx %p data %p data_len %u", tx, tx->buffer, tx->buffer_len);
+        InspectionBufferSetup(det_ctx, list_id, buffer, tx->buffer, tx->buffer_len);
         InspectionBufferApplyTransforms(buffer, transforms);
     }
     return buffer;
@@ -425,11 +421,10 @@ static int DetectDNP3FuncMatch(DetectEngineThreadCtx *det_ctx,
     DetectDNP3 *detect = (DetectDNP3 *)ctx;
     int match = 0;
 
-    if (flags & STREAM_TOSERVER) {
-        match = detect->function_code == tx->request_ah.function_code;
-    }
-    else if (flags & STREAM_TOCLIENT) {
-        match = detect->function_code == tx->response_ah.function_code;
+    if (flags & STREAM_TOSERVER && tx->is_request) {
+        match = detect->function_code == tx->ah.function_code;
+    } else if (flags & STREAM_TOCLIENT && !tx->is_request) {
+        match = detect->function_code == tx->ah.function_code;
     }
 
     return match;
@@ -443,11 +438,10 @@ static int DetectDNP3ObjMatch(DetectEngineThreadCtx *det_ctx,
     DetectDNP3 *detect = (DetectDNP3 *)ctx;
     DNP3ObjectList *objects = NULL;
 
-    if (flags & STREAM_TOSERVER) {
-        objects = &tx->request_objects;
-    }
-    else if (flags & STREAM_TOCLIENT) {
-        objects = &tx->response_objects;
+    if (flags & STREAM_TOSERVER && tx->is_request) {
+        objects = &tx->objects;
+    } else if (flags & STREAM_TOCLIENT && !tx->is_request) {
+        objects = &tx->objects;
     }
 
     if (objects != NULL) {
@@ -471,8 +465,8 @@ static int DetectDNP3IndMatch(DetectEngineThreadCtx *det_ctx,
     DetectDNP3 *detect = (DetectDNP3 *)ctx;
 
     if (flags & STREAM_TOCLIENT) {
-        if ((tx->response_iin.iin1 & (detect->ind_flags >> 8)) ||
-            (tx->response_iin.iin2 & (detect->ind_flags & 0xf))) {
+        if ((tx->iin.iin1 & (detect->ind_flags >> 8)) ||
+                (tx->iin.iin2 & (detect->ind_flags & 0xf))) {
             return 1;
         }
     }
index 69ab4dfd311a0d1f57e6981f54b8275963f78c24..3ec980b83c87f9135601b33d00b2a57e2dc56fe2 100644 (file)
@@ -198,13 +198,13 @@ static void AlertJsonDnp3(const Flow *f, const uint64_t tx_id, JsonBuilder *js)
             jb_get_mark(js, &mark);
             bool logged = false;
             jb_open_object(js, "dnp3");
-            if (tx->has_request && tx->request_done) {
+            if (tx->is_request && tx->done) {
                 jb_open_object(js, "request");
                 JsonDNP3LogRequest(js, tx);
                 jb_close(js);
                 logged = true;
             }
-            if (tx->has_response && tx->response_done) {
+            if (!tx->is_request && tx->done) {
                 jb_open_object(js, "response");
                 JsonDNP3LogResponse(js, tx);
                 jb_close(js);
index 5bf1e1359ea892f062f79f260d8f58e3b414317d..05d6aab048ebd7bbdc9390f6b6d67f08e91627ae 100644 (file)
@@ -145,27 +145,27 @@ void JsonDNP3LogRequest(JsonBuilder *js, DNP3Transaction *dnp3tx)
     JB_SET_STRING(js, "type", "request");
 
     jb_open_object(js, "control");
-    JsonDNP3LogLinkControl(js, dnp3tx->request_lh.control);
+    JsonDNP3LogLinkControl(js, dnp3tx->lh.control);
     jb_close(js);
 
-    jb_set_uint(js, "src", DNP3_SWAP16(dnp3tx->request_lh.src));
-    jb_set_uint(js, "dst", DNP3_SWAP16(dnp3tx->request_lh.dst));
+    jb_set_uint(js, "src", DNP3_SWAP16(dnp3tx->lh.src));
+    jb_set_uint(js, "dst", DNP3_SWAP16(dnp3tx->lh.dst));
 
     jb_open_object(js, "application");
 
     jb_open_object(js, "control");
-    JsonDNP3LogApplicationControl(js, dnp3tx->request_ah.control);
+    JsonDNP3LogApplicationControl(js, dnp3tx->ah.control);
     jb_close(js);
 
-    jb_set_uint(js, "function_code", dnp3tx->request_ah.function_code);
+    jb_set_uint(js, "function_code", dnp3tx->ah.function_code);
 
-    if (!TAILQ_EMPTY(&dnp3tx->request_objects)) {
+    if (!TAILQ_EMPTY(&dnp3tx->objects)) {
         jb_open_array(js, "objects");
-        JsonDNP3LogObjects(js, &dnp3tx->request_objects);
+        JsonDNP3LogObjects(js, &dnp3tx->objects);
         jb_close(js);
     }
 
-    jb_set_bool(js, "complete", dnp3tx->request_complete);
+    jb_set_bool(js, "complete", dnp3tx->complete);
 
     /* Close application. */
     jb_close(js);
@@ -173,41 +173,40 @@ void JsonDNP3LogRequest(JsonBuilder *js, DNP3Transaction *dnp3tx)
 
 void JsonDNP3LogResponse(JsonBuilder *js, DNP3Transaction *dnp3tx)
 {
-    if (dnp3tx->response_ah.function_code == DNP3_APP_FC_UNSOLICITED_RESP) {
+    if (dnp3tx->ah.function_code == DNP3_APP_FC_UNSOLICITED_RESP) {
         JB_SET_STRING(js, "type", "unsolicited_response");
-    }
-    else {
+    } else {
         JB_SET_STRING(js, "type", "response");
     }
 
     jb_open_object(js, "control");
-    JsonDNP3LogLinkControl(js, dnp3tx->response_lh.control);
+    JsonDNP3LogLinkControl(js, dnp3tx->lh.control);
     jb_close(js);
 
-    jb_set_uint(js, "src", DNP3_SWAP16(dnp3tx->response_lh.src));
-    jb_set_uint(js, "dst", DNP3_SWAP16(dnp3tx->response_lh.dst));
+    jb_set_uint(js, "src", DNP3_SWAP16(dnp3tx->lh.src));
+    jb_set_uint(js, "dst", DNP3_SWAP16(dnp3tx->lh.dst));
 
     jb_open_object(js, "application");
 
     jb_open_object(js, "control");
-    JsonDNP3LogApplicationControl(js, dnp3tx->response_ah.control);
+    JsonDNP3LogApplicationControl(js, dnp3tx->ah.control);
     jb_close(js);
 
-    jb_set_uint(js, "function_code", dnp3tx->response_ah.function_code);
+    jb_set_uint(js, "function_code", dnp3tx->ah.function_code);
 
-    if (!TAILQ_EMPTY(&dnp3tx->response_objects)) {
+    if (!TAILQ_EMPTY(&dnp3tx->objects)) {
         jb_open_array(js, "objects");
-        JsonDNP3LogObjects(js, &dnp3tx->response_objects);
+        JsonDNP3LogObjects(js, &dnp3tx->objects);
         jb_close(js);
     }
 
-    jb_set_bool(js, "complete", dnp3tx->response_complete);
+    jb_set_bool(js, "complete", dnp3tx->complete);
 
     /* Close application. */
     jb_close(js);
 
     jb_open_object(js, "iin");
-    JsonDNP3LogIin(js, (uint16_t)(dnp3tx->response_iin.iin1 << 8 | dnp3tx->response_iin.iin2));
+    JsonDNP3LogIin(js, (uint16_t)(dnp3tx->iin.iin1 << 8 | dnp3tx->iin.iin2));
     jb_close(js);
 }
 
@@ -218,20 +217,17 @@ static int JsonDNP3LoggerToServer(ThreadVars *tv, void *thread_data,
     LogDNP3LogThread *thread = (LogDNP3LogThread *)thread_data;
     DNP3Transaction *tx = vtx;
 
-    if (tx->has_request && tx->request_done) {
-        JsonBuilder *js =
-                CreateEveHeader(p, LOG_DIR_FLOW, "dnp3", NULL, thread->dnp3log_ctx->eve_ctx);
-        if (unlikely(js == NULL)) {
-            return TM_ECODE_OK;
-        }
-
-        jb_open_object(js, "dnp3");
-        JsonDNP3LogRequest(js, tx);
-        jb_close(js);
-        OutputJsonBuilderBuffer(js, thread->ctx);
-        jb_free(js);
+    JsonBuilder *js = CreateEveHeader(p, LOG_DIR_FLOW, "dnp3", NULL, thread->dnp3log_ctx->eve_ctx);
+    if (unlikely(js == NULL)) {
+        return TM_ECODE_OK;
     }
 
+    jb_open_object(js, "dnp3");
+    JsonDNP3LogRequest(js, tx);
+    jb_close(js);
+    OutputJsonBuilderBuffer(js, thread->ctx);
+    jb_free(js);
+
     SCReturnInt(TM_ECODE_OK);
 }
 
@@ -242,20 +238,32 @@ static int JsonDNP3LoggerToClient(ThreadVars *tv, void *thread_data,
     LogDNP3LogThread *thread = (LogDNP3LogThread *)thread_data;
     DNP3Transaction *tx = vtx;
 
-    if (tx->has_response && tx->response_done) {
-        JsonBuilder *js =
-                CreateEveHeader(p, LOG_DIR_FLOW, "dnp3", NULL, thread->dnp3log_ctx->eve_ctx);
-        if (unlikely(js == NULL)) {
-            return TM_ECODE_OK;
-        }
-
-        jb_open_object(js, "dnp3");
-        JsonDNP3LogResponse(js, tx);
-        jb_close(js);
-        OutputJsonBuilderBuffer(js, thread->ctx);
-        jb_free(js);
+    JsonBuilder *js = CreateEveHeader(p, LOG_DIR_FLOW, "dnp3", NULL, thread->dnp3log_ctx->eve_ctx);
+    if (unlikely(js == NULL)) {
+        return TM_ECODE_OK;
     }
 
+    jb_open_object(js, "dnp3");
+    JsonDNP3LogResponse(js, tx);
+    jb_close(js);
+    OutputJsonBuilderBuffer(js, thread->ctx);
+    jb_free(js);
+
+    SCReturnInt(TM_ECODE_OK);
+}
+
+static int JsonDNP3Logger(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f, void *state,
+        void *vtx, uint64_t tx_id)
+{
+    SCEnter();
+    DNP3Transaction *tx = vtx;
+    static int count = 0;
+    if (tx->is_request && tx->done) {
+        JsonDNP3LoggerToServer(tv, thread_data, p, f, state, vtx, tx_id);
+    } else if (!tx->is_request && tx->done) {
+        JsonDNP3LoggerToClient(tv, thread_data, p, f, state, vtx, tx_id);
+    }
+    SCLogNotice("count = %d", ++count);
     SCReturnInt(TM_ECODE_OK);
 }
 
@@ -338,13 +346,7 @@ static TmEcode JsonDNP3LogThreadDeinit(ThreadVars *t, void *data)
 
 void JsonDNP3LogRegister(void)
 {
-    /* Register direction aware eve sub-modules. */
-    OutputRegisterTxSubModuleWithProgress(LOGGER_JSON_DNP3_TS, "eve-log",
-        "JsonDNP3Log", "eve-log.dnp3", OutputDNP3LogInitSub, ALPROTO_DNP3,
-        JsonDNP3LoggerToServer, 0, 1, JsonDNP3LogThreadInit,
-        JsonDNP3LogThreadDeinit, NULL);
-    OutputRegisterTxSubModuleWithProgress(LOGGER_JSON_DNP3_TC, "eve-log",
-        "JsonDNP3Log", "eve-log.dnp3", OutputDNP3LogInitSub, ALPROTO_DNP3,
-        JsonDNP3LoggerToClient, 1, 1, JsonDNP3LogThreadInit,
-        JsonDNP3LogThreadDeinit, NULL);
+    OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonDNP3Log", "eve-log.dnp3",
+            OutputDNP3LogInitSub, ALPROTO_DNP3, JsonDNP3Logger, JsonDNP3LogThreadInit,
+            JsonDNP3LogThreadDeinit, NULL);
 }
index 0b79005b2773d26f3987894df02b68f568c1d5d3..1e7e316af13e81f0701a7a2d7fa9b84ce1ed13c7 100644 (file)
@@ -446,13 +446,6 @@ typedef enum {
     LOGGER_TLS_STORE,
     LOGGER_TLS,
     LOGGER_JSON_TX,
-
-    /* DNP3 loggers. These ones don't fit the common model of a
-       transaction logger yet so are left with their own IDs for
-       now. */
-    LOGGER_JSON_DNP3_TS,
-    LOGGER_JSON_DNP3_TC,
-
     LOGGER_FILE,
     LOGGER_FILEDATA,
 
index 7a791a0db07fc218aab788c81debdc02f2f6beac..09f8694d6c1542785904442c07375b3631e3ca04 100644 (file)
@@ -106,21 +106,21 @@ static void DNP3PushRequest(lua_State *luastate, DNP3Transaction *tx)
     /* Link header. */
     lua_pushliteral(luastate, "link_header");
     lua_newtable(luastate);
-    DNP3PushLinkHeader(luastate, &tx->request_lh);
+    DNP3PushLinkHeader(luastate, &tx->lh);
     lua_settable(luastate, -3);
 
     /* Transport header. */
-    LUA_PUSHT_INT(luastate, "transport_header", tx->request_th);
+    LUA_PUSHT_INT(luastate, "transport_header", tx->th);
 
     /* Application header. */
     lua_pushliteral(luastate, "application_header");
     lua_newtable(luastate);
-    DNP3PushApplicationHeader(luastate, &tx->request_ah);
+    DNP3PushApplicationHeader(luastate, &tx->ah);
     lua_settable(luastate, -3);
 
     lua_pushliteral(luastate, "objects");
     lua_newtable(luastate);
-    DNP3PushObjects(luastate, &tx->request_objects);
+    DNP3PushObjects(luastate, &tx->objects);
     lua_settable(luastate, -3);
 }
 
@@ -129,25 +129,24 @@ static void DNP3PushResponse(lua_State *luastate, DNP3Transaction *tx)
     /* Link header. */
     lua_pushliteral(luastate, "link_header");
     lua_newtable(luastate);
-    DNP3PushLinkHeader(luastate, &tx->response_lh);
+    DNP3PushLinkHeader(luastate, &tx->lh);
     lua_settable(luastate, -3);
 
     /* Transport header. */
-    LUA_PUSHT_INT(luastate, "transport_header", tx->response_th);
+    LUA_PUSHT_INT(luastate, "transport_header", tx->th);
 
     /* Application header. */
     lua_pushliteral(luastate, "application_header");
     lua_newtable(luastate);
-    DNP3PushApplicationHeader(luastate, &tx->response_ah);
+    DNP3PushApplicationHeader(luastate, &tx->ah);
     lua_settable(luastate, -3);
 
     /* Internal indicators. */
-    LUA_PUSHT_INT(luastate, "indicators",
-        tx->response_iin.iin1 << 8 | tx->response_iin.iin2);
+    LUA_PUSHT_INT(luastate, "indicators", tx->iin.iin1 << 8 | tx->iin.iin2);
 
     lua_pushliteral(luastate, "objects");
     lua_newtable(luastate);
-    DNP3PushObjects(luastate, &tx->response_objects);
+    DNP3PushObjects(luastate, &tx->objects);
     lua_settable(luastate, -3);
 }
 
@@ -168,22 +167,22 @@ static int DNP3GetTx(lua_State *luastate)
     lua_pushinteger(luastate, tx->tx_num);
     lua_settable(luastate, -3);
 
-    LUA_PUSHT_INT(luastate, "has_request", tx->has_request);
-    if (tx->has_request) {
+    LUA_PUSHT_INT(luastate, "has_request", tx->is_request ? 1 : 0);
+    if (tx->is_request) {
         lua_pushliteral(luastate, "request");
         lua_newtable(luastate);
-        LUA_PUSHT_INT(luastate, "done", tx->request_done);
-        LUA_PUSHT_INT(luastate, "complete", tx->request_complete);
+        LUA_PUSHT_INT(luastate, "done", tx->done);
+        LUA_PUSHT_INT(luastate, "complete", tx->complete);
         DNP3PushRequest(luastate, tx);
         lua_settable(luastate, -3);
     }
 
-    LUA_PUSHT_INT(luastate, "has_response", tx->has_response);
-    if (tx->has_response) {
+    LUA_PUSHT_INT(luastate, "has_response", tx->is_request ? 0 : 1);
+    if (!tx->is_request) {
         lua_pushliteral(luastate, "response");
         lua_newtable(luastate);
-        LUA_PUSHT_INT(luastate, "done", tx->response_done);
-        LUA_PUSHT_INT(luastate, "complete", tx->response_complete);
+        LUA_PUSHT_INT(luastate, "done", tx->done);
+        LUA_PUSHT_INT(luastate, "complete", tx->complete);
         DNP3PushResponse(luastate, tx);
         lua_settable(luastate, -3);
     }
index 0805ce6f96fb262d29eb2a0506c4e683d0cade47..55b5029fd29d0e6bcdb0e9a458d033e65ce63e37 100644 (file)
@@ -1262,8 +1262,6 @@ const char * PacketProfileLoggertIdToString(LoggerId id)
         CASE_CODE(LOGGER_TLS_STORE);
         CASE_CODE(LOGGER_TLS);
         CASE_CODE(LOGGER_JSON_TX);
-        CASE_CODE(LOGGER_JSON_DNP3_TS);
-        CASE_CODE(LOGGER_JSON_DNP3_TC);
         CASE_CODE(LOGGER_FILE);
         CASE_CODE(LOGGER_FILEDATA);
         CASE_CODE (LOGGER_ALERT_DEBUG);