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.
741 lines
22 KiB
Python
741 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"
|
|
|
|
#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;
|
|
}
|
|
}
|
|
|
|
"""
|
|
|
|
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())
|