mirror of https://github.com/OISF/suricata
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			745 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			745 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Python
		
	
#! /usr/bin/env python
 | 
						|
#
 | 
						|
# 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.
 | 
						|
 | 
						|
# This script generates DNP3 related source code based on definitions
 | 
						|
# of DNP3 objects (currently the object structs).
 | 
						|
 | 
						|
from __future__ import print_function
 | 
						|
 | 
						|
import sys
 | 
						|
import re
 | 
						|
import yaml
 | 
						|
 | 
						|
import jinja2
 | 
						|
 | 
						|
IN_PLACE_START = "/* START GENERATED CODE */"
 | 
						|
IN_PLACE_END = "/* END GENERATED CODE */"
 | 
						|
 | 
						|
util_lua_dnp3_objects_c_template = """/* 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.
 | 
						|
 */
 | 
						|
 | 
						|
/**
 | 
						|
 * DO NOT EDIT. THIS FILE IS AUTO-GENERATED.
 | 
						|
 *
 | 
						|
 * Generated by command:
 | 
						|
 *    {{command_line}}
 | 
						|
 */
 | 
						|
 | 
						|
#include "suricata-common.h"
 | 
						|
 | 
						|
#include "app-layer-dnp3.h"
 | 
						|
#include "app-layer-dnp3-objects.h"
 | 
						|
 | 
						|
#ifdef HAVE_LUA
 | 
						|
 | 
						|
#include <lua.h>
 | 
						|
#include <lualib.h>
 | 
						|
#include <lauxlib.h>
 | 
						|
 | 
						|
#include "util-lua.h"
 | 
						|
#include "util-lua-dnp3-objects.h"
 | 
						|
 | 
						|
/**
 | 
						|
 * \\brief Push an object point item onto the stack.
 | 
						|
 */
 | 
						|
void DNP3PushPoint(lua_State *luastate, DNP3Object *object,
 | 
						|
    DNP3Point *point)
 | 
						|
{
 | 
						|
    switch (DNP3_OBJECT_CODE(object->group, object->variation)) {
 | 
						|
{% for object in objects %}
 | 
						|
        case DNP3_OBJECT_CODE({{object.group}}, {{object.variation}}): {
 | 
						|
            DNP3ObjectG{{object.group}}V{{object.variation}} *data = point->data;
 | 
						|
{% for field in object.fields %}
 | 
						|
{% if is_integer_type(field.type) %}
 | 
						|
            lua_pushliteral(luastate, "{{field.name}}");
 | 
						|
            lua_pushinteger(luastate, data->{{field.name}});
 | 
						|
            lua_settable(luastate, -3);
 | 
						|
{% elif field["type"] in ["flt32", "flt64"] %}
 | 
						|
            lua_pushliteral(luastate, "{{field.name}}");
 | 
						|
            lua_pushnumber(luastate, data->{{field.name}});
 | 
						|
            lua_settable(luastate, -3);
 | 
						|
{% elif field["type"] == "chararray" %}
 | 
						|
            lua_pushliteral(luastate, "{{field.name}}");
 | 
						|
            LuaPushStringBuffer(luastate, (uint8_t *)data->{{field.name}},
 | 
						|
                strlen(data->{{field.name}}));
 | 
						|
            lua_settable(luastate, -3);
 | 
						|
{% elif field["type"] == "vstr4" %}
 | 
						|
            lua_pushliteral(luastate, "{{field.name}}");
 | 
						|
            LuaPushStringBuffer(luastate, (uint8_t *)data->{{field.name}},
 | 
						|
                strlen(data->{{field.name}}));
 | 
						|
            lua_settable(luastate, -3);
 | 
						|
{% elif field.type == "bytearray" %}
 | 
						|
            lua_pushliteral(luastate, "{{field.name}}");
 | 
						|
            lua_pushlstring(luastate, (const char *)data->{{field.name}},
 | 
						|
                data->{{field.len_field}});
 | 
						|
            lua_settable(luastate, -3);
 | 
						|
{% elif field.type == "bstr8" %}
 | 
						|
{% for field in field.fields %}
 | 
						|
            lua_pushliteral(luastate, "{{field.name}}");
 | 
						|
            lua_pushinteger(luastate, data->{{field.name}});
 | 
						|
            lua_settable(luastate, -3);
 | 
						|
{% endfor %}
 | 
						|
{% else %}
 | 
						|
{{ raise("Unhandled datatype: %s" % (field.type)) }}
 | 
						|
{% endif %}
 | 
						|
{% endfor %}
 | 
						|
            break;
 | 
						|
        }
 | 
						|
{% endfor %}
 | 
						|
        default:
 | 
						|
            break;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
#endif /* HAVE_LUA */
 | 
						|
 | 
						|
"""
 | 
						|
 | 
						|
