dnp3-gen.py 22 KB

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