]> git.ipfire.org Git - people/ms/suricata.git/blame - scripts/dnp3-gen/dnp3-gen.py
dnp3: fix int warnings
[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 %}
86b5c81e
PA
504 if (prefix < (offset - *len)) {
505 goto error;
506 }
507 object->{{field.len_field}} = (uint16_t) (prefix - (offset - *len));
240d789c
JI
508{% endif %}
509 if (object->{{field.len_field}} > 0) {
510 if (*len < object->{{field.len_field}}) {
511 /* Not enough data. */
512 goto error;
513 }
514 object->{{field.name}} = SCCalloc(1, object->{{field.len_field}});
515 if (unlikely(object->{{field.name}} == NULL)) {
516 goto error;
517 }
518 memcpy(object->{{field.name}}, *buf, object->{{field.len_field}});
519 *buf += object->{{field.len_field}};
520 *len -= object->{{field.len_field}};
521 }
522{% elif field.type == "chararray" %}
523{% if field.len_from_prefix %}
86b5c81e 524 if (prefix - (offset - *len) >= {{field.size}} || prefix < (offset - *len)) {
126a7dcb
PA
525 goto error;
526 }
86b5c81e
PA
527{% if field.size == 255 %}
528 object->{{field.len_field}} = (uint8_t) (prefix - (offset - *len));
529{% else %}
530 object->{{field.len_field}} = (uint16_t) (prefix - (offset - *len));
531{% endif %}
240d789c
JI
532{% endif %}
533 if (object->{{field.len_field}} > 0) {
6a6aa04f
PA
534 if (*len < object->{{field.len_field}}) {
535 /* Not enough data. */
536 goto error;
537 }
240d789c
JI
538 memcpy(object->{{field.name}}, *buf, object->{{field.len_field}});
539 *buf += object->{{field.len_field}};
540 *len -= object->{{field.len_field}};
541 }
542 object->{{field.name}}[object->{{field.len_field}}] = '\\\\0';
543{% elif field.type == "bstr8" %}
544 {
545 uint8_t octet;
546 if (!DNP3ReadUint8(buf, len, &octet)) {
547 goto error;
548 }
6a6aa04f 549{% set ns = namespace(shift=0) %}
240d789c
JI
550{% for field in field.fields %}
551{% if field.width == 1 %}
6a6aa04f 552 object->{{field.name}} = (octet >> {{ns.shift}}) & 0x1;
240d789c 553{% elif field.width == 2 %}
6a6aa04f 554 object->{{field.name}} = (octet >> {{ns.shift}}) & 0x3;
240d789c 555{% elif field.width == 4 %}
6a6aa04f 556 object->{{field.name}} = (octet >> {{ns.shift}}) & 0xf;
240d789c 557{% elif field.width == 7 %}
6a6aa04f 558 object->{{field.name}} = (octet >> {{ns.shift}}) & 0x7f;
240d789c
JI
559{% else %}
560{{ raise("Unhandled width of %d." % (field.width)) }}
561{% endif %}
6a6aa04f 562{% set ns.shift = ns.shift + field.width %}
240d789c
JI
563{% endfor %}
564 }
565{% else %}
566{{ raise("Unhandled datatype '%s' for object %d:%d." % (field.type,
567 object.group, object.variation)) }}
568{% endif %}
569{% endfor %}
570
2a0bb108 571 if (!DNP3AddPoint(points, object, point_index, prefix_code, prefix)) {
240d789c
JI
572 goto error;
573 }
574
575 object = NULL;
2a0bb108 576 point_index++;
240d789c
JI
577 }
578
579 return 1;
580error:
581 if (object != NULL) {
5749024e
PA
582{% for field in object.fields %}
583{% if field.type == "bytearray" %}
584 if (object->{{field.name}} != NULL) {
585 SCFree(object->{{field.name}});
586 }
587{% endif %}
588{% endfor %}
240d789c
JI
589 SCFree(object);
590 }
591
592 return 0;
593}
594
595{% endif %}
596{% endfor %}
597
598void DNP3FreeObjectPoint(int group, int variation, void *point)
599{
600 switch(DNP3_OBJECT_CODE(group, variation)) {
601{% for object in objects %}
602{% if f_has_freeable_types(object.fields) %}
603 case DNP3_OBJECT_CODE({{object.group}}, {{object.variation}}): {
604 DNP3ObjectG{{object.group}}V{{object.variation}} *object = (DNP3ObjectG{{object.group}}V{{object.variation}} *) point;
605{% for field in object.fields %}
606{% if field.type == "bytearray" %}
607 if (object->{{field.name}} != NULL) {
608 SCFree(object->{{field.name}});
609 }
610{% endif %}
611{% endfor %}
612 break;
613 }
614{% endif %}
615{% endfor %}
616 default:
617 break;
618 }
619 SCFree(point);
620}
621
622/**
623 * \\\\brief Decode a DNP3 object.
624 *
625 * \\\\retval 0 on success. On failure a positive integer corresponding
626 * to a DNP3 application layer event will be returned.
627 */
628int DNP3DecodeObject(int group, int variation, const uint8_t **buf,
629 uint32_t *len, uint8_t prefix_code, uint32_t start,
630 uint32_t count, DNP3PointList *points)
631{
632 int rc = 0;
633
634 switch (DNP3_OBJECT_CODE(group, variation)) {
635{% for object in objects %}
636 case DNP3_OBJECT_CODE({{object.group}}, {{object.variation}}):
637 rc = DNP3DecodeObjectG{{object.group}}V{{object.variation}}(buf, len, prefix_code, start, count,
638 points);
639 break;
640{% endfor %}
641 default:
642 return DNP3_DECODER_EVENT_UNKNOWN_OBJECT;
643 }
644
645 return rc ? 0 : DNP3_DECODER_EVENT_MALFORMED;
646}
647
648"""
649
650 try:
651 filename = "src/app-layer-dnp3-objects.c"
652 env = jinja2.Environment(trim_blocks=True, lstrip_blocks=True)
653 code = env.from_string(template).render(context)
654 content = open(filename).read()
655 content = re.sub(
656 "(%s).*(%s)" % (re.escape(IN_PLACE_START), re.escape(IN_PLACE_END)),
657 r"\1%s\n\2" % (code), content, 1, re.M | re.DOTALL)
658 open(filename, "w").write(content)
659 print("Updated %s." % (filename))
660 except Exception as err:
661 print("Failed to update %s: %s" % (filename, err), file=sys.stderr)
662 sys.exit(1)
663
664def preprocess_object(obj):
665
666 valid_keys = [
667 "group",
668 "variation",
669 "constraints",
670 "extra_fields",
671 "fields",
672 "packed",
673 ]
674
675 valid_field_keys = [
676 "type",
677 "name",
678 "width",
679 "len_from_prefix",
680 "len_field",
681 "fields",
682 "size",
683 ]
684
685 if "unimplemented" in obj:
686 print("Object not implemented: %s:%s: %s" % (
687 str(obj["group"]), str(obj["variation"]), obj["unimplemented"]))
688 return None
689
690 for key, val in obj.items():
691
692 if key not in valid_keys:
693 print("Invalid key '%s' in object %d:%d" % (
694 key, obj["group"], obj["variation"]), file=sys.stderr)
695 sys.exit(1)
696
697 for field in obj["fields"]:
698
699 for key in field.keys():
700 if key not in valid_field_keys:
701 print("Invalid key '%s' in object %d:%d" % (
702 key, obj["group"], obj["variation"]), file=sys.stderr)
703 sys.exit(1)
704
705 if "len_from_prefix" in field and field["len_from_prefix"]:
706 obj["_track_offset"] = True
707 break
708
709 if field["type"] == "bstr8":
710 width = 0
711 for subfield in field["fields"]:
712 width += int(subfield["width"])
713 assert(width == 8)
714
715 return obj
716
717def main():
718
f70e8d00
JI
719 # Require Jinja2 2.10 or greater.
720 jv = jinja2.__version__.split(".")
721 if int(jv[0]) < 2 or (int(jv[0]) == 2 and int(jv[1]) < 10):
722 print("error: jinja2 v2.10 or great required")
723 return 1
724
240d789c
JI
725 definitions = yaml.load(open("scripts/dnp3-gen/dnp3-objects.yaml"))
726 print("Loaded %s objects." % (len(definitions["objects"])))
727 definitions["objects"] = map(preprocess_object, definitions["objects"])
728
729 # Filter out unimplemented objects.
730 definitions["objects"] = [
731 obj for obj in definitions["objects"] if obj != None]
732
733 context = {
734 "raise": raise_helper,
735 "objects": definitions["objects"],
736 "is_integer_type": is_integer_type,
737 "f_to_type": to_type,
738 "f_has_freeable_types": has_freeable_types,
739 "command_line": " ".join(sys.argv),
740 }
741
742 gen_object_structs(context)
743 gen_object_decoders(context)
744 generate(util_lua_dnp3_objects_c_template,
745 "src/util-lua-dnp3-objects.c",
746 context)
747 generate(output_json_dnp3_objects_template,
748 "src/output-json-dnp3-objects.c",
749 context)
750
751if __name__ == "__main__":
752 sys.exit(main())