output_json_dnp3_objects_template = """/* 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.
 | 
						|
 */
 | 
						|
 | 
						|
/**
 | 
						|
 * DO NOT EDIT. THIS FILE IS AUTO-GENERATED.
 | 
						|
 *
 | 
						|
 * Generated by command:
 | 
						|
 *    {{command_line}}
 | 
						|
 */
 | 
						|
 | 
						|
#include "suricata-common.h"
 | 
						|
 | 
						|
#include "app-layer-dnp3.h"
 | 
						|
#include "app-layer-dnp3-objects.h"
 | 
						|
#include "output-json-dnp3-objects.h"
 | 
						|
#include "output-json.h"
 | 
						|
 | 
						|
// clang-format off
 | 
						|
void OutputJsonDNP3SetItem(JsonBuilder *js, DNP3Object *object,
 | 
						|
    DNP3Point *point)
 | 
						|
{
 | 
						|
 | 
						|
    switch (DNP3_OBJECT_CODE(object->group, object->variation)) {
 | 
						|
{% for object in objects %}
 | 
						|
        case DNP3_OBJECT_CODE({{object.group}}, {{object.variation}}): {
 | 
						|
            DNP3ObjectG{{object.group}}V{{object.variation}} *data = point->data;
 | 
						|
{% for field in object.fields %}
 | 
						|
{% if is_integer_type(field.type) %}
 | 
						|
            jb_set_uint(js, "{{field.name}}", data->{{field.name}});
 | 
						|
{% elif field.type in ["flt32", "flt64"] %}
 | 
						|
            jb_set_float(js, "{{field.name}}", data->{{field.name}});
 | 
						|
{% elif field.type == "bytearray" %}
 | 
						|
            jb_set_base64(js, "data->{{field.name}}", data->{{field.name}}, data->{{field.len_field}});
 | 
						|
{% elif field.type == "vstr4" %}
 | 
						|
            jb_set_string(js, "data->{{field.name}}", data->{{field.name}});
 | 
						|
{% elif field.type == "chararray" %}
 | 
						|
            if (data->{{field.len_field}} > 0) {
 | 
						|
                jb_set_string_from_bytes(
 | 
						|
                        js, "{{field.name}}", (const uint8_t *)data->{{field.name}}, data->{{field.len_field}});
 | 
						|
            } else {
 | 
						|
                jb_set_string(js, "{{field.name}}", "");
 | 
						|
            }
 | 
						|
{% elif field.type == "bstr8" %}
 | 
						|
{% for field in field.fields %}
 | 
						|
            jb_set_uint(js, "{{field.name}}", data->{{field.name}});
 | 
						|
{% endfor %}
 | 
						|
{% else %}
 | 
						|
{{ raise("Unhandled datatype: %s" % (field.type)) }}
 | 
						|
{% endif %}
 | 
						|
{% endfor %}
 | 
						|
            break;
 | 
						|
        }
 | 
						|
{% endfor %}
 | 
						|
        default:
 | 
						|
            SCLogDebug("Unknown object: %d:%d", object->group,
 | 
						|
                object->variation);
 | 
						|
            break;
 | 
						|
    }
 | 
						|
 | 
						|
}
 | 
						|
// clang-format on
 | 
						|
 | 
						|
"""
 | 
						|
 | 
						|
