From bbaa79b80e4faea321593eb4aa552f976357ac86 Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Fri, 10 Apr 2015 09:33:20 -0600 Subject: [PATCH] DNP3: Application layer decoder. Decodes TCP DNP3 and raises some DNP3 decoder alerts. --- Makefile.am | 1 + rules/Makefile.am | 3 +- rules/dnp3-events.rules | 26 + src/Makefile.am | 2 + src/app-layer-detect-proto.c | 4 + src/app-layer-dnp3-objects.c | 9148 ++++++++++++++++++++++++++++++++++ src/app-layer-dnp3-objects.h | 1464 ++++++ src/app-layer-dnp3.c | 2661 ++++++++++ src/app-layer-dnp3.h | 277 + src/app-layer-parser.c | 2 + src/app-layer-protos.c | 3 + src/app-layer-protos.h | 1 + src/util-error.c | 1 + src/util-error.h | 1 + suricata.yaml.in | 8 + 15 files changed, 13601 insertions(+), 1 deletion(-) create mode 100644 rules/dnp3-events.rules create mode 100644 src/app-layer-dnp3-objects.c create mode 100644 src/app-layer-dnp3-objects.h create mode 100644 src/app-layer-dnp3.c create mode 100644 src/app-layer-dnp3.h diff --git a/Makefile.am b/Makefile.am index e71290c4b8..b265b6f2dc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -44,6 +44,7 @@ endif @test -e "$(DESTDIR)$(e_sysconfrulesdir)tls-events.rules" || install -m 600 "$(top_srcdir)/rules/tls-events.rules" "$(DESTDIR)$(e_sysconfrulesdir)" @test -e "$(DESTDIR)$(e_sysconfrulesdir)modbus-events.rules" || install -m 600 "$(top_srcdir)/rules/modbus-events.rules" "$(DESTDIR)$(e_sysconfrulesdir)" @test -e "$(DESTDIR)$(e_sysconfrulesdir)app-layer-events.rules" || install -m 600 "$(top_srcdir)/rules/app-layer-events.rules" "$(DESTDIR)$(e_sysconfrulesdir)" + @test -e "$(DESTDIR)$(e_sysconfrulesdir)dnp3-events.rules" || install -m 600 "$(top_srcdir)/rules/dnp3-events.rules" "$(DESTDIR)$(e_sysconfrulesdir)" @echo "" @echo "You can now start suricata by running as root something like '$(DESTDIR)$(bindir)/suricata -c $(DESTDIR)$(e_sysconfdir)/suricata.yaml -i eth0'." @echo "" diff --git a/rules/Makefile.am b/rules/Makefile.am index 7ab27bdf1e..98a484d3a5 100644 --- a/rules/Makefile.am +++ b/rules/Makefile.am @@ -7,4 +7,5 @@ dns-events.rules \ tls-events.rules \ modbus-events.rules \ app-layer-events.rules \ -files.rules +files.rules \ +dnp3-events.rules diff --git a/rules/dnp3-events.rules b/rules/dnp3-events.rules new file mode 100644 index 0000000000..dfb1cf2217 --- /dev/null +++ b/rules/dnp3-events.rules @@ -0,0 +1,26 @@ +# DNP3 application decoder event rules. +# +# This SIDs fall in the 2270000+ range. See: +# http://doc.emergingthreats.net/bin/view/Main/SidAllocation + +# Flooded. +alert dnp3 any any -> any any (msg:"SURICATA DNP3 Request flood detected"; \ + app-layer-event:dnp3.flooded; sid:2270000; rev:1;) + +# Length to small for PDU type. For example, link specifies the type +# as user data, but the length field is not large enough for user +# data. +alert dnp3 any any -> any any (msg:"SURICATA DNP3 Length too small"; \ + app-layer-event:dnp3.len_too_small; sid:2270001; rev:2;) + +# Bad link layer CRC. +alert dnp3 any any -> any any (msg:"SURICATA DNP3 Bad link CRC"; \ + app-layer-event:dnp3.bad_link_crc; sid:2270002; rev:1;) + +# Bad transport layer CRC. +alert dnp3 any any -> any any (msg:"SURICATA DNP3 Bad transport CRC"; \ + app-layer-event:dnp3.bad_transport_crc; sid:2270003; rev:1;) + +# Unknown object. +alert dnp3 any any -> any any (msg:"SURICATA DNP3 Unknown object"; \ + app-layer-event:dnp3.unknown_object; sid:2270004; rev:1;) diff --git a/src/Makefile.am b/src/Makefile.am index 191776a5d2..c5bb60e7a2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -16,6 +16,8 @@ app-layer.c app-layer.h \ app-layer-dcerpc.c app-layer-dcerpc.h \ app-layer-dcerpc-udp.c app-layer-dcerpc-udp.h \ app-layer-detect-proto.c app-layer-detect-proto.h \ +app-layer-dnp3.c app-layer-dnp3.h \ +app-layer-dnp3-objects.c app-layer-dnp3-objects.h \ app-layer-dns-common.c app-layer-dns-common.h \ app-layer-dns-tcp.c app-layer-dns-tcp.h \ app-layer-dns-udp.c app-layer-dns-udp.h \ diff --git a/src/app-layer-detect-proto.c b/src/app-layer-detect-proto.c index 74e22d4ae0..286dde89cf 100644 --- a/src/app-layer-detect-proto.c +++ b/src/app-layer-detect-proto.c @@ -695,6 +695,8 @@ void AppLayerProtoDetectPrintProbingParsers(AppLayerProtoDetectProbingParser *pp printf(" alproto: ALPROTO_ENIP\n"); else if (pp_pe->alproto == ALPROTO_TEMPLATE) printf(" alproto: ALPROTO_TEMPLATE\n"); + else if (pp_pe->alproto == ALPROTO_DNP3) + printf(" alproto: ALPROTO_DNP3\n"); else printf("impossible\n"); @@ -750,6 +752,8 @@ void AppLayerProtoDetectPrintProbingParsers(AppLayerProtoDetectProbingParser *pp printf(" alproto: ALPROTO_ENIP\n"); else if (pp_pe->alproto == ALPROTO_TEMPLATE) printf(" alproto: ALPROTO_TEMPLATE\n"); + else if (pp_pe->alproto == ALPROTO_DNP3) + printf(" alproto: ALPROTO_DNP3\n"); else printf("impossible\n"); diff --git a/src/app-layer-dnp3-objects.c b/src/app-layer-dnp3-objects.c new file mode 100644 index 0000000000..275e646b2c --- /dev/null +++ b/src/app-layer-dnp3-objects.c @@ -0,0 +1,9148 @@ +/* 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 + * + * \author Jason Ish + * + * This file contains the DNP3 object decoders. + */ + +#include "suricata-common.h" + +#include "app-layer-dnp3.h" +#include "app-layer-dnp3-objects.h" + +void DNP3FreeObjectPoint(int group, int variation, void *point); + +#if 0 +static void DNP3HexDump(uint8_t *data, int len) +{ + for (int i = 0; i < len; i++) { + printf("%02x ", data[i]); + } +} +#endif + +/** + * \brief Allocate a list for DNP3 points. + */ +DNP3PointList *DNP3PointListAlloc(void) +{ + DNP3PointList *items = SCCalloc(1, sizeof(*items)); + if (unlikely(items == NULL)) { + return NULL; + } + TAILQ_INIT(items); + return items; +} + +/** + * \brief Free a DNP3PointList. + */ +void DNP3FreeObjectPointList(int group, int variation, DNP3PointList *list) +{ + DNP3Point *point; + while ((point = TAILQ_FIRST(list)) != NULL) { + TAILQ_REMOVE(list, point, next); + if (point->data != NULL) { + DNP3FreeObjectPoint(group, variation, point->data); + } + SCFree(point); + } + SCFree(list); +} + +/** + * \brief Read an uint8_t from a buffer. + * + * Reads a uint8_t from a buffer advancing the pointer and + * decrementing the length. + * + * \param buf A pointer to the buffer to read from. + * \param len A pointer to the buffer length. + * \param out A pointer to where the value will be stored. + * + * \retval Returns 1 if there was enough space in the buffer to read from, + * otherwise 0 is returned. + */ +static int DNP3ReadUint8(const uint8_t **buf, uint32_t *len, uint8_t *out) +{ + if (*len < (int)sizeof(*out)) { + return 0; + } + *out = *(uint8_t *)(*buf); + *buf += sizeof(*out); + *len -= sizeof(*out); + return 1; +} + +/** + * \brief Read an uint16_t from a buffer. + * + * Reads an uint16_t from a buffer advancing the pointer and + * decrementing the length. + * + * \param buf A pointer to the buffer to read from. + * \param len A pointer to the buffer length. + * \param out A pointer to where the value will be stored. + * + * \retval Returns 1 if there was enough space in the buffer to read from, + * otherwise 0 is returned. + */ +static int DNP3ReadUint16(const uint8_t **buf, uint32_t *len, uint16_t *out) +{ + if (*len < (int)sizeof(*out)) { + return 0; + } + *out = DNP3_SWAP16(*(uint16_t *)(*buf)); + *buf += sizeof(*out); + *len -= sizeof(*out); + return 1; +} + +/** + * \brief Read an unsigned 24 bit integer from a buffer. + * + * Reads an an unsigned 24 bit integer from a buffer advancing the + * pointer and decrementing the length. + * + * \param buf A pointer to the buffer to read from. + * \param len A pointer to the buffer length. + * \param out A pointer to where the value will be stored. + * + * \retval Returns 1 if there was enough space in the buffer to read from, + * otherwise 0 is returned. + */ +static int DNP3ReadUint24(const uint8_t **buf, uint32_t *len, uint32_t *out) +{ + if (*len < (int)(sizeof(uint8_t) * 3)) { + return 0; + } + +#if __BYTE_ORDER__ == __BIG_ENDIAN + *out = ((uint32_t)(*buf)[0] << 16) | ((uint32_t)(*buf)[1] << 8) | + (uint32_t)(*buf)[2]; +#elif __BYTE_ORDER == __LITTLE_ENDIAN + *out = ((uint64_t)(*buf)[0]) | ((uint64_t)(*buf)[1] << 8) | + ((uint64_t)(*buf)[2] << 16); +#endif + + *buf += 3; + *len -= 3; + + return 1; +} + +/** + * \brief Read an uint32_t from a buffer. + * + * Reads an uint32_t from a buffer advancing the pointer and + * decrementing the length. + * + * \param buf A pointer to the buffer to read from. + * \param len A pointer to the buffer length. + * \param out A pointer to where the value will be stored. + * + * \retval Returns 1 if there was enough space in the buffer to read from, + * otherwise 0 is returned. + */ +static int DNP3ReadUint32(const uint8_t **buf, uint32_t *len, uint32_t *out) +{ + if (*len < (int)sizeof(*out)) { + return 0; + } + *out = DNP3_SWAP32(*(uint32_t *)(*buf)); + *buf += sizeof(*out); + *len -= sizeof(*out); + return 1; +} + +/** + * \brief Read an unsigned 48 bit integer from a buffer. + * + * Reads an an unsigned 48 bit integer from a buffer advancing the + * pointer and decrementing the length. + * + * \param buf A pointer to the buffer to read from. + * \param len A pointer to the buffer length. + * \param out A pointer to where the value will be stored. + * + * \retval Returns 1 if there was enough space in the buffer to read from, + * otherwise 0 is returned. + */ +static int DNP3ReadUint48(const uint8_t **buf, uint32_t *len, uint64_t *out) +{ + if (*len < (int)(sizeof(uint8_t) * 6)) { + return 0; + } + +#if __BYTE_ORDER__ == __BIG_ENDIAN + *out = ((uint64_t)(*buf)[0] << 40) | ((uint64_t)(*buf)[1] << 32) | + ((uint64_t)(*buf)[2] << 24) | ((uint64_t)(*buf)[3] << 16) | + ((uint64_t)(*buf)[4] << 8) | (uint64_t)(*buf)[5]; +#elif __BYTE_ORDER == __LITTLE_ENDIAN + *out = ((uint64_t)(*buf)[0]) | ((uint64_t)(*buf)[1] << 8) | + ((uint64_t)(*buf)[2] << 16) | ((uint64_t)(*buf)[3] << 24) | + ((uint64_t)(*buf)[4] << 32) | ((uint64_t)(*buf)[5] << 40); +#endif + + *buf += 6; + *len -= 6; + + return 1; +} + +/** + * \brief Read a 32 bit float from a buffer. + * + * Reads an 32 bit float from a buffer advancing the pointer and + * decrementing the length. + * + * \param buf A pointer to the buffer to read from. + * \param len A pointer to the buffer length. + * \param out A pointer to where the value will be stored. + * + * \retval Returns 1 if there was enough space in the buffer to read from, + * otherwise 0 is returned. + */ +static int DNP3ReadFloat32(const uint8_t **buf, uint32_t *len, float *out) +{ + if (*len < 4) { + return 0; + } + +#if __BYTE_ORDER == __LITTLE_ENDIAN + *((uint8_t *)out + 0) = (*buf)[0]; + *((uint8_t *)out + 1) = (*buf)[1]; + *((uint8_t *)out + 2) = (*buf)[2]; + *((uint8_t *)out + 3) = (*buf)[3]; +#else + *((uint8_t *)out + 3) = (*buf)[0]; + *((uint8_t *)out + 2) = (*buf)[1]; + *((uint8_t *)out + 1) = (*buf)[2]; + *((uint8_t *)out + 0) = (*buf)[3]; +#endif + *len -= 4; + *buf += 4; + + return 1; +} + +/** + * \brief Read a 64 bit float from a buffer. + * + * Reads an 64 bit float from a buffer advancing the pointer and + * decrementing the length. + * + * \param buf A pointer to the buffer to read from. + * \param len A pointer to the buffer length. + * \param out A pointer to where the value will be stored. + * + * \retval Returns 1 if there was enough space in the buffer to read from, + * otherwise 0 is returned. + */ +static int DNP3ReadFloat64(const uint8_t **buf, uint32_t *len, double *out) +{ + if (*len < 8) { + return 0; + } + +#if __BYTE_ORDER == __LITTLE_ENDIAN + *((uint8_t *)out + 0) = (*buf)[0]; + *((uint8_t *)out + 1) = (*buf)[1]; + *((uint8_t *)out + 2) = (*buf)[2]; + *((uint8_t *)out + 3) = (*buf)[3]; + *((uint8_t *)out + 4) = (*buf)[4]; + *((uint8_t *)out + 5) = (*buf)[5]; + *((uint8_t *)out + 6) = (*buf)[6]; + *((uint8_t *)out + 7) = (*buf)[7]; +#else + *((uint8_t *)out + 7) = (*buf)[0]; + *((uint8_t *)out + 6) = (*buf)[1]; + *((uint8_t *)out + 5) = (*buf)[2]; + *((uint8_t *)out + 4) = (*buf)[3]; + *((uint8_t *)out + 3) = (*buf)[4]; + *((uint8_t *)out + 2) = (*buf)[5]; + *((uint8_t *)out + 1) = (*buf)[6]; + *((uint8_t *)out + 0) = (*buf)[7]; +#endif + *len -= 8; + *buf += 8; + + return 1; +} + +/** + * \brief Get the prefix value and advance the buffer. + */ +static int DNP3ReadPrefix( + const uint8_t **buf, uint32_t *len, uint8_t prefix_code, uint32_t *out) +{ + uint8_t prefix_len = 0; + + switch (prefix_code) { + case 0x01: + case 0x04: + prefix_len = 1; + break; + case 0x02: + case 0x05: + prefix_len = 2; + break; + case 0x03: + case 0x06: + prefix_len = 4; + default: + break; + } + + if (*len < (uint32_t)prefix_len) { + return 0; + } + + switch (prefix_len) { + case sizeof(uint32_t): + DNP3ReadUint32(buf, len, out); + break; + case sizeof(uint16_t): { + /* Temp value for strict-aliasing. */ + uint16_t val = 0; + DNP3ReadUint16(buf, len, &val); + *out = val; + break; + } + case sizeof(uint8_t): { + /* Temp value for strict-aliasing. */ + uint8_t val = 0; + DNP3ReadUint8(buf, len, &val); + *out = val; + break; + } + default: + *out = 0; + break; + } + + return 1; +} + +/** + * \brief Add an object to a DNP3PointList. + * + * \retval 1 if successfull, 0 on failure. + */ +static int DNP3AddPoint(DNP3PointList *list, void *object, uint32_t index, + uint8_t prefix_code, uint32_t prefix) +{ + DNP3Point *point = SCCalloc(1, sizeof(*point)); + if (unlikely(point == NULL)) { + return 0; + } + TAILQ_INSERT_TAIL(list, point, next); + point->data = object; + point->prefix = prefix; + point->index = index; + switch (prefix_code) { + case 0x00: + break; + case 0x01: + case 0x02: + case 0x03: + point->index = prefix; + break; + case 0x04: + case 0x05: + case 0x06: + point->size = prefix; + break; + default: + break; + } + + return 1; +} + +/* START GENERATED CODE */ + +/* Code generated by: + * ./scripts/dnp3-gen/dnp3-gen.py + */ + +static int DNP3DecodeObjectG1V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG1V1 *object = NULL; + int bytes = (count / 8) + 1; + uint32_t prefix = 0; + int index = start; + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + for (int i = 0; i < bytes; i++) { + + uint8_t octet; + + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + + for (int j = 0; j < 8 && count; j = j + 1) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + object->state = (octet >> j) & 0x1; + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + count--; + index++; + } + + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + return 0; +} + +static int DNP3DecodeObjectG1V2(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG1V2 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->chatter_filter = (octet >> 5) & 0x1; + object->reserved = (octet >> 6) & 0x1; + object->state = (octet >> 7) & 0x1; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG2V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG2V1 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint8(buf, len, &object->state)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG2V2(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG2V2 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->chatter_filter = (octet >> 5) & 0x1; + object->reserved = (octet >> 6) & 0x1; + object->state = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG2V3(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG2V3 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->chatter_filter = (octet >> 5) & 0x1; + object->reserved = (octet >> 6) & 0x1; + object->state = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG3V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG3V1 *object = NULL; + int bytes = (count / 8) + 1; + uint32_t prefix = 0; + int index = start; + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + for (int i = 0; i < bytes; i++) { + + uint8_t octet; + + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + + for (int j = 0; j < 8 && count; j = j + 2) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + object->state = (octet >> j) & 0x3; + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + count--; + index++; + } + + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + return 0; +} + +static int DNP3DecodeObjectG3V2(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG3V2 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->chatter_filter = (octet >> 5) & 0x1; + object->state = (octet >> 6) & 0x3; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG4V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG4V1 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->chatter_filter = (octet >> 5) & 0x1; + object->state = (octet >> 6) & 0x3; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG4V2(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG4V2 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->chatter_filter = (octet >> 5) & 0x1; + object->state = (octet >> 6) & 0x3; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG4V3(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG4V3 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->chatter_filter = (octet >> 5) & 0x1; + object->state = (octet >> 6) & 0x3; + } + if (!DNP3ReadUint16(buf, len, &object->relative_time_ms)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG10V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG10V1 *object = NULL; + int bytes = (count / 8) + 1; + uint32_t prefix = 0; + int index = start; + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + for (int i = 0; i < bytes; i++) { + + uint8_t octet; + + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + + for (int j = 0; j < 8 && count; j = j + 1) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + object->state = (octet >> j) & 0x1; + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + count--; + index++; + } + + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + return 0; +} + +static int DNP3DecodeObjectG10V2(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG10V2 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->reserved0 = (octet >> 5) & 0x1; + object->reserved1 = (octet >> 6) & 0x1; + object->state = (octet >> 7) & 0x1; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG11V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG11V1 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->reserved0 = (octet >> 5) & 0x1; + object->reserved1 = (octet >> 6) & 0x1; + object->state = (octet >> 7) & 0x1; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG11V2(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG11V2 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->reserved0 = (octet >> 5) & 0x1; + object->reserved1 = (octet >> 6) & 0x1; + object->state = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG12V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG12V1 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->op_type = (octet >> 0) & 0xf; + object->qu = (octet >> 4) & 0x1; + object->cr = (octet >> 5) & 0x1; + object->tcc = (octet >> 6) & 0x3; + } + if (!DNP3ReadUint8(buf, len, &object->count)) { + goto error; + } + if (!DNP3ReadUint32(buf, len, &object->ontime)) { + goto error; + } + if (!DNP3ReadUint32(buf, len, &object->offtime)) { + goto error; + } + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->status_code = (octet >> 0) & 0x7f; + object->reserved = (octet >> 7) & 0x1; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG12V2(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG12V2 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->op_type = (octet >> 0) & 0xf; + object->qu = (octet >> 4) & 0x1; + object->cr = (octet >> 5) & 0x1; + object->tcc = (octet >> 6) & 0x3; + } + if (!DNP3ReadUint8(buf, len, &object->count)) { + goto error; + } + if (!DNP3ReadUint32(buf, len, &object->ontime)) { + goto error; + } + if (!DNP3ReadUint32(buf, len, &object->offtime)) { + goto error; + } + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->status_code = (octet >> 0) & 0x7f; + object->reserved = (octet >> 7) & 0x1; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG12V3(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG12V3 *object = NULL; + int bytes = (count / 8) + 1; + uint32_t prefix = 0; + int index = start; + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + for (int i = 0; i < bytes; i++) { + + uint8_t octet; + + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + + for (int j = 0; j < 8 && count; j = j + 1) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + object->point = (octet >> j) & 0x1; + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + count--; + index++; + } + + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + return 0; +} + +static int DNP3DecodeObjectG13V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG13V1 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->status_code = (octet >> 0) & 0x7f; + object->commanded_state = (octet >> 7) & 0x1; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG13V2(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG13V2 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->status_code = (octet >> 0) & 0x7f; + object->commanded_state = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG20V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG20V1 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->rollover = (octet >> 5) & 0x1; + object->discontinuity = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint32(buf, len, &object->count)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG20V2(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG20V2 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->rollover = (octet >> 5) & 0x1; + object->discontinuity = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, &object->count)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG20V3(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG20V3 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->rollover = (octet >> 5) & 0x1; + object->reserved0 = (octet >> 6) & 0x1; + object->reserved1 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint32(buf, len, &object->count)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG20V4(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG20V4 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->rollover = (octet >> 5) & 0x1; + object->reserved0 = (octet >> 6) & 0x1; + object->reserved1 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, &object->count)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG20V5(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG20V5 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint32(buf, len, &object->count)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG20V6(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG20V6 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint16(buf, len, &object->count)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG20V7(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG20V7 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint32(buf, len, &object->count)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG20V8(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG20V8 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint16(buf, len, &object->count)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG21V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG21V1 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->rollover = (octet >> 5) & 0x1; + object->discontinuity = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint32(buf, len, &object->count)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG21V2(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG21V2 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->rollover = (octet >> 5) & 0x1; + object->discontinuity = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, &object->count)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG21V3(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG21V3 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->rollover = (octet >> 5) & 0x1; + object->reserved0 = (octet >> 6) & 0x1; + object->reserved1 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint32(buf, len, &object->count)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG21V4(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG21V4 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->rollover = (octet >> 5) & 0x1; + object->reserved0 = (octet >> 6) & 0x1; + object->reserved1 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, &object->count)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG21V5(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG21V5 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->rollover = (octet >> 5) & 0x1; + object->discontinuity = (octet >> 6) & 0x1; + object->reserved1 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint32(buf, len, &object->count)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG21V6(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG21V6 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->rollover = (octet >> 5) & 0x1; + object->discontinuity = (octet >> 6) & 0x1; + object->reserved1 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, &object->count)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG21V7(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG21V7 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->rollover = (octet >> 5) & 0x1; + object->reserved0 = (octet >> 6) & 0x1; + object->reserved1 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint32(buf, len, &object->count)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG21V8(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG21V8 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->rollover = (octet >> 5) & 0x1; + object->reserved0 = (octet >> 6) & 0x1; + object->reserved1 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, &object->count)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG21V9(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG21V9 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint32(buf, len, &object->count)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG21V10(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG21V10 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint16(buf, len, &object->count)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG21V11(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG21V11 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint32(buf, len, &object->count)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG21V12(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG21V12 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint16(buf, len, &object->count)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG22V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG22V1 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->rollover = (octet >> 5) & 0x1; + object->discontinuity = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint32(buf, len, &object->count)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG22V2(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG22V2 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->rollover = (octet >> 5) & 0x1; + object->discontinuity = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, &object->count)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG22V3(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG22V3 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->rollover = (octet >> 5) & 0x1; + object->reserved0 = (octet >> 6) & 0x1; + object->reserved1 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint32(buf, len, &object->count)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG22V4(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG22V4 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->rollover = (octet >> 5) & 0x1; + object->reserved0 = (octet >> 6) & 0x1; + object->reserved1 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, &object->count)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG22V5(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG22V5 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->rollover = (octet >> 5) & 0x1; + object->reserved0 = (octet >> 6) & 0x1; + object->reserved1 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint32(buf, len, &object->count)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG22V6(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG22V6 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->rollover = (octet >> 5) & 0x1; + object->discontinuity = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, &object->count)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG22V7(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG22V7 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->rollover = (octet >> 5) & 0x1; + object->reserved0 = (octet >> 6) & 0x1; + object->reserved1 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint32(buf, len, &object->count)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG22V8(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG22V8 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->rollover = (octet >> 5) & 0x1; + object->reserved0 = (octet >> 6) & 0x1; + object->reserved1 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, &object->count)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG23V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG23V1 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->rollover = (octet >> 5) & 0x1; + object->discontinuity = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint32(buf, len, &object->count)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG23V2(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG23V2 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->rollover = (octet >> 5) & 0x1; + object->reserved0 = (octet >> 6) & 0x1; + object->reserved1 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, &object->count)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG23V3(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG23V3 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->rollover = (octet >> 5) & 0x1; + object->reserved0 = (octet >> 6) & 0x1; + object->reserved1 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint32(buf, len, &object->count)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG23V4(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG23V4 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->rollover = (octet >> 5) & 0x1; + object->reserved0 = (octet >> 6) & 0x1; + object->reserved1 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, &object->count)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG23V5(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG23V5 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->rollover = (octet >> 5) & 0x1; + object->discontinuity = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint32(buf, len, &object->count)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG23V6(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG23V6 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->rollover = (octet >> 5) & 0x1; + object->discontinuity = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, &object->count)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG23V7(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG23V7 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->rollover = (octet >> 5) & 0x1; + object->reserved0 = (octet >> 6) & 0x1; + object->reserved1 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint32(buf, len, &object->count)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG23V8(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG23V8 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->rollover = (octet >> 5) & 0x1; + object->reserved0 = (octet >> 6) & 0x1; + object->reserved1 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, &object->count)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG30V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG30V1 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint32(buf, len, (uint32_t *)&object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG30V2(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG30V2 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, (uint16_t *)&object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG30V3(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG30V3 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint32(buf, len, (uint32_t *)&object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG30V4(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG30V4 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint16(buf, len, (uint16_t *)&object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG30V5(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG30V5 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadFloat32(buf, len, &object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG30V6(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG30V6 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadFloat64(buf, len, &object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG31V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG31V1 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint32(buf, len, (uint32_t *)&object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG31V2(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG31V2 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, (uint16_t *)&object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG31V3(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG31V3 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint32(buf, len, (uint32_t *)&object->value)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG31V4(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG31V4 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, (uint16_t *)&object->value)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG31V5(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG31V5 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint32(buf, len, (uint32_t *)&object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG31V6(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG31V6 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint16(buf, len, (uint16_t *)&object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG31V7(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG31V7 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadFloat32(buf, len, &object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG31V8(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG31V8 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadFloat64(buf, len, &object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG32V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG32V1 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint32(buf, len, (uint32_t *)&object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG32V2(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG32V2 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, (uint16_t *)&object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG32V3(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG32V3 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint32(buf, len, (uint32_t *)&object->value)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG32V4(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG32V4 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, (uint16_t *)&object->value)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG32V5(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG32V5 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadFloat32(buf, len, &object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG32V6(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG32V6 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadFloat64(buf, len, &object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG32V7(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG32V7 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadFloat32(buf, len, &object->value)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG32V8(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG32V8 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadFloat64(buf, len, &object->value)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG33V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG33V1 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint32(buf, len, (uint32_t *)&object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG33V2(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG33V2 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, (uint16_t *)&object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG33V3(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG33V3 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint32(buf, len, (uint32_t *)&object->value)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG33V4(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG33V4 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, (uint16_t *)&object->value)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG33V5(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG33V5 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadFloat32(buf, len, &object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG33V6(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG33V6 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadFloat64(buf, len, &object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG33V7(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG33V7 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadFloat32(buf, len, &object->value)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG33V8(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG33V8 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadFloat64(buf, len, &object->value)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG34V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG34V1 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint16(buf, len, &object->deadband_value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG34V2(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG34V2 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint32(buf, len, &object->deadband_value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG34V3(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG34V3 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadFloat32(buf, len, &object->deadband_value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG40V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG40V1 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint32(buf, len, (uint32_t *)&object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG40V2(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG40V2 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, (uint16_t *)&object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG40V3(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG40V3 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadFloat32(buf, len, &object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG40V4(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG40V4 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadFloat64(buf, len, &object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG41V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG41V1 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint32(buf, len, (uint32_t *)&object->value)) { + goto error; + } + if (!DNP3ReadUint8(buf, len, &object->control_status)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG41V2(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG41V2 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint16(buf, len, (uint16_t *)&object->value)) { + goto error; + } + if (!DNP3ReadUint8(buf, len, &object->control_status)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG41V3(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG41V3 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadFloat32(buf, len, &object->value)) { + goto error; + } + if (!DNP3ReadUint8(buf, len, &object->control_status)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG41V4(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG41V4 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadFloat64(buf, len, &object->value)) { + goto error; + } + if (!DNP3ReadUint8(buf, len, &object->control_status)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG42V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG42V1 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint32(buf, len, (uint32_t *)&object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG42V2(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG42V2 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, (uint16_t *)&object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG42V3(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG42V3 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint32(buf, len, (uint32_t *)&object->value)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG42V4(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG42V4 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, (uint16_t *)&object->value)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG42V5(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG42V5 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadFloat32(buf, len, &object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG42V6(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG42V6 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadFloat64(buf, len, &object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG42V7(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG42V7 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadFloat32(buf, len, &object->value)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG42V8(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG42V8 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->over_range = (octet >> 5) & 0x1; + object->reference_err = (octet >> 6) & 0x1; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadFloat64(buf, len, &object->value)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG43V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG43V1 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->status_code = (octet >> 0) & 0x7f; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint32(buf, len, (uint32_t *)&object->commanded_value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG43V2(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG43V2 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->status_code = (octet >> 0) & 0x7f; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, (uint16_t *)&object->commanded_value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG43V3(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG43V3 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->status_code = (octet >> 0) & 0x7f; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint32(buf, len, (uint32_t *)&object->commanded_value)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG43V4(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG43V4 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->status_code = (octet >> 0) & 0x7f; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, (uint16_t *)&object->commanded_value)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG43V5(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG43V5 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->status_code = (octet >> 0) & 0x7f; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadFloat32(buf, len, &object->commanded_value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG43V6(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG43V6 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->status_code = (octet >> 0) & 0x7f; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadFloat64(buf, len, &object->commanded_value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG43V7(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG43V7 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->status_code = (octet >> 0) & 0x7f; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadFloat32(buf, len, &object->commanded_value)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG43V8(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG43V8 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->status_code = (octet >> 0) & 0x7f; + object->reserved0 = (octet >> 7) & 0x1; + } + if (!DNP3ReadFloat64(buf, len, &object->commanded_value)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG50V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG50V1 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG50V2(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG50V2 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + if (!DNP3ReadUint32(buf, len, &object->interval)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG50V3(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG50V3 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG50V4(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG50V4 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + if (!DNP3ReadUint32(buf, len, &object->interval_count)) { + goto error; + } + if (!DNP3ReadUint8(buf, len, &object->interval_units)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG51V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG51V1 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG51V2(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG51V2 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG52V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG52V1 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint16(buf, len, &object->delay_secs)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG52V2(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG52V2 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint16(buf, len, &object->delay_ms)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG70V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG70V1 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint16(buf, len, &object->filename_size)) { + goto error; + } + if (!DNP3ReadUint8(buf, len, &object->filetype_code)) { + goto error; + } + if (!DNP3ReadUint8(buf, len, &object->attribute_code)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->start_record)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->end_record)) { + goto error; + } + if (!DNP3ReadUint32(buf, len, &object->file_size)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->created_timestamp)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->permission)) { + goto error; + } + if (!DNP3ReadUint32(buf, len, &object->file_id)) { + goto error; + } + if (!DNP3ReadUint32(buf, len, &object->owner_id)) { + goto error; + } + if (!DNP3ReadUint32(buf, len, &object->group_id)) { + goto error; + } + if (!DNP3ReadUint8(buf, len, &object->file_function_code)) { + goto error; + } + if (!DNP3ReadUint8(buf, len, &object->status_code)) { + goto error; + } + if (object->filename_size > 0) { + memcpy(object->filename, *buf, object->filename_size); + *buf += object->filename_size; + *len -= object->filename_size; + } + object->filename[object->filename_size] = '\0'; + if (!DNP3ReadUint16(buf, len, &object->data_size)) { + goto error; + } + if (object->data_size > 0) { + memcpy(object->data, *buf, object->data_size); + *buf += object->data_size; + *len -= object->data_size; + } + object->data[object->data_size] = '\0'; + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG70V2(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG70V2 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint16(buf, len, &object->username_offset)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->username_size)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->password_offset)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->password_size)) { + goto error; + } + if (!DNP3ReadUint32(buf, len, &object->authentication_key)) { + goto error; + } + if (object->username_size > 0) { + memcpy(object->username, *buf, object->username_size); + *buf += object->username_size; + *len -= object->username_size; + } + object->username[object->username_size] = '\0'; + if (object->password_size > 0) { + memcpy(object->password, *buf, object->password_size); + *buf += object->password_size; + *len -= object->password_size; + } + object->password[object->password_size] = '\0'; + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG70V3(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG70V3 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint16(buf, len, &object->filename_offset)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->filename_size)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->created)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->permissions)) { + goto error; + } + if (!DNP3ReadUint32(buf, len, &object->authentication_key)) { + goto error; + } + if (!DNP3ReadUint32(buf, len, &object->file_size)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->operational_mode)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->maximum_block_size)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->request_id)) { + goto error; + } + if (object->filename_size > 0) { + memcpy(object->filename, *buf, object->filename_size); + *buf += object->filename_size; + *len -= object->filename_size; + } + object->filename[object->filename_size] = '\0'; + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG70V4(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG70V4 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + uint32_t offset; + + if (!DNP3PrefixIsSize(prefix_code)) { + goto error; + } + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + offset = *len; + + if (!DNP3ReadUint32(buf, len, &object->file_handle)) { + goto error; + } + if (!DNP3ReadUint32(buf, len, &object->file_size)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->maximum_block_size)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->request_id)) { + goto error; + } + if (!DNP3ReadUint8(buf, len, &object->status_code)) { + goto error; + } + object->optional_text_len = prefix - (offset - *len); + if (object->optional_text_len > 0) { + memcpy(object->optional_text, *buf, object->optional_text_len); + *buf += object->optional_text_len; + *len -= object->optional_text_len; + } + object->optional_text[object->optional_text_len] = '\0'; + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG70V5(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG70V5 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + uint32_t offset; + + if (!DNP3PrefixIsSize(prefix_code)) { + goto error; + } + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + offset = *len; + + if (!DNP3ReadUint32(buf, len, &object->file_handle)) { + goto error; + } + if (!DNP3ReadUint32(buf, len, &object->block_number)) { + goto error; + } + object->file_data_len = prefix - (offset - *len); + if (object->file_data_len > 0) { + memcpy(object->file_data, *buf, object->file_data_len); + *buf += object->file_data_len; + *len -= object->file_data_len; + } + object->file_data[object->file_data_len] = '\0'; + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG70V6(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG70V6 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + uint32_t offset; + + if (!DNP3PrefixIsSize(prefix_code)) { + goto error; + } + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + offset = *len; + + if (!DNP3ReadUint32(buf, len, &object->file_handle)) { + goto error; + } + if (!DNP3ReadUint32(buf, len, &object->block_number)) { + goto error; + } + if (!DNP3ReadUint8(buf, len, &object->status_code)) { + goto error; + } + object->optional_text_len = prefix - (offset - *len); + if (object->optional_text_len > 0) { + memcpy(object->optional_text, *buf, object->optional_text_len); + *buf += object->optional_text_len; + *len -= object->optional_text_len; + } + object->optional_text[object->optional_text_len] = '\0'; + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG70V7(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG70V7 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint16(buf, len, &object->filename_offset)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->filename_size)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->file_type)) { + goto error; + } + if (!DNP3ReadUint32(buf, len, &object->file_size)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->created_timestamp)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->permissions)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->request_id)) { + goto error; + } + if (object->filename_size > 0) { + memcpy(object->filename, *buf, object->filename_size); + *buf += object->filename_size; + *len -= object->filename_size; + } + object->filename[object->filename_size] = '\0'; + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG70V8(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG70V8 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + uint32_t offset; + + if (prefix_code != 5) { + goto error; + } + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + offset = *len; + + object->file_specification_len = prefix - (offset - *len); + if (object->file_specification_len > 0) { + memcpy(object->file_specification, *buf, object->file_specification_len); + *buf += object->file_specification_len; + *len -= object->file_specification_len; + } + object->file_specification[object->file_specification_len] = '\0'; + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG80V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG80V1 *object = NULL; + int bytes = (count / 8) + 1; + uint32_t prefix = 0; + int index = start; + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + for (int i = 0; i < bytes; i++) { + + uint8_t octet; + + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + + for (int j = 0; j < 8 && count; j = j + 1) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + object->state = (octet >> j) & 0x1; + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + count--; + index++; + } + + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + return 0; +} + +static int DNP3DecodeObjectG81V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG81V1 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->fill_percentage = (octet >> 0) & 0x7f; + object->overflow_state = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint8(buf, len, &object->group)) { + goto error; + } + if (!DNP3ReadUint8(buf, len, &object->variation)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG83V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG83V1 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (*len < 4) { + goto error; + } + memcpy(object->vendor_code, *buf, 4); + object->vendor_code[4] = '\0'; + *buf += 4; + *len -= 4; + if (!DNP3ReadUint16(buf, len, &object->object_id)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->length)) { + goto error; + } + if (object->length > 0) { + if (*len < object->length) { + /* Not enough data. */ + goto error; + } + object->data_objects = SCCalloc(1, object->length); + if (unlikely(object->data_objects == NULL)) { + goto error; + } + memcpy(object->data_objects, *buf, object->length); + *buf += object->length; + *len -= object->length; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG86V2(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG86V2 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->rd = (octet >> 0) & 0x1; + object->wr = (octet >> 1) & 0x1; + object->st = (octet >> 2) & 0x1; + object->ev = (octet >> 3) & 0x1; + object->df = (octet >> 4) & 0x1; + object->padding0 = (octet >> 5) & 0x1; + object->padding1 = (octet >> 6) & 0x1; + object->padding2 = (octet >> 7) & 0x1; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG102V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG102V1 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint8(buf, len, &object->value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG120V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG120V1 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + uint32_t offset; + + if (prefix_code != 5) { + goto error; + } + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + offset = *len; + + if (!DNP3ReadUint32(buf, len, &object->csq)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->usr)) { + goto error; + } + if (!DNP3ReadUint8(buf, len, &object->mal)) { + goto error; + } + if (!DNP3ReadUint8(buf, len, &object->reason)) { + goto error; + } + object->challenge_data_len = prefix - (offset - *len); + if (object->challenge_data_len > 0) { + if (*len < object->challenge_data_len) { + /* Not enough data. */ + goto error; + } + object->challenge_data = SCCalloc(1, object->challenge_data_len); + if (unlikely(object->challenge_data == NULL)) { + goto error; + } + memcpy(object->challenge_data, *buf, object->challenge_data_len); + *buf += object->challenge_data_len; + *len -= object->challenge_data_len; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG120V2(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG120V2 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + uint32_t offset; + + if (prefix_code != 5) { + goto error; + } + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + offset = *len; + + if (!DNP3ReadUint32(buf, len, &object->csq)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->usr)) { + goto error; + } + object->mac_value_len = prefix - (offset - *len); + if (object->mac_value_len > 0) { + if (*len < object->mac_value_len) { + /* Not enough data. */ + goto error; + } + object->mac_value = SCCalloc(1, object->mac_value_len); + if (unlikely(object->mac_value == NULL)) { + goto error; + } + memcpy(object->mac_value, *buf, object->mac_value_len); + *buf += object->mac_value_len; + *len -= object->mac_value_len; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG120V3(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG120V3 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint32(buf, len, &object->csq)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->user_number)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG120V4(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG120V4 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint16(buf, len, &object->user_number)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG120V5(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG120V5 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + uint32_t offset; + + if (prefix_code != 5) { + goto error; + } + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + offset = *len; + + if (!DNP3ReadUint32(buf, len, &object->ksq)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->user_number)) { + goto error; + } + if (!DNP3ReadUint8(buf, len, &object->key_wrap_alg)) { + goto error; + } + if (!DNP3ReadUint8(buf, len, &object->key_status)) { + goto error; + } + if (!DNP3ReadUint8(buf, len, &object->mal)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->challenge_data_len)) { + goto error; + } + if (object->challenge_data_len > 0) { + if (*len < object->challenge_data_len) { + /* Not enough data. */ + goto error; + } + object->challenge_data = SCCalloc(1, object->challenge_data_len); + if (unlikely(object->challenge_data == NULL)) { + goto error; + } + memcpy(object->challenge_data, *buf, object->challenge_data_len); + *buf += object->challenge_data_len; + *len -= object->challenge_data_len; + } + object->mac_value_len = prefix - (offset - *len); + if (object->mac_value_len > 0) { + if (*len < object->mac_value_len) { + /* Not enough data. */ + goto error; + } + object->mac_value = SCCalloc(1, object->mac_value_len); + if (unlikely(object->mac_value == NULL)) { + goto error; + } + memcpy(object->mac_value, *buf, object->mac_value_len); + *buf += object->mac_value_len; + *len -= object->mac_value_len; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG120V6(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG120V6 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + uint32_t offset; + + if (prefix_code != 5) { + goto error; + } + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + offset = *len; + + if (!DNP3ReadUint24(buf, len, &object->ksq)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->usr)) { + goto error; + } + object->wrapped_key_data_len = prefix - (offset - *len); + if (object->wrapped_key_data_len > 0) { + if (*len < object->wrapped_key_data_len) { + /* Not enough data. */ + goto error; + } + object->wrapped_key_data = SCCalloc(1, object->wrapped_key_data_len); + if (unlikely(object->wrapped_key_data == NULL)) { + goto error; + } + memcpy(object->wrapped_key_data, *buf, object->wrapped_key_data_len); + *buf += object->wrapped_key_data_len; + *len -= object->wrapped_key_data_len; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG120V7(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG120V7 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + uint32_t offset; + + if (prefix_code != 5) { + goto error; + } + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + offset = *len; + + if (!DNP3ReadUint32(buf, len, &object->sequence_number)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->usr)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->association_id)) { + goto error; + } + if (!DNP3ReadUint8(buf, len, &object->error_code)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->time_of_error)) { + goto error; + } + object->error_text_len = prefix - (offset - *len); + if (object->error_text_len > 0) { + memcpy(object->error_text, *buf, object->error_text_len); + *buf += object->error_text_len; + *len -= object->error_text_len; + } + object->error_text[object->error_text_len] = '\0'; + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG120V8(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG120V8 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + uint32_t offset; + + if (prefix_code != 5) { + goto error; + } + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + offset = *len; + + if (!DNP3ReadUint8(buf, len, &object->key_change_method)) { + goto error; + } + if (!DNP3ReadUint8(buf, len, &object->certificate_type)) { + goto error; + } + object->certificate_len = prefix - (offset - *len); + if (object->certificate_len > 0) { + if (*len < object->certificate_len) { + /* Not enough data. */ + goto error; + } + object->certificate = SCCalloc(1, object->certificate_len); + if (unlikely(object->certificate == NULL)) { + goto error; + } + memcpy(object->certificate, *buf, object->certificate_len); + *buf += object->certificate_len; + *len -= object->certificate_len; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG120V9(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG120V9 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + uint32_t offset; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + offset = *len; + + object->mac_value_len = prefix - (offset - *len); + if (object->mac_value_len > 0) { + if (*len < object->mac_value_len) { + /* Not enough data. */ + goto error; + } + object->mac_value = SCCalloc(1, object->mac_value_len); + if (unlikely(object->mac_value == NULL)) { + goto error; + } + memcpy(object->mac_value, *buf, object->mac_value_len); + *buf += object->mac_value_len; + *len -= object->mac_value_len; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG120V10(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG120V10 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + if (prefix_code != 5) { + goto error; + } + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint8(buf, len, &object->key_change_method)) { + goto error; + } + if (!DNP3ReadUint8(buf, len, &object->operation)) { + goto error; + } + if (!DNP3ReadUint32(buf, len, &object->scs)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->user_role)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->user_role_expiry_interval)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->username_len)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->user_public_key_len)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->certification_data_len)) { + goto error; + } + if (object->username_len > 0) { + memcpy(object->username, *buf, object->username_len); + *buf += object->username_len; + *len -= object->username_len; + } + object->username[object->username_len] = '\0'; + if (object->user_public_key_len > 0) { + if (*len < object->user_public_key_len) { + /* Not enough data. */ + goto error; + } + object->user_public_key = SCCalloc(1, object->user_public_key_len); + if (unlikely(object->user_public_key == NULL)) { + goto error; + } + memcpy(object->user_public_key, *buf, object->user_public_key_len); + *buf += object->user_public_key_len; + *len -= object->user_public_key_len; + } + if (object->certification_data_len > 0) { + if (*len < object->certification_data_len) { + /* Not enough data. */ + goto error; + } + object->certification_data = SCCalloc(1, object->certification_data_len); + if (unlikely(object->certification_data == NULL)) { + goto error; + } + memcpy(object->certification_data, *buf, object->certification_data_len); + *buf += object->certification_data_len; + *len -= object->certification_data_len; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG120V11(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG120V11 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + if (prefix_code != 5) { + goto error; + } + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint8(buf, len, &object->key_change_method)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->username_len)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->master_challenge_data_len)) { + goto error; + } + if (object->username_len > 0) { + memcpy(object->username, *buf, object->username_len); + *buf += object->username_len; + *len -= object->username_len; + } + object->username[object->username_len] = '\0'; + if (object->master_challenge_data_len > 0) { + if (*len < object->master_challenge_data_len) { + /* Not enough data. */ + goto error; + } + object->master_challenge_data = SCCalloc(1, object->master_challenge_data_len); + if (unlikely(object->master_challenge_data == NULL)) { + goto error; + } + memcpy(object->master_challenge_data, *buf, object->master_challenge_data_len); + *buf += object->master_challenge_data_len; + *len -= object->master_challenge_data_len; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG120V12(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG120V12 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + if (prefix_code != 5) { + goto error; + } + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint32(buf, len, &object->ksq)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->user_number)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->challenge_data_len)) { + goto error; + } + if (object->challenge_data_len > 0) { + if (*len < object->challenge_data_len) { + /* Not enough data. */ + goto error; + } + object->challenge_data = SCCalloc(1, object->challenge_data_len); + if (unlikely(object->challenge_data == NULL)) { + goto error; + } + memcpy(object->challenge_data, *buf, object->challenge_data_len); + *buf += object->challenge_data_len; + *len -= object->challenge_data_len; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG120V13(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG120V13 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + if (prefix_code != 5) { + goto error; + } + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + if (!DNP3ReadUint32(buf, len, &object->ksq)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->user_number)) { + goto error; + } + if (!DNP3ReadUint16(buf, len, &object->encrypted_update_key_len)) { + goto error; + } + if (object->encrypted_update_key_len > 0) { + if (*len < object->encrypted_update_key_len) { + /* Not enough data. */ + goto error; + } + object->encrypted_update_key_data = SCCalloc(1, object->encrypted_update_key_len); + if (unlikely(object->encrypted_update_key_data == NULL)) { + goto error; + } + memcpy(object->encrypted_update_key_data, *buf, object->encrypted_update_key_len); + *buf += object->encrypted_update_key_len; + *len -= object->encrypted_update_key_len; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG120V14(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG120V14 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + uint32_t offset; + + if (prefix_code != 5) { + goto error; + } + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + offset = *len; + + object->digital_signature_len = prefix - (offset - *len); + if (object->digital_signature_len > 0) { + if (*len < object->digital_signature_len) { + /* Not enough data. */ + goto error; + } + object->digital_signature = SCCalloc(1, object->digital_signature_len); + if (unlikely(object->digital_signature == NULL)) { + goto error; + } + memcpy(object->digital_signature, *buf, object->digital_signature_len); + *buf += object->digital_signature_len; + *len -= object->digital_signature_len; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG120V15(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG120V15 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + uint32_t offset; + + if (prefix_code != 5) { + goto error; + } + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + offset = *len; + + object->mac_len = prefix - (offset - *len); + if (object->mac_len > 0) { + if (*len < object->mac_len) { + /* Not enough data. */ + goto error; + } + object->mac = SCCalloc(1, object->mac_len); + if (unlikely(object->mac == NULL)) { + goto error; + } + memcpy(object->mac, *buf, object->mac_len); + *buf += object->mac_len; + *len -= object->mac_len; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG121V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG121V1 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->reserved0 = (octet >> 5) & 0x1; + object->discontinuity = (octet >> 6) & 0x1; + object->reserved1 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, &object->association_id)) { + goto error; + } + if (!DNP3ReadUint32(buf, len, &object->count_value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG122V1(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG122V1 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->reserved0 = (octet >> 5) & 0x1; + object->discontinuity = (octet >> 6) & 0x1; + object->reserved1 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, &object->association_id)) { + goto error; + } + if (!DNP3ReadUint32(buf, len, &object->count_value)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + +static int DNP3DecodeObjectG122V2(const uint8_t **buf, uint32_t *len, + uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *points) +{ + DNP3ObjectG122V2 *object = NULL; + uint32_t prefix = 0; + uint32_t index = start; + + while (count--) { + + object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + goto error; + } + + if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) { + goto error; + } + + { + uint8_t octet; + if (!DNP3ReadUint8(buf, len, &octet)) { + goto error; + } + object->online = (octet >> 0) & 0x1; + object->restart = (octet >> 1) & 0x1; + object->comm_lost = (octet >> 2) & 0x1; + object->remote_forced = (octet >> 3) & 0x1; + object->local_forced = (octet >> 4) & 0x1; + object->reserved0 = (octet >> 5) & 0x1; + object->discontinuity = (octet >> 6) & 0x1; + object->reserved1 = (octet >> 7) & 0x1; + } + if (!DNP3ReadUint16(buf, len, &object->association_id)) { + goto error; + } + if (!DNP3ReadUint32(buf, len, &object->count_value)) { + goto error; + } + if (!DNP3ReadUint48(buf, len, &object->timestamp)) { + goto error; + } + + if (!DNP3AddPoint(points, object, index, prefix_code, prefix)) { + goto error; + } + + object = NULL; + index++; + } + + return 1; +error: + if (object != NULL) { + SCFree(object); + } + + return 0; +} + + +void DNP3FreeObjectPoint(int group, int variation, void *point) +{ + switch(DNP3_OBJECT_CODE(group, variation)) { + case DNP3_OBJECT_CODE(83, 1): { + DNP3ObjectG83V1 *object = (DNP3ObjectG83V1 *) point; + if (object->data_objects != NULL) { + SCFree(object->data_objects); + } + break; + } + case DNP3_OBJECT_CODE(120, 1): { + DNP3ObjectG120V1 *object = (DNP3ObjectG120V1 *) point; + if (object->challenge_data != NULL) { + SCFree(object->challenge_data); + } + break; + } + case DNP3_OBJECT_CODE(120, 2): { + DNP3ObjectG120V2 *object = (DNP3ObjectG120V2 *) point; + if (object->mac_value != NULL) { + SCFree(object->mac_value); + } + break; + } + case DNP3_OBJECT_CODE(120, 5): { + DNP3ObjectG120V5 *object = (DNP3ObjectG120V5 *) point; + if (object->challenge_data != NULL) { + SCFree(object->challenge_data); + } + if (object->mac_value != NULL) { + SCFree(object->mac_value); + } + break; + } + case DNP3_OBJECT_CODE(120, 6): { + DNP3ObjectG120V6 *object = (DNP3ObjectG120V6 *) point; + if (object->wrapped_key_data != NULL) { + SCFree(object->wrapped_key_data); + } + break; + } + case DNP3_OBJECT_CODE(120, 8): { + DNP3ObjectG120V8 *object = (DNP3ObjectG120V8 *) point; + if (object->certificate != NULL) { + SCFree(object->certificate); + } + break; + } + case DNP3_OBJECT_CODE(120, 9): { + DNP3ObjectG120V9 *object = (DNP3ObjectG120V9 *) point; + if (object->mac_value != NULL) { + SCFree(object->mac_value); + } + break; + } + case DNP3_OBJECT_CODE(120, 10): { + DNP3ObjectG120V10 *object = (DNP3ObjectG120V10 *) point; + if (object->user_public_key != NULL) { + SCFree(object->user_public_key); + } + if (object->certification_data != NULL) { + SCFree(object->certification_data); + } + break; + } + case DNP3_OBJECT_CODE(120, 11): { + DNP3ObjectG120V11 *object = (DNP3ObjectG120V11 *) point; + if (object->master_challenge_data != NULL) { + SCFree(object->master_challenge_data); + } + break; + } + case DNP3_OBJECT_CODE(120, 12): { + DNP3ObjectG120V12 *object = (DNP3ObjectG120V12 *) point; + if (object->challenge_data != NULL) { + SCFree(object->challenge_data); + } + break; + } + case DNP3_OBJECT_CODE(120, 13): { + DNP3ObjectG120V13 *object = (DNP3ObjectG120V13 *) point; + if (object->encrypted_update_key_data != NULL) { + SCFree(object->encrypted_update_key_data); + } + break; + } + case DNP3_OBJECT_CODE(120, 14): { + DNP3ObjectG120V14 *object = (DNP3ObjectG120V14 *) point; + if (object->digital_signature != NULL) { + SCFree(object->digital_signature); + } + break; + } + case DNP3_OBJECT_CODE(120, 15): { + DNP3ObjectG120V15 *object = (DNP3ObjectG120V15 *) point; + if (object->mac != NULL) { + SCFree(object->mac); + } + break; + } + default: + break; + } + SCFree(point); +} + +/** + * \brief Decode a DNP3 object. + * + * \retval 0 on success. On failure a positive integer corresponding + * to a DNP3 application layer event will be returned. + */ +int DNP3DecodeObject(int group, int variation, const uint8_t **buf, + uint32_t *len, uint8_t prefix_code, uint32_t start, + uint32_t count, DNP3PointList *points) +{ + int rc = 0; + + switch (DNP3_OBJECT_CODE(group, variation)) { + case DNP3_OBJECT_CODE(1, 1): + rc = DNP3DecodeObjectG1V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(1, 2): + rc = DNP3DecodeObjectG1V2(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(2, 1): + rc = DNP3DecodeObjectG2V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(2, 2): + rc = DNP3DecodeObjectG2V2(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(2, 3): + rc = DNP3DecodeObjectG2V3(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(3, 1): + rc = DNP3DecodeObjectG3V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(3, 2): + rc = DNP3DecodeObjectG3V2(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(4, 1): + rc = DNP3DecodeObjectG4V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(4, 2): + rc = DNP3DecodeObjectG4V2(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(4, 3): + rc = DNP3DecodeObjectG4V3(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(10, 1): + rc = DNP3DecodeObjectG10V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(10, 2): + rc = DNP3DecodeObjectG10V2(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(11, 1): + rc = DNP3DecodeObjectG11V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(11, 2): + rc = DNP3DecodeObjectG11V2(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(12, 1): + rc = DNP3DecodeObjectG12V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(12, 2): + rc = DNP3DecodeObjectG12V2(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(12, 3): + rc = DNP3DecodeObjectG12V3(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(13, 1): + rc = DNP3DecodeObjectG13V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(13, 2): + rc = DNP3DecodeObjectG13V2(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(20, 1): + rc = DNP3DecodeObjectG20V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(20, 2): + rc = DNP3DecodeObjectG20V2(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(20, 3): + rc = DNP3DecodeObjectG20V3(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(20, 4): + rc = DNP3DecodeObjectG20V4(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(20, 5): + rc = DNP3DecodeObjectG20V5(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(20, 6): + rc = DNP3DecodeObjectG20V6(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(20, 7): + rc = DNP3DecodeObjectG20V7(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(20, 8): + rc = DNP3DecodeObjectG20V8(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(21, 1): + rc = DNP3DecodeObjectG21V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(21, 2): + rc = DNP3DecodeObjectG21V2(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(21, 3): + rc = DNP3DecodeObjectG21V3(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(21, 4): + rc = DNP3DecodeObjectG21V4(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(21, 5): + rc = DNP3DecodeObjectG21V5(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(21, 6): + rc = DNP3DecodeObjectG21V6(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(21, 7): + rc = DNP3DecodeObjectG21V7(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(21, 8): + rc = DNP3DecodeObjectG21V8(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(21, 9): + rc = DNP3DecodeObjectG21V9(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(21, 10): + rc = DNP3DecodeObjectG21V10(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(21, 11): + rc = DNP3DecodeObjectG21V11(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(21, 12): + rc = DNP3DecodeObjectG21V12(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(22, 1): + rc = DNP3DecodeObjectG22V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(22, 2): + rc = DNP3DecodeObjectG22V2(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(22, 3): + rc = DNP3DecodeObjectG22V3(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(22, 4): + rc = DNP3DecodeObjectG22V4(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(22, 5): + rc = DNP3DecodeObjectG22V5(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(22, 6): + rc = DNP3DecodeObjectG22V6(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(22, 7): + rc = DNP3DecodeObjectG22V7(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(22, 8): + rc = DNP3DecodeObjectG22V8(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(23, 1): + rc = DNP3DecodeObjectG23V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(23, 2): + rc = DNP3DecodeObjectG23V2(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(23, 3): + rc = DNP3DecodeObjectG23V3(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(23, 4): + rc = DNP3DecodeObjectG23V4(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(23, 5): + rc = DNP3DecodeObjectG23V5(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(23, 6): + rc = DNP3DecodeObjectG23V6(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(23, 7): + rc = DNP3DecodeObjectG23V7(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(23, 8): + rc = DNP3DecodeObjectG23V8(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(30, 1): + rc = DNP3DecodeObjectG30V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(30, 2): + rc = DNP3DecodeObjectG30V2(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(30, 3): + rc = DNP3DecodeObjectG30V3(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(30, 4): + rc = DNP3DecodeObjectG30V4(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(30, 5): + rc = DNP3DecodeObjectG30V5(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(30, 6): + rc = DNP3DecodeObjectG30V6(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(31, 1): + rc = DNP3DecodeObjectG31V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(31, 2): + rc = DNP3DecodeObjectG31V2(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(31, 3): + rc = DNP3DecodeObjectG31V3(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(31, 4): + rc = DNP3DecodeObjectG31V4(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(31, 5): + rc = DNP3DecodeObjectG31V5(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(31, 6): + rc = DNP3DecodeObjectG31V6(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(31, 7): + rc = DNP3DecodeObjectG31V7(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(31, 8): + rc = DNP3DecodeObjectG31V8(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(32, 1): + rc = DNP3DecodeObjectG32V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(32, 2): + rc = DNP3DecodeObjectG32V2(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(32, 3): + rc = DNP3DecodeObjectG32V3(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(32, 4): + rc = DNP3DecodeObjectG32V4(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(32, 5): + rc = DNP3DecodeObjectG32V5(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(32, 6): + rc = DNP3DecodeObjectG32V6(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(32, 7): + rc = DNP3DecodeObjectG32V7(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(32, 8): + rc = DNP3DecodeObjectG32V8(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(33, 1): + rc = DNP3DecodeObjectG33V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(33, 2): + rc = DNP3DecodeObjectG33V2(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(33, 3): + rc = DNP3DecodeObjectG33V3(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(33, 4): + rc = DNP3DecodeObjectG33V4(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(33, 5): + rc = DNP3DecodeObjectG33V5(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(33, 6): + rc = DNP3DecodeObjectG33V6(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(33, 7): + rc = DNP3DecodeObjectG33V7(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(33, 8): + rc = DNP3DecodeObjectG33V8(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(34, 1): + rc = DNP3DecodeObjectG34V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(34, 2): + rc = DNP3DecodeObjectG34V2(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(34, 3): + rc = DNP3DecodeObjectG34V3(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(40, 1): + rc = DNP3DecodeObjectG40V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(40, 2): + rc = DNP3DecodeObjectG40V2(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(40, 3): + rc = DNP3DecodeObjectG40V3(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(40, 4): + rc = DNP3DecodeObjectG40V4(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(41, 1): + rc = DNP3DecodeObjectG41V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(41, 2): + rc = DNP3DecodeObjectG41V2(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(41, 3): + rc = DNP3DecodeObjectG41V3(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(41, 4): + rc = DNP3DecodeObjectG41V4(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(42, 1): + rc = DNP3DecodeObjectG42V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(42, 2): + rc = DNP3DecodeObjectG42V2(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(42, 3): + rc = DNP3DecodeObjectG42V3(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(42, 4): + rc = DNP3DecodeObjectG42V4(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(42, 5): + rc = DNP3DecodeObjectG42V5(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(42, 6): + rc = DNP3DecodeObjectG42V6(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(42, 7): + rc = DNP3DecodeObjectG42V7(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(42, 8): + rc = DNP3DecodeObjectG42V8(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(43, 1): + rc = DNP3DecodeObjectG43V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(43, 2): + rc = DNP3DecodeObjectG43V2(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(43, 3): + rc = DNP3DecodeObjectG43V3(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(43, 4): + rc = DNP3DecodeObjectG43V4(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(43, 5): + rc = DNP3DecodeObjectG43V5(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(43, 6): + rc = DNP3DecodeObjectG43V6(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(43, 7): + rc = DNP3DecodeObjectG43V7(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(43, 8): + rc = DNP3DecodeObjectG43V8(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(50, 1): + rc = DNP3DecodeObjectG50V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(50, 2): + rc = DNP3DecodeObjectG50V2(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(50, 3): + rc = DNP3DecodeObjectG50V3(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(50, 4): + rc = DNP3DecodeObjectG50V4(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(51, 1): + rc = DNP3DecodeObjectG51V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(51, 2): + rc = DNP3DecodeObjectG51V2(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(52, 1): + rc = DNP3DecodeObjectG52V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(52, 2): + rc = DNP3DecodeObjectG52V2(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(70, 1): + rc = DNP3DecodeObjectG70V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(70, 2): + rc = DNP3DecodeObjectG70V2(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(70, 3): + rc = DNP3DecodeObjectG70V3(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(70, 4): + rc = DNP3DecodeObjectG70V4(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(70, 5): + rc = DNP3DecodeObjectG70V5(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(70, 6): + rc = DNP3DecodeObjectG70V6(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(70, 7): + rc = DNP3DecodeObjectG70V7(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(70, 8): + rc = DNP3DecodeObjectG70V8(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(80, 1): + rc = DNP3DecodeObjectG80V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(81, 1): + rc = DNP3DecodeObjectG81V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(83, 1): + rc = DNP3DecodeObjectG83V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(86, 2): + rc = DNP3DecodeObjectG86V2(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(102, 1): + rc = DNP3DecodeObjectG102V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(120, 1): + rc = DNP3DecodeObjectG120V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(120, 2): + rc = DNP3DecodeObjectG120V2(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(120, 3): + rc = DNP3DecodeObjectG120V3(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(120, 4): + rc = DNP3DecodeObjectG120V4(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(120, 5): + rc = DNP3DecodeObjectG120V5(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(120, 6): + rc = DNP3DecodeObjectG120V6(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(120, 7): + rc = DNP3DecodeObjectG120V7(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(120, 8): + rc = DNP3DecodeObjectG120V8(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(120, 9): + rc = DNP3DecodeObjectG120V9(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(120, 10): + rc = DNP3DecodeObjectG120V10(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(120, 11): + rc = DNP3DecodeObjectG120V11(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(120, 12): + rc = DNP3DecodeObjectG120V12(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(120, 13): + rc = DNP3DecodeObjectG120V13(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(120, 14): + rc = DNP3DecodeObjectG120V14(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(120, 15): + rc = DNP3DecodeObjectG120V15(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(121, 1): + rc = DNP3DecodeObjectG121V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(122, 1): + rc = DNP3DecodeObjectG122V1(buf, len, prefix_code, start, count, + points); + break; + case DNP3_OBJECT_CODE(122, 2): + rc = DNP3DecodeObjectG122V2(buf, len, prefix_code, start, count, + points); + break; + default: + return DNP3_DECODER_EVENT_UNKNOWN_OBJECT; + } + + return rc ? 0 : DNP3_DECODER_EVENT_MALFORMED; +} + +/* END GENERATED CODE */ diff --git a/src/app-layer-dnp3-objects.h b/src/app-layer-dnp3-objects.h new file mode 100644 index 0000000000..e292f01275 --- /dev/null +++ b/src/app-layer-dnp3-objects.h @@ -0,0 +1,1464 @@ +/* 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 + * + * \author Jason Ish + * + * This file contains the types (definitions) of the DNP3 objects. + */ + +#ifndef __APP_LAYER_DNP3_OBJECTS_H__ +#define __APP_LAYER_DNP3_OBJECTS_H__ + +#define DNP3_OBJECT_CODE(group, variation) (group << 8 | variation) + +/* START GENERATED CODE */ + +/* Code generated by: + * ./scripts/dnp3-gen/dnp3-gen.py + */ + +typedef struct DNP3ObjectG1V1_ { + uint8_t state; +} DNP3ObjectG1V1; + +typedef struct DNP3ObjectG1V2_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t chatter_filter:1; + uint8_t reserved:1; + uint8_t state:1; +} DNP3ObjectG1V2; + +typedef struct DNP3ObjectG2V1_ { + uint8_t state; +} DNP3ObjectG2V1; + +typedef struct DNP3ObjectG2V2_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t chatter_filter:1; + uint8_t reserved:1; + uint8_t state:1; + uint64_t timestamp; +} DNP3ObjectG2V2; + +typedef struct DNP3ObjectG2V3_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t chatter_filter:1; + uint8_t reserved:1; + uint8_t state:1; + uint16_t timestamp; +} DNP3ObjectG2V3; + +typedef struct DNP3ObjectG3V1_ { + uint8_t state; +} DNP3ObjectG3V1; + +typedef struct DNP3ObjectG3V2_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t chatter_filter:1; + uint8_t state:2; +} DNP3ObjectG3V2; + +typedef struct DNP3ObjectG4V1_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t chatter_filter:1; + uint8_t state:2; +} DNP3ObjectG4V1; + +typedef struct DNP3ObjectG4V2_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t chatter_filter:1; + uint8_t state:2; + uint64_t timestamp; +} DNP3ObjectG4V2; + +typedef struct DNP3ObjectG4V3_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t chatter_filter:1; + uint8_t state:2; + uint16_t relative_time_ms; +} DNP3ObjectG4V3; + +typedef struct DNP3ObjectG10V1_ { + uint8_t state; +} DNP3ObjectG10V1; + +typedef struct DNP3ObjectG10V2_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t reserved0:1; + uint8_t reserved1:1; + uint8_t state:1; +} DNP3ObjectG10V2; + +typedef struct DNP3ObjectG11V1_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t reserved0:1; + uint8_t reserved1:1; + uint8_t state:1; +} DNP3ObjectG11V1; + +typedef struct DNP3ObjectG11V2_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t reserved0:1; + uint8_t reserved1:1; + uint8_t state:1; + uint64_t timestamp; +} DNP3ObjectG11V2; + +typedef struct DNP3ObjectG12V1_ { + uint8_t op_type:4; + uint8_t qu:1; + uint8_t cr:1; + uint8_t tcc:2; + uint8_t count; + uint32_t ontime; + uint32_t offtime; + uint8_t status_code:7; + uint8_t reserved:1; +} DNP3ObjectG12V1; + +typedef struct DNP3ObjectG12V2_ { + uint8_t op_type:4; + uint8_t qu:1; + uint8_t cr:1; + uint8_t tcc:2; + uint8_t count; + uint32_t ontime; + uint32_t offtime; + uint8_t status_code:7; + uint8_t reserved:1; +} DNP3ObjectG12V2; + +typedef struct DNP3ObjectG12V3_ { + uint8_t point; +} DNP3ObjectG12V3; + +typedef struct DNP3ObjectG13V1_ { + uint8_t status_code:7; + uint8_t commanded_state:1; +} DNP3ObjectG13V1; + +typedef struct DNP3ObjectG13V2_ { + uint8_t status_code:7; + uint8_t commanded_state:1; + uint64_t timestamp; +} DNP3ObjectG13V2; + +typedef struct DNP3ObjectG20V1_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t rollover:1; + uint8_t discontinuity:1; + uint8_t reserved0:1; + uint32_t count; +} DNP3ObjectG20V1; + +typedef struct DNP3ObjectG20V2_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t rollover:1; + uint8_t discontinuity:1; + uint8_t reserved0:1; + uint16_t count; +} DNP3ObjectG20V2; + +typedef struct DNP3ObjectG20V3_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t rollover:1; + uint8_t reserved0:1; + uint8_t reserved1:1; + uint32_t count; +} DNP3ObjectG20V3; + +typedef struct DNP3ObjectG20V4_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t rollover:1; + uint8_t reserved0:1; + uint8_t reserved1:1; + uint16_t count; +} DNP3ObjectG20V4; + +typedef struct DNP3ObjectG20V5_ { + uint32_t count; +} DNP3ObjectG20V5; + +typedef struct DNP3ObjectG20V6_ { + uint16_t count; +} DNP3ObjectG20V6; + +typedef struct DNP3ObjectG20V7_ { + uint32_t count; +} DNP3ObjectG20V7; + +typedef struct DNP3ObjectG20V8_ { + uint16_t count; +} DNP3ObjectG20V8; + +typedef struct DNP3ObjectG21V1_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t rollover:1; + uint8_t discontinuity:1; + uint8_t reserved0:1; + uint32_t count; +} DNP3ObjectG21V1; + +typedef struct DNP3ObjectG21V2_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t rollover:1; + uint8_t discontinuity:1; + uint8_t reserved0:1; + uint16_t count; +} DNP3ObjectG21V2; + +typedef struct DNP3ObjectG21V3_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t rollover:1; + uint8_t reserved0:1; + uint8_t reserved1:1; + uint32_t count; +} DNP3ObjectG21V3; + +typedef struct DNP3ObjectG21V4_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t rollover:1; + uint8_t reserved0:1; + uint8_t reserved1:1; + uint16_t count; +} DNP3ObjectG21V4; + +typedef struct DNP3ObjectG21V5_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t rollover:1; + uint8_t discontinuity:1; + uint8_t reserved1:1; + uint32_t count; + uint64_t timestamp; +} DNP3ObjectG21V5; + +typedef struct DNP3ObjectG21V6_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t rollover:1; + uint8_t discontinuity:1; + uint8_t reserved1:1; + uint16_t count; + uint64_t timestamp; +} DNP3ObjectG21V6; + +typedef struct DNP3ObjectG21V7_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t rollover:1; + uint8_t reserved0:1; + uint8_t reserved1:1; + uint32_t count; + uint64_t timestamp; +} DNP3ObjectG21V7; + +typedef struct DNP3ObjectG21V8_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t rollover:1; + uint8_t reserved0:1; + uint8_t reserved1:1; + uint16_t count; + uint64_t timestamp; +} DNP3ObjectG21V8; + +typedef struct DNP3ObjectG21V9_ { + uint32_t count; +} DNP3ObjectG21V9; + +typedef struct DNP3ObjectG21V10_ { + uint16_t count; +} DNP3ObjectG21V10; + +typedef struct DNP3ObjectG21V11_ { + uint32_t count; +} DNP3ObjectG21V11; + +typedef struct DNP3ObjectG21V12_ { + uint16_t count; +} DNP3ObjectG21V12; + +typedef struct DNP3ObjectG22V1_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t rollover:1; + uint8_t discontinuity:1; + uint8_t reserved0:1; + uint32_t count; +} DNP3ObjectG22V1; + +typedef struct DNP3ObjectG22V2_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t rollover:1; + uint8_t discontinuity:1; + uint8_t reserved0:1; + uint16_t count; +} DNP3ObjectG22V2; + +typedef struct DNP3ObjectG22V3_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t rollover:1; + uint8_t reserved0:1; + uint8_t reserved1:1; + uint32_t count; +} DNP3ObjectG22V3; + +typedef struct DNP3ObjectG22V4_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t rollover:1; + uint8_t reserved0:1; + uint8_t reserved1:1; + uint16_t count; +} DNP3ObjectG22V4; + +typedef struct DNP3ObjectG22V5_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t rollover:1; + uint8_t reserved0:1; + uint8_t reserved1:1; + uint32_t count; + uint64_t timestamp; +} DNP3ObjectG22V5; + +typedef struct DNP3ObjectG22V6_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t rollover:1; + uint8_t discontinuity:1; + uint8_t reserved0:1; + uint16_t count; + uint64_t timestamp; +} DNP3ObjectG22V6; + +typedef struct DNP3ObjectG22V7_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t rollover:1; + uint8_t reserved0:1; + uint8_t reserved1:1; + uint32_t count; + uint64_t timestamp; +} DNP3ObjectG22V7; + +typedef struct DNP3ObjectG22V8_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t rollover:1; + uint8_t reserved0:1; + uint8_t reserved1:1; + uint16_t count; + uint64_t timestamp; +} DNP3ObjectG22V8; + +typedef struct DNP3ObjectG23V1_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t rollover:1; + uint8_t discontinuity:1; + uint8_t reserved0:1; + uint32_t count; +} DNP3ObjectG23V1; + +typedef struct DNP3ObjectG23V2_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t rollover:1; + uint8_t reserved0:1; + uint8_t reserved1:1; + uint16_t count; +} DNP3ObjectG23V2; + +typedef struct DNP3ObjectG23V3_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t rollover:1; + uint8_t reserved0:1; + uint8_t reserved1:1; + uint32_t count; +} DNP3ObjectG23V3; + +typedef struct DNP3ObjectG23V4_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t rollover:1; + uint8_t reserved0:1; + uint8_t reserved1:1; + uint16_t count; +} DNP3ObjectG23V4; + +typedef struct DNP3ObjectG23V5_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t rollover:1; + uint8_t discontinuity:1; + uint8_t reserved0:1; + uint32_t count; + uint64_t timestamp; +} DNP3ObjectG23V5; + +typedef struct DNP3ObjectG23V6_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t rollover:1; + uint8_t discontinuity:1; + uint8_t reserved0:1; + uint16_t count; + uint64_t timestamp; +} DNP3ObjectG23V6; + +typedef struct DNP3ObjectG23V7_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t rollover:1; + uint8_t reserved0:1; + uint8_t reserved1:1; + uint32_t count; + uint64_t timestamp; +} DNP3ObjectG23V7; + +typedef struct DNP3ObjectG23V8_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t rollover:1; + uint8_t reserved0:1; + uint8_t reserved1:1; + uint16_t count; + uint64_t timestamp; +} DNP3ObjectG23V8; + +typedef struct DNP3ObjectG30V1_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + int32_t value; +} DNP3ObjectG30V1; + +typedef struct DNP3ObjectG30V2_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + int16_t value; +} DNP3ObjectG30V2; + +typedef struct DNP3ObjectG30V3_ { + int32_t value; +} DNP3ObjectG30V3; + +typedef struct DNP3ObjectG30V4_ { + int16_t value; +} DNP3ObjectG30V4; + +typedef struct DNP3ObjectG30V5_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + float value; +} DNP3ObjectG30V5; + +typedef struct DNP3ObjectG30V6_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + double value; +} DNP3ObjectG30V6; + +typedef struct DNP3ObjectG31V1_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + int32_t value; +} DNP3ObjectG31V1; + +typedef struct DNP3ObjectG31V2_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + int16_t value; +} DNP3ObjectG31V2; + +typedef struct DNP3ObjectG31V3_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + int32_t value; + uint64_t timestamp; +} DNP3ObjectG31V3; + +typedef struct DNP3ObjectG31V4_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + int16_t value; + uint64_t timestamp; +} DNP3ObjectG31V4; + +typedef struct DNP3ObjectG31V5_ { + int32_t value; +} DNP3ObjectG31V5; + +typedef struct DNP3ObjectG31V6_ { + int16_t value; +} DNP3ObjectG31V6; + +typedef struct DNP3ObjectG31V7_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + float value; +} DNP3ObjectG31V7; + +typedef struct DNP3ObjectG31V8_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + double value; +} DNP3ObjectG31V8; + +typedef struct DNP3ObjectG32V1_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + int32_t value; +} DNP3ObjectG32V1; + +typedef struct DNP3ObjectG32V2_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + int16_t value; +} DNP3ObjectG32V2; + +typedef struct DNP3ObjectG32V3_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + int32_t value; + uint64_t timestamp; +} DNP3ObjectG32V3; + +typedef struct DNP3ObjectG32V4_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + int16_t value; + uint64_t timestamp; +} DNP3ObjectG32V4; + +typedef struct DNP3ObjectG32V5_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + float value; +} DNP3ObjectG32V5; + +typedef struct DNP3ObjectG32V6_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + double value; +} DNP3ObjectG32V6; + +typedef struct DNP3ObjectG32V7_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + float value; + uint64_t timestamp; +} DNP3ObjectG32V7; + +typedef struct DNP3ObjectG32V8_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + double value; + uint64_t timestamp; +} DNP3ObjectG32V8; + +typedef struct DNP3ObjectG33V1_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + int32_t value; +} DNP3ObjectG33V1; + +typedef struct DNP3ObjectG33V2_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + int16_t value; +} DNP3ObjectG33V2; + +typedef struct DNP3ObjectG33V3_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + int32_t value; + uint64_t timestamp; +} DNP3ObjectG33V3; + +typedef struct DNP3ObjectG33V4_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + int16_t value; + uint64_t timestamp; +} DNP3ObjectG33V4; + +typedef struct DNP3ObjectG33V5_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + float value; +} DNP3ObjectG33V5; + +typedef struct DNP3ObjectG33V6_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + double value; +} DNP3ObjectG33V6; + +typedef struct DNP3ObjectG33V7_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + float value; + uint64_t timestamp; +} DNP3ObjectG33V7; + +typedef struct DNP3ObjectG33V8_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + double value; + uint64_t timestamp; +} DNP3ObjectG33V8; + +typedef struct DNP3ObjectG34V1_ { + uint16_t deadband_value; +} DNP3ObjectG34V1; + +typedef struct DNP3ObjectG34V2_ { + uint32_t deadband_value; +} DNP3ObjectG34V2; + +typedef struct DNP3ObjectG34V3_ { + float deadband_value; +} DNP3ObjectG34V3; + +typedef struct DNP3ObjectG40V1_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + int32_t value; +} DNP3ObjectG40V1; + +typedef struct DNP3ObjectG40V2_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + int16_t value; +} DNP3ObjectG40V2; + +typedef struct DNP3ObjectG40V3_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + float value; +} DNP3ObjectG40V3; + +typedef struct DNP3ObjectG40V4_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + double value; +} DNP3ObjectG40V4; + +typedef struct DNP3ObjectG41V1_ { + int32_t value; + uint8_t control_status; +} DNP3ObjectG41V1; + +typedef struct DNP3ObjectG41V2_ { + int16_t value; + uint8_t control_status; +} DNP3ObjectG41V2; + +typedef struct DNP3ObjectG41V3_ { + float value; + uint8_t control_status; +} DNP3ObjectG41V3; + +typedef struct DNP3ObjectG41V4_ { + double value; + uint8_t control_status; +} DNP3ObjectG41V4; + +typedef struct DNP3ObjectG42V1_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + int32_t value; +} DNP3ObjectG42V1; + +typedef struct DNP3ObjectG42V2_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + int16_t value; +} DNP3ObjectG42V2; + +typedef struct DNP3ObjectG42V3_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + int32_t value; + uint64_t timestamp; +} DNP3ObjectG42V3; + +typedef struct DNP3ObjectG42V4_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + int16_t value; + uint64_t timestamp; +} DNP3ObjectG42V4; + +typedef struct DNP3ObjectG42V5_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + float value; +} DNP3ObjectG42V5; + +typedef struct DNP3ObjectG42V6_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + double value; +} DNP3ObjectG42V6; + +typedef struct DNP3ObjectG42V7_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + float value; + uint64_t timestamp; +} DNP3ObjectG42V7; + +typedef struct DNP3ObjectG42V8_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t over_range:1; + uint8_t reference_err:1; + uint8_t reserved0:1; + double value; + uint64_t timestamp; +} DNP3ObjectG42V8; + +typedef struct DNP3ObjectG43V1_ { + uint8_t status_code:7; + uint8_t reserved0:1; + int32_t commanded_value; +} DNP3ObjectG43V1; + +typedef struct DNP3ObjectG43V2_ { + uint8_t status_code:7; + uint8_t reserved0:1; + int16_t commanded_value; +} DNP3ObjectG43V2; + +typedef struct DNP3ObjectG43V3_ { + uint8_t status_code:7; + uint8_t reserved0:1; + int32_t commanded_value; + uint64_t timestamp; +} DNP3ObjectG43V3; + +typedef struct DNP3ObjectG43V4_ { + uint8_t status_code:7; + uint8_t reserved0:1; + int16_t commanded_value; + uint64_t timestamp; +} DNP3ObjectG43V4; + +typedef struct DNP3ObjectG43V5_ { + uint8_t status_code:7; + uint8_t reserved0:1; + float commanded_value; +} DNP3ObjectG43V5; + +typedef struct DNP3ObjectG43V6_ { + uint8_t status_code:7; + uint8_t reserved0:1; + double commanded_value; +} DNP3ObjectG43V6; + +typedef struct DNP3ObjectG43V7_ { + uint8_t status_code:7; + uint8_t reserved0:1; + float commanded_value; + uint64_t timestamp; +} DNP3ObjectG43V7; + +typedef struct DNP3ObjectG43V8_ { + uint8_t status_code:7; + uint8_t reserved0:1; + double commanded_value; + uint64_t timestamp; +} DNP3ObjectG43V8; + +typedef struct DNP3ObjectG50V1_ { + uint64_t timestamp; +} DNP3ObjectG50V1; + +typedef struct DNP3ObjectG50V2_ { + uint64_t timestamp; + uint32_t interval; +} DNP3ObjectG50V2; + +typedef struct DNP3ObjectG50V3_ { + uint64_t timestamp; +} DNP3ObjectG50V3; + +typedef struct DNP3ObjectG50V4_ { + uint64_t timestamp; + uint32_t interval_count; + uint8_t interval_units; +} DNP3ObjectG50V4; + +typedef struct DNP3ObjectG51V1_ { + uint64_t timestamp; +} DNP3ObjectG51V1; + +typedef struct DNP3ObjectG51V2_ { + uint64_t timestamp; +} DNP3ObjectG51V2; + +typedef struct DNP3ObjectG52V1_ { + uint16_t delay_secs; +} DNP3ObjectG52V1; + +typedef struct DNP3ObjectG52V2_ { + uint16_t delay_ms; +} DNP3ObjectG52V2; + +typedef struct DNP3ObjectG70V1_ { + uint16_t filename_size; + uint8_t filetype_code; + uint8_t attribute_code; + uint16_t start_record; + uint16_t end_record; + uint32_t file_size; + uint64_t created_timestamp; + uint16_t permission; + uint32_t file_id; + uint32_t owner_id; + uint32_t group_id; + uint8_t file_function_code; + uint8_t status_code; + char filename[65535]; + uint16_t data_size; + char data[65535]; +} DNP3ObjectG70V1; + +typedef struct DNP3ObjectG70V2_ { + uint16_t username_offset; + uint16_t username_size; + uint16_t password_offset; + uint16_t password_size; + uint32_t authentication_key; + char username[65535]; + char password[65535]; +} DNP3ObjectG70V2; + +typedef struct DNP3ObjectG70V3_ { + uint16_t filename_offset; + uint16_t filename_size; + uint64_t created; + uint16_t permissions; + uint32_t authentication_key; + uint32_t file_size; + uint16_t operational_mode; + uint16_t maximum_block_size; + uint16_t request_id; + char filename[65535]; +} DNP3ObjectG70V3; + +typedef struct DNP3ObjectG70V4_ { + uint32_t file_handle; + uint32_t file_size; + uint16_t maximum_block_size; + uint16_t request_id; + uint8_t status_code; + char optional_text[255]; + uint8_t optional_text_len; +} DNP3ObjectG70V4; + +typedef struct DNP3ObjectG70V5_ { + uint32_t file_handle; + uint32_t block_number; + char file_data[255]; + uint8_t file_data_len; +} DNP3ObjectG70V5; + +typedef struct DNP3ObjectG70V6_ { + uint32_t file_handle; + uint32_t block_number; + uint8_t status_code; + char optional_text[255]; + uint8_t optional_text_len; +} DNP3ObjectG70V6; + +typedef struct DNP3ObjectG70V7_ { + uint16_t filename_offset; + uint16_t filename_size; + uint16_t file_type; + uint32_t file_size; + uint64_t created_timestamp; + uint16_t permissions; + uint16_t request_id; + char filename[65535]; +} DNP3ObjectG70V7; + +typedef struct DNP3ObjectG70V8_ { + char file_specification[65535]; + uint16_t file_specification_len; +} DNP3ObjectG70V8; + +typedef struct DNP3ObjectG80V1_ { + uint8_t state; +} DNP3ObjectG80V1; + +typedef struct DNP3ObjectG81V1_ { + uint8_t fill_percentage:7; + uint8_t overflow_state:1; + uint8_t group; + uint8_t variation; +} DNP3ObjectG81V1; + +typedef struct DNP3ObjectG83V1_ { + char vendor_code[5]; + uint16_t object_id; + uint16_t length; + uint8_t *data_objects; +} DNP3ObjectG83V1; + +typedef struct DNP3ObjectG86V2_ { + uint8_t rd:1; + uint8_t wr:1; + uint8_t st:1; + uint8_t ev:1; + uint8_t df:1; + uint8_t padding0:1; + uint8_t padding1:1; + uint8_t padding2:1; +} DNP3ObjectG86V2; + +typedef struct DNP3ObjectG102V1_ { + uint8_t value; +} DNP3ObjectG102V1; + +typedef struct DNP3ObjectG120V1_ { + uint32_t csq; + uint16_t usr; + uint8_t mal; + uint8_t reason; + uint8_t *challenge_data; + uint16_t challenge_data_len; +} DNP3ObjectG120V1; + +typedef struct DNP3ObjectG120V2_ { + uint32_t csq; + uint16_t usr; + uint8_t *mac_value; + uint16_t mac_value_len; +} DNP3ObjectG120V2; + +typedef struct DNP3ObjectG120V3_ { + uint32_t csq; + uint16_t user_number; +} DNP3ObjectG120V3; + +typedef struct DNP3ObjectG120V4_ { + uint16_t user_number; +} DNP3ObjectG120V4; + +typedef struct DNP3ObjectG120V5_ { + uint32_t ksq; + uint16_t user_number; + uint8_t key_wrap_alg; + uint8_t key_status; + uint8_t mal; + uint16_t challenge_data_len; + uint8_t *challenge_data; + uint8_t *mac_value; + uint16_t mac_value_len; +} DNP3ObjectG120V5; + +typedef struct DNP3ObjectG120V6_ { + uint32_t ksq; + uint16_t usr; + uint8_t *wrapped_key_data; + uint16_t wrapped_key_data_len; +} DNP3ObjectG120V6; + +typedef struct DNP3ObjectG120V7_ { + uint32_t sequence_number; + uint16_t usr; + uint16_t association_id; + uint8_t error_code; + uint64_t time_of_error; + char error_text[65535]; + uint16_t error_text_len; +} DNP3ObjectG120V7; + +typedef struct DNP3ObjectG120V8_ { + uint8_t key_change_method; + uint8_t certificate_type; + uint8_t *certificate; + uint16_t certificate_len; +} DNP3ObjectG120V8; + +typedef struct DNP3ObjectG120V9_ { + uint8_t *mac_value; + uint16_t mac_value_len; +} DNP3ObjectG120V9; + +typedef struct DNP3ObjectG120V10_ { + uint8_t key_change_method; + uint8_t operation; + uint32_t scs; + uint16_t user_role; + uint16_t user_role_expiry_interval; + uint16_t username_len; + uint16_t user_public_key_len; + uint16_t certification_data_len; + char username[65535]; + uint8_t *user_public_key; + uint8_t *certification_data; +} DNP3ObjectG120V10; + +typedef struct DNP3ObjectG120V11_ { + uint8_t key_change_method; + uint16_t username_len; + uint16_t master_challenge_data_len; + char username[65535]; + uint8_t *master_challenge_data; +} DNP3ObjectG120V11; + +typedef struct DNP3ObjectG120V12_ { + uint32_t ksq; + uint16_t user_number; + uint16_t challenge_data_len; + uint8_t *challenge_data; +} DNP3ObjectG120V12; + +typedef struct DNP3ObjectG120V13_ { + uint32_t ksq; + uint16_t user_number; + uint16_t encrypted_update_key_len; + uint8_t *encrypted_update_key_data; +} DNP3ObjectG120V13; + +typedef struct DNP3ObjectG120V14_ { + uint8_t *digital_signature; + uint16_t digital_signature_len; +} DNP3ObjectG120V14; + +typedef struct DNP3ObjectG120V15_ { + uint8_t *mac; + uint32_t mac_len; +} DNP3ObjectG120V15; + +typedef struct DNP3ObjectG121V1_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t reserved0:1; + uint8_t discontinuity:1; + uint8_t reserved1:1; + uint16_t association_id; + uint32_t count_value; +} DNP3ObjectG121V1; + +typedef struct DNP3ObjectG122V1_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t reserved0:1; + uint8_t discontinuity:1; + uint8_t reserved1:1; + uint16_t association_id; + uint32_t count_value; +} DNP3ObjectG122V1; + +typedef struct DNP3ObjectG122V2_ { + uint8_t online:1; + uint8_t restart:1; + uint8_t comm_lost:1; + uint8_t remote_forced:1; + uint8_t local_forced:1; + uint8_t reserved0:1; + uint8_t discontinuity:1; + uint8_t reserved1:1; + uint16_t association_id; + uint32_t count_value; + uint64_t timestamp; +} DNP3ObjectG122V2; + +/* END GENERATED CODE */ + +int DNP3DecodeObject(int group, int variation, const uint8_t **buf, + uint32_t *len, uint8_t prefix_code, uint32_t start, uint32_t count, + DNP3PointList *); +DNP3PointList *DNP3PointListAlloc(void); +void DNP3FreeObjectPointList(int group, int variation, DNP3PointList *); + +#endif /* __APP_LAYER_DNP3_OBJECTS_H__ */ diff --git a/src/app-layer-dnp3.c b/src/app-layer-dnp3.c new file mode 100644 index 0000000000..a2bba22702 --- /dev/null +++ b/src/app-layer-dnp3.c @@ -0,0 +1,2661 @@ +/* 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. + */ + +#include "suricata-common.h" +#include "stream.h" +#include "util-byte.h" +#include "util-unittest.h" +#include "util-hashlist.h" + +#include "util-print.h" + +#include "app-layer-protos.h" +#include "app-layer-parser.h" +#include "app-layer-detect-proto.h" + +#include "app-layer-dnp3.h" +#include "app-layer-dnp3-objects.h" + +/* For hexdump(). */ +#include "app-layer-dcerpc-common.h" + +/* Default number of unreplied requests to be considered a flood. */ +#define DNP3_DEFAULT_REQ_FLOOD_COUNT 500 + +#define DNP3_DEFAULT_PORT "20000" + +/* Expected values for the start bytes. */ +#define DNP3_START_BYTE0 0x05 +#define DNP3_START_BYTE1 0x64 + +/* Minimum length for a DNP3 frame. */ +#define DNP3_MIN_LEN 5 + +/* Length of each CRC. */ +#define DNP3_CRC_LEN 2 + +/* DNP3 block size. After the link header a CRC is inserted after + * after 16 bytes of data. */ +#define DNP3_BLOCK_SIZE 16 + +/* Maximum transport layer sequence number. */ +#define DNP3_MAX_TRAN_SEQNO 64 + +/* Maximum application layer sequence number. */ +#define DNP3_MAX_APP_SEQNO 16 + +/* The number of bytes in the header that are counted as part of the + * header length field. */ +#define DNP3_LINK_HDR_LEN 5 + +/* Link function codes. */ +enum { + DNP3_LINK_FC_CONFIRMED_USER_DATA = 3, + DNP3_LINK_FC_UNCONFIRMED_USER_DATA +}; + +/* Reserved addresses. */ +#define DNP3_RESERVED_ADDR_MIN 0xfff0 +#define DNP3_RESERVED_ADDR_MAX 0xfffb + +/* Source addresses must be < 0xfff0. */ +#define DNP3_SRC_ADDR_MAX 0xfff0 + +#define DNP3_OBJ_TIME_SIZE 6 /* AKA UINT48. */ +#define DNP3_OBJ_G12_V1_SIZE 11 +#define DNP3_OBJ_G12_V2_SIZE 11 +#define DNP3_OBJ_G12_V3_SIZE 1 + +/* Extract the prefix code from the object qualifier. */ +#define DNP3_OBJ_PREFIX(x) ((x >> 4) & 0x7) + +/* Extract the range code from the object qualifier. */ +#define DNP3_OBJ_RANGE(x) (x & 0xf) + +/* Decoder event map. */ +SCEnumCharMap dnp3_decoder_event_table[] = { + {"FLOODED", DNP3_DECODER_EVENT_FLOODED}, + {"LEN_TOO_SMALL", DNP3_DECODER_EVENT_LEN_TOO_SMALL}, + {"BAD_LINK_CRC", DNP3_DECODER_EVENT_BAD_LINK_CRC}, + {"BAD_TRANSPORT_CRC", DNP3_DECODER_EVENT_BAD_TRANSPORT_CRC}, + {"MALFORMED", DNP3_DECODER_EVENT_MALFORMED}, + {"UNKNOWN_OBJECT", DNP3_DECODER_EVENT_UNKNOWN_OBJECT}, + {NULL, -1}, +}; + +/* Some DNP3 servers start with a banner. */ +static const char banner[] = "DNP3"; + +/* Calculate the next transport sequence number. */ +#define NEXT_TH_SEQNO(current) ((current + 1) % DNP3_MAX_TRAN_SEQNO) + +/* Calculate the next application sequence number. */ +#define NEXT_APP_SEQNO(current) ((current + 1) % DNP3_MAX_APP_SEQNO) + +/* CRC table generated by pycrc - http://github.com/tpircher/pycrc. + * - Polynomial: 0x3d65. */ +static const uint16_t crc_table[256] = { + 0x0000, 0x365e, 0x6cbc, 0x5ae2, 0xd978, 0xef26, 0xb5c4, 0x839a, + 0xff89, 0xc9d7, 0x9335, 0xa56b, 0x26f1, 0x10af, 0x4a4d, 0x7c13, + 0xb26b, 0x8435, 0xded7, 0xe889, 0x6b13, 0x5d4d, 0x07af, 0x31f1, + 0x4de2, 0x7bbc, 0x215e, 0x1700, 0x949a, 0xa2c4, 0xf826, 0xce78, + 0x29af, 0x1ff1, 0x4513, 0x734d, 0xf0d7, 0xc689, 0x9c6b, 0xaa35, + 0xd626, 0xe078, 0xba9a, 0x8cc4, 0x0f5e, 0x3900, 0x63e2, 0x55bc, + 0x9bc4, 0xad9a, 0xf778, 0xc126, 0x42bc, 0x74e2, 0x2e00, 0x185e, + 0x644d, 0x5213, 0x08f1, 0x3eaf, 0xbd35, 0x8b6b, 0xd189, 0xe7d7, + 0x535e, 0x6500, 0x3fe2, 0x09bc, 0x8a26, 0xbc78, 0xe69a, 0xd0c4, + 0xacd7, 0x9a89, 0xc06b, 0xf635, 0x75af, 0x43f1, 0x1913, 0x2f4d, + 0xe135, 0xd76b, 0x8d89, 0xbbd7, 0x384d, 0x0e13, 0x54f1, 0x62af, + 0x1ebc, 0x28e2, 0x7200, 0x445e, 0xc7c4, 0xf19a, 0xab78, 0x9d26, + 0x7af1, 0x4caf, 0x164d, 0x2013, 0xa389, 0x95d7, 0xcf35, 0xf96b, + 0x8578, 0xb326, 0xe9c4, 0xdf9a, 0x5c00, 0x6a5e, 0x30bc, 0x06e2, + 0xc89a, 0xfec4, 0xa426, 0x9278, 0x11e2, 0x27bc, 0x7d5e, 0x4b00, + 0x3713, 0x014d, 0x5baf, 0x6df1, 0xee6b, 0xd835, 0x82d7, 0xb489, + 0xa6bc, 0x90e2, 0xca00, 0xfc5e, 0x7fc4, 0x499a, 0x1378, 0x2526, + 0x5935, 0x6f6b, 0x3589, 0x03d7, 0x804d, 0xb613, 0xecf1, 0xdaaf, + 0x14d7, 0x2289, 0x786b, 0x4e35, 0xcdaf, 0xfbf1, 0xa113, 0x974d, + 0xeb5e, 0xdd00, 0x87e2, 0xb1bc, 0x3226, 0x0478, 0x5e9a, 0x68c4, + 0x8f13, 0xb94d, 0xe3af, 0xd5f1, 0x566b, 0x6035, 0x3ad7, 0x0c89, + 0x709a, 0x46c4, 0x1c26, 0x2a78, 0xa9e2, 0x9fbc, 0xc55e, 0xf300, + 0x3d78, 0x0b26, 0x51c4, 0x679a, 0xe400, 0xd25e, 0x88bc, 0xbee2, + 0xc2f1, 0xf4af, 0xae4d, 0x9813, 0x1b89, 0x2dd7, 0x7735, 0x416b, + 0xf5e2, 0xc3bc, 0x995e, 0xaf00, 0x2c9a, 0x1ac4, 0x4026, 0x7678, + 0x0a6b, 0x3c35, 0x66d7, 0x5089, 0xd313, 0xe54d, 0xbfaf, 0x89f1, + 0x4789, 0x71d7, 0x2b35, 0x1d6b, 0x9ef1, 0xa8af, 0xf24d, 0xc413, + 0xb800, 0x8e5e, 0xd4bc, 0xe2e2, 0x6178, 0x5726, 0x0dc4, 0x3b9a, + 0xdc4d, 0xea13, 0xb0f1, 0x86af, 0x0535, 0x336b, 0x6989, 0x5fd7, + 0x23c4, 0x159a, 0x4f78, 0x7926, 0xfabc, 0xcce2, 0x9600, 0xa05e, + 0x6e26, 0x5878, 0x029a, 0x34c4, 0xb75e, 0x8100, 0xdbe2, 0xedbc, + 0x91af, 0xa7f1, 0xfd13, 0xcb4d, 0x48d7, 0x7e89, 0x246b, 0x1235 +}; + +/** + * \brief Compute the CRC for a buffer. + * + * \param buf Buffer to create CRC from. + * \param len Length of buffer (number of bytes to use for CRC). + + */ +static uint16_t DNP3ComputeCRC(const uint8_t *buf, uint32_t len) +{ + const uint8_t *byte = buf; + uint16_t crc = 0; + int idx; + + while (len--) { + idx = (crc ^ *byte) & 0xff; + crc = (crc_table[idx] ^ (crc >> 8)) & 0xffff; + byte++; + } + + return ~crc & 0xffff; +} + +/** + * \brief Check the CRC of a block. + * + * \param block The block of data with CRC to be checked. + * \param len The size of the data block. + * + * \retval 1 if CRC is OK, otherwise 0. + */ +static int DNP3CheckCRC(const uint8_t *block, uint32_t len) +{ + uint32_t crc_offset; + uint16_t crc; + + /* Need at least one byte plus the CRC. */ + if (len < DNP3_CRC_LEN + 1) { + return 0; + } + + crc_offset = len - DNP3_CRC_LEN; + crc = DNP3ComputeCRC(block, len - DNP3_CRC_LEN); + if (((crc & 0xff) == block[crc_offset]) && + ((crc >> 8) == block[crc_offset + 1])) { + return 1; + } + + return 0; +} + +/** + * \brief Check the CRC of the link header. + * + * \param header Point to the link header. + * + * \retval 1 if header CRC is OK, otherwise 0. + */ +static int DNP3CheckLinkHeaderCRC(const DNP3LinkHeader *header) +{ + return DNP3CheckCRC((uint8_t *)header, sizeof(DNP3LinkHeader)); +} + +/** + * \brief Check user data CRCs. + * + * \param data Pointer to user data. + * \param len Length of user data. + * + * \retval 1 if CRCs are OK, otherwise 0. + */ +static int DNP3CheckUserDataCRCs(const uint8_t *data, uint32_t len) +{ + uint32_t offset = 0; + uint32_t block_size; + + while (offset < len) { + if (len - offset >= DNP3_BLOCK_SIZE + DNP3_CRC_LEN) { + block_size = DNP3_BLOCK_SIZE + DNP3_CRC_LEN; + } + else { + block_size = len - offset; + } + + if (!DNP3CheckCRC(data + offset, block_size)) { + /* Once failed, may as well return immediately. */ + return 0; + } + + offset += block_size; + } + + return 1; +} + +/** + * \brief Check the DNP3 frame start bytes. + * + * \retval 1 if valid, 0 if not. + */ +static int DNP3CheckStartBytes(const DNP3LinkHeader *header) +{ + return header->start_byte0 == DNP3_START_BYTE0 && + header->start_byte1 == DNP3_START_BYTE1; +} + +/** + * \brief Check if a frame contains a banner. + * + * Some servers (outstations) appear to send back a banner that fails + * the normal frame checks. So first check for a banner. + * + * \retval 1 if a banner is found, 0 if not. + */ +static int DNP3ContainsBanner(const uint8_t *input, uint32_t len) +{ + return memmem(input, len, banner, strlen(banner)) != NULL; +} + +/** + * \brief DNP3 probing parser. + */ +static uint16_t DNP3ProbingParser(uint8_t *input, uint32_t len, + uint32_t *offset) +{ + DNP3LinkHeader *hdr = (DNP3LinkHeader *)input; + + /* Check that we have the minimum amount of bytes. */ + if (len < sizeof(DNP3LinkHeader)) { + SCLogDebug("Length too small to be a DNP3 header."); + return ALPROTO_UNKNOWN; + } + + /* May be a banner. */ + if (DNP3ContainsBanner(input, len)) { + SCLogDebug("Packet contains a DNP3 banner."); + goto end; + } + + /* Verify start value (from AN2013-004b). */ + if (!DNP3CheckStartBytes(hdr)) { + SCLogDebug("Invalid start bytes."); + return ALPROTO_FAILED; + } + + /* Verify minimum length. */ + if (hdr->len < DNP3_MIN_LEN) { + SCLogDebug("Packet too small to be a valid DNP3 fragment."); + return ALPROTO_FAILED; + } + +end: + SCLogDebug("Detected DNP3."); + return ALPROTO_DNP3; +} + +/** + * \brief Caculate the length of the transport layer with CRCs removed. + * + * \param input_len The length of the transport layer buffer. + * + * \retval The length of the buffer after CRCs are removed. + */ +static int DNP3CalculateTransportLengthWithoutCRCs(uint32_t input_len) +{ + /* Too small. */ + if (input_len < DNP3_CRC_LEN) { + return -1; + } + + /* Get the number of complete blocks. */ + int blocks = input_len / (DNP3_BLOCK_SIZE + DNP3_CRC_LEN); + + /* And the number of bytes in the last block. */ + int rem = input_len - (blocks * (DNP3_BLOCK_SIZE + DNP3_CRC_LEN)); + + if (rem) { + if (rem < DNP3_CRC_LEN) { + return -1; + } + return (blocks * DNP3_BLOCK_SIZE) + (rem - DNP3_CRC_LEN); + } + else { + return (blocks * DNP3_BLOCK_SIZE); + } +} + +/** + * \brief Reassemble the application layer by stripping the CRCs. + * + * Remove the CRCs from the user data blocks. The output is the user + * data with the CRCs removed as well as the transport header removed, + * but the input data still needs to include the transport header as + * its part of the first user data block. + * + * If the output length passed in is non-null, the new input data will + * be appended, and the output length pointer incremented as needed. + * + * \param input Input buffer starting at the transport header (which + * will be removed from the output). + * \param input_len Length of the input buffer. + * \param output Pointer to output buffer (may be realloc'd). + * \param output_len Pointer to output length. + * + * \retval 1 if reassembly was successful, otherwise 0. + */ +static int DNP3ReassembleApplicationLayer(const uint8_t *input, + uint32_t input_len, uint8_t **output, uint32_t *output_len) +{ + int len = DNP3CalculateTransportLengthWithoutCRCs(input_len); + + if (len <= 0) { + return 0; + } + + /* Remove one byte for the transport header and make sure we have + * at least one byte of user data. */ + if (--len < 1) { + return 0; + } + + if (*output == NULL) { + *output = SCCalloc(1, len); + if (unlikely(*output == NULL)) { + return 0; + } + } + else { + uint8_t *ptr = SCRealloc(*output, (size_t)(*output_len + len)); + if (unlikely(ptr == NULL)) { + return 0; + } + *output = ptr; + } + + int offset = 0, block_size; + while ((uint32_t)offset < input_len) { + if (input_len - offset > DNP3_BLOCK_SIZE + DNP3_CRC_LEN) { + block_size = DNP3_BLOCK_SIZE + DNP3_CRC_LEN; + } + else { + block_size = input_len - offset; + } + + /* If handling the first block (offset is 0), trim off the + * first byte which is the transport header, and not part of + * the application data. */ + if (offset == 0) { + offset++; + block_size--; + } + + /* Need at least 3 bytes to continue. One for application + * data, and 2 for the CRC. If not, return failure for + * malformed frame. */ + if (block_size < DNP3_CRC_LEN + 1) { + SCLogDebug("Not enough data to continue."); + return 0; + } + + /* Make sure there is enough space to write into. */ + if (block_size - DNP3_CRC_LEN > len) { + SCLogDebug("Not enough data to continue."); + return 0; + } + + memcpy(*output + *output_len, input + offset, + block_size - DNP3_CRC_LEN); + *output_len += block_size - DNP3_CRC_LEN; + offset += block_size; + len -= block_size - DNP3_CRC_LEN; + } + + return 1; +} + +/** + * \brief Allocate a DNP3 state object. + * + * The DNP3 state object represents a single DNP3 TCP session. + */ +static void *DNP3StateAlloc(void) +{ + SCEnter(); + DNP3State *dnp3; + + dnp3 = (DNP3State *)SCCalloc(1, sizeof(DNP3State)); + if (unlikely(dnp3 == NULL)) { + return NULL; + } + TAILQ_INIT(&dnp3->tx_list); + + SCReturnPtr(dnp3, "void"); +} + +/** + * \brief Set a DNP3 application layer event. + * + * Sets an event on the current transaction object. + */ +static void DNP3SetEvent(DNP3State *dnp3, uint8_t event) +{ + if (dnp3 && dnp3->curr) { + AppLayerDecoderEventsSetEventRaw(&dnp3->curr->decoder_events, event); + dnp3->events++; + } + else { + SCLogWarning(SC_ERR_ALPARSER, + "Fail set set event, state or txn was NULL."); + } +} + +/** + * \brief Set a DNP3 application layer event on a transaction. + */ +static void DNP3SetEventTx(DNP3Transaction *tx, uint8_t event) +{ + AppLayerDecoderEventsSetEventRaw(&tx->decoder_events, event); + tx->dnp3->events++; +} + +/** + * \brief Allocation a DNP3 transaction. + */ +static DNP3Transaction *DNP3TxAlloc(DNP3State *dnp3) +{ + DNP3Transaction *tx = SCCalloc(1, sizeof(DNP3Transaction)); + if (unlikely(tx == NULL)) { + return NULL; + } + dnp3->transaction_max++; + dnp3->unreplied++; + dnp3->curr = tx; + tx->dnp3 = dnp3; + tx->tx_num = dnp3->transaction_max; + TAILQ_INIT(&tx->request_objects); + TAILQ_INIT(&tx->response_objects); + TAILQ_INSERT_TAIL(&dnp3->tx_list, tx, next); + + /* Check for flood state. */ + if (dnp3->unreplied > DNP3_DEFAULT_REQ_FLOOD_COUNT) { + DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_FLOODED); + dnp3->flooded = 1; + } + + return tx; +} + +/** + * \brief Calculate the length of a link frame with CRCs. + * + * This is required as the length parameter in the DNP3 header does not + * include the added CRCs. + * + * \param length The length from the DNP3 link header. + * + * \retval The length of the frame with CRCs included or 0 if the length isn't + * long enough to be a valid DNP3 frame. + */ +static uint32_t DNP3CalculateLinkLength(uint8_t length) +{ + uint32_t frame_len = 0; + int rem; + + /* Fail early if the length is less than the minimum size. */ + if (length < DNP3_LINK_HDR_LEN) { + return 0; + } + + /* Subtract the 5 bytes of the header that are included in the + * length. */ + length -= DNP3_LINK_HDR_LEN; + + rem = length % DNP3_BLOCK_SIZE; + frame_len = (length / DNP3_BLOCK_SIZE) * (DNP3_BLOCK_SIZE + DNP3_CRC_LEN); + if (rem) { + frame_len += rem + DNP3_CRC_LEN; + } + + return frame_len + sizeof(DNP3LinkHeader); +} + +/** + * \brief Check if the link function code specifies user data. + * + * \param header Point to link header. + * + * \retval 1 if frame contains user data, otherwise 0. + */ +static int DNP3IsUserData(const DNP3LinkHeader *header) +{ + switch (DNP3_LINK_FC(header->control)) { + case DNP3_LINK_FC_CONFIRMED_USER_DATA: + case DNP3_LINK_FC_UNCONFIRMED_USER_DATA: + return 1; + default: + return 0; + } +} + +/** + * \brief Check if the frame has user data. + * + * Check if the DNP3 frame actually has user data by checking if data + * exists after the headers. + * + * \retval 1 if user data exists, otherwise 0. + */ +static int DNP3HasUserData(const DNP3LinkHeader *header) +{ + if (DNP3_LINK_DIR(header->control)) { + return header->len >= DNP3_LINK_HDR_LEN + sizeof(DNP3TransportHeader) + + sizeof(DNP3ApplicationHeader); + } + else { + return header->len >= DNP3_LINK_HDR_LEN + sizeof(DNP3TransportHeader) + + sizeof(DNP3ApplicationHeader) + sizeof(DNP3InternalInd); + } +} + +/** + * \brief Reset a DNP3Buffer. + */ +static void DNP3BufferReset(DNP3Buffer *buffer) +{ + buffer->offset = 0; + buffer->len = 0; +} + +/** + * \brief Add data to a DNP3 buffer, enlarging the buffer if required. + * + * \param buffer Buffer to add data data. + * \param data Data to be added to buffer. + * \param len Size of data to be added to buffer. + * + * \param 1 if data was added successful, otherwise 0. + */ +static int DNP3BufferAdd(DNP3Buffer *buffer, const uint8_t *data, uint32_t len) +{ + if (buffer->size == 0) { + buffer->buffer = SCCalloc(1, len); + if (unlikely(buffer->buffer == NULL)) { + return 0; + } + buffer->size = len; + } + else if (buffer->len + len > buffer->size) { + uint8_t *tmp = SCRealloc(buffer->buffer, buffer->len + len); + if (unlikely(tmp == NULL)) { + return 0; + } + buffer->buffer = tmp; + buffer->size = buffer->len + len; + } + memcpy(buffer->buffer + buffer->len, data, len); + buffer->len += len; + + return 1; +} + +/** + * \brief Trim a DNP3 buffer. + * + * Trimming a buffer moves the data in the buffer up to the front of + * the buffer freeing up room at the end for more incoming data. + * + * \param buffer The buffer to trim. + */ +static void DNP3BufferTrim(DNP3Buffer *buffer) +{ + if (buffer->offset == buffer->len) { + DNP3BufferReset(buffer); + } + else if (buffer->offset > 0) { + memmove(buffer->buffer, buffer->buffer + buffer->offset, + buffer->len - buffer->offset); + buffer->len = buffer->len - buffer->offset; + buffer->offset = 0; + } +} + +/** + * \brief Free a DNP3 object. + */ +static void DNP3ObjectFree(DNP3Object *object) +{ + if (object->points != NULL) { + DNP3FreeObjectPointList(object->group, object->variation, + object->points); + } + SCFree(object); +} + +/** + * \breif Allocate a DNP3 object. + */ +static DNP3Object *DNP3ObjectAlloc(void) +{ + DNP3Object *object = SCCalloc(1, sizeof(*object)); + if (unlikely(object == NULL)) { + return NULL; + } + object->points = DNP3PointListAlloc(); + if (object->points == NULL) { + DNP3ObjectFree(object); + return NULL; + } + return object; +} + +/** + * \brief Decode DNP3 application objects. + * + * This function decoded known DNP3 application objects. As the + * protocol isn't self describing, we can only decode the buffer while + * the application objects are known. As soon as an unknown + * group/variation is hit, we must stop processing. + * + * \param buf the input buffer + * \param len length of the input buffer + * \param objects pointer to list where decoded objects will be stored. + * + * \retval 1 if all objects decoded, 0 if all objects could not be decoded ( + * unknown group/variations) + */ +static int DNP3DecodeApplicationObjects(DNP3Transaction *tx, const uint8_t *buf, + uint32_t len, DNP3ObjectList *objects) +{ + int retval = 0; + + if (buf == NULL || len == 0) { + return 1; + } + + while (len) { + uint32_t offset = 0; + + if (len < sizeof(DNP3ObjHeader)) { + goto done; + } + DNP3ObjHeader *header = (DNP3ObjHeader *)buf; + offset += sizeof(DNP3ObjHeader); + + DNP3Object *object = DNP3ObjectAlloc(); + if (unlikely(object == NULL)) { + goto done; + } + TAILQ_INSERT_TAIL(objects, object, next); + + object->group = header->group; + object->variation = header->variation; + object->qualifier = header->qualifier; + object->prefix_code = DNP3_OBJ_PREFIX(header->qualifier); + object->range_code = DNP3_OBJ_RANGE(header->qualifier); + + /* IEEE 1815-2012, Table 4-5. */ + switch (object->range_code) { + case 0x00: + case 0x03: { + /* 1 octet start and stop indexes OR 1 octet start and + * stop virtual addresses. */ + if (offset + (sizeof(uint8_t) * 2) > len) { + /* Not enough data. */ + SCLogDebug("Not enough data."); + goto not_enough_data; + } + object->start = buf[offset++]; + object->stop = buf[offset++]; + object->count = object->stop - object->start + 1; + break; + } + case 0x01: + case 0x04: { + /* 2 octet start and stop indexes OR 2 octect start + * and stop virtual addresses. */ + if (offset + (sizeof(uint16_t) * 2) > len) { + /* Not enough data. */ + SCLogDebug("Not enough data."); + goto not_enough_data; + } + object->start = DNP3_SWAP16(*(uint16_t *)(buf + offset)); + offset += sizeof(uint16_t); + object->stop = DNP3_SWAP16(*(uint16_t *)(buf + offset)); + offset += sizeof(uint16_t); + object->count = object->stop - object->start + 1; + break; + } + case 0x02: + case 0x05: { + /* 4 octet start and stop indexes OR 4 octect start + * and stop virtual addresses. */ + if (offset + (sizeof(uint32_t) * 2) > len) { + /* Not enough data. */ + SCLogDebug("Not enough data."); + goto not_enough_data; + } + object->start = DNP3_SWAP32(*(uint32_t *)(buf + offset)); + offset += sizeof(uint32_t); + object->stop = DNP3_SWAP32(*(uint32_t *)(buf + offset)); + offset += sizeof(uint32_t); + object->count = object->stop - object->start + 1; + break; + } + case 0x06: + /* No range field. */ + object->count = 0; + break; + case 0x07: + /* 1 octet count of objects. */ + if (offset + sizeof(uint8_t) > len) { + SCLogDebug("Not enough data."); + goto not_enough_data; + } + object->count = buf[offset]; + offset += sizeof(uint8_t); + break; + case 0x08: { + /* 2 octet count of objects. */ + if (offset + sizeof(uint16_t) > len) { + SCLogDebug("Not enough data."); + goto not_enough_data; + } + object->count = DNP3_SWAP16(*(uint16_t *)(buf + offset)); + offset += sizeof(uint16_t); + break; + } + case 0x09: { + /* 4 octet count of objects. */ + if (offset + sizeof(uint32_t) > len) { + SCLogDebug("Not enough data."); + goto not_enough_data; + } + object->count = DNP3_SWAP32(*(uint32_t *)(buf + offset)); + offset += sizeof(uint32_t); + break; + } + case 0x0b: { + if (offset + sizeof(uint8_t) > len) { + /* Not enough data. */ + SCLogDebug("Not enough data."); + goto not_enough_data; + } + object->count = *(uint8_t *)(buf + offset); + offset += sizeof(uint8_t); + break; + } + default: + SCLogDebug("Range code 0x%02x is reserved.", + object->range_code); + goto done; + } + + buf += offset; + len -= offset; + + if (object->variation == 0 || object->count == 0) { + goto next; + } + + int event = DNP3DecodeObject(header->group, header->variation, &buf, + &len, object->prefix_code, object->start, object->count, + object->points); + if (event) { + DNP3SetEventTx(tx, DNP3_DECODER_EVENT_UNKNOWN_OBJECT); + goto done; + } + + next: + continue; + } + + /* All objects were decoded. */ + retval = 1; + +not_enough_data: +done: + return retval; +} + +/** + * \brief Handle DNP3 request user data. + * + * \param dnp3 the current DNP3State + * \param input pointer to the DNP3 frame (starting with link header) + * \param input_len length of the input frame + */ +static void DNP3HandleUserDataRequest(DNP3State *dnp3, const uint8_t *input, + uint32_t input_len) +{ + DNP3LinkHeader *lh; + DNP3TransportHeader th; + DNP3ApplicationHeader *ah; + DNP3Transaction *tx = NULL, *ttx; + + lh = (DNP3LinkHeader *)input; + + if (!DNP3CheckUserDataCRCs(input + sizeof(DNP3LinkHeader), + input_len - sizeof(DNP3LinkHeader))) { + return; + } + + th = input[sizeof(DNP3LinkHeader)]; + + 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)) + { + tx = ttx; + break; + } + } + + if (tx == NULL) { + return; + } + + /* Update the saved transport header so subsequent segments + * will be matched to this sequence number. */ + tx->response_th = th; + } + else { + ah = (DNP3ApplicationHeader *)(input + sizeof(DNP3LinkHeader) + + sizeof(DNP3TransportHeader)); + + /* Ignore confirms - for now. */ + if (ah->function_code == DNP3_APP_FC_CONFIRM) { + return; + } + + /* Create a transaction. */ + tx = DNP3TxAlloc(dnp3); + if (unlikely(tx == NULL)) { + return; + } + tx->request_lh = *lh; + tx->request_th = th; + tx->request_ah = *ah; + tx->has_request = 1; + + } + + if (!DNP3ReassembleApplicationLayer(input + sizeof(DNP3LinkHeader), + input_len - sizeof(DNP3LinkHeader), + &tx->request_buffer, &tx->request_buffer_len)) { + + /* Malformed, set event and mark as done. */ + DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_MALFORMED); + tx->request_done = 1; + return; + } + + /* If this is not the final segment, just return. */ + if (!DNP3_TH_FIN(th)) { + 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; + } + + if (DNP3DecodeApplicationObjects( + tx, tx->request_buffer + sizeof(DNP3ApplicationHeader), + tx->request_buffer_len - sizeof(DNP3ApplicationHeader), + &tx->request_objects)) { + tx->request_complete = 1; + } +} + +static void DNP3HandleUserDataResponse(DNP3State *dnp3, const uint8_t *input, + uint32_t input_len) +{ + DNP3LinkHeader *lh; + DNP3TransportHeader th; + DNP3ApplicationHeader *ah; + DNP3InternalInd *iin; + DNP3Transaction *tx = NULL, *ttx; + uint32_t offset = 0; + + lh = (DNP3LinkHeader *)input; + offset += sizeof(DNP3LinkHeader); + + if (!DNP3CheckUserDataCRCs(input + offset, input_len - offset)) { + return; + } + + th = input[offset++]; + + 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)) + { + tx = ttx; + break; + } + } + + if (tx == NULL) { + return; + } + + /* Replace the transport header in the transaction with this + * one in case there are more frames. */ + tx->response_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->has_response = 1; + tx->response_lh = *lh; + tx->response_th = th; + tx->response_ah = *ah; + tx->response_iin = *iin; + } + + if (!DNP3ReassembleApplicationLayer(input + sizeof(DNP3LinkHeader), + input_len - sizeof(DNP3LinkHeader), + &tx->response_buffer, &tx->response_buffer_len)) { + DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_MALFORMED); + return; + } + + if (!DNP3_TH_FIN(th)) { + return; + } + + tx->response_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; + } +} + +/** + * \brief Decode the DNP3 request link layer. + * + * \retval number of bytes processed or -1 if the data stream does not look + * like DNP3. + */ +static int DNP3HandleRequestLinkLayer(DNP3State *dnp3, const uint8_t *input, + uint32_t input_len) +{ + SCEnter(); + uint32_t processed = 0; + + while (input_len) { + + /* Need at least enough bytes for a DNP3 header. */ + if (input_len < sizeof(DNP3LinkHeader)) { + break; + } + + DNP3LinkHeader *header = (DNP3LinkHeader *)input; + + if (!DNP3CheckStartBytes(header)) { + goto error; + } + + if (!DNP3CheckLinkHeaderCRC(header)) { + DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_BAD_LINK_CRC); + goto error; + } + + uint32_t frame_len = DNP3CalculateLinkLength(header->len); + if (frame_len == 0) { + DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_LEN_TOO_SMALL); + goto error; + } + if (input_len < frame_len) { + /* Insufficient data, just break - will wait for more data. */ + break; + } + + /* Ignore non-user data for now. */ + if (!DNP3IsUserData(header)) { + goto next; + } + + /* Make sure the header length is large enough for transport and + * application headers. */ + if (!DNP3HasUserData(header)) { + DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_LEN_TOO_SMALL); + goto next; + } + + if (!DNP3CheckUserDataCRCs(input + sizeof(DNP3LinkHeader), + frame_len - sizeof(DNP3LinkHeader))) { + DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_BAD_TRANSPORT_CRC); + goto next; + } + + DNP3HandleUserDataRequest(dnp3, input, frame_len); + + next: + /* Advance the input buffer. */ + input += frame_len; + input_len -= frame_len; + processed += frame_len; + } + + SCReturnInt(processed); +error: + /* Error out. Should only happen if this doesn't look like a DNP3 + * frame. */ + SCReturnInt(-1); +} + +/** + * \brief Handle incoming request data. + * + * The actual request PDU parsing is done in + * DNP3HandleRequestLinkLayer. This function takes care of buffering TCP + * date if a segment does not contain a complete frame (or contains + * multiple frames, but not the complete final frame). + */ +static int DNP3ParseRequest(Flow *f, void *state, AppLayerParserState *pstate, + uint8_t *input, uint32_t input_len, void *local_data) +{ + SCEnter(); + DNP3State *dnp3 = (DNP3State *)state; + DNP3Buffer *buffer = &dnp3->request_buffer; + int processed = 0; + + if (input_len == 0) { + SCReturnInt(1); + } + + if (buffer->len) { + if (!DNP3BufferAdd(buffer, input, input_len)) { + SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate memory to buffer " + "DNP3 request data"); + goto error; + } + processed = DNP3HandleRequestLinkLayer(dnp3, + buffer->buffer + buffer->offset, + buffer->len - buffer->offset); + if (processed < 0) { + goto error; + } + buffer->offset += processed; + DNP3BufferTrim(buffer); + } + else { + processed = DNP3HandleRequestLinkLayer(dnp3, input, input_len); + if (processed < 0) { + SCLogDebug("Failed to process request link layer."); + goto error; + } + + input += processed; + input_len -= processed; + + /* Not all data was processed, buffer it. */ + if (input_len) { + if (!DNP3BufferAdd(buffer, input, input_len)) { + SCLogError(SC_ERR_MEM_ALLOC, + "Failed to allocate memory to buffer DNP3 request data"); + goto error; + } + } + } + + SCReturnInt(1); + +error: + /* Reset the buffer. */ + DNP3BufferReset(buffer); + SCReturnInt(-1); +} + +/** + * \brief Decode the DNP3 response link layer. + * + * \retval number of bytes processed or -1 if the data stream does not + * like look DNP3. + */ +static int DNP3HandleResponseLinkLayer(DNP3State *dnp3, const uint8_t *input, + uint32_t input_len) +{ + SCEnter(); + uint32_t processed = 0; + + while (input_len) { + + /* Need at least enough bytes for a DNP3 header. */ + if (input_len < sizeof(DNP3LinkHeader)) { + break; + } + + DNP3LinkHeader *header = (DNP3LinkHeader *)input; + + if (!DNP3CheckStartBytes(header)) { + goto error; + } + + if (!DNP3CheckLinkHeaderCRC(header)) { + DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_BAD_LINK_CRC); + goto error; + } + + /* Calculate the number of bytes needed to for this frame. */ + uint32_t frame_len = DNP3CalculateLinkLength(header->len); + if (frame_len == 0) { + DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_LEN_TOO_SMALL); + goto error; + } + if (input_len < frame_len) { + /* Insufficient data, just break - will wait for more data. */ + break; + } + + /* Only handle user data frames for now. */ + if (!DNP3IsUserData(header)) { + goto next; + } + + /* Make sure the header length is large enough for transport and + * application headers. */ + if (!DNP3HasUserData(header)) { + DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_LEN_TOO_SMALL); + goto error; + } + + if (!DNP3CheckUserDataCRCs(input + sizeof(DNP3LinkHeader), + frame_len - sizeof(DNP3LinkHeader))) { + DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_BAD_TRANSPORT_CRC); + goto next; + } + + DNP3HandleUserDataResponse(dnp3, input, frame_len); + + next: + /* Advance the input buffer. */ + input += frame_len; + input_len -= frame_len; + processed += frame_len; + } + + SCReturnInt(processed); +error: + /* Error out. Should only happen if the data stream no longer + * looks like DNP3. */ + SCReturnInt(-1); +} + +/** + * \brief Parse incoming data. + * + * This is the entry function for DNP3 application layer data. Its + * main responsibility is buffering incoming data that cannot be + * processed. + * + * See DNP3ParseResponsePDUs for DNP3 frame handling. + */ +static int DNP3ParseResponse(Flow *f, void *state, AppLayerParserState *pstate, + uint8_t *input, uint32_t input_len, void *local_data) +{ + SCEnter(); + DNP3State *dnp3 = (DNP3State *)state; + DNP3Buffer *buffer = &dnp3->response_buffer; + int processed; + + if (buffer->len) { + if (!DNP3BufferAdd(buffer, input, input_len)) { + SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate memory to buffer " + "DNP3 response data"); + goto error; + } + processed = DNP3HandleResponseLinkLayer(dnp3, + buffer->buffer + buffer->offset, + buffer->len - buffer->offset); + if (processed < 0) { + goto error; + } + buffer->offset += processed; + DNP3BufferTrim(buffer); + } + else { + + /* Check if this is a banner, ignore if it is. */ + if (DNP3ContainsBanner(input, input_len)) { + goto done; + } + + processed = DNP3HandleResponseLinkLayer(dnp3, input, input_len); + if (processed < 0) { + goto error; + } + input += processed; + input_len -= processed; + + /* Not all data was processed, buffer it. */ + if (input_len) { + if (!DNP3BufferAdd(buffer, input, input_len)) { + SCLogError(SC_ERR_MEM_ALLOC, + "Failed to allocate memory to buffer DNP3 response data"); + goto error; + } + } + } + +done: + SCReturnInt(1); + +error: + /* An error occurred while processing DNP3 frames. Dump the + * buffer as we can't be assured that they are valid anymore. */ + DNP3BufferReset(buffer); + SCReturnInt(-1); +} + +static AppLayerDecoderEvents *DNP3GetEvents(void *state, uint64_t tx_id) +{ + DNP3State *dnp3 = state; + DNP3Transaction *tx; + uint64_t tx_num = tx_id + 1; + + if (dnp3->curr && dnp3->curr->tx_num == tx_num) { + return dnp3->curr->decoder_events; + } + + TAILQ_FOREACH(tx, &dnp3->tx_list, next) { + if (tx->tx_num == tx_num) { + return tx->decoder_events; + } + } + + return NULL; +} + +static int DNP3HasEvents(void *state) +{ + SCEnter(); + uint16_t events = (((DNP3State *)state)->events); + SCReturnInt((events > 0)); +} + +static void *DNP3GetTx(void *alstate, uint64_t tx_id) +{ + SCEnter(); + DNP3State *dnp3 = (DNP3State *)alstate; + DNP3Transaction *tx = NULL; + uint64_t tx_num = tx_id + 1; + + if (dnp3->curr && dnp3->curr->tx_num == (tx_num)) { + SCReturnPtr(dnp3->curr, "void"); + } + + TAILQ_FOREACH(tx, &dnp3->tx_list, next) { + if (tx_num != tx->tx_num) { + continue; + } + SCReturnPtr(tx, "void"); + } + + SCReturnPtr(NULL, "void"); +} + +static uint64_t DNP3GetTxCnt(void *state) +{ + SCEnter(); + uint64_t count = ((uint64_t)((DNP3State *)state)->transaction_max); + SCReturnUInt(count); +} + +/** + * \brief Free all the objects in a DNP3ObjectList. + */ +static void DNP3TxFreeObjectList(DNP3ObjectList *objects) +{ + DNP3Object *object; + + while ((object = TAILQ_FIRST(objects)) != NULL) { + TAILQ_REMOVE(objects, object, next); + DNP3ObjectFree(object); + } +} + +/** + * \brief Free a DNP3 transaction. + */ +static void DNP3TxFree(DNP3Transaction *tx) +{ + SCEnter(); + + if (tx->request_buffer != NULL) { + SCFree(tx->request_buffer); + } + + if (tx->response_buffer != NULL) { + SCFree(tx->response_buffer); + } + + AppLayerDecoderEventsFreeEvents(&tx->decoder_events); + + if (tx->de_state != NULL) { + DetectEngineStateFree(tx->de_state); + } + + DNP3TxFreeObjectList(&tx->request_objects); + DNP3TxFreeObjectList(&tx->response_objects); + + SCFree(tx); + SCReturn; +} + +/** + * \brief Free a transaction by ID on a specific DNP3 state. + * + * This function is called by the app-layer to free a transaction on a + * specific DNP3 state object. + */ +static void DNP3StateTxFree(void *state, uint64_t tx_id) +{ + SCEnter(); + DNP3State *dnp3 = state; + DNP3Transaction *tx = NULL, *ttx; + uint64_t tx_num = tx_id + 1; + + TAILQ_FOREACH_SAFE(tx, &dnp3->tx_list, next, ttx) { + + if (tx->tx_num != tx_num) { + continue; + } + + if (tx == dnp3->curr) { + dnp3->curr = NULL; + } + + if (tx->decoder_events != NULL) { + if (tx->decoder_events->cnt <= dnp3->events) { + dnp3->events -= tx->decoder_events->cnt; + } + else { + dnp3->events = 0; + } + } + dnp3->unreplied--; + + /* Check flood state. */ + if (dnp3->flooded && dnp3->unreplied < DNP3_DEFAULT_REQ_FLOOD_COUNT) { + dnp3->flooded = 0; + } + + TAILQ_REMOVE(&dnp3->tx_list, tx, next); + DNP3TxFree(tx); + break; + } + + SCReturn; +} + +/** + * \brief Free a DNP3 state. + */ +static void DNP3StateFree(void *state) +{ + SCEnter(); + DNP3State *dnp3 = state; + DNP3Transaction *tx; + if (state != NULL) { + while ((tx = TAILQ_FIRST(&dnp3->tx_list)) != NULL) { + TAILQ_REMOVE(&dnp3->tx_list, tx, next); + DNP3TxFree(tx); + } + if (dnp3->request_buffer.buffer != NULL) { + SCFree(dnp3->request_buffer.buffer); + } + if (dnp3->response_buffer.buffer != NULL) { + SCFree(dnp3->response_buffer.buffer); + } + SCFree(dnp3); + } + SCReturn; +} + +/** + * \brief Called by the app-layer to get the state progress. + */ +static int DNP3GetAlstateProgress(void *tx, uint8_t direction) +{ + DNP3Transaction *dnp3tx = (DNP3Transaction *)tx; + DNP3State *dnp3 = dnp3tx->dnp3; + int retval = 0; + + /* If flooded, "ack" old transactions. */ + if (dnp3->flooded && (dnp3->transaction_max - + dnp3tx->tx_num >= DNP3_DEFAULT_REQ_FLOOD_COUNT)) { + SCLogDebug("flooded: returning tx as done."); + SCReturnInt(1); + } + + if (direction & STREAM_TOCLIENT && dnp3tx->response_done) { + retval = 1; + } + else if (direction & STREAM_TOSERVER && dnp3tx->request_done) { + retval = 1; + } + + SCReturnInt(retval); +} + +/** + * \brief App-layer support. + */ +static int DNP3GetAlstateProgressCompletionStatus(uint8_t direction) +{ + return 1; +} + +/** + * \brief App-layer support. + */ +static int DNP3StateGetEventInfo(const char *event_name, int *event_id, + AppLayerEventType *event_type) +{ + *event_id = SCMapEnumNameToValue(event_name, dnp3_decoder_event_table); + if (*event_id == -1) { + SCLogError(SC_ERR_INVALID_ENUM_MAP, "Event \"%s\" not present in " + "the DNP3 enum event map table.", event_name); + return -1; + } + + *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION; + + return 0; +} + +/** + * \brief App-layer support. + */ +static DetectEngineState *DNP3GetTxDetectState(void *vtx) +{ + DNP3Transaction *tx = vtx; + return tx->de_state; +} + +/** + * \brief App-layer support. + */ +static int DNP3SetTxDetectState(void *state, void *vtx, DetectEngineState *s) +{ + DNP3Transaction *tx = vtx; + tx->de_state = s; + return 0; +} + +static void DNP3SetTxLogged(void *alstate, void *vtx, uint32_t logger) +{ + DNP3Transaction *tx = (DNP3Transaction *)vtx; + tx->logged |= logger; +} + +static int DNP3GetTxLogged(void *alstate, void *vtx, uint32_t logger) +{ + DNP3Transaction *tx = (DNP3Transaction *)vtx; + if (tx->logged & logger) { + return 1; + } + + return 0; +} + +/** + * \brief Check if the prefix code is a size prefix. + * + * \retval 1 if the prefix_code specifies a size prefix, 0 if not. + */ +int DNP3PrefixIsSize(uint8_t prefix_code) +{ + switch (prefix_code) { + case 0x04: + case 0x05: + case 0x06: + return 1; + break; + default: + return 0; + } +} + +/** + * \brief Register the DNP3 application protocol parser. + */ +void RegisterDNP3Parsers(void) +{ + SCEnter(); + + char *proto_name = "dnp3"; + + if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) { + + AppLayerProtoDetectRegisterProtocol(ALPROTO_DNP3, proto_name); + + if (RunmodeIsUnittests()) { + AppLayerProtoDetectPPRegister(IPPROTO_TCP, DNP3_DEFAULT_PORT, + ALPROTO_DNP3, 0, sizeof(DNP3LinkHeader), STREAM_TOSERVER, + DNP3ProbingParser); + } + else { + if (!AppLayerProtoDetectPPParseConfPorts("tcp", IPPROTO_TCP, + proto_name, ALPROTO_DNP3, 0, sizeof(DNP3LinkHeader), + DNP3ProbingParser)) { + SCLogWarning(SC_ERR_DNP3_CONFIG, + "No DNP3 configuration found, enabling DNP3 detection on " + "port " DNP3_DEFAULT_PORT); + AppLayerProtoDetectPPRegister(IPPROTO_TCP, DNP3_DEFAULT_PORT, + ALPROTO_DNP3, 0, sizeof(DNP3LinkHeader), STREAM_TOSERVER, + DNP3ProbingParser); + } + } + + } + else { + SCLogInfo("Protocol detection and parser disabled for DNP3."); + SCReturn; + } + + if (AppLayerParserConfParserEnabled("tcp", proto_name)) { + + SCLogInfo("Registering DNP3/tcp parsers."); + + AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_DNP3, STREAM_TOSERVER, + DNP3ParseRequest); + AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_DNP3, STREAM_TOCLIENT, + DNP3ParseResponse); + + AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_DNP3, + DNP3StateAlloc, DNP3StateFree); + + AppLayerParserRegisterGetEventsFunc(IPPROTO_TCP, ALPROTO_DNP3, + DNP3GetEvents); + AppLayerParserRegisterHasEventsFunc(IPPROTO_TCP, ALPROTO_DNP3, + DNP3HasEvents); + AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP, ALPROTO_DNP3, NULL, + DNP3GetTxDetectState, DNP3SetTxDetectState); + + AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_DNP3, DNP3GetTx); + AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_DNP3, DNP3GetTxCnt); + AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_DNP3, + DNP3StateTxFree); + + AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_DNP3, + DNP3GetAlstateProgress); + AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_DNP3, + DNP3GetAlstateProgressCompletionStatus); + + AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_DNP3, + DNP3StateGetEventInfo); + + AppLayerParserRegisterLoggerFuncs(IPPROTO_TCP, ALPROTO_DNP3, + DNP3GetTxLogged, DNP3SetTxLogged); + } + else { + SCLogInfo("Parser disabled for protocol %s. " + "Protocol detection still on.", proto_name); + } + +#ifdef UNITTESTS + AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_DNP3, + DNP3ParserRegisterTests); +#endif + + SCReturn; +} + +#ifdef UNITTESTS + +#include "flow-util.h" +#include "stream-tcp.h" + +/** + * \brief Utility function to fix CRCs when mangling a frame. + */ +static void DNP3FixCrc(uint8_t *data, uint32_t len) +{ + uint32_t block_size; + + while (len) { + if (len >= DNP3_BLOCK_SIZE + DNP3_CRC_LEN) { + block_size = DNP3_BLOCK_SIZE; + } else { + block_size = len - DNP3_CRC_LEN; + } + uint16_t crc = DNP3ComputeCRC(data, block_size); + data[block_size + 1] = (crc >> 8) & 0xff; + data[block_size] = crc & 0xff; + data += block_size + DNP3_CRC_LEN; + len -= block_size + DNP3_CRC_LEN; + } +} + +/** + * \test Test CRC checking on partial and full blocks. + */ +static int DNP3ParserTestCheckCRC(void) +{ + uint8_t request[] = { + /* DNP3 start. */ + 0x05, 0x64, 0x1a, 0xc4, 0x02, 0x00, 0x01, 0x00, + 0xa5, 0xe9, + + /* Transport header. */ + 0xff, + + /* Application layer - segment 1. */ + 0xc9, 0x05, 0x0c, 0x01, 0x28, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x72, + 0xef, + + /* Application layer - segment 2. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff + }; + + /* Check link header CRC. */ + FAIL_IF(!DNP3CheckCRC(request, sizeof(DNP3LinkHeader))); + + /* Check first application layer segment. */ + FAIL_IF(!DNP3CheckCRC(request + sizeof(DNP3LinkHeader), + DNP3_BLOCK_SIZE + DNP3_CRC_LEN)); + + /* Change a byte in link header, should fail now. */ + request[2]++; + FAIL_IF(DNP3CheckCRC(request, sizeof(DNP3LinkHeader))); + + /* Change a byte in the first application segment, should fail + * now. */ + request[sizeof(DNP3LinkHeader) + 3]++; + FAIL_IF(DNP3CheckCRC(request + sizeof(DNP3LinkHeader), + DNP3_BLOCK_SIZE + DNP3_CRC_LEN)); + + PASS; +} + +/** + * \test Test validation of all CRCs in user data. + */ +static int DNP3CheckUserDataCRCsTest(void) +{ + /* Multi-block data with valid CRCs. */ + uint8_t data_valid[] = { + 0xff, 0xc9, 0x05, 0x0c, + 0x01, 0x28, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, + 0x72, 0xef, /* CRC. */ + + 0xff, 0xc9, 0x05, 0x0c, + 0x01, 0x28, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, + 0x72, 0xef, /* CRC. */ + + 0xff, 0xc9, 0x05, 0x0c, + 0x01, 0x28, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, + 0x72, 0xef, /* CRC. */ + + 0x00, 0x00, 0x00, 0x00, + 0x00, + 0xff, 0xff, /* CRC. */ + }; + FAIL_IF(!DNP3CheckUserDataCRCs(data_valid, sizeof(data_valid))); + + /* Multi-block data with one non-crc byte altered. */ + uint8_t data_invalid[] = { + 0xff, 0xc9, 0x05, 0x0c, + 0x01, 0x28, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, + 0x72, 0xef, /* CRC. */ + + 0xff, 0xc9, 0x05, 0x0c, + 0x01, 0x28, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, + 0x72, 0xef, /* CRC. */ + + 0xff, 0xc9, 0x05, 0x0c, + 0x01, 0x28, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, + 0x72, 0xef, /* CRC. */ + + 0x00, 0x00, 0x00, 0x00, + 0x01, /* Invalid byte. */ + 0xff, 0xff, /* CRC. */ + }; + FAIL_IF(DNP3CheckUserDataCRCs(data_invalid, sizeof(data_invalid))); + + /* 1 byte - need at least 3. */ + uint8_t one_byte_nocrc[] = { 0x01 }; + FAIL_IF(DNP3CheckUserDataCRCs(one_byte_nocrc, sizeof(one_byte_nocrc))); + + /* 2 bytes - need at least 3. */ + uint8_t two_byte_nocrc[] = { 0x01, 0x02 }; + FAIL_IF(DNP3CheckUserDataCRCs(two_byte_nocrc, sizeof(two_byte_nocrc))); + + /* 3 bytes, valid CRC. */ + uint8_t three_bytes_good_crc[] = { 0x00, 0x00, 0x00 }; + *(uint16_t *)(three_bytes_good_crc + 1) = DNP3ComputeCRC( + three_bytes_good_crc, 1); + FAIL_IF(!DNP3CheckUserDataCRCs(three_bytes_good_crc, + sizeof(three_bytes_good_crc))); + + PASS; +} + +/** + * \test Test the link layer length calculation. + * + * Test the calculation that converts the link provided in the DNP3 + * header to the actual length of the frame. That is the length with + * CRCs as the length in the header does not include CRCs. + */ +static int DNP3CalculateLinkLengthTest(void) +{ + /* These are invalid. */ + FAIL_IF(DNP3CalculateLinkLength(0) != 0); + FAIL_IF(DNP3CalculateLinkLength(1) != 0); + FAIL_IF(DNP3CalculateLinkLength(2) != 0); + FAIL_IF(DNP3CalculateLinkLength(3) != 0); + FAIL_IF(DNP3CalculateLinkLength(4) != 0); + + /* This is the minimum size. */ + FAIL_IF(DNP3CalculateLinkLength(5) != 10); + + /* 1 full user data blocks of data. */ + FAIL_IF(DNP3CalculateLinkLength(21) != 28); + + /* 2 full user data blocks of data. */ + FAIL_IF(DNP3CalculateLinkLength(37) != 46); + + /* 2 full user data blocks, plus one more byte. */ + /* 2 full user data blocks of data. */ + FAIL_IF(DNP3CalculateLinkLength(38) != 49); + + /* The maximum size. */ + FAIL_IF(DNP3CalculateLinkLength(255) != 292); + + PASS; +} + +/** + * \test The conversion of length with CRCs to the length without + * CRCs. + */ +static int DNP3CalculateTransportLengthWithoutCRCsTest(void) +{ + FAIL_IF(DNP3CalculateTransportLengthWithoutCRCs(0) != -1); + FAIL_IF(DNP3CalculateTransportLengthWithoutCRCs(1) != -1); + FAIL_IF(DNP3CalculateTransportLengthWithoutCRCs(2) != 0); + FAIL_IF(DNP3CalculateTransportLengthWithoutCRCs(3) != 1); + FAIL_IF(DNP3CalculateTransportLengthWithoutCRCs(16) != 14); + FAIL_IF(DNP3CalculateTransportLengthWithoutCRCs(17) != 15); + FAIL_IF(DNP3CalculateTransportLengthWithoutCRCs(18) != 16); + + /* 19 bytes is not enough for a second block. */ + FAIL_IF(DNP3CalculateTransportLengthWithoutCRCs(19) != -1); + + /* 20 bytes really isn't enough either, but is large enough to + * satisfy the CRC on the second block. */ + FAIL_IF(DNP3CalculateTransportLengthWithoutCRCs(20) != 16); + + FAIL_IF(DNP3CalculateTransportLengthWithoutCRCs(21) != 17); + + PASS; +} + +/** + * \test Test the validation of the link header CRC. + */ +static int DNP3ParserCheckLinkHeaderCRC(void) +{ + /* DNP3 frame with valid headers and CRCs. */ + uint8_t request[] = { + /* DNP3 start. */ + 0x05, 0x64, 0x1a, 0xc4, 0x02, 0x00, 0x01, 0x00, + 0xa5, 0xe9, + + /* Transport header. */ + 0xff, + + /* Application layer. */ + 0xc9, 0x05, 0x0c, 0x01, 0x28, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x72, + 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff + }; + + DNP3LinkHeader *header = (DNP3LinkHeader *)request; + FAIL_IF(!DNP3CheckLinkHeaderCRC(header)); + + /* Alter a byte in the header. */ + request[4] = 0; + FAIL_IF(DNP3CheckLinkHeaderCRC(header)); + + PASS; +} + +/** + * \test Test removal of CRCs from user data. + */ +static int DNP3ReassembleApplicationLayerTest01(void) +{ + uint32_t reassembled_len = 0; + uint8_t *output = NULL; + + uint8_t payload[] = { + + 0xff, 0xc9, 0x05, 0x0c, + 0x01, 0x28, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, + 0x72, 0xef, /* CRC. */ + + 0xff, 0xc9, 0x05, 0x0c, + 0x01, 0x28, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, + 0x72, 0xef, /* CRC. */ + + 0xff, 0xc9, 0x05, 0x0c, + 0x01, 0x28, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, + 0x72, 0xef, /* CRC. */ + + 0x00, 0x00, 0x00, 0x00, + 0x00, + 0xff, 0xff, /* CRC. */ + }; + + uint8_t expected[] = { + 0xc9, 0x05, 0x0c, + 0x01, 0x28, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, + /* CRC removed. */ + 0xff, 0xc9, 0x05, 0x0c, + 0x01, 0x28, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, + /* CRC removed. */ + 0xff, 0xc9, 0x05, 0x0c, + 0x01, 0x28, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, + /* CRC removed. */ + 0x00, 0x00, 0x00, 0x00, + 0x00 + /* CRC removed. */ + }; + + /* Valid frame. */ + FAIL_IF(!DNP3ReassembleApplicationLayer(payload, + sizeof(payload), &output, &reassembled_len)); + FAIL_IF(output == NULL); + FAIL_IF(reassembled_len != sizeof(expected)); + FAIL_IF(memcmp(expected, output, reassembled_len)); + SCFree(output); + + /* 1 byte, invalid. */ + reassembled_len = 0; + output = NULL; + FAIL_IF(DNP3ReassembleApplicationLayer(payload, 1, &output, + &reassembled_len)); + FAIL_IF(output != NULL); + FAIL_IF(reassembled_len != 0); + + /* 2 bytes, invalid. */ + reassembled_len = 0; + output = NULL; + FAIL_IF(DNP3ReassembleApplicationLayer(payload, 2, &output, + &reassembled_len)); + FAIL_IF(output != NULL); + FAIL_IF(reassembled_len != 0); + + /* 3 bytes, minimum - but that would only be the transport header + * which isn't included in the output. */ + reassembled_len = 0; + output = NULL; + FAIL_IF(DNP3ReassembleApplicationLayer(payload, 3, &output, + &reassembled_len)); + FAIL_IF(output != NULL); + FAIL_IF(reassembled_len != 0); + + /* 4 bytes is the minimum to get any reassembled data. */ + reassembled_len = 0; + output = NULL; + FAIL_IF(!DNP3ReassembleApplicationLayer(payload, 4, &output, + &reassembled_len)); + FAIL_IF(output == NULL); + FAIL_IF(reassembled_len != 1); + + /* Last block too short (by 1 byte) for data + CRC. */ + uint8_t short_payload1[] = { + + 0xff, 0xc9, 0x05, 0x0c, + 0x01, 0x28, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, + 0x72, 0xef, /* CRC. */ + + 0xff, 0xc9, 0x05, 0x0c, + 0x01, 0x28, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, + 0x72, 0xef, /* CRC. */ + + 0xff, 0xc9, 0x05, 0x0c, + 0x01, 0x28, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, + 0x72, 0xef, /* CRC. */ + + 0x00, 0x00 + }; + reassembled_len = 0; + FAIL_IF(DNP3ReassembleApplicationLayer(short_payload1, + sizeof(short_payload1), &output, &reassembled_len)); + + /* Last block too short (by 2 bytes) for data + CRC. */ + uint8_t short_payload2[] = { + + 0xff, 0xc9, 0x05, 0x0c, + 0x01, 0x28, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, + 0x72, 0xef, /* CRC. */ + + 0xff, 0xc9, 0x05, 0x0c, + 0x01, 0x28, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, + 0x72, 0xef, /* CRC. */ + + 0xff, 0xc9, 0x05, 0x0c, + 0x01, 0x28, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, + 0x72, 0xef, /* CRC. */ + + 0x00, + }; + reassembled_len = 0; + FAIL_IF(DNP3ReassembleApplicationLayer(short_payload2, + sizeof(short_payload2), &output, &reassembled_len)); + + PASS; +} + +/** + * \test Test the probing parser. + */ +static int DNP3ProbingParserTest(void) +{ + uint8_t pkt[] = { + 0x05, 0x64, 0x05, 0xc9, 0x03, 0x00, 0x04, 0x00, + 0xbd, 0x71 + }; + + /* Valid frame. */ + FAIL_IF(DNP3ProbingParser(pkt, sizeof(pkt), NULL) != ALPROTO_DNP3); + + /* Send too little bytes. */ + FAIL_IF(DNP3ProbingParser(pkt, sizeof(DNP3LinkHeader) - 1, NULL) != ALPROTO_UNKNOWN); + + /* Bad start bytes. */ + pkt[0] = 0x06; + FAIL_IF(DNP3ProbingParser(pkt, sizeof(pkt), NULL) != ALPROTO_FAILED); + + /* Restore start byte. */ + pkt[0] = 0x05; + + /* Set the length to a value less than the minimum length of 5. */ + pkt[2] = 0x03; + FAIL_IF(DNP3ProbingParser(pkt, sizeof(pkt), NULL) != ALPROTO_FAILED); + + /* Send a banner. */ + char banner[] = "Welcome to DNP3 SCADA."; + FAIL_IF(DNP3ProbingParser((uint8_t *)banner, sizeof(banner), NULL) != ALPROTO_DNP3); + + PASS; +} + +/** + * \test Test a basic request/response. + */ +int DNP3ParserTestRequestResponse(void) +{ + DNP3State *state = NULL; + + uint8_t request[] = { + /* DNP3 start. */ + 0x05, 0x64, 0x1a, 0xc4, 0x02, 0x00, 0x01, 0x00, + 0xa5, 0xe9, + + /* Transport header. */ + 0xff, + + /* Application layer. */ + 0xc9, 0x05, 0x0c, 0x01, 0x28, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x72, + 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff + }; + + uint8_t response[] = { + /* DNP3 start. */ + 0x05, 0x64, 0x1c, 0x44, 0x01, 0x00, 0x02, 0x00, + 0xe2, 0x59, + + /* Transport header. */ + 0xc3, + + /* Application layer. */ + 0xc9, 0x81, 0x00, 0x00, 0x0c, 0x01, 0x28, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x7a, + 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff + }; + + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + Flow flow; + TcpSession ssn; + + memset(&flow, 0, sizeof(flow)); + memset(&ssn, 0, sizeof(ssn)); + + flow.protoctx = (void *)&ssn; + flow.proto = IPPROTO_TCP; + + StreamTcpInitConfig(TRUE); + + SCMutexLock(&flow.m); + FAIL_IF(AppLayerParserParse(NULL, alp_tctx, &flow, ALPROTO_DNP3, + STREAM_TOSERVER, request, sizeof(request))); + SCMutexUnlock(&flow.m); + + state = flow.alstate; + FAIL_IF(state == NULL); + FAIL_IF(DNP3HasEvents(state)); + + DNP3Transaction *tx = DNP3GetTx(state, 0); + 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); + + 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); + + AppLayerParserThreadCtxFree(alp_tctx); + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&flow); + DNP3StateFree(state); + PASS; +} + +/** + * \test Test an unsolicited response from an outstation. + * + * This is kind of like a request initiated from the "server". + */ +static int DNP3ParserTestUnsolicitedResponseConfirm(void) +{ + DNP3State *state = NULL; + + /* Unsolicited response with confirm bit set. */ + uint8_t response[] = { + 0x05, 0x64, 0x16, 0x44, 0x01, 0x00, 0x02, 0x00, + 0x89, 0xe5, 0xc4, 0xfa, 0x82, 0x00, 0x00, 0x02, + 0x02, 0x17, 0x01, 0x01, 0x81, 0xa7, 0x75, 0xd8, + 0x32, 0x4c, 0x81, 0x3e, 0x01, 0xa1, 0xc9 + }; + + /* Confirm. */ + uint8_t confirm[] = { + 0x05, 0x64, 0x08, 0xc4, 0x02, 0x00, + 0x01, 0x00, 0xd3, 0xb7, 0xc0, 0xda, 0x00, 0x6a, + 0x3d + }; + + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + Flow flow; + TcpSession ssn; + + memset(&flow, 0, sizeof(flow)); + memset(&ssn, 0, sizeof(ssn)); + + flow.protoctx = (void *)&ssn; + flow.proto = IPPROTO_TCP; + + StreamTcpInitConfig(TRUE); + + SCMutexLock(&flow.m); + FAIL_IF(AppLayerParserParse(NULL, alp_tctx, &flow, ALPROTO_DNP3, + STREAM_TOCLIENT, response, sizeof(response))); + SCMutexUnlock(&flow.m); + + state = flow.alstate; + FAIL_IF(state == NULL); + FAIL_IF(DNP3HasEvents(state)); + + DNP3Transaction *tx = DNP3GetTx(state, 0); + 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); + + 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); */ + + AppLayerParserThreadCtxFree(alp_tctx); + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&flow); + DNP3StateFree(state); + PASS; +} + +/** + * \test Test flood state. + */ +int DNP3ParserTestFlooded(void) +{ + DNP3State *state = NULL; + + uint8_t request[] = { + /* DNP3 start. */ + 0x05, 0x64, 0x1a, 0xc4, 0x02, 0x00, 0x01, 0x00, + 0xa5, 0xe9, + + /* Transport header. */ + 0xff, + + /* Application layer. */ + 0xc9, 0x05, 0x0c, 0x01, 0x28, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x72, + 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff + }; + + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + Flow flow; + TcpSession ssn; + + memset(&flow, 0, sizeof(flow)); + memset(&ssn, 0, sizeof(ssn)); + + flow.protoctx = (void *)&ssn; + flow.proto = IPPROTO_TCP; + + StreamTcpInitConfig(TRUE); + + SCMutexLock(&flow.m); + FAIL_IF(AppLayerParserParse(NULL, alp_tctx, &flow, ALPROTO_DNP3, + STREAM_TOSERVER, request, sizeof(request))); + SCMutexUnlock(&flow.m); + + state = flow.alstate; + FAIL_IF(state == NULL); + FAIL_IF(DNP3HasEvents(state)); + + DNP3Transaction *tx = DNP3GetTx(state, 0); + 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); + + for (int i = 0; i < DNP3_DEFAULT_REQ_FLOOD_COUNT - 1; i++) { + SCMutexLock(&flow.m); + FAIL_IF(AppLayerParserParse(NULL, alp_tctx, &flow, ALPROTO_DNP3, + STREAM_TOSERVER, request, sizeof(request))); + SCMutexUnlock(&flow.m); + } + FAIL_IF(state->flooded); + FAIL_IF(DNP3GetAlstateProgress(tx, 0)); + + /* One more request should trip us into flooded state. */ + SCMutexLock(&flow.m); + FAIL_IF(AppLayerParserParse(NULL, alp_tctx, &flow, ALPROTO_DNP3, + STREAM_TOSERVER, request, sizeof(request))); + SCMutexUnlock(&flow.m); + FAIL_IF(!state->flooded); + + /* 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); + DNP3StateFree(state); + PASS; +} + +/** + * \test Test parsing of partial frames. + * + * As DNP3 operates over TCP, it is possible that a partial DNP3 frame + * is received. Test that the partial frame will be buffered until the + * remainder is seen. + */ +static int DNP3ParserTestPartialFrame(void) +{ + DNP3State *state = NULL; + DNP3Transaction *tx; + int r; + + uint8_t request_partial1[] = { + /* DNP3 start. */ + 0x05, 0x64, 0x1a, 0xc4, 0x02, 0x00, 0x01, 0x00, + 0xa5, 0xe9, + + /* Transport header. */ + 0xff, + + /* Application layer. */ + 0xc9, 0x05, 0x0c, 0x01, 0x28, 0x01, 0x00, 0x00, + }; + + uint8_t request_partial2[] = { + /* Remainder of application layer. */ + 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x72, + 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff + }; + + uint8_t response_partial1[] = { + /* DNP3 start. */ + 0x05, 0x64, 0x1c, 0x44, 0x01, 0x00, 0x02, 0x00, + 0xe2, 0x59, + + /* Transport header. */ + 0xc3, + + /* Application layer. */ + 0xc9, 0x81, 0x00, 0x00, 0x0c, 0x01, 0x28, 0x01, + }; + + uint8_t response_partial2[] = { + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x7a, + 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff + }; + + /* Boiler plate for app layer setup. */ + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + Flow flow; + TcpSession ssn; + memset(&flow, 0, sizeof(flow)); + memset(&ssn, 0, sizeof(ssn)); + flow.protoctx = (void *)&ssn; + flow.proto = IPPROTO_TCP; + StreamTcpInitConfig(TRUE); + + /* Pass in the first partial frame. */ + + SCMutexLock(&flow.m); + r = AppLayerParserParse(NULL, alp_tctx, &flow, ALPROTO_DNP3, + STREAM_TOSERVER, request_partial1, sizeof(request_partial1)); + SCMutexUnlock(&flow.m); + FAIL_IF(r != 0); + + /* Frame should just be buffered, but not yet processed. */ + state = flow.alstate; + FAIL_IF(state == NULL); + FAIL_IF(state->request_buffer.len != sizeof(request_partial1)); + FAIL_IF(state->request_buffer.offset != 0); + FAIL_IF(memcmp(state->request_buffer.buffer, request_partial1, + sizeof(request_partial1))); + + /* There should not be a transaction yet. */ + FAIL_IF(state->transaction_max != 0); + FAIL_IF(DNP3GetTx(state, 0) != NULL); + + /* Send the second partial. */ + SCMutexLock(&flow.m); + r = AppLayerParserParse(NULL, alp_tctx, &flow, ALPROTO_DNP3, + STREAM_TOSERVER, request_partial2, sizeof(request_partial2)); + SCMutexUnlock(&flow.m); + FAIL_IF(r != 0); + + /* The second partial completed the frame, the buffer should now + * be clear. */ + FAIL_IF(state->request_buffer.len != 0); + FAIL_IF(state->request_buffer.offset != 0); + + /* Should now have a complete transaction. */ + tx = DNP3GetTx(state, 0); + 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); + + /* Send partial response. */ + SCMutexLock(&flow.m); + r = AppLayerParserParse(NULL, alp_tctx, &flow, ALPROTO_DNP3, + STREAM_TOCLIENT, response_partial1, sizeof(response_partial1)); + SCMutexUnlock(&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); + + /* Send rest of response. */ + SCMutexLock(&flow.m); + r = AppLayerParserParse(NULL, alp_tctx, &flow, ALPROTO_DNP3, + STREAM_TOCLIENT, response_partial2, sizeof(response_partial2)); + SCMutexUnlock(&flow.m); + FAIL_IF(r != 0); + + /* Buffer should now be empty. */ + 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); + + AppLayerParserThreadCtxFree(alp_tctx); + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&flow); + DNP3StateFree(state); + PASS; +} + +/** + * \test Test multiple DNP3 frames in one TCP read. + */ +static int DNP3ParserTestMultiFrame(void) +{ + DNP3State *state = NULL; + + /* Unsolicited response 1. */ + uint8_t unsol_response1[] = { + 0x05, 0x64, 0x16, 0x44, 0x01, 0x00, 0x02, 0x00, + 0x89, 0xe5, 0xc4, 0xfa, 0x82, 0x00, 0x00, 0x02, + 0x02, 0x17, 0x01, 0x01, 0x81, 0xa7, 0x75, 0xd8, + 0x32, 0x4c, 0x81, 0x3e, 0x01, 0xa1, 0xc9, + }; + + /* Unsolicited response 2. */ + uint8_t unsol_response2[] = { + 0x05, 0x64, 0x16, 0x44, 0x01, 0x00, 0x02, 0x00, + 0x89, 0xe5, 0xc5, 0xfb, 0x82, 0x00, 0x00, 0x02, + 0x02, 0x17, 0x01, 0x0c, 0x01, 0xd8, 0x75, 0xd8, + 0x32, 0x4c, 0xc9, 0x3c, 0x01, 0xa1, 0xc9, + }; + + uint8_t combined[sizeof(unsol_response1) + sizeof(unsol_response2)]; + memcpy(combined, unsol_response1, sizeof(unsol_response1)); + memcpy(combined + sizeof(unsol_response1), unsol_response2, + sizeof(unsol_response2)); + + /* Setup. */ + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + Flow flow; + TcpSession ssn; + int r; + memset(&flow, 0, sizeof(flow)); + memset(&ssn, 0, sizeof(ssn)); + flow.protoctx = (void *)&ssn; + flow.proto = IPPROTO_TCP; + StreamTcpInitConfig(TRUE); + + SCMutexLock(&flow.m); + r = AppLayerParserParse(NULL, alp_tctx, &flow, ALPROTO_DNP3, + STREAM_TOCLIENT, combined, sizeof(combined)); + SCMutexUnlock(&flow.m); + FAIL_IF(r != 0); + + state = flow.alstate; + FAIL_IF(state == NULL); + FAIL_IF(state->transaction_max != 2); + + AppLayerParserThreadCtxFree(alp_tctx); + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&flow); + DNP3StateFree(state); + PASS; +} + +/** + * \test Test the parsing of a request PDU. + * + * The PDU under test contains a single read request object: + * - Group: 1 + * - Variation: 0 + * - Count: 0 + */ +static int DNP3ParserTestParsePDU01(void) +{ + /* Frame to be tested. This frame is a DNP3 request with one read + * request data object, group 1, variation 0. */ + const uint8_t pkt[] = { + 0x05, 0x64, + 0x0b, 0xc4, 0x17, 0x00, 0xef, 0xff, 0xc4, 0x8f, + 0xe1, 0xc8, 0x01, 0x01, 0x00, 0x06, 0x77, 0x6e + }; + + DNP3State *dnp3state = DNP3StateAlloc(); + int pdus = DNP3HandleRequestLinkLayer(dnp3state, pkt, sizeof(pkt)); + 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(object->group != 1 || object->variation != 0); + FAIL_IF(object->count != 0); + + DNP3StateFree(dnp3state); + PASS; +} + +/** + * \test Test the decode of a DNP3 fragment with a single 70:3 object. + */ +static int DNP3ParserDecodeG70V3Test(void) +{ + const uint8_t pkt[] = { + 0x05, 0x64, + 0x63, 0xc4, 0x04, 0x00, 0x03, 0x00, 0xc7, 0xee, + 0xc7, 0xc9, 0x1b, 0x46, 0x03, 0x5b, 0x01, 0x55, + 0x00, 0x1a, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x1e, 0x00, 0x43, + 0x3a, 0x2f, 0x74, 0x65, 0x6d, 0x70, 0x2f, 0x44, + 0x4e, 0x50, 0x44, 0x65, 0x67, 0x7d, 0x76, 0x69, + 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x93, 0x0c, + 0x6e, 0x20, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, + 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x52, 0x65, 0x6d, + 0x35, 0x20, 0x6f, 0x74, 0x65, 0x20, 0x44, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x2e, 0x78, 0x6d, 0x6c, + 0xc4, 0x8b + }; + + DNP3State *dnp3state = DNP3StateAlloc(); + FAIL_IF_NULL(dnp3state); + int bytes = DNP3HandleRequestLinkLayer(dnp3state, pkt, sizeof(pkt)); + FAIL_IF(bytes != sizeof(pkt)); + FAIL_IF(DNP3HasEvents(dnp3state)); + DNP3Transaction *tx = DNP3GetTx(dnp3state, 0); + FAIL_IF_NULL(tx); + FAIL_IF_NOT(tx->has_request); + DNP3Object *obj = TAILQ_FIRST(&tx->request_objects); + FAIL_IF_NULL(obj); + FAIL_IF_NOT(obj->group == 70); + FAIL_IF_NOT(obj->variation == 3); + FAIL_IF_NOT(obj->prefix_code == 0x5); + FAIL_IF_NOT(obj->range_code == 0xb); + FAIL_IF_NOT(obj->count == 1); + DNP3Point *point = TAILQ_FIRST(obj->points); + FAIL_IF_NULL(point); + FAIL_IF_NOT(point->prefix == 85); + FAIL_IF_NOT(point->size == 85); + FAIL_IF_NULL(point->data); + DNP3ObjectG70V3 *data = point->data; + FAIL_IF_NOT(strcmp( + data->filename, + "C:/temp/DNPDeviceConfiguration written to Remote Device.xml") == 0); + DNP3StateFree(dnp3state); + PASS; +} + +/** + * \brief Test that an alert is raised on an unknown object. + */ +static int DNP3ParserUnknownEventAlertTest(void) +{ + /* Valid DNP3 frame with 70:3 object. */ + uint8_t pkt[] = { + 0x05, 0x64, 0x63, 0xc4, 0x04, 0x00, 0x03, 0x00, + 0xc7, 0xee, + + 0xc7, 0xc9, 0x1b, + + /* Object and variation. Originally 70:3, now 70:99, an + * unknown object. */ + 0x46, 0x63, + + 0x5b, 0x01, 0x55, + 0x00, 0x1a, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x1e, 0x00, 0x43, + 0x3a, 0x2f, 0x74, 0x65, 0x6d, 0x70, 0x2f, 0x44, + 0x4e, 0x50, 0x44, 0x65, 0x67, 0x7d, 0x76, 0x69, + 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x93, 0x0c, + 0x6e, 0x20, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, + 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x52, 0x65, 0x6d, + 0x35, 0x20, 0x6f, 0x74, 0x65, 0x20, 0x44, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x2e, 0x78, 0x6d, 0x6c, + 0xc4, 0x8b + }; + + DNP3FixCrc(pkt + 10, sizeof(pkt) - 10); + + DNP3State *dnp3state = DNP3StateAlloc(); + FAIL_IF_NULL(dnp3state); + int bytes = DNP3HandleRequestLinkLayer(dnp3state, pkt, sizeof(pkt)); + FAIL_IF(bytes != sizeof(pkt)); + + /* Should have an event now. */ + FAIL_IF_NOT(DNP3HasEvents(dnp3state)); + + DNP3StateFree(dnp3state); + PASS; +} + +#endif + +void DNP3ParserRegisterTests(void) +{ +#ifdef UNITTESTS + UtRegisterTest("DNP3ParserTestCheckCRC", DNP3ParserTestCheckCRC); + UtRegisterTest("DNP3ParserCheckLinkHeaderCRC", + DNP3ParserCheckLinkHeaderCRC); + UtRegisterTest("DNP3CheckUserDataCRCsTest", DNP3CheckUserDataCRCsTest); + UtRegisterTest("DNP3CalculateLinkLengthTest", DNP3CalculateLinkLengthTest); + UtRegisterTest("DNP3CalculateTransportLengthWithoutCRCsTest", + DNP3CalculateTransportLengthWithoutCRCsTest); + UtRegisterTest("DNP3ReassembleApplicationLayerTest01", + DNP3ReassembleApplicationLayerTest01); + UtRegisterTest("DNP3ProbingParserTest", DNP3ProbingParserTest); + UtRegisterTest("DNP3ParserTestRequestResponse", + DNP3ParserTestRequestResponse); + UtRegisterTest("DNP3ParserTestUnsolicitedResponseConfirm", + DNP3ParserTestUnsolicitedResponseConfirm); + UtRegisterTest("DNP3ParserTestPartialFrame", DNP3ParserTestPartialFrame); + UtRegisterTest("DNP3ParserTestMultiFrame", DNP3ParserTestMultiFrame); + UtRegisterTest("DNP3ParserTestFlooded", DNP3ParserTestFlooded); + UtRegisterTest("DNP3ParserTestParsePDU01", DNP3ParserTestParsePDU01); + UtRegisterTest("DNP3ParserDecodeG70V3Test", DNP3ParserDecodeG70V3Test); + UtRegisterTest("DNP3ParserUnknownEventAlertTest", + DNP3ParserUnknownEventAlertTest); +#endif +} diff --git a/src/app-layer-dnp3.h b/src/app-layer-dnp3.h new file mode 100644 index 0000000000..13cebc7478 --- /dev/null +++ b/src/app-layer-dnp3.h @@ -0,0 +1,277 @@ +/* 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_DNP3_H__ +#define __APP_LAYER_DNP3_H__ + +#include "detect-engine-state.h" +#include "util-hashlist.h" +#include "util-byte.h" + +/** + * The maximum size of a DNP3 link PDU. + */ +#define DNP3_MAX_LINK_PDU_LEN 292 + +/* DNP3 application request function codes. */ +#define DNP3_APP_FC_CONFIRM 0x00 +#define DNP3_APP_FC_READ 0x01 +#define DNP3_APP_FC_WRITE 0x02 +#define DNP3_APP_FC_SELECT 0x03 +#define DNP3_APP_FC_OPERATE 0x04 +#define DNP3_APP_FC_DIR_OPERATE 0x05 +#define DNP3_APP_FC_DIR_OPERATE_NR 0x06 +#define DNP3_APP_FC_FREEZE 0x07 +#define DNP3_APP_FC_FREEZE_NR 0x08 +#define DNP3_APP_FC_FREEZE_CLEAR 0x09 +#define DNP3_APP_FC_FREEZE_CLEAR_NR 0x0a +#define DNP3_APP_FC_FREEZE_AT_TIME 0x0b +#define DNP3_APP_FC_FREEZE_AT_TIME_NR 0x0c +#define DNP3_APP_FC_COLD_RESTART 0x0d +#define DNP3_APP_FC_WARM_RESTART 0x0e +#define DNP3_APP_FC_INITIALIZE_DATA 0x0f +#define DNP3_APP_FC_INITIALIZE_APPLICATION 0x10 +#define DNP3_APP_FC_START_APPLICATION 0x11 +#define DNP3_APP_FC_STOP_APPLICATION 0x12 +#define DNP3_APP_FC_SAVE_CONFIGURATION 0x13 +#define DNP3_APP_FC_ENABLE_UNSOLICITED 0x14 +#define DNP3_APP_FC_DISABLE_UNSOLICTED 0x15 +#define DNP3_APP_FC_ASSIGN_CLASS 0x16 +#define DNP3_APP_FC_DELAY_MEASUREMENT 0x17 +#define DNP3_APP_FC_RECORD_CURRENT_TIME 0x18 +#define DNP3_APP_FC_OPEN_TIME 0x19 +#define DNP3_APP_FC_CLOSE_FILE 0x1a +#define DNP3_APP_FC_DELETE_FILE 0x1b +#define DNP3_APP_FC_GET_FILE_INFO 0x1c +#define DNP3_APP_FC_AUTHENTICATE_FILE 0x1d +#define DNP3_APP_FC_ABORT_FILE 0x1e +#define DNP3_APP_FC_ACTIVATE_CONFIG 0x1f +#define DNP3_APP_FC_AUTH_REQ 0x20 +#define DNP3_APP_FC_AUTH_REQ_NR 0x21 + +/* DNP3 application response function codes. */ +#define DNP3_APP_FC_RESPONSE 0x81 +#define DNP3_APP_FC_UNSOLICITED_RESP 0x82 +#define DNP3_APP_FC_AUTH_RESP 0x83 + +/* Extract fields from the link control octet. */ +#define DNP3_LINK_DIR(control) (control & 0x80) +#define DNP3_LINK_PRI(control) (control & 0x40) +#define DNP3_LINK_FCB(control) (control & 0x20) +#define DNP3_LINK_FCV(control) (control & 0x10) +#define DNP3_LINK_FC(control) (control & 0x0f) + +/* Extract fields from transport layer header octet. */ +#define DNP3_TH_FIN(x) (x & 0x80) +#define DNP3_TH_FIR(x) (x & 0x40) +#define DNP3_TH_SEQ(x) (x & 0x3f) + +/* Extract fields from the application control octet. */ +#define DNP3_APP_FIR(x) (x & 0x80) +#define DNP3_APP_FIN(x) (x & 0x40) +#define DNP3_APP_CON(x) (x & 0x20) +#define DNP3_APP_UNS(x) (x & 0x10) +#define DNP3_APP_SEQ(x) (x & 0x0f) + +/* DNP3 values are stored in little endian on the wire, so swapping will be + * needed on big endian architectures. */ +#if __BYTE_ORDER == __BIG_ENDIAN +#define DNP3_SWAP16(x) SCByteSwap16(x) +#define DNP3_SWAP32(x) SCByteSwap32(x) +#define DNP3_SWAP64(x) SCByteSwap64(x) +#elif __BYTE_ORDER == __LITTLE_ENDIAN +#define DNP3_SWAP16(x) x +#define DNP3_SWAP32(x) x +#define DNP3_SWAP64(x) x +#endif + +/* DNP3 decoder events. */ +enum { + DNP3_DECODER_EVENT_FLOODED = 1, + DNP3_DECODER_EVENT_LEN_TOO_SMALL, + DNP3_DECODER_EVENT_BAD_LINK_CRC, + DNP3_DECODER_EVENT_BAD_TRANSPORT_CRC, + DNP3_DECODER_EVENT_MALFORMED, + DNP3_DECODER_EVENT_UNKNOWN_OBJECT, +}; + +/** + * \brief DNP3 link header. + */ +typedef struct DNP3LinkHeader_ { + uint8_t start_byte0; /**< First check byte. */ + uint8_t start_byte1; /**< Second check byte. */ + uint8_t len; /**< Length of PDU without CRCs. */ + uint8_t control; /**< Control flags. */ + uint16_t dst; /**< DNP3 destination address. */ + uint16_t src; /**< DNP3 source address. */ + uint16_t crc; /**< Link header CRC. */ +} __attribute__((__packed__)) DNP3LinkHeader; + +/** + * \brief DNP3 transport header. + */ +typedef uint8_t DNP3TransportHeader; + +/** + * \brief DNP3 application header. + */ +typedef struct DNP3ApplicationHeader_ { + uint8_t control; /**< Control flags. */ + uint8_t function_code; /**< Application function code. */ +} __attribute__((__packed__)) DNP3ApplicationHeader; + +/** + * \brief DNP3 internal indicators. + * + * Part of the application header for responses only. + */ +typedef struct DNP3InternalInd_ { + uint8_t iin1; + uint8_t iin2; +} __attribute__((__packed__)) DNP3InternalInd; + +/** + * \brief A struct used for buffering incoming data prior to reassembly. + */ +typedef struct DNP3Buffer_ { + uint8_t *buffer; + size_t size; + int len; + int offset; +} DNP3Buffer; + +/** + * \brief DNP3 application object header. + */ +typedef struct DNP3ObjHeader_ { + uint8_t group; + uint8_t variation; + uint8_t qualifier; +} __attribute__((packed)) DNP3ObjHeader; + +/** + * \brief DNP3 object point. + * + * Each DNP3 object can have 0 or more points representing the values + * of the object. + */ +typedef struct DNP3Point_ { + uint32_t prefix; /**< Prefix value for point. */ + uint32_t index; /**< Index of point. If the object is prefixed + * with an index then this will be that + * value. Otherwise this is the place the point + * was in the list of points (starting at 0). */ + uint32_t size; /**< Size of point if the object prefix was a + * size. */ + void *data; /**< Data for this point. */ + TAILQ_ENTRY(DNP3Point_) next; +} DNP3Point; + +typedef TAILQ_HEAD(DNP3PointList_, DNP3Point_) DNP3PointList; + +/** + * \brief Struct to hold the list of decoded objects. + */ +typedef struct DNP3Object_ { + uint8_t group; + uint8_t variation; + uint8_t qualifier; + uint8_t prefix_code; + uint8_t range_code; + uint32_t start; + uint32_t stop; + uint32_t count; + DNP3PointList *points; /**< List of points for this object. */ + + TAILQ_ENTRY(DNP3Object_) next; +} DNP3Object; + +typedef TAILQ_HEAD(DNP3ObjectList_, DNP3Object_) DNP3ObjectList; + +/** + * \brief DNP3 transaction. + */ +typedef struct DNP3Transaction_ { + uint64_t tx_num; /**< Internal transaction ID. */ + uint32_t logged; /**< Flags indicating which loggers have logged this tx. */ + + 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; + + AppLayerDecoderEvents *decoder_events; /**< Per transcation + * decoder events. */ + DetectEngineState *de_state; + + TAILQ_ENTRY(DNP3Transaction_) next; +} DNP3Transaction; + +TAILQ_HEAD(TxListHead, DNP3Transaction_); + +/** + * \brief Per flow DNP3 state. + */ +typedef struct DNP3State_ { + TAILQ_HEAD(, DNP3Transaction_) tx_list; + DNP3Transaction *curr; /**< Current transaction. */ + uint64_t transaction_max; + uint16_t events; + uint32_t unreplied; /**< Number of unreplied requests. */ + uint8_t flooded; /**< Flag indicating flood. */ + + DNP3Buffer request_buffer; /**< Request buffer for buffering + * incomplete request PDUs received + * over TCP. */ + DNP3Buffer response_buffer; /**< Response buffer for buffering + * incomplete response PDUs received + * over TCP. */ + +} DNP3State; + +void RegisterDNP3Parsers(void); +void DNP3ParserRegisterTests(void); +int DNP3PrefixIsSize(uint8_t); + +#endif /* __APP_LAYER_DNP3_H__ */ diff --git a/src/app-layer-parser.c b/src/app-layer-parser.c index 02bfd63a9c..6730e8144e 100644 --- a/src/app-layer-parser.c +++ b/src/app-layer-parser.c @@ -59,6 +59,7 @@ #include "app-layer-dns-tcp.h" #include "app-layer-modbus.h" #include "app-layer-enip.h" +#include "app-layer-dnp3.h" #include "app-layer-template.h" #include "conf.h" @@ -1222,6 +1223,7 @@ void AppLayerParserRegisterProtocolParsers(void) RegisterModbusParsers(); RegisterENIPUDPParsers(); RegisterENIPTCPParsers(); + RegisterDNP3Parsers(); RegisterTemplateParsers(); /** IMAP */ diff --git a/src/app-layer-protos.c b/src/app-layer-protos.c index 596a0defe7..9830af5b2c 100644 --- a/src/app-layer-protos.c +++ b/src/app-layer-protos.c @@ -78,6 +78,9 @@ const char *AppProtoToString(AppProto alproto) case ALPROTO_ENIP: proto_name = "enip"; break; + case ALPROTO_DNP3: + proto_name = "dnp3"; + break; case ALPROTO_TEMPLATE: proto_name = "template"; break; diff --git a/src/app-layer-protos.h b/src/app-layer-protos.h index 907bd8629b..bcecc79577 100644 --- a/src/app-layer-protos.h +++ b/src/app-layer-protos.h @@ -43,6 +43,7 @@ enum AppProtoEnum { ALPROTO_DNS, ALPROTO_MODBUS, ALPROTO_ENIP, + ALPROTO_DNP3, ALPROTO_TEMPLATE, /* used by the probing parser when alproto detection fails diff --git a/src/util-error.c b/src/util-error.c index 82c820a335..d76c2d3c22 100644 --- a/src/util-error.c +++ b/src/util-error.c @@ -330,6 +330,7 @@ const char * SCErrorToString(SCError err) CASE_CODE (SC_ERR_INVALID_HASH); CASE_CODE (SC_ERR_NO_SHA1_SUPPORT); CASE_CODE (SC_ERR_NO_SHA256_SUPPORT); + CASE_CODE (SC_ERR_DNP3_CONFIG); } return "UNKNOWN_ERROR"; diff --git a/src/util-error.h b/src/util-error.h index 1b63e43554..b57e6b2285 100644 --- a/src/util-error.h +++ b/src/util-error.h @@ -320,6 +320,7 @@ typedef enum { SC_ERR_NO_SHA1_SUPPORT, SC_ERR_NO_SHA256_SUPPORT, SC_ERR_ENIP_CONFIG, + SC_ERR_DNP3_CONFIG, } SCError; const char *SCErrorToString(SCError); diff --git a/suricata.yaml.in b/suricata.yaml.in index 6fa50fea23..b4cfd843d7 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -99,6 +99,7 @@ rule-files: - tls-events.rules # available in suricata sources under rules dir # - modbus-events.rules # available in suricata sources under rules dir # - app-layer-events.rules # available in suricata sources under rules dir +# - dnp3-events.rules # available in suricata sources under rules dir classification-file: @e_sysconfdir@classification.config reference-config-file: @e_sysconfdir@reference.config @@ -163,6 +164,7 @@ outputs: tls: yes # enable dumping of tls fields ssh: yes # enable dumping of ssh fields smtp: yes # enable dumping of smtp fields + dnp3: yes # enable dumping of DNP3 fields # Enable the logging of tagged packets for rules using the # "tag" keyword. @@ -231,6 +233,7 @@ outputs: - flow # uni-directional flows #- netflow + #- dnp3 # alert output for use with Barnyard2 - unified2-alert: @@ -722,6 +725,11 @@ app-layer: # and not to open and close it for each MODBUS/TCP transaction. In that # case, it is important to set the depth of the stream reassembling as # unlimited (stream.reassembly.depth: 0) + # DNP3 + dnp3: + enabled: no + detection-ports: + dp: 20000 # smb2 detection is disabled internally inside the engine. #smb2: # enabled: yes