/**
* \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)) {
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. */
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;
}
/* 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) +
}
/* 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;
}
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;
}
}
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;
}
/* 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;
}
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;
}
}
{
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);
DetectEngineStateFree(tx->tx_data.de_state);
}
- DNP3TxFreeObjectList(&tx->request_objects);
- DNP3TxFreeObjectList(&tx->response_objects);
+ DNP3TxFreeObjectList(&tx->objects);
SCFree(tx);
SCReturn;
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);
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. "
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);
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);
/**
* \test Test flood state.
+ *
+ * Note that flood state needs to revisited with the modification to a
+ * unidirectional protocol.
*/
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);
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);
/* 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);
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);
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);
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);
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);
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);
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;
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;
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;
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) {
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;
}
}
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);
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);
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);
}
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);
}
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);
}
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);
}
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,
/* 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);
}
/* 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);
}
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);
}
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);