def has_freeable_types(fields):
 | 
						|
    freeable_types = [
 | 
						|
        "bytearray",
 | 
						|
    ]
 | 
						|
    for field in fields:
 | 
						|
        if field["type"] in freeable_types:
 | 
						|
            return True
 | 
						|
    return False
 | 
						|
 | 
						|
def is_integer_type(datatype):
 | 
						|
    integer_types = [
 | 
						|
        "uint64",
 | 
						|
        "uint32",
 | 
						|
        "uint24",
 | 
						|
        "uint16",
 | 
						|
        "uint8",
 | 
						|
        "int64",
 | 
						|
        "int32",
 | 
						|
        "int16",
 | 
						|
        "int8",
 | 
						|
        "dnp3time",
 | 
						|
    ]
 | 
						|
    return datatype in integer_types
 | 
						|
 | 
						|
def to_type(datatype):
 | 
						|
    type_map = {
 | 
						|
        "uint8": "uint8_t",
 | 
						|
    }
 | 
						|
    if datatype in type_map:
 | 
						|
        return type_map[datatype]
 | 
						|
    else:
 | 
						|
        raise Exception("Unknown datatype: %s" % (datatype))
 | 
						|
 | 
						|
def generate(template, filename, context):
 | 
						|
    print("Generating %s." % (filename))
 | 
						|
    try:
 | 
						|
        env = jinja2.Environment(trim_blocks=True)
 | 
						|
        output = env.from_string(template).render(context)
 | 
						|
        with open(filename, "w") as fileobj:
 | 
						|
            fileobj.write(output)
 | 
						|
    except Exception as err:
 | 
						|
        print("Failed to generate %s: %s" % (filename, err))
 | 
						|
        sys.exit(1)
 | 
						|
 | 
						|
def raise_helper(msg):
 | 
						|
    raise Exception(msg)
 | 
						|
 | 
						|
def gen_object_structs(context):
 | 
						|
    """ Generate structs for all the define DNP3 objects. """
 | 
						|
 | 
						|
    template = """
 | 
						|
 | 
						|
/* Code generated by:
 | 
						|
 *     {{command_line}}
 | 
						|
 */
 | 
						|
 | 
						|
{% for object in objects %}
 | 
						|
typedef struct DNP3ObjectG{{object.group}}V{{object.variation}}_ {
 | 
						|
{% for field in object.fields %}
 | 
						|
{% if field.type == "bstr8" %}
 | 
						|
{% for field in field.fields %}
 | 
						|
    uint8_t {{field.name}}:{{field.width}};
 | 
						|
{% endfor %}
 | 
						|
{% else %}
 | 
						|
{% if field.type == "int16" %}
 | 
						|
    int16_t {{field.name}};
 | 
						|
{% elif field.type == "int32" %}
 | 
						|
    int32_t {{field.name}};
 | 
						|
{% elif field.type == "uint8" %}
 | 
						|
    uint8_t {{field.name}};
 | 
						|
{% elif field.type == "uint16" %}
 | 
						|
    uint16_t {{field.name}};
 | 
						|
{% elif field.type == "uint24" %}
 | 
						|
    uint32_t {{field.name}};
 | 
						|
{% elif field.type == "uint32" %}
 | 
						|
    uint32_t {{field.name}};
 | 
						|
{% elif field.type == "uint64" %}
 | 
						|
    uint64_t {{field.name}};
 | 
						|
{% elif field.type == "flt32" %}
 | 
						|
    float {{field.name}};
 | 
						|
{% elif field.type == "flt64" %}
 | 
						|
    double {{field.name}};
 | 
						|
{% elif field.type == "dnp3time" %}
 | 
						|
    uint64_t {{field.name}};
 | 
						|
{% elif field.type == "bytearray" %}
 | 
						|
    uint8_t *{{field.name}};
 | 
						|
{% elif field.type == "vstr4" %}
 | 
						|
    char {{field.name}}[5];
 | 
						|
{% elif field.type == "chararray" %}
 | 
						|
    char {{field.name}}[{{field.size}}];
 | 
						|
{% else %}
 | 
						|
    {{ raise("Unknown datatype type '%s' for object %d:%d" % (
 | 
						|
           field.type, object.group, object.variation)) }}
 | 
						|
{% endif %}
 | 
						|
{% endif %}
 | 
						|
{% endfor %}
 | 
						|
{% if object.extra_fields %}
 | 
						|
{% for field in object.extra_fields %}
 | 
						|
{% if field.type == "uint8" %}
 | 
						|
    uint8_t {{field.name}};
 | 
						|
{% elif field.type == "uint16" %}
 | 
						|
    uint16_t {{field.name}};
 | 
						|
{% elif field.type == "uint32" %}
 | 
						|
    uint32_t {{field.name}};
 | 
						|
{% else %}
 | 
						|
    {{ raise("Unknown datatype: %s" % (field.type)) }}
 | 
						|
{% endif %}
 | 
						|
{% endfor %}
 | 
						|
{% endif %}
 | 
						|
} DNP3ObjectG{{object.group}}V{{object.variation}};
 | 
						|
 | 
						|
{% endfor %}
 | 
						|
"""
 | 
						|
 | 
						|
    filename = "src/app-layer-dnp3-objects.h"
 | 
						|
    try:
 | 
						|
        env = jinja2.Environment(trim_blocks=True)
 | 
						|
        code = env.from_string(template).render(context)
 | 
						|
        content = open(filename).read()
 | 
						|
        content = re.sub(
 | 
						|
            "(%s).*(%s)" % (re.escape(IN_PLACE_START), re.escape(IN_PLACE_END)),
 | 
						|
            r"\1%s\2" % (code), content, 1, re.M | re.DOTALL)
 | 
						|
        open(filename, "w").write(content)
 | 
						|
        print("Updated %s." % (filename))
 | 
						|
    except Exception as err:
 | 
						|
        print("Failed to update %s: %s" % (filename, err), file=sys.stderr)
 | 
						|
        sys.exit(1)
 | 
						|
 | 
						|
