]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
DNP3: Application layer decoder.
authorJason Ish <ish@unx.ca>
Fri, 10 Apr 2015 15:33:20 +0000 (09:33 -0600)
committerVictor Julien <victor@inliniac.net>
Thu, 27 Oct 2016 08:25:34 +0000 (10:25 +0200)
Decodes TCP DNP3 and raises some DNP3 decoder alerts.

15 files changed:
Makefile.am
rules/Makefile.am
rules/dnp3-events.rules [new file with mode: 0644]
src/Makefile.am
src/app-layer-detect-proto.c
src/app-layer-dnp3-objects.c [new file with mode: 0644]
src/app-layer-dnp3-objects.h [new file with mode: 0644]
src/app-layer-dnp3.c [new file with mode: 0644]
src/app-layer-dnp3.h [new file with mode: 0644]
src/app-layer-parser.c
src/app-layer-protos.c
src/app-layer-protos.h
src/util-error.c
src/util-error.h
suricata.yaml.in

index e71290c4b85f476000dc34f3082b5ac6efd86c02..b265b6f2dc5a7f4b138ce29fcdc12f0bf52a3ebb 100644 (file)
@@ -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 ""
index 7ab27bdf1e5612efd6342b8fdd129c5b80444704..98a484d3a5b2659d0eefa21ada62d8a81f925a22 100644 (file)
@@ -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 (file)
index 0000000..dfb1cf2
--- /dev/null
@@ -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;)
index 191776a5d288cd68799b541c56b7be20f7a4b34e..c5bb60e7a257ae47781d2546a196596f34959309 100644 (file)
@@ -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 \
index 74e22d4ae08aac735f4ee05029ccbfe1b1b8835e..286dde89cfa63bc80c5728d73c6d7a1fca51d78c 100644 (file)
@@ -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 (file)
index 0000000..275e646
--- /dev/null
@@ -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 <jason.ish@oisf.net>
+ *
+ * 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 (file)
index 0000000..e292f01
--- /dev/null
@@ -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 <jason.ish@oisf.net>
+ *
+ * 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 (file)
index 0000000..a2bba22
--- /dev/null
@@ -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 (file)
index 0000000..13cebc7
--- /dev/null
@@ -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__ */
index 02bfd63a9ce3d0138242f2bed5f60773c272a9a0..6730e8144e97a5e9d1e6e5d20b3b3c2436451e1f 100644 (file)
@@ -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 */
index 596a0defe76323ef391e5d2af982f63d75472dc1..9830af5b2cbdc852906a9c4c986e7c15b0f19dc5 100644 (file)
@@ -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;
index 907bd8629b0e0f9b596db71de399a176b20b9692..bcecc79577e47cfe9ffabf77cbed0526923844e8 100644 (file)
@@ -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
index 82c820a335aa936114c3982f10451fe718979476..d76c2d3c2291ea5c3c8e462f58734e07d2066c18 100644 (file)
@@ -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";
index 1b63e43554e20d1e26159a88eef62452c390461d..b57e6b228518678b88d985c84dd9a48bbe61ccdc 100644 (file)
@@ -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);
index 6fa50fea232bcba9d21900f01bcc477b4f179d26..b4cfd843d7eb31fbdf15d1dc1c1e85652636213e 100644 (file)
@@ -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