]> git.ipfire.org Git - people/ms/suricata.git/blame - scripts/dnp3-gen/dnp3-gen.py
dnp3: adds bounds check for prefix chararray
[people/ms/suricata.git] / scripts / dnp3-gen / dnp3-gen.py
CommitLineData
240d789c
JI
1#! /usr/bin/env python
2#
3# Copyright (C) 2015 Open Information Security Foundation
4#
5# You can copy, redistribute or modify this Program under the terms of
6# the GNU General Public License version 2 as published by the Free
7# Software Foundation.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# version 2 along with this program; if not, write to the Free Software
16# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17# 02110-1301, USA.
18
19# This script generates DNP3 related source code based on definitions
20# of DNP3 objects (currently the object structs).
21
22from __future__ import print_function
23
24import sys
25import re
240d789c 26import yaml
240d789c
JI
27
28import jinja2
29
30IN_PLACE_START = "/* START GENERATED CODE */"
31IN_PLACE_END = "/* END GENERATED CODE */"
32
33util_lua_dnp3_objects_c_template = """/* Copyright (C) 2015 Open Information Security Foundation
34 *
35 * You can copy, redistribute or modify this Program under the terms of
36 * the GNU General Public License version 2 as published by the Free
37 * Software Foundation.
38 *
39 * This program is distributed in the hope that it will be useful,
40 * but WITHOUT ANY WARRANTY; without even the implied warranty of
41 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
42 * GNU General Public License for more details.
43 *
44 * You should have received a copy of the GNU General Public License
45 * version 2 along with this program; if not, write to the Free Software
46 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
47 * 02110-1301, USA.
48 */
49
50/**
51 * DO NOT EDIT. THIS FILE IS AUTO-GENERATED.
52 *
53 * Generated by command:
54 * {{command_line}}
55 */
56
57#include "suricata-common.h"
58
59#include "app-layer-dnp3.h"
60#include "app-layer-dnp3-objects.h"
61
62#ifdef HAVE_LUA
63
64#include <lua.h>
65#include <lualib.h>
66#include <lauxlib.h>
67
68#include "util-lua.h"
ac7cf48a 69#include "util-lua-dnp3-objects.h"
240d789c
JI
70
71/**
72 * \\brief Push an object point item onto the stack.
73 */
74void DNP3PushPoint(lua_State *luastate, DNP3Object *object,
75 DNP3Point *point)
76{
77 switch (DNP3_OBJECT_CODE(object->group, object->variation)) {
78{% for object in objects %}
79 case DNP3_OBJECT_CODE({{object.group}}, {{object.variation}}): {
80 DNP3ObjectG{{object.group}}V{{object.variation}} *data = point->data;
81{% for field in object.fields %}
82{% if is_integer_type(field.type) %}
83 lua_pushliteral(luastate, "{{field.name}}");
84 lua_pushinteger(luastate, data->{{field.name}});
85 lua_settable(luastate, -3);
86{% elif field["type"] in ["flt32", "flt64"] %}
87 lua_pushliteral(luastate, "{{field.name}}");
88 lua_pushnumber(luastate, data->{{field.name}});
89 lua_settable(luastate, -3);
90{% elif field["type"] == "chararray" %}
91 lua_pushliteral(luastate, "{{field.name}}");
92 LuaPushStringBuffer(luastate, (uint8_t *)data->{{field.name}},
93 strlen(data->{{field.name}}));
94 lua_settable(luastate, -3);
95{% elif field["type"] == "vstr4" %}
96 lua_pushliteral(luastate, "{{field.name}}");
97 LuaPushStringBuffer(luastate, (uint8_t *)data->{{field.name}},
98 strlen(data->{{field.name}}));
99 lua_settable(luastate, -3);
100{% elif field.type == "bytearray" %}
101 lua_pushliteral(luastate, "{{field.name}}");
102 lua_pushlstring(luastate, (const char *)data->{{field.name}},
103 data->{{field.len_field}});
104 lua_settable(luastate, -3);
105{% elif field.type == "bstr8" %}
106{% for field in field.fields %}
107 lua_pushliteral(luastate, "{{field.name}}");
108 lua_pushinteger(luastate, data->{{field.name}});
109 lua_settable(luastate, -3);
110{% endfor %}
111{% else %}
112{{ raise("Unhandled datatype: %s" % (field.type)) }}
113{% endif %}
114{% endfor %}
115 break;
116 }
117{% endfor %}
118 default:
119 break;
120 }
121}
122
123#endif /* HAVE_LUA */
124
125"""
126
127output_json_dnp3_objects_template = """/* Copyright (C) 2015 Open Information Security Foundation
128 *
129 * You can copy, redistribute or modify this Program under the terms of
130 * the GNU General Public License version 2 as published by the Free
131 * Software Foundation.
132 *
133 * This program is distributed in the hope that it will be useful,
134 * but WITHOUT ANY WARRANTY; without even the implied warranty of
135 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
136 * GNU General Public License for more details.
137 *
138 * You should have received a copy of the GNU General Public License
139 * version 2 along with this program; if not, write to the Free Software
140 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
141 * 02110-1301, USA.
142 */
143
144/**
145 * DO NOT EDIT. THIS FILE IS AUTO-GENERATED.
146 *
147 * Generated by command:
148 * {{command_line}}
149 */
150
151#include "suricata-common.h"
152
153#include "util-crypt.h"
154
155#include "app-layer-dnp3.h"
156#include "app-layer-dnp3-objects.h"
ac7cf48a 157#include "output-json-dnp3-objects.h"
4976afd9 158#include "output-json.h"
240d789c 159
85eaa227 160void OutputJsonDNP3SetItem(JsonBuilder *js, DNP3Object *object,
240d789c
JI
161 DNP3Point *point)
162{
163
164 switch (DNP3_OBJECT_CODE(object->group, object->variation)) {
165{% for object in objects %}
166 case DNP3_OBJECT_CODE({{object.group}}, {{object.variation}}): {
167 DNP3ObjectG{{object.group}}V{{object.variation}} *data = point->data;
168{% for field in object.fields %}
169{% if is_integer_type(field.type) %}
85eaa227 170 jb_set_uint(js, "{{field.name}}", data->{{field.name}});
240d789c 171{% elif field.type in ["flt32", "flt64"] %}
85eaa227 172 jb_set_float(js, "{{field.name}}", data->{{field.name}});
240d789c 173{% elif field.type == "bytearray" %}
5ec9688f 174 unsigned long {{field.name}}_b64_len = BASE64_BUFFER_SIZE(data->{{field.len_field}});
240d789c
JI
175 uint8_t {{field.name}}_b64[{{field.name}}_b64_len];
176 Base64Encode(data->{{field.name}}, data->{{field.len_field}},
177 {{field.name}}_b64, &{{field.name}}_b64_len);
85eaa227 178 jb_set_string(js, "data->{{field.name}}", (char *){{field.name}}_b64);
240d789c 179{% elif field.type == "vstr4" %}
85eaa227 180 jb_set_string(js, "data->{{field.name}}", data->{{field.name}});
240d789c
JI
181{% elif field.type == "chararray" %}
182 if (data->{{field.len_field}} > 0) {
183 /* First create a null terminated string as not all versions
184 * of jansson have json_stringn. */
185 char tmpbuf[data->{{field.len_field}} + 1];
186 memcpy(tmpbuf, data->{{field.name}}, data->{{field.len_field}});
187 tmpbuf[data->{{field.len_field}}] = '\\0';
85eaa227 188 jb_set_string(js, "{{field.name}}", tmpbuf);
240d789c 189 } else {
85eaa227 190 jb_set_string(js, "{{field.name}}", "");
240d789c
JI
191 }
192{% elif field.type == "bstr8" %}
193{% for field in field.fields %}
85eaa227 194 jb_set_uint(js, "{{field.name}}", data->{{field.name}});
240d789c
JI
195{% endfor %}
196{% else %}
197{{ raise("Unhandled datatype: %s" % (field.type)) }}
198{% endif %}
199{% endfor %}
200 break;
201 }
202{% endfor %}
203 default:
204 SCLogDebug("Unknown object: %d:%d", object->group,
205 object->variation);
206 break;
207 }
208
209}
210
240d789c
JI
211"""
212
213def has_freeable_types(fields):
214 freeable_types = [
215 "bytearray",
216 ]
217 for field in fields:
218 if field["type"] in freeable_types:
219 return True
220 return False
221
222def is_integer_type(datatype):
223 integer_types = [
224 "uint64",
225 "uint32",
226 "uint24",
227 "uint16",
228 "uint8",
229 "int64",
230 "int32",
231 "int16",
232 "int8",
233 "dnp3time",
234 ]
235 return datatype in integer_types
236
237def to_type(datatype):
238 type_map = {
239 "uint8": "uint8_t",
240 }
241 if datatype in type_map:
242 return type_map[datatype]
243 else:
244 raise Exception("Unknown datatype: %s" % (datatype))
245
246def generate(template, filename, context):
247 print("Generating %s." % (filename))
248 try:
249 env = jinja2.Environment(trim_blocks=True)
250 output = env.from_string(template).render(context)
251 with open(filename, "w") as fileobj:
252 fileobj.write(output)
253 except Exception as err:
254 print("Failed to generate %s: %s" % (filename, err))
255 sys.exit(1)
256
257def raise_helper(msg):
258 raise Exception(msg)
259
260def gen_object_structs(context):
261 """ Generate structs for all the define DNP3 objects. """
262
263 template = """
264
265/* Code generated by:
266 * {{command_line}}
267 */
268
269{% for object in objects %}
270typedef struct DNP3ObjectG{{object.group}}V{{object.variation}}_ {
271{% for field in object.fields %}
272{% if field.type == "bstr8" %}
273{% for field in field.fields %}
274 uint8_t {{field.name}}:{{field.width}};
275{% endfor %}
276{% else %}
277{% if field.type == "int16" %}
278 int16_t {{field.name}};
279{% elif field.type == "int32" %}
280 int32_t {{field.name}};
281{% elif field.type == "uint8" %}
282 uint8_t {{field.name}};
283{% elif field.type == "uint16" %}
284 uint16_t {{field.name}};
285{% elif field.type == "uint24" %}
286 uint32_t {{field.name}};
287{% elif field.type == "uint32" %}
288 uint32_t {{field.name}};
289{% elif field.type == "uint64" %}
290 uint64_t {{field.name}};
291{% elif field.type == "flt32" %}
292 float {{field.name}};
293{% elif field.type == "flt64" %}
294 double {{field.name}};
295{% elif field.type == "dnp3time" %}
296 uint64_t {{field.name}};
297{% elif field.type == "bytearray" %}
298 uint8_t *{{field.name}};
299{% elif field.type == "vstr4" %}
300 char {{field.name}}[5];
301{% elif field.type == "chararray" %}
302 char {{field.name}}[{{field.size}}];
303{% else %}
304 {{ raise("Unknown datatype type '%s' for object %d:%d" % (
305 field.type, object.group, object.variation)) }}
306{% endif %}
307{% endif %}
308{% endfor %}
309{% if object.extra_fields %}
310{% for field in object.extra_fields %}
311{% if field.type == "uint8" %}
312 uint8_t {{field.name}};
313{% elif field.type == "uint16" %}
314 uint16_t {{field.name}};
315{% elif field.type == "uint32" %}
316 uint32_t {{field.name}};
317{% else %}
318 {{ raise("Unknown datatype: %s" % (field.type)) }}
319{% endif %}
320{% endfor %}
321{% endif %}
322} DNP3ObjectG{{object.group}}V{{object.variation}};
323
324{% endfor %}
325"""
326
327 filename = "src/app-layer-dnp3-objects.h"
328 try:
329 env = jinja2.Environment(trim_blocks=True)
330 code = env.from_string(template).render(context)
331 content = open(filename).read()
332 content = re.sub(
333 "(%s).*(%s)" % (re.escape(IN_PLACE_START), re.escape(IN_PLACE_END)),
334 r"\1%s\2" % (code), content, 1, re.M | re.DOTALL)
335 open(filename, "w").write(content)
336 print("Updated %s." % (filename))
337 except Exception as err:
338 print("Failed to update %s: %s" % (filename, err), file=sys.stderr)
339 sys.exit(1)
340
341def gen_object_decoders(context):
342 """ Generate decoders for all defined DNP3 objects. """
343
344 template = """
345
346/* Code generated by:
347 * {{command_line}}
348 */
349
350{% for object in objects %}
351{% if object.packed %}
352static int DNP3DecodeObjectG{{object.group}}V{{object.variation}}(const uint8_t **buf, uint32_t *len,
353 uint8_t prefix_code, uint32_t start, uint32_t count,
354 DNP3PointList *points)
355{
356 DNP3ObjectG{{object.group}}V{{object.variation}} *object = NULL;
00135054 357 uint32_t bytes = (count / 8) + 1;
240d789c 358 uint32_t prefix = 0;
00135054 359 uint32_t point_index = start;
240d789c
JI
360
361 if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) {
362 goto error;
363 }
364
00135054 365 for (uint32_t i = 0; i < bytes; i++) {
240d789c
JI
366
367 uint8_t octet;
368
369 if (!DNP3ReadUint8(buf, len, &octet)) {
370 goto error;
371 }
372
373 for (int j = 0; j < 8 && count; j = j + {{object.fields[0].width}}) {
374
375 object = SCCalloc(1, sizeof(*object));
376 if (unlikely(object == NULL)) {
377 goto error;
378 }
379
380{% if object.fields[0].width == 1 %}
381 object->{{object.fields[0].name}} = (octet >> j) & 0x1;
382{% elif object.fields[0].width == 2 %}
383 object->{{object.fields[0].name}} = (octet >> j) & 0x3;
384{% else %}
385#error "Unhandled field width: {{object.fields[0].width}}"
386{% endif %}
387
2a0bb108 388 if (!DNP3AddPoint(points, object, point_index, prefix_code, prefix)) {
240d789c
JI
389 goto error;
390 }
391
392 object = NULL;
393 count--;
2a0bb108 394 point_index++;
240d789c
JI
395 }
396
397 }
398
399 return 1;
400error:
401 if (object != NULL) {
402 SCFree(object);
403 }
404 return 0;
405}
406
407{% else %}
408static int DNP3DecodeObjectG{{object.group}}V{{object.variation}}(const uint8_t **buf, uint32_t *len,
409 uint8_t prefix_code, uint32_t start, uint32_t count,
410 DNP3PointList *points)
411{
412 DNP3ObjectG{{object.group}}V{{object.variation}} *object = NULL;
413 uint32_t prefix = 0;
2a0bb108 414 uint32_t point_index = start;
240d789c
JI
415{% if object._track_offset %}
416 uint32_t offset;
417{% endif %}
418{% if object.constraints %}
419
420{% for (key, val) in object.constraints.items() %}
421{% if key == "require_size_prefix" %}
422 if (!DNP3PrefixIsSize(prefix_code)) {
423 goto error;
424 }
425{% elif key == "require_prefix_code" %}
426 if (prefix_code != {{val}}) {
427 goto error;
428 }
429{% else %}
430{{ raise("Unhandled constraint: %s" % (key)) }}
431{% endif %}
432{% endfor %}
433{% endif %}
434
15980af7
PA
435 if (*len < count/8) {
436 goto error;
437 }
240d789c
JI
438 while (count--) {
439
440 object = SCCalloc(1, sizeof(*object));
441 if (unlikely(object == NULL)) {
442 goto error;
443 }
444
445 if (!DNP3ReadPrefix(buf, len, prefix_code, &prefix)) {
446 goto error;
447 }
448{% if object._track_offset %}
449
450 offset = *len;
451{% endif %}
452
453{% for field in object.fields %}
454{% if field.type == "int16" %}
455 if (!DNP3ReadUint16(buf, len, (uint16_t *)&object->{{field.name}})) {
456 goto error;
457 }
458{% elif field.type == "int32" %}
459 if (!DNP3ReadUint32(buf, len, (uint32_t *)&object->{{field.name}})) {
460 goto error;
461 }
462{% elif field.type == "uint8" %}
463 if (!DNP3ReadUint8(buf, len, &object->{{field.name}})) {
464 goto error;
465 }
466{% elif field.type == "uint16" %}
467 if (!DNP3ReadUint16(buf, len, &object->{{field.name}})) {
468 goto error;
469 }
470{% elif field.type == "uint24" %}
471 if (!DNP3ReadUint24(buf, len, &object->{{field.name}})) {
472 goto error;
473 }
474{% elif field.type == "uint32" %}
475 if (!DNP3ReadUint32(buf, len, &object->{{field.name}})) {
476 goto error;
477 }
478{% elif field.type == "uint64" %}
479 if (!DNP3ReadUint64(buf, len, &object->{{field.name}})) {
480 goto error;
481 }
482{% elif field.type == "flt32" %}
483 if (!DNP3ReadFloat32(buf, len, &object->{{field.name}})) {
484 goto error;
485 }
486{% elif field.type == "flt64" %}
487 if (!DNP3ReadFloat64(buf, len, &object->{{field.name}})) {
488 goto error;
489 }
490{% elif field.type == "dnp3time" %}
491 if (!DNP3ReadUint48(buf, len, &object->{{field.name}})) {
492 goto error;
493 }
494{% elif field.type == "vstr4" %}
495 if (*len < 4) {
496 goto error;
497 }
498 memcpy(object->{{field.name}}, *buf, 4);
499 object->{{field.name}}[4] = '\\\\0';
500 *buf += 4;
501 *len -= 4;
502{% elif field.type == "bytearray" %}
503{% if field.len_from_prefix %}
504 object->{{field.len_field}} = prefix - (offset - *len);
505{% endif %}
506 if (object->{{field.len_field}} > 0) {
507 if (*len < object->{{field.len_field}}) {
508 /* Not enough data. */
509 goto error;
510 }
511 object->{{field.name}} = SCCalloc(1, object->{{field.len_field}});
512 if (unlikely(object->{{field.name}} == NULL)) {
513 goto error;
514 }
515 memcpy(object->{{field.name}}, *buf, object->{{field.len_field}});
516 *buf += object->{{field.len_field}};
517 *len -= object->{{field.len_field}};
518 }
519{% elif field.type == "chararray" %}
520{% if field.len_from_prefix %}
126a7dcb
PA
521 if (prefix - (offset - *len) >= {{field.size}}) {
522 goto error;
523 }
240d789c
JI
524 object->{{field.len_field}} = prefix - (offset - *len);
525{% endif %}
526 if (object->{{field.len_field}} > 0) {
6a6aa04f
PA
527 if (*len < object->{{field.len_field}}) {
528 /* Not enough data. */
529 goto error;
530 }
240d789c
JI
531 memcpy(object->{{field.name}}, *buf, object->{{field.len_field}});
532 *buf += object->{{field.len_field}};
533 *len -= object->{{field.len_field}};
534 }
535 object->{{field.name}}[object->{{field.len_field}}] = '\\\\0';
536{% elif field.type == "bstr8" %}
537 {
538 uint8_t octet;
539 if (!DNP3ReadUint8(buf, len, &octet)) {
540 goto error;
541 }
6a6aa04f 542{% set ns = namespace(shift=0) %}
240d789c
JI
543{% for field in field.fields %}
544{% if field.width == 1 %}
6a6aa04f 545 object->{{field.name}} = (octet >> {{ns.shift}}) & 0x1;
240d789c 546{% elif field.width == 2 %}
6a6aa04f 547 object->{{field.name}} = (octet >> {{ns.shift}}) & 0x3;
240d789c 548{% elif field.width == 4 %}
6a6aa04f 549 object->{{field.name}} = (octet >> {{ns.shift}}) & 0xf;
240d789c 550{% elif field.width == 7 %}
6a6aa04f 551 object->{{field.name}} = (octet >> {{ns.shift}}) & 0x7f;
240d789c
JI
552{% else %}
553{{ raise("Unhandled width of %d." % (field.width)) }}
554{% endif %}
6a6aa04f 555{% set ns.shift = ns.shift + field.width %}
240d789c
JI
556{% endfor %}
557 }
558{% else %}
559{{ raise("Unhandled datatype '%s' for object %d:%d." % (field.type,
560 object.group, object.variation)) }}
561{% endif %}
562{% endfor %}
563
2a0bb108 564 if (!DNP3AddPoint(points, object, point_index, prefix_code, prefix)) {
240d789c
JI
565 goto error;
566 }
567
568 object = NULL;
2a0bb108 569 point_index++;
240d789c
JI
570 }
571
572 return 1;
573error:
574 if (object != NULL) {
5749024e
PA
575{% for field in object.fields %}
576{% if field.type == "bytearray" %}
577 if (object->{{field.name}} != NULL) {
578 SCFree(object->{{field.name}});
579 }
580{% endif %}
581{% endfor %}
240d789c
JI
582 SCFree(object);
583 }
584
585 return 0;
586}
587
588{% endif %}
589{% endfor %}
590
591void DNP3FreeObjectPoint(int group, int variation, void *point)
592{
593 switch(DNP3_OBJECT_CODE(group, variation)) {
594{% for object in objects %}
595{% if f_has_freeable_types(object.fields) %}
596 case DNP3_OBJECT_CODE({{object.group}}, {{object.variation}}): {
597 DNP3ObjectG{{object.group}}V{{object.variation}} *object = (DNP3ObjectG{{object.group}}V{{object.variation}} *) point;
598{% for field in object.fields %}
599{% if field.type == "bytearray" %}
600 if (object->{{field.name}} != NULL) {
601 SCFree(object->{{field.name}});
602 }
603{% endif %}
604{% endfor %}
605 break;
606 }
607{% endif %}
608{% endfor %}
609 default:
610 break;
611 }
612 SCFree(point);
613}
614
615/**
616 * \\\\brief Decode a DNP3 object.
617 *
618 * \\\\retval 0 on success. On failure a positive integer corresponding
619 * to a DNP3 application layer event will be returned.
620 */
621int DNP3DecodeObject(int group, int variation, const uint8_t **buf,
622 uint32_t *len, uint8_t prefix_code, uint32_t start,
623 uint32_t count, DNP3PointList *points)
624{
625 int rc = 0;
626
627 switch (DNP3_OBJECT_CODE(group, variation)) {
628{% for object in objects %}
629 case DNP3_OBJECT_CODE({{object.group}}, {{object.variation}}):
630 rc = DNP3DecodeObjectG{{object.group}}V{{object.variation}}(buf, len, prefix_code, start, count,
631 points);
632 break;
633{% endfor %}
634 default:
635 return DNP3_DECODER_EVENT_UNKNOWN_OBJECT;
636 }
637
638 return rc ? 0 : DNP3_DECODER_EVENT_MALFORMED;
639}
640
641"""
642
643 try:
644 filename = "src/app-layer-dnp3-objects.c"
645 env = jinja2.Environment(trim_blocks=True, lstrip_blocks=True)
646 code = env.from_string(template).render(context)
647 content = open(filename).read()
648 content = re.sub(
649 "(%s).*(%s)" % (re.escape(IN_PLACE_START), re.escape(IN_PLACE_END)),
650 r"\1%s\n\2" % (code), content, 1, re.M | re.DOTALL)
651 open(filename, "w").write(content)
652 print("Updated %s." % (filename))
653 except Exception as err:
654 print("Failed to update %s: %s" % (filename, err), file=sys.stderr)
655 sys.exit(1)
656
657def preprocess_object(obj):
658
659 valid_keys = [
660 "group",
661 "variation",
662 "constraints",
663 "extra_fields",
664 "fields",
665 "packed",
666 ]
667
668 valid_field_keys = [
669 "type",
670 "name",
671 "width",
672 "len_from_prefix",
673 "len_field",
674 "fields",
675 "size",
676 ]
677
678 if "unimplemented" in obj:
679 print("Object not implemented: %s:%s: %s" % (
680 str(obj["group"]), str(obj["variation"]), obj["unimplemented"]))
681 return None
682
683 for key, val in obj.items():
684
685 if key not in valid_keys:
686 print("Invalid key '%s' in object %d:%d" % (
687 key, obj["group"], obj["variation"]), file=sys.stderr)
688 sys.exit(1)
689
690 for field in obj["fields"]:
691
692 for key in field.keys():
693 if key not in valid_field_keys:
694 print("Invalid key '%s' in object %d:%d" % (
695 key, obj["group"], obj["variation"]), file=sys.stderr)
696 sys.exit(1)
697
698 if "len_from_prefix" in field and field["len_from_prefix"]:
699 obj["_track_offset"] = True
700 break
701
702 if field["type"] == "bstr8":
703 width = 0
704 for subfield in field["fields"]:
705 width += int(subfield["width"])
706 assert(width == 8)
707
708 return obj
709
710def main():
711
f70e8d00
JI
712 # Require Jinja2 2.10 or greater.
713 jv = jinja2.__version__.split(".")
714 if int(jv[0]) < 2 or (int(jv[0]) == 2 and int(jv[1]) < 10):
715 print("error: jinja2 v2.10 or great required")
716 return 1
717
240d789c
JI
718 definitions = yaml.load(open("scripts/dnp3-gen/dnp3-objects.yaml"))
719 print("Loaded %s objects." % (len(definitions["objects"])))
720 definitions["objects"] = map(preprocess_object, definitions["objects"])
721
722 # Filter out unimplemented objects.
723 definitions["objects"] = [
724 obj for obj in definitions["objects"] if obj != None]
725
726 context = {
727 "raise": raise_helper,
728 "objects": definitions["objects"],
729 "is_integer_type": is_integer_type,
730 "f_to_type": to_type,
731 "f_has_freeable_types": has_freeable_types,
732 "command_line": " ".join(sys.argv),
733 }
734
735 gen_object_structs(context)
736 gen_object_decoders(context)
737 generate(util_lua_dnp3_objects_c_template,
738 "src/util-lua-dnp3-objects.c",
739 context)
740 generate(output_json_dnp3_objects_template,
741 "src/output-json-dnp3-objects.c",
742 context)
743
744if __name__ == "__main__":
745 sys.exit(main())