def gen_object_decoders(context):
 | 
						|
    """ Generate decoders for all defined DNP3 objects. """
 | 
						|
 | 
						|
    template = """
 | 
						|
 | 
						|
/* Code generated by:
 | 
						|
 *     {{command_line}}
 | 
						|
 */
 | 
						|
 | 
						|
{% for object in objects %}
 | 
						|
{% if object.packed %}
 | 
						|
static int DNP3DecodeObjectG{{object.group}}V{{object.variation}}(const uint8_t **buf, uint32_t *len,
 | 
						|
    uint8_t prefix_code, uint32_t start, uint32_t count,
 | 
						|
    DNP3PointList *points)
 | 
						|
{
 | 
						|
    DNP3ObjectG{{object.group}}V{{object.variation}} *object = NULL;
 | 
						|
    uint32_t bytes = (count / 8) + 1;
 | 
						|
    uint32_t prefix = 0;
 | 
						|
    uint32_t point_index = start;
 | 
						|
 | 
						|
    if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) {
 | 
						|
        goto error;
 | 
						|
    }
 | 
						|
 | 
						|
    for (uint32_t i = 0; i < bytes; i++) {
 | 
						|
 | 
						|
        uint8_t octet;
 | 
						|
 | 
						|
        if (!DNP3ReadUint8(buf, len, &octet)) {
 | 
						|
            goto error;
 | 
						|
        }
 | 
						|
 | 
						|
        for (int j = 0; j < 8 && count; j = j + {{object.fields[0].width}}) {
 | 
						|
 | 
						|
            object = SCCalloc(1, sizeof(*object));
 | 
						|
            if (unlikely(object == NULL)) {
 | 
						|
                goto error;
 | 
						|
            }
 | 
						|
 | 
						|
{% if object.fields[0].width == 1 %}
 | 
						|
            object->{{object.fields[0].name}} = (octet >> j) & 0x1;
 | 
						|
{% elif object.fields[0].width == 2 %}
 | 
						|
            object->{{object.fields[0].name}} = (octet >> j) & 0x3;
 | 
						|
{% else %}
 | 
						|
#error "Unhandled field width: {{object.fields[0].width}}"
 | 
						|
{% endif %}
 | 
						|
 | 
						|
            if (!DNP3AddPoint(points, object, point_index, prefix_code, prefix)) {
 | 
						|
                goto error;
 | 
						|
            }
 | 
						|
 | 
						|
            object = NULL;
 | 
						|
            count--;
 | 
						|
            point_index++;
 | 
						|
        }
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    return 1;
 | 
						|
error:
 | 
						|
    if (object != NULL) {
 | 
						|
        SCFree(object);
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
{% else %}
 | 
						|
static int DNP3DecodeObjectG{{object.group}}V{{object.variation}}(const uint8_t **buf, uint32_t *len,
 | 
						|
    uint8_t prefix_code, uint32_t start, uint32_t count,
 | 
						|
    DNP3PointList *points)
 | 
						|
{
 | 
						|
    DNP3ObjectG{{object.group}}V{{object.variation}} *object = NULL;
 | 
						|
    uint32_t prefix = 0;
 | 
						|
    uint32_t point_index = start;
 | 
						|
{% if object._track_offset %}
 | 
						|
    uint32_t offset;
 | 
						|
{% endif %}
 | 
						|
{% if object.constraints %}
 | 
						|
 | 
						|
{% for (key, val) in object.constraints.items() %}
 | 
						|
{% if key == "require_size_prefix" %}
 | 
						|
    if (!DNP3PrefixIsSize(prefix_code)) {
 | 
						|
        goto error;
 | 
						|
    }
 | 
						|
{% elif key == "require_prefix_code" %}
 | 
						|
    if (prefix_code != {{val}}) {
 | 
						|
        goto error;
 | 
						|
    }
 | 
						|
{% else %}
 | 
						|
{{ raise("Unhandled constraint: %s" % (key)) }}
 | 
						|
{% endif %}
 | 
						|
{% endfor %}
 | 
						|
{% endif %}
 | 
						|
 | 
						|
    if (*len < count/8) {
 | 
						|
        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 object._track_offset %}
 | 
						|
 | 
						|
        offset = *len;
 | 
						|
{% endif %}
 | 
						|
 | 
						|
{% for field in object.fields %}
 | 
						|
{% if field.type == "int16" %}
 | 
						|
        if (!DNP3ReadUint16(buf, len, (uint16_t *)&object->{{field.name}})) {
 | 
						|
            goto error;
 | 
						|
        }
 | 
						|
{% elif field.type == "int32" %}
 | 
						|
        if (!DNP3ReadUint32(buf, len, (uint32_t *)&object->{{field.name}})) {
 | 
						|
            goto error;
 | 
						|
        }
 | 
						|
{% elif field.type == "uint8" %}
 | 
						|
        if (!DNP3ReadUint8(buf, len, &object->{{field.name}})) {
 | 
						|
            goto error;
 | 
						|
        }
 | 
						|
{% elif field.type == "uint16" %}
 | 
						|
        if (!DNP3ReadUint16(buf, len, &object->{{field.name}})) {
 | 
						|
            goto error;
 | 
						|
        }
 | 
						|
{% elif field.type == "uint24" %}
 | 
						|
        if (!DNP3ReadUint24(buf, len, &object->{{field.name}})) {
 | 
						|
            goto error;
 | 
						|
        }
 | 
						|
{% elif field.type == "uint32" %}
 | 
						|
        if (!DNP3ReadUint32(buf, len, &object->{{field.name}})) {
 | 
						|
            goto error;
 | 
						|
        }
 | 
						|
{% elif field.type == "uint64" %}
 | 
						|
        if (!DNP3ReadUint64(buf, len, &object->{{field.name}})) {
 | 
						|
            goto error;
 | 
						|
        }
 | 
						|
{% elif field.type == "flt32" %}
 | 
						|
        if (!DNP3ReadFloat32(buf, len, &object->{{field.name}})) {
 | 
						|
            goto error;
 | 
						|
        }
 | 
						|
{% elif field.type == "flt64" %}
 | 
						|
        if (!DNP3ReadFloat64(buf, len, &object->{{field.name}})) {
 | 
						|
            goto error;
 | 
						|
        }
 | 
						|
{% elif field.type == "dnp3time" %}
 | 
						|
        if (!DNP3ReadUint48(buf, len, &object->{{field.name}})) {
 | 
						|
            goto error;
 | 
						|
        }
 | 
						|
{% elif field.type == "vstr4" %}
 | 
						|
        if (*len < 4) {
 | 
						|
            goto error;
 | 
						|
        }
 | 
						|
        memcpy(object->{{field.name}}, *buf, 4);
 | 
						|
        object->{{field.name}}[4] = '\\\\0';
 | 
						|
        *buf += 4;
 | 
						|
        *len -= 4;
 | 
						|
{% elif field.type == "bytearray" %}
 | 
						|
{% if field.len_from_prefix %}
 | 
						|
        if (prefix < (offset - *len)) {
 | 
						|
            goto error;
 | 
						|
        }
 | 
						|
        object->{{field.len_field}} = (uint16_t) (prefix - (offset - *len));
 | 
						|
{% endif %}
 | 
						|
        if (object->{{field.len_field}} > 0) {
 | 
						|
            if (*len < object->{{field.len_field}}) {
 | 
						|
                /* Not enough data. */
 | 
						|
                goto error;
 | 
						|
            }
 | 
						|
            object->{{field.name}} = SCCalloc(1, object->{{field.len_field}});
 | 
						|
            if (unlikely(object->{{field.name}} == NULL)) {
 | 
						|
                goto error;
 | 
						|
            }
 | 
						|
            memcpy(object->{{field.name}}, *buf, object->{{field.len_field}});
 | 
						|
            *buf += object->{{field.len_field}};
 | 
						|
            *len -= object->{{field.len_field}};
 | 
						|
        }
 | 
						|
{% elif field.type == "chararray" %}
 | 
						|
{% if field.len_from_prefix %}
 | 
						|
        if (prefix - (offset - *len) >= {{field.size}} || prefix < (offset - *len)) {
 | 
						|
            goto error;
 | 
						|
        }
 | 
						|
{% if field.size == 255 %}
 | 
						|
        object->{{field.len_field}} = (uint8_t) (prefix - (offset - *len));
 | 
						|
{% else %}
 | 
						|
        object->{{field.len_field}} = (uint16_t) (prefix - (offset - *len));
 | 
						|
{% endif %}
 | 
						|
{% endif %}
 | 
						|
        if (object->{{field.len_field}} > 0) {
 | 
						|
            if (*len < object->{{field.len_field}}) {
 | 
						|
                /* Not enough data. */
 | 
						|
                goto error;
 | 
						|
            }
 | 
						|
            memcpy(object->{{field.name}}, *buf, object->{{field.len_field}});
 | 
						|
            *buf += object->{{field.len_field}};
 | 
						|
            *len -= object->{{field.len_field}};
 | 
						|
        }
 | 
						|
        object->{{field.name}}[object->{{field.len_field}}] = '\\\\0';
 | 
						|
{% elif field.type == "bstr8" %}
 | 
						|
        {
 | 
						|
            uint8_t octet;
 | 
						|
            if (!DNP3ReadUint8(buf, len, &octet)) {
 | 
						|
                goto error;
 | 
						|
            }
 | 
						|
{% set ns = namespace(shift=0) %}
 | 
						|
{% for field in field.fields %}
 | 
						|
{% if field.width == 1 %}
 | 
						|
            object->{{field.name}} = (octet >> {{ns.shift}}) & 0x1;
 | 
						|
{% elif field.width == 2 %}
 | 
						|
            object->{{field.name}} = (octet >> {{ns.shift}}) & 0x3;
 | 
						|
{% elif field.width == 4 %}
 | 
						|
            object->{{field.name}} = (octet >> {{ns.shift}}) & 0xf;
 | 
						|
{% elif field.width == 7 %}
 | 
						|
            object->{{field.name}} = (octet >> {{ns.shift}}) & 0x7f;
 | 
						|
{% else %}
 | 
						|
{{ raise("Unhandled width of %d." % (field.width)) }}
 | 
						|
{% endif %}
 | 
						|
{% set ns.shift = ns.shift + field.width %}
 | 
						|
{% endfor %}
 | 
						|
        }
 | 
						|
{% else %}
 | 
						|
{{ raise("Unhandled datatype '%s' for object %d:%d." % (field.type,
 | 
						|
       object.group, object.variation)) }}
 | 
						|
{% endif %}
 | 
						|
{% endfor %}
 | 
						|
 | 
						|
        if (!DNP3AddPoint(points, object, point_index, prefix_code, prefix)) {
 | 
						|
            goto error;
 | 
						|
        }
 | 
						|
 | 
						|
        object = NULL;
 | 
						|
        point_index++;
 | 
						|
    }
 | 
						|
 | 
						|
    return 1;
 | 
						|
error:
 | 
						|
    if (object != NULL) {
 | 
						|
{% for field in object.fields %}
 | 
						|
{% if field.type == "bytearray" %}
 | 
						|
        if (object->{{field.name}} != NULL) {
 | 
						|
            SCFree(object->{{field.name}});
 | 
						|
        }
 | 
						|
{% endif %}
 | 
						|
{% endfor %}
 | 
						|
        SCFree(object);
 | 
						|
    }
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
{% endif %}
 | 
						|
{% endfor %}
 | 
						|
 | 
						|
void DNP3FreeObjectPoint(int group, int variation, void *point)
 | 
						|
{
 | 
						|
    switch(DNP3_OBJECT_CODE(group, variation)) {
 | 
						|
{% for object in objects %}
 | 
						|
{% if f_has_freeable_types(object.fields) %}
 | 
						|
        case DNP3_OBJECT_CODE({{object.group}}, {{object.variation}}): {
 | 
						|
            DNP3ObjectG{{object.group}}V{{object.variation}} *object = (DNP3ObjectG{{object.group}}V{{object.variation}} *) point;
 | 
						|
{% for field in object.fields %}
 | 
						|
{% if field.type == "bytearray" %}
 | 
						|
            if (object->{{field.name}} != NULL) {
 | 
						|
                SCFree(object->{{field.name}});
 | 
						|
            }
 | 
						|
{% endif %}
 | 
						|
{% endfor %}
 | 
						|
            break;
 | 
						|
        }
 | 
						|
{% endif %}
 | 
						|
{% endfor %}
 | 
						|
        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)) {
 | 
						|
{% for object in objects %}
 | 
						|
        case DNP3_OBJECT_CODE({{object.group}}, {{object.variation}}):
 | 
						|
            rc = DNP3DecodeObjectG{{object.group}}V{{object.variation}}(buf, len, prefix_code, start, count,
 | 
						|
                points);
 | 
						|
            break;
 | 
						|
{% endfor %}
 | 
						|
        default:
 | 
						|
            return DNP3_DECODER_EVENT_UNKNOWN_OBJECT;
 | 
						|
    }
 | 
						|
 | 
						|
    return rc ? 0 : DNP3_DECODER_EVENT_MALFORMED;
 | 
						|
}
 | 
						|
 | 
						|
"""
 | 
						|
 | 
						|
    try:
 | 
						|
        filename = "src/app-layer-dnp3-objects.c"
 | 
						|
        env = jinja2.Environment(trim_blocks=True, lstrip_blocks=True)
 | 
						|
        code = env.from_string(template).render(context)
 | 
						|
        content = open(filename).read()
 | 
						|
        content = re.sub(
 | 
						|
            "(%s).*(%s)" % (re.escape(IN_PLACE_START), re.escape(IN_PLACE_END)),
 | 
						|
            r"\1%s\n\2" % (code), content, 1, re.M | re.DOTALL)
 | 
						|
        open(filename, "w").write(content)
 | 
						|
        print("Updated %s." % (filename))
 | 
						|
    except Exception as err:
 | 
						|
        print("Failed to update %s: %s" % (filename, err), file=sys.stderr)
 | 
						|
        sys.exit(1)
 | 
						|
 | 
						|
