]> git.ipfire.org Git - people/ms/suricata.git/blame - src/app-layer-enip.c
queue.h: wrap the system sys/queue.h
[people/ms/suricata.git] / src / app-layer-enip.c
CommitLineData
a3ffebd8 1/* Copyright (C) 2015 Open Information Security Foundation
2 *
3 * You can copy, redistribute or modify this Program under the terms of
4 * the GNU General Public License version 2 as published by the Free
5 * Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * version 2 along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 */
17
18/**
19 * \file
20 *
21 * \author Kevin Wong <kwong@solananetworks.com>
22 *
23 * App-layer parser for ENIP protocol
24 *
25 */
26
27#include "suricata-common.h"
28
29#include "util-debug.h"
30#include "util-byte.h"
31#include "util-enum.h"
32#include "util-mem.h"
33#include "util-misc.h"
34
35#include "stream.h"
36
37#include "app-layer-protos.h"
38#include "app-layer-parser.h"
39#include "app-layer-enip.h"
40#include "app-layer-enip-common.h"
41
42#include "app-layer-detect-proto.h"
43
44#include "conf.h"
45#include "decode.h"
46
47#include "detect-parse.h"
48#include "detect-engine.h"
49#include "util-byte.h"
50#include "util-unittest.h"
51#include "util-unittest-helper.h"
52#include "pkt-var.h"
53#include "util-profiling.h"
54
55
56SCEnumCharMap enip_decoder_event_table[ ] = {
57 { NULL, -1 },
58};
59
60/** \brief get value for 'complete' status in ENIP
61 *
62 * For ENIP we use a simple bool.
63 */
ab1200fb 64static int ENIPGetAlstateProgress(void *tx, uint8_t direction)
a3ffebd8 65{
66 return 1;
67}
68
ab1200fb 69static DetectEngineState *ENIPGetTxDetectState(void *vtx)
a3ffebd8 70{
71 ENIPTransaction *tx = (ENIPTransaction *)vtx;
72 return tx->de_state;
73}
74
7548944b 75static int ENIPSetTxDetectState(void *vtx, DetectEngineState *s)
a3ffebd8 76{
77 ENIPTransaction *tx = (ENIPTransaction *)vtx;
78 tx->de_state = s;
79 return 0;
80}
81
7d663ed5 82static AppLayerTxData *ENIPGetTxData(void *vtx)
706558d4
JI
83{
84 ENIPTransaction *tx = (ENIPTransaction *)vtx;
7d663ed5 85 return &tx->tx_data;
706558d4
JI
86}
87
ab1200fb
VJ
88static void *ENIPGetTx(void *alstate, uint64_t tx_id)
89{
a3ffebd8 90 ENIPState *enip = (ENIPState *) alstate;
91 ENIPTransaction *tx = NULL;
92
93 if (enip->curr && enip->curr->tx_num == tx_id + 1)
94 return enip->curr;
95
96 TAILQ_FOREACH(tx, &enip->tx_list, next) {
97 if (tx->tx_num != (tx_id+1))
98 continue;
99
100 SCLogDebug("returning tx %p", tx);
101 return tx;
102 }
103
104 return NULL;
105}
106
ab1200fb
VJ
107static uint64_t ENIPGetTxCnt(void *alstate)
108{
a3ffebd8 109 return ((uint64_t) ((ENIPState *) alstate)->transaction_max);
110}
111
d568e7fa 112static AppLayerDecoderEvents *ENIPGetEvents(void *tx)
ab1200fb 113{
d568e7fa 114 return ((ENIPTransaction *)tx)->decoder_events;
a3ffebd8 115}
116
ab1200fb
VJ
117static int ENIPStateGetEventInfo(const char *event_name, int *event_id, AppLayerEventType *event_type)
118{
a3ffebd8 119 *event_id = SCMapEnumNameToValue(event_name, enip_decoder_event_table);
120
121 if (*event_id == -1) {
122 SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%s\" not present in "
123 "enip's enum map table.", event_name);
124 /* yes this is fatal */
125 return -1;
126 }
127
128 *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
129
130 return 0;
131}
132
f7b934f8
JL
133static int ENIPStateGetEventInfoById(int event_id, const char **event_name,
134 AppLayerEventType *event_type)
135{
136 *event_name = SCMapEnumValueToName(event_id, enip_decoder_event_table);
137 if (*event_name == NULL) {
138 SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%d\" not present in "
139 "enip's enum map table.", event_id);
140 /* yes this is fatal */
141 return -1;
142 }
143
144 *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
145
146 return 0;
147}
148
a3ffebd8 149/** \brief Allocate enip state
150 *
151 * return state
152 */
547d6c2d 153static void *ENIPStateAlloc(void *orig_state, AppProto proto_orig)
a3ffebd8 154{
72b5da43 155 SCLogDebug("ENIPStateAlloc");
a3ffebd8 156 void *s = SCMalloc(sizeof(ENIPState));
157 if (unlikely(s == NULL))
158 return NULL;
159
160 memset(s, 0, sizeof(ENIPState));
161
162 ENIPState *enip_state = (ENIPState *) s;
163
164 TAILQ_INIT(&enip_state->tx_list);
165 return s;
166}
167
168/** \internal
169 * \brief Free a ENIP TX
170 * \param tx ENIP TX to free */
171static void ENIPTransactionFree(ENIPTransaction *tx, ENIPState *state)
172{
173 SCEnter();
72b5da43 174 SCLogDebug("ENIPTransactionFree");
a3ffebd8 175 CIPServiceEntry *svc = NULL;
176 while ((svc = TAILQ_FIRST(&tx->service_list)))
177 {
178 TAILQ_REMOVE(&tx->service_list, svc, next);
179
180 SegmentEntry *seg = NULL;
181 while ((seg = TAILQ_FIRST(&svc->segment_list)))
182 {
183 TAILQ_REMOVE(&svc->segment_list, seg, next);
184 SCFree(seg);
185 }
186
187 AttributeEntry *attr = NULL;
188 while ((attr = TAILQ_FIRST(&svc->attrib_list)))
189 {
190 TAILQ_REMOVE(&svc->attrib_list, attr, next);
191 SCFree(attr);
192 }
193
194 SCFree(svc);
195 }
196
197 AppLayerDecoderEventsFreeEvents(&tx->decoder_events);
198
199 if (tx->de_state != NULL)
200 {
201 DetectEngineStateFree(tx->de_state);
202
203 state->tx_with_detect_state_cnt--;
204 }
205
206 if (state->iter == tx)
207 state->iter = NULL;
208
209 SCFree(tx);
210 SCReturn;
211}
212
213/** \brief Free enip state
214 *
215 */
ab1200fb 216static void ENIPStateFree(void *s)
a3ffebd8 217{
218 SCEnter();
72b5da43 219 SCLogDebug("ENIPStateFree");
a3ffebd8 220 if (s)
221 {
222 ENIPState *enip_state = (ENIPState *) s;
223
224 ENIPTransaction *tx = NULL;
225 while ((tx = TAILQ_FIRST(&enip_state->tx_list)))
226 {
227 TAILQ_REMOVE(&enip_state->tx_list, tx, next);
228 ENIPTransactionFree(tx, enip_state);
229 }
230
231 if (enip_state->buffer != NULL)
232 {
233 SCFree(enip_state->buffer);
234 }
235
236 SCFree(s);
237 }
238 SCReturn;
239}
240
241/** \internal
242 * \brief Allocate a ENIP TX
243 * \retval tx or NULL */
244static ENIPTransaction *ENIPTransactionAlloc(ENIPState *state)
245{
72b5da43 246 SCLogDebug("ENIPStateTransactionAlloc");
a3ffebd8 247 ENIPTransaction *tx = (ENIPTransaction *) SCCalloc(1,
248 sizeof(ENIPTransaction));
249 if (unlikely(tx == NULL))
250 return NULL;
251
252 state->curr = tx;
253 state->transaction_max++;
254
255 memset(tx, 0x00, sizeof(ENIPTransaction));
256 TAILQ_INIT(&tx->service_list);
257
258 tx->enip = state;
259 tx->tx_num = state->transaction_max;
260 tx->service_count = 0;
261
262 TAILQ_INSERT_TAIL(&state->tx_list, tx, next);
263
264 return tx;
265}
266
267/**
268 * \brief enip transaction cleanup callback
269 */
ab1200fb 270static void ENIPStateTransactionFree(void *state, uint64_t tx_id)
a3ffebd8 271{
272 SCEnter();
72b5da43 273 SCLogDebug("ENIPStateTransactionFree");
a3ffebd8 274 ENIPState *enip_state = state;
275 ENIPTransaction *tx = NULL;
276 TAILQ_FOREACH(tx, &enip_state->tx_list, next)
277 {
278
279 if ((tx_id+1) < tx->tx_num)
280 break;
281 else if ((tx_id+1) > tx->tx_num)
282 continue;
283
284 if (tx == enip_state->curr)
285 enip_state->curr = NULL;
286
287 if (tx->decoder_events != NULL)
288 {
289 if (tx->decoder_events->cnt <= enip_state->events)
290 enip_state->events -= tx->decoder_events->cnt;
291 else
292 enip_state->events = 0;
293 }
294
295 TAILQ_REMOVE(&enip_state->tx_list, tx, next);
296 ENIPTransactionFree(tx, state);
297 break;
298 }
299 SCReturn;
300}
301
302/** \internal
303 *
304 * \brief This function is called to retrieve a ENIP
305 *
306 * \param state ENIP state structure for the parser
307 * \param input Input line of the command
308 * \param input_len Length of the request
309 *
310 * \retval 1 when the command is parsed, 0 otherwise
311 */
44d3f264 312static AppLayerResult ENIPParse(Flow *f, void *state, AppLayerParserState *pstate,
579cc9f0 313 const uint8_t *input, uint32_t input_len, void *local_data,
7bc3c3ac 314 const uint8_t flags)
a3ffebd8 315{
316 SCEnter();
317 ENIPState *enip = (ENIPState *) state;
318 ENIPTransaction *tx;
319
320 if (input == NULL && AppLayerParserStateIssetFlag(pstate,
4f73943d 321 APP_LAYER_PARSER_EOF_TS|APP_LAYER_PARSER_EOF_TC))
a3ffebd8 322 {
44d3f264 323 SCReturnStruct(APP_LAYER_OK);
700781c5
VJ
324 } else if (input == NULL && input_len != 0) {
325 // GAP
44d3f264 326 SCReturnStruct(APP_LAYER_OK);
a3ffebd8 327 } else if (input == NULL || input_len == 0)
328 {
44d3f264 329 SCReturnStruct(APP_LAYER_ERROR);
a3ffebd8 330 }
331
332 while (input_len > 0)
333 {
334 tx = ENIPTransactionAlloc(enip);
335 if (tx == NULL)
44d3f264 336 SCReturnStruct(APP_LAYER_OK);
a3ffebd8 337
72b5da43 338 SCLogDebug("ENIPParse input len %d", input_len);
a3ffebd8 339 DecodeENIPPDU(input, input_len, tx);
340 uint32_t pkt_len = tx->header.length + sizeof(ENIPEncapHdr);
72b5da43 341 SCLogDebug("ENIPParse packet len %d", pkt_len);
a3ffebd8 342 if (pkt_len > input_len)
343 {
72b5da43 344 SCLogDebug("Invalid packet length");
a3ffebd8 345 break;
346 }
347
348 input += pkt_len;
349 input_len -= pkt_len;
72b5da43 350 //SCLogDebug("remaining %d", input_len);
a3ffebd8 351
352 if (input_len < sizeof(ENIPEncapHdr))
353 {
72b5da43 354 //SCLogDebug("Not enough data"); //not enough data for ENIP
a3ffebd8 355 break;
356 }
357 }
358
44d3f264 359 SCReturnStruct(APP_LAYER_OK);
a3ffebd8 360}
361
0c948142 362#define ENIP_LEN_REGISTER_SESSION 4 // protocol u16, options u16
a3ffebd8 363
422e4892 364static uint16_t ENIPProbingParser(Flow *f, uint8_t direction,
579cc9f0 365 const uint8_t *input, uint32_t input_len, uint8_t *rdir)
a3ffebd8 366{
72b5da43 367 // SCLogDebug("ENIPProbingParser %d", input_len);
a3ffebd8 368 if (input_len < sizeof(ENIPEncapHdr))
369 {
72b5da43 370 SCLogDebug("length too small to be a ENIP header");
a3ffebd8 371 return ALPROTO_UNKNOWN;
372 }
a15e503b 373 uint16_t cmd;
0c948142 374 uint16_t enip_len;
0da4dc0d 375 uint32_t status;
0c948142
PA
376 uint32_t option;
377 uint16_t nbitems;
378
379 int ret = ByteExtractUint16(
380 &enip_len, BYTE_LITTLE_ENDIAN, sizeof(uint16_t), (const uint8_t *)(input + 2));
381 if (ret < 0) {
382 return ALPROTO_FAILED;
383 }
384 if (enip_len < sizeof(ENIPEncapHdr)) {
385 return ALPROTO_FAILED;
386 }
387 ret = ByteExtractUint32(
388 &status, BYTE_LITTLE_ENDIAN, sizeof(uint32_t), (const uint8_t *)(input + 8));
389 if (ret < 0) {
390 return ALPROTO_FAILED;
391 }
392 switch (status) {
393 case SUCCESS:
394 case INVALID_CMD:
395 case NO_RESOURCES:
396 case INCORRECT_DATA:
397 case INVALID_SESSION:
398 case INVALID_LENGTH:
399 case UNSUPPORTED_PROT_REV:
400 case ENCAP_HEADER_ERROR:
401 break;
402 default:
403 return ALPROTO_FAILED;
404 }
405 ret = ByteExtractUint16(&cmd, BYTE_LITTLE_ENDIAN, sizeof(uint16_t), (const uint8_t *)(input));
a15e503b
PA
406 if(ret < 0) {
407 return ALPROTO_FAILED;
408 }
0c948142
PA
409 ret = ByteExtractUint32(
410 &option, BYTE_LITTLE_ENDIAN, sizeof(uint32_t), (const uint8_t *)(input + 20));
411 if (ret < 0) {
412 return ALPROTO_FAILED;
413 }
414
a15e503b
PA
415 //ok for all the known commands
416 switch(cmd) {
417 case NOP:
0c948142
PA
418 if (option != 0) {
419 return ALPROTO_FAILED;
420 }
421 break;
a15e503b 422 case REGISTER_SESSION:
0c948142
PA
423 if (enip_len != ENIP_LEN_REGISTER_SESSION) {
424 return ALPROTO_FAILED;
425 }
426 break;
a15e503b 427 case UNREGISTER_SESSION:
0c948142
PA
428 if (enip_len != ENIP_LEN_REGISTER_SESSION && enip_len != 0) {
429 // 0 for request and 4 for response
430 return ALPROTO_FAILED;
431 }
432 break;
433 case LIST_SERVICES:
434 case LIST_IDENTITY:
a15e503b
PA
435 case SEND_RR_DATA:
436 case SEND_UNIT_DATA:
437 case INDICATE_STATUS:
438 case CANCEL:
0c948142
PA
439 break;
440 case LIST_INTERFACES:
441 if (input_len < sizeof(ENIPEncapHdr) + 2) {
442 SCLogDebug("length too small to be a ENIP LIST_INTERFACES");
443 return ALPROTO_UNKNOWN;
444 }
445 ret = ByteExtractUint16(
446 &nbitems, BYTE_LITTLE_ENDIAN, sizeof(uint16_t), (const uint8_t *)(input));
0da4dc0d
PA
447 if(ret < 0) {
448 return ALPROTO_FAILED;
449 }
0c948142
PA
450 if (enip_len < sizeof(ENIPEncapHdr) + 2 * (size_t)nbitems) {
451 return ALPROTO_FAILED;
0da4dc0d 452 }
0c948142
PA
453 break;
454 default:
455 return ALPROTO_FAILED;
a15e503b 456 }
0c948142 457 return ALPROTO_ENIP;
a3ffebd8 458}
459
460/**
461 * \brief Function to register the ENIP protocol parsers and other functions
462 */
463void RegisterENIPUDPParsers(void)
464{
465 SCEnter();
ab1200fb 466 const char *proto_name = "enip";
a3ffebd8 467
468 if (AppLayerProtoDetectConfProtoDetectionEnabled("udp", proto_name))
469 {
470 AppLayerProtoDetectRegisterProtocol(ALPROTO_ENIP, proto_name);
471
472 if (RunmodeIsUnittests())
473 {
474 AppLayerProtoDetectPPRegister(IPPROTO_UDP, "44818", ALPROTO_ENIP,
c35c18a7 475 0, sizeof(ENIPEncapHdr), STREAM_TOSERVER, ENIPProbingParser, NULL);
a3ffebd8 476
477 AppLayerProtoDetectPPRegister(IPPROTO_UDP, "44818", ALPROTO_ENIP,
c35c18a7 478 0, sizeof(ENIPEncapHdr), STREAM_TOCLIENT, ENIPProbingParser, NULL);
a3ffebd8 479
480 } else
481 {
482 if (!AppLayerProtoDetectPPParseConfPorts("udp", IPPROTO_UDP,
483 proto_name, ALPROTO_ENIP, 0, sizeof(ENIPEncapHdr),
c35c18a7 484 ENIPProbingParser, ENIPProbingParser))
a3ffebd8 485 {
486 SCLogDebug(
487 "no ENIP UDP config found enabling ENIP detection on port 44818.");
488
489 AppLayerProtoDetectPPRegister(IPPROTO_UDP, "44818",
490 ALPROTO_ENIP, 0, sizeof(ENIPEncapHdr), STREAM_TOSERVER,
c35c18a7 491 ENIPProbingParser, NULL);
a3ffebd8 492
493 AppLayerProtoDetectPPRegister(IPPROTO_UDP, "44818",
494 ALPROTO_ENIP, 0, sizeof(ENIPEncapHdr), STREAM_TOCLIENT,
c35c18a7 495 ENIPProbingParser, NULL);
a3ffebd8 496 }
497 }
498
499 } else
500 {
72b5da43 501 SCLogConfig("Protocol detection and parser disabled for %s protocol.",
a3ffebd8 502 proto_name);
503 return;
504 }
505
506 if (AppLayerParserConfParserEnabled("udp", proto_name))
507 {
508 AppLayerParserRegisterParser(IPPROTO_UDP, ALPROTO_ENIP,
509 STREAM_TOSERVER, ENIPParse);
510 AppLayerParserRegisterParser(IPPROTO_UDP, ALPROTO_ENIP,
511 STREAM_TOCLIENT, ENIPParse);
512
513 AppLayerParserRegisterStateFuncs(IPPROTO_UDP, ALPROTO_ENIP,
514 ENIPStateAlloc, ENIPStateFree);
515
516 AppLayerParserRegisterGetEventsFunc(IPPROTO_UDP, ALPROTO_ENIP, ENIPGetEvents);
a3ffebd8 517
7548944b
VJ
518 AppLayerParserRegisterDetectStateFuncs(IPPROTO_UDP, ALPROTO_ENIP,
519 ENIPGetTxDetectState, ENIPSetTxDetectState);
a3ffebd8 520
521 AppLayerParserRegisterGetTx(IPPROTO_UDP, ALPROTO_ENIP, ENIPGetTx);
7d663ed5 522 AppLayerParserRegisterTxDataFunc(IPPROTO_UDP, ALPROTO_ENIP, ENIPGetTxData);
a3ffebd8 523 AppLayerParserRegisterGetTxCnt(IPPROTO_UDP, ALPROTO_ENIP, ENIPGetTxCnt);
524 AppLayerParserRegisterTxFreeFunc(IPPROTO_UDP, ALPROTO_ENIP, ENIPStateTransactionFree);
525
526 AppLayerParserRegisterGetStateProgressFunc(IPPROTO_UDP, ALPROTO_ENIP, ENIPGetAlstateProgress);
efc9a7a3 527 AppLayerParserRegisterStateProgressCompletionStatus(ALPROTO_ENIP, 1, 1);
a3ffebd8 528
529 AppLayerParserRegisterGetEventInfo(IPPROTO_UDP, ALPROTO_ENIP, ENIPStateGetEventInfo);
f7b934f8 530 AppLayerParserRegisterGetEventInfoById(IPPROTO_UDP, ALPROTO_ENIP, ENIPStateGetEventInfoById);
a3ffebd8 531
532 AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_UDP,
533 ALPROTO_ENIP, STREAM_TOSERVER | STREAM_TOCLIENT);
2b215a45
JI
534 AppLayerParserRegisterOptionFlags(
535 IPPROTO_UDP, ALPROTO_ENIP, APP_LAYER_PARSER_OPT_UNIDIR_TXS);
a3ffebd8 536 } else
537 {
538 SCLogInfo(
539 "Parsed disabled for %s protocol. Protocol detection" "still on.",
540 proto_name);
541 }
542
543#ifdef UNITTESTS
544 AppLayerParserRegisterProtocolUnittests(IPPROTO_UDP, ALPROTO_ENIP, ENIPParserRegisterTests);
545#endif
546
547 SCReturn;
548}
549
550/**
551 * \brief Function to register the ENIP protocol parsers and other functions
552 */
553void RegisterENIPTCPParsers(void)
554{
555 SCEnter();
ab1200fb 556 const char *proto_name = "enip";
a3ffebd8 557
558 if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name))
559 {
560 AppLayerProtoDetectRegisterProtocol(ALPROTO_ENIP, proto_name);
561
562 if (RunmodeIsUnittests())
563 {
564 AppLayerProtoDetectPPRegister(IPPROTO_TCP, "44818", ALPROTO_ENIP,
c35c18a7 565 0, sizeof(ENIPEncapHdr), STREAM_TOSERVER, ENIPProbingParser, NULL);
a3ffebd8 566
567 AppLayerProtoDetectPPRegister(IPPROTO_TCP, "44818", ALPROTO_ENIP,
c35c18a7 568 0, sizeof(ENIPEncapHdr), STREAM_TOCLIENT, ENIPProbingParser, NULL);
a3ffebd8 569
570 } else
571 {
572 if (!AppLayerProtoDetectPPParseConfPorts("tcp", IPPROTO_TCP,
573 proto_name, ALPROTO_ENIP, 0, sizeof(ENIPEncapHdr),
c35c18a7 574 ENIPProbingParser, ENIPProbingParser))
a3ffebd8 575 {
238163bc 576 return;
a3ffebd8 577 }
578 }
579
580 } else
581 {
582 SCLogDebug("Protocol detection and parser disabled for %s protocol.",
583 proto_name);
584 return;
585 }
586
587 if (AppLayerParserConfParserEnabled("tcp", proto_name))
588 {
589 AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_ENIP,
590 STREAM_TOSERVER, ENIPParse);
591 AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_ENIP,
592 STREAM_TOCLIENT, ENIPParse);
593 AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_ENIP,
594 ENIPStateAlloc, ENIPStateFree);
595
596 AppLayerParserRegisterGetEventsFunc(IPPROTO_TCP, ALPROTO_ENIP, ENIPGetEvents);
a3ffebd8 597
7548944b
VJ
598 AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP, ALPROTO_ENIP,
599 ENIPGetTxDetectState, ENIPSetTxDetectState);
a3ffebd8 600
601 AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_ENIP, ENIPGetTx);
7d663ed5 602 AppLayerParserRegisterTxDataFunc(IPPROTO_TCP, ALPROTO_ENIP, ENIPGetTxData);
a3ffebd8 603 AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_ENIP, ENIPGetTxCnt);
604 AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_ENIP, ENIPStateTransactionFree);
605
606 AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_ENIP, ENIPGetAlstateProgress);
efc9a7a3 607 AppLayerParserRegisterStateProgressCompletionStatus(ALPROTO_ENIP, 1, 1);
a3ffebd8 608
609 AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_ENIP, ENIPStateGetEventInfo);
610
611 AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP,
612 ALPROTO_ENIP, STREAM_TOSERVER | STREAM_TOCLIENT);
700781c5
VJ
613
614 /* This parser accepts gaps. */
615 AppLayerParserRegisterOptionFlags(IPPROTO_TCP, ALPROTO_ENIP,
616 APP_LAYER_PARSER_OPT_ACCEPT_GAPS);
617
2b215a45
JI
618 AppLayerParserRegisterOptionFlags(
619 IPPROTO_TCP, ALPROTO_ENIP, APP_LAYER_PARSER_OPT_UNIDIR_TXS);
a3ffebd8 620 } else
621 {
238163bc 622 SCLogConfig("Parser disabled for %s protocol. Protocol detection still on.",
a3ffebd8 623 proto_name);
624 }
625
626#ifdef UNITTESTS
627 AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_ENIP, ENIPParserRegisterTests);
628#endif
629
630 SCReturn;
631}
632
633/* UNITTESTS */
634#ifdef UNITTESTS
635#include "app-layer-parser.h"
636#include "detect-parse.h"
637#include "detect-engine.h"
638#include "flow-util.h"
639#include "stream-tcp.h"
640#include "util-unittest.h"
641#include "util-unittest-helper.h"
642
643static uint8_t listIdentity[] = {/* List ID */ 0x63, 0x00,
644 /* Length */ 0x00, 0x00,
645 /* Session */ 0x00, 0x00, 0x00, 0x00,
646 /* Status */ 0x00, 0x00, 0x00, 0x00,
647 /* Delay*/ 0x00,
648 /* Context */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
649 /* Quantity of coils */ 0x00, 0x00, 0x00, 0x00, 0x00};
650
651/**
652 * \brief Test if ENIP Packet matches signature
653 */
ab1200fb 654static int ALDecodeENIPTest(void)
a3ffebd8 655{
656 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
657 Flow f;
658 TcpSession ssn;
659
660 memset(&f, 0, sizeof(f));
661 memset(&ssn, 0, sizeof(ssn));
662
663 f.protoctx = (void *)&ssn;
664 f.proto = IPPROTO_TCP;
5c01b409 665 f.alproto = ALPROTO_ENIP;
a3ffebd8 666
1eeb9669 667 StreamTcpInitConfig(true);
a3ffebd8 668
669 int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_ENIP, STREAM_TOSERVER,
670 listIdentity, sizeof(listIdentity));
671 FAIL_IF(r != 0);
672
673 ENIPState *enip_state = f.alstate;
674 FAIL_IF_NULL(enip_state);
675
676 ENIPTransaction *tx = ENIPGetTx(enip_state, 0);
677 FAIL_IF_NULL(tx);
678
679 FAIL_IF(tx->header.command != 99);
680
681 AppLayerParserThreadCtxFree(alp_tctx);
1eeb9669 682 StreamTcpFreeConfig(true);
a3ffebd8 683 FLOW_DESTROY(&f);
684
685 PASS;
686}
687
688#endif /* UNITTESTS */
689
690void ENIPParserRegisterTests(void)
691{
692#ifdef UNITTESTS
693 UtRegisterTest("ALDecodeENIPTest", ALDecodeENIPTest);
694#endif /* UNITTESTS */
695}