def preprocess_object(obj):
 | 
						|
 | 
						|
    valid_keys = [
 | 
						|
        "group",
 | 
						|
        "variation",
 | 
						|
        "constraints",
 | 
						|
        "extra_fields",
 | 
						|
        "fields",
 | 
						|
        "packed",
 | 
						|
    ]
 | 
						|
 | 
						|
    valid_field_keys = [
 | 
						|
        "type",
 | 
						|
        "name",
 | 
						|
        "width",
 | 
						|
        "len_from_prefix",
 | 
						|
        "len_field",
 | 
						|
        "fields",
 | 
						|
        "size",
 | 
						|
    ]
 | 
						|
 | 
						|
    if "unimplemented" in obj:
 | 
						|
        print("Object not implemented: %s:%s: %s" % (
 | 
						|
            str(obj["group"]), str(obj["variation"]), obj["unimplemented"]))
 | 
						|
        return None
 | 
						|
 | 
						|
    for key, val in obj.items():
 | 
						|
 | 
						|
        if key not in valid_keys:
 | 
						|
            print("Invalid key '%s' in object %d:%d" % (
 | 
						|
                key, obj["group"], obj["variation"]), file=sys.stderr)
 | 
						|
            sys.exit(1)
 | 
						|
 | 
						|
    for field in obj["fields"]:
 | 
						|
 | 
						|
        for key in field.keys():
 | 
						|
            if key not in valid_field_keys:
 | 
						|
                print("Invalid key '%s' in object %d:%d" % (
 | 
						|
                    key, obj["group"], obj["variation"]), file=sys.stderr)
 | 
						|
                sys.exit(1)
 | 
						|
 | 
						|
        if "len_from_prefix" in field and field["len_from_prefix"]:
 | 
						|
            obj["_track_offset"] = True
 | 
						|
            break
 | 
						|
 | 
						|
        if field["type"] == "bstr8":
 | 
						|
            width = 0
 | 
						|
            for subfield in field["fields"]:
 | 
						|
                width += int(subfield["width"])
 | 
						|
            assert(width == 8)
 | 
						|
 | 
						|
    return obj
 | 
						|
 | 
						|
def main():
 | 
						|
 | 
						|
    # Require Jinja2 2.10 or greater.
 | 
						|
    jv = jinja2.__version__.split(".")
 | 
						|
    if int(jv[0]) < 2 or (int(jv[0]) == 2 and int(jv[1]) < 10):
 | 
						|
        print("error: jinja2 v2.10 or great required")
 | 
						|
        return 1
 | 
						|
 | 
						|
    definitions = yaml.load(open("scripts/dnp3-gen/dnp3-objects.yaml"))
 | 
						|
    print("Loaded %s objects." % (len(definitions["objects"])))
 | 
						|
    definitions["objects"] = map(preprocess_object, definitions["objects"])
 | 
						|
 | 
						|
    # Filter out unimplemented objects.
 | 
						|
    definitions["objects"] = [
 | 
						|
        obj for obj in definitions["objects"] if obj != None]
 | 
						|
 | 
						|
    context = {
 | 
						|
        "raise": raise_helper,
 | 
						|
        "objects": definitions["objects"],
 | 
						|
        "is_integer_type": is_integer_type,
 | 
						|
        "f_to_type": to_type,
 | 
						|
        "f_has_freeable_types": has_freeable_types,
 | 
						|
        "command_line": " ".join(sys.argv),
 | 
						|
    }
 | 
						|
 | 
						|
    gen_object_structs(context)
 | 
						|
    gen_object_decoders(context)
 | 
						|
    generate(util_lua_dnp3_objects_c_template,
 | 
						|
             "src/util-lua-dnp3-objects.c",
 | 
						|
             context)
 | 
						|
    generate(output_json_dnp3_objects_template,
 | 
						|
             "src/output-json-dnp3-objects.c",
 | 
						|
             context)
 | 
						|
 | 
						|
if __name__ == "__main__":
 | 
						|
    sys.exit(main())
 |