summaryrefslogtreecommitdiffstats
path: root/src/libknot/yparser
diff options
context:
space:
mode:
Diffstat (limited to 'src/libknot/yparser')
-rw-r--r--src/libknot/yparser/yparser.c176
-rw-r--r--src/libknot/yparser/yparser.h149
-rw-r--r--src/libknot/yparser/ypbody.c460
-rw-r--r--src/libknot/yparser/ypformat.c121
-rw-r--r--src/libknot/yparser/ypformat.h101
-rw-r--r--src/libknot/yparser/ypschema.c598
-rw-r--r--src/libknot/yparser/ypschema.h354
-rw-r--r--src/libknot/yparser/yptrafo.c1104
-rw-r--r--src/libknot/yparser/yptrafo.h311
9 files changed, 3374 insertions, 0 deletions
diff --git a/src/libknot/yparser/yparser.c b/src/libknot/yparser/yparser.c
new file mode 100644
index 0000000..3004761
--- /dev/null
+++ b/src/libknot/yparser/yparser.c
@@ -0,0 +1,176 @@
+/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "libknot/yparser/yparser.h"
+#include "libknot/attribute.h"
+#include "libknot/errcode.h"
+
+extern int _yp_start_state;
+extern int _yp_parse(yp_parser_t *parser);
+
+_public_
+void yp_init(
+ yp_parser_t *parser)
+{
+ if (parser == NULL) {
+ return;
+ }
+
+ memset(parser, 0, sizeof(*parser));
+
+ parser->cs = _yp_start_state;
+ parser->file.descriptor = -1;
+ parser->line_count = 1;
+}
+
+_public_
+void yp_deinit(
+ yp_parser_t *parser)
+{
+ if (parser == NULL) {
+ return;
+ }
+
+ if (parser->file.descriptor != -1) {
+ munmap((void *)parser->input.start,
+ parser->input.end - parser->input.start);
+ close(parser->file.descriptor);
+ free(parser->file.name);
+ }
+}
+
+_public_
+int yp_set_input_string(
+ yp_parser_t *parser,
+ const char *input,
+ size_t size)
+{
+ if (parser == NULL || input == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Reinitialize the parser.
+ yp_deinit(parser);
+ yp_init(parser);
+
+ // Set the parser input limits.
+ parser->input.start = input;
+ parser->input.current = input;
+ parser->input.end = input + size;
+ parser->input.eof = false;
+
+ return KNOT_EOK;
+}
+
+_public_
+int yp_set_input_file(
+ yp_parser_t *parser,
+ const char *file_name)
+{
+ if (parser == NULL || file_name == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Reinitialize the parser.
+ yp_deinit(parser);
+ yp_init(parser);
+
+ // Try to open the file.
+ parser->file.descriptor = open(file_name, O_RDONLY);
+ if (parser->file.descriptor == -1) {
+ return knot_map_errno();
+ }
+
+ // Check for regular file input.
+ struct stat file_stat;
+ if (fstat(parser->file.descriptor, &file_stat) == -1) {
+ close(parser->file.descriptor);
+ return knot_map_errno();
+ } else if (!S_ISREG(file_stat.st_mode)) {
+ close(parser->file.descriptor);
+ return KNOT_EFILE;
+ }
+
+ // Check for empty file (cannot mmap).
+ if (file_stat.st_size > 0) {
+ // Map the file to the memory.
+ char *start = mmap(0, file_stat.st_size, PROT_READ, MAP_SHARED,
+ parser->file.descriptor, 0);
+ if (start == MAP_FAILED) {
+ close(parser->file.descriptor);
+ return KNOT_ENOMEM;
+ }
+
+ // Try to set the mapped memory advise to sequential.
+#if defined(MADV_SEQUENTIAL) && !defined(__sun)
+ (void)madvise(start, file_stat.st_size, MADV_SEQUENTIAL);
+#else
+#ifdef POSIX_MADV_SEQUENTIAL
+ (void)posix_madvise(start, file_stat.st_size, POSIX_MADV_SEQUENTIAL);
+#endif /* POSIX_MADV_SEQUENTIAL */
+#endif /* MADV_SEQUENTIAL && !__sun */
+
+ // Set the parser input limits.
+ parser->input.start = start;
+ parser->input.current = start;
+ parser->input.end = start + file_stat.st_size;
+
+ parser->input.eof = false;
+ } else {
+ parser->input.eof = true;
+ }
+
+ parser->file.name = strdup(file_name);
+
+ return KNOT_EOK;
+}
+
+_public_
+int yp_parse(
+ yp_parser_t *parser)
+{
+ if (parser == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = KNOT_EPARSEFAIL;
+
+ // Run the parser until found new item, error or end of input.
+ do {
+ // Check for the end of the input.
+ if (parser->input.current == parser->input.end) {
+ if (parser->input.eof) {
+ // End of parsing.
+ return KNOT_EOF;
+ } else {
+ // Set the parser to final parsing.
+ parser->input.eof = true;
+ }
+ }
+
+ // Parse the next item.
+ ret = _yp_parse(parser);
+ } while (ret == KNOT_EFEWDATA);
+
+ return ret;
+}
diff --git a/src/libknot/yparser/yparser.h b/src/libknot/yparser/yparser.h
new file mode 100644
index 0000000..88e0e00
--- /dev/null
+++ b/src/libknot/yparser/yparser.h
@@ -0,0 +1,149 @@
+/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Simple parser (Yparser) of a YAML-inspired data format.
+ *
+ * \addtogroup yparser
+ * @{
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stddef.h>
+
+/*! Maximal length of textual key value. */
+#define YP_MAX_TXT_KEY_LEN 127
+/*! Maximal length of textual data value. */
+#define YP_MAX_TXT_DATA_LEN 32767
+
+/*! Parser events indicating type of lastly parsed item. */
+typedef enum {
+ YP_ENULL = 0, /*!< No valid data. */
+ YP_EKEY0, /*!< First level item. */
+ YP_EKEY1, /*!< Second level item. */
+ YP_EID, /*!< Second level identifier. */
+} yp_event_t;
+
+/*! Context structure of yparser. */
+typedef struct {
+ /*! Current parser state (Ragel internals). */
+ int cs;
+ /*! Indication if the current item was already processed. */
+ bool processed;
+ /*! Current block indentation. */
+ size_t indent;
+ /*! Last id dash position. */
+ size_t id_pos;
+
+ /*! Input parameters. */
+ struct {
+ /*! Start of the block. */
+ const char *start;
+ /*! Current parser position. */
+ const char *current;
+ /*! End of the block. */
+ const char *end;
+ /*! Indication for the final block parsing. */
+ bool eof;
+ } input;
+
+ /*! File input parameters. */
+ struct {
+ /*! File name. */
+ char *name;
+ /*! File descriptor. */
+ int descriptor;
+ } file;
+
+ /*! [out] Current line number (error location). */
+ size_t line_count;
+ /*! [out] Current event. */
+ yp_event_t event;
+ /*! [out] Parsed key (zero terminated string). */
+ char key[YP_MAX_TXT_KEY_LEN + 1];
+ /*! [out] Key length. */
+ size_t key_len;
+ /*! [out] Parsed data (zero terminated string). */
+ char data[YP_MAX_TXT_DATA_LEN + 1];
+ /*! [out] Data length. */
+ size_t data_len;
+} yp_parser_t;
+
+/*!
+ * Initializes the parser.
+ *
+ * \param[in] parser Parser context.
+ */
+void yp_init(
+ yp_parser_t *parser
+);
+
+/*!
+ * Deinitializes the parser.
+ *
+ * \param[in] parser Parser context.
+ */
+void yp_deinit(
+ yp_parser_t *parser
+);
+
+/*!
+ * Sets the parser to parse given string.
+ *
+ * \param[in] parser Parser context.
+ * \param[in] input The string to parse.
+ * \param[in] size Length of the string.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int yp_set_input_string(
+ yp_parser_t *parser,
+ const char *input,
+ size_t size
+);
+
+/*!
+ * Sets the parser to parse given file.
+ *
+ * \param[in] parser Parser context.
+ * \param[in] file_name The filename to parse.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int yp_set_input_file(
+ yp_parser_t *parser,
+ const char *file_name
+);
+
+/*!
+ * Parses one item from the input.
+ *
+ * If the item has more values, this function returns for each value. The item
+ * can also have no value.
+ *
+ * \param[in] parser Parser context.
+ *
+ * \return Error code, KNOT_EOK if success, KNOT_EOF if end of data.
+ */
+int yp_parse(
+ yp_parser_t *parser
+);
+
+/*! @} */
diff --git a/src/libknot/yparser/ypbody.c b/src/libknot/yparser/ypbody.c
new file mode 100644
index 0000000..ad9cf1f
--- /dev/null
+++ b/src/libknot/yparser/ypbody.c
@@ -0,0 +1,460 @@
+
+/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+
+#include "libknot/yparser/yparser.h"
+#include "libknot/errcode.h"
+
+
+
+
+// Include parser static data (Ragel internals).
+
+static const char _yparser_actions[] = {
+ 0, 1, 0, 1, 1, 1, 2, 1,
+ 3, 1, 4, 1, 6, 1, 8, 1,
+ 9, 1, 10, 1, 11, 1, 12, 1,
+ 15, 2, 1, 0, 2, 1, 2, 2,
+ 1, 15, 2, 2, 0, 2, 3, 4,
+ 2, 5, 4, 2, 6, 0, 2, 7,
+ 8, 2, 12, 13, 2, 14, 12, 3,
+ 1, 2, 0, 3, 1, 7, 8, 3,
+ 1, 12, 13, 3, 1, 14, 12, 3,
+ 2, 7, 8, 3, 2, 12, 13, 3,
+ 2, 14, 12, 4, 1, 2, 7, 8,
+ 4, 1, 2, 12, 13, 4, 1, 2,
+ 14, 12
+};
+
+static const unsigned char _yparser_key_offsets[] = {
+ 0, 0, 14, 16, 27, 29, 32, 43,
+ 44, 54, 65, 67, 68, 78, 90, 94,
+ 96, 99, 104, 107, 110, 122, 132, 142,
+ 145, 148, 151, 156, 159, 162, 176, 190,
+ 204
+};
+
+static const char _yparser_trans_keys[] = {
+ 10, 13, 32, 35, 42, 45, 92, 95,
+ 46, 57, 65, 90, 97, 122, 10, 13,
+ 32, 42, 58, 92, 95, 45, 57, 65,
+ 90, 97, 122, 32, 58, 10, 13, 32,
+ 32, 42, 58, 92, 95, 45, 57, 65,
+ 90, 97, 122, 32, 32, 42, 92, 95,
+ 46, 57, 65, 90, 97, 122, 32, 42,
+ 58, 92, 95, 45, 57, 65, 90, 97,
+ 122, 32, 58, 32, 32, 34, 35, 44,
+ 92, 127, 0, 31, 91, 93, 10, 13,
+ 32, 44, 92, 127, 0, 31, 34, 35,
+ 91, 93, 10, 13, 32, 35, 10, 13,
+ 34, 32, 126, 34, 92, 127, 0, 31,
+ 10, 13, 32, 34, 32, 126, 10, 13,
+ 32, 34, 35, 44, 91, 92, 93, 127,
+ 0, 31, 32, 34, 35, 44, 92, 127,
+ 0, 31, 91, 93, 32, 44, 91, 92,
+ 93, 127, 0, 31, 34, 35, 32, 44,
+ 93, 10, 13, 32, 34, 32, 126, 34,
+ 92, 127, 0, 31, 32, 44, 93, 34,
+ 32, 126, 10, 13, 32, 35, 42, 45,
+ 92, 95, 46, 57, 65, 90, 97, 122,
+ 10, 13, 32, 35, 42, 45, 92, 95,
+ 46, 57, 65, 90, 97, 122, 10, 13,
+ 32, 35, 42, 45, 92, 95, 46, 57,
+ 65, 90, 97, 122, 10, 13, 32, 35,
+ 42, 45, 92, 95, 46, 57, 65, 90,
+ 97, 122, 0
+};
+
+static const char _yparser_single_lengths[] = {
+ 0, 8, 2, 5, 2, 3, 5, 1,
+ 4, 5, 2, 1, 6, 6, 4, 2,
+ 1, 3, 3, 1, 10, 6, 6, 3,
+ 3, 1, 3, 3, 1, 8, 8, 8,
+ 8
+};
+
+static const char _yparser_range_lengths[] = {
+ 0, 3, 0, 3, 0, 0, 3, 0,
+ 3, 3, 0, 0, 2, 3, 0, 0,
+ 1, 1, 0, 1, 1, 2, 2, 0,
+ 0, 1, 1, 0, 1, 3, 3, 3,
+ 3
+};
+
+static const unsigned char _yparser_index_offsets[] = {
+ 0, 0, 12, 15, 24, 27, 31, 40,
+ 42, 50, 59, 62, 64, 73, 83, 88,
+ 91, 94, 99, 103, 106, 118, 127, 136,
+ 140, 144, 147, 152, 156, 159, 171, 183,
+ 195
+};
+
+static const char _yparser_indicies[] = {
+ 1, 2, 3, 4, 5, 6, 5, 5,
+ 5, 5, 5, 0, 1, 2, 4, 7,
+ 8, 9, 8, 8, 8, 8, 8, 0,
+ 10, 11, 0, 12, 13, 14, 0, 15,
+ 16, 17, 16, 16, 16, 16, 16, 0,
+ 18, 0, 18, 19, 19, 19, 19, 19,
+ 19, 0, 20, 21, 22, 21, 21, 21,
+ 21, 21, 0, 23, 24, 0, 25, 0,
+ 25, 27, 0, 0, 28, 0, 0, 0,
+ 26, 30, 31, 32, 0, 33, 0, 0,
+ 0, 0, 29, 12, 13, 34, 35, 0,
+ 12, 13, 35, 36, 29, 0, 38, 39,
+ 0, 0, 37, 30, 31, 32, 0, 40,
+ 37, 0, 12, 13, 14, 27, 35, 0,
+ 41, 28, 0, 0, 0, 26, 41, 43,
+ 0, 0, 44, 0, 0, 0, 42, 46,
+ 47, 0, 48, 49, 0, 0, 0, 45,
+ 50, 41, 51, 0, 12, 13, 34, 0,
+ 52, 45, 0, 54, 55, 0, 0, 53,
+ 46, 47, 49, 0, 56, 53, 0, 1,
+ 2, 3, 4, 57, 6, 57, 57, 57,
+ 57, 57, 0, 59, 60, 61, 62, 63,
+ 64, 63, 63, 63, 63, 63, 58, 65,
+ 66, 67, 68, 69, 70, 69, 69, 69,
+ 69, 69, 0, 71, 72, 73, 74, 75,
+ 76, 75, 75, 75, 75, 75, 58, 0
+};
+
+static const char _yparser_trans_targs[] = {
+ 0, 30, 31, 1, 2, 3, 7, 4,
+ 3, 5, 4, 5, 32, 29, 20, 4,
+ 6, 5, 8, 9, 10, 9, 11, 10,
+ 11, 12, 13, 17, 16, 13, 32, 29,
+ 14, 16, 14, 15, 13, 17, 18, 19,
+ 17, 21, 22, 26, 25, 22, 23, 21,
+ 25, 24, 23, 24, 22, 26, 27, 28,
+ 26, 6, 0, 30, 31, 1, 2, 6,
+ 7, 30, 31, 1, 2, 6, 7, 30,
+ 31, 1, 2, 6, 7
+};
+
+static const char _yparser_trans_actions[] = {
+ 23, 1, 0, 49, 0, 46, 52, 17,
+ 13, 17, 0, 0, 1, 0, 0, 15,
+ 13, 15, 21, 46, 19, 13, 19, 0,
+ 0, 0, 37, 7, 37, 9, 43, 11,
+ 11, 9, 0, 0, 40, 9, 0, 9,
+ 40, 0, 37, 7, 37, 9, 11, 11,
+ 9, 11, 0, 0, 40, 9, 0, 9,
+ 40, 46, 31, 55, 28, 88, 28, 83,
+ 93, 34, 5, 75, 5, 71, 79, 25,
+ 3, 63, 3, 59, 67
+};
+
+static const char _yparser_eof_actions[] = {
+ 0, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 0, 28, 5,
+ 3
+};
+
+
+
+
+
+int _yp_start_state = 29;
+
+int _yp_parse(
+ yp_parser_t *parser)
+{
+ // Parser input limits (Ragel internals).
+ const char *p, *pe, *eof;
+
+ // Current item indent.
+ size_t indent = 0;
+ // Current id dash position.
+ size_t id_pos = 0;
+ // Indicates if the current parsing step contains an item.
+ bool found = false;
+
+ if (!parser->input.eof) { // Restore parser input limits.
+ p = parser->input.current;
+ pe = parser->input.end;
+ eof = NULL;
+ } else { // Set the last artificial block with just one new line char.
+ p = "\n";
+ pe = p + 1;
+ eof = pe;
+ }
+
+ // Include parser body.
+
+ {
+ int _klen;
+ unsigned int _trans;
+ const char *_acts;
+ unsigned int _nacts;
+ const char *_keys;
+
+ if ( p == pe )
+ goto _test_eof;
+ if ( parser->cs == 0 )
+ goto _out;
+_resume:
+ _keys = _yparser_trans_keys + _yparser_key_offsets[ parser->cs];
+ _trans = _yparser_index_offsets[ parser->cs];
+
+ _klen = _yparser_single_lengths[ parser->cs];
+ if ( _klen > 0 ) {
+ const char *_lower = _keys;
+ const char *_mid;
+ const char *_upper = _keys + _klen - 1;
+ while (1) {
+ if ( _upper < _lower )
+ break;
+
+ _mid = _lower + ((_upper-_lower) >> 1);
+ if ( (*p) < *_mid )
+ _upper = _mid - 1;
+ else if ( (*p) > *_mid )
+ _lower = _mid + 1;
+ else {
+ _trans += (unsigned int)(_mid - _keys);
+ goto _match;
+ }
+ }
+ _keys += _klen;
+ _trans += _klen;
+ }
+
+ _klen = _yparser_range_lengths[ parser->cs];
+ if ( _klen > 0 ) {
+ const char *_lower = _keys;
+ const char *_mid;
+ const char *_upper = _keys + (_klen<<1) - 2;
+ while (1) {
+ if ( _upper < _lower )
+ break;
+
+ _mid = _lower + (((_upper-_lower) >> 1) & ~1);
+ if ( (*p) < _mid[0] )
+ _upper = _mid - 2;
+ else if ( (*p) > _mid[1] )
+ _lower = _mid + 2;
+ else {
+ _trans += (unsigned int)((_mid - _keys)>>1);
+ goto _match;
+ }
+ }
+ _trans += _klen;
+ }
+
+_match:
+ _trans = _yparser_indicies[_trans];
+ parser->cs = _yparser_trans_targs[_trans];
+
+ if ( _yparser_trans_actions[_trans] == 0 )
+ goto _again;
+
+ _acts = _yparser_actions + _yparser_trans_actions[_trans];
+ _nacts = (unsigned int) *_acts++;
+ while ( _nacts-- > 0 )
+ {
+ switch ( *_acts++ )
+ {
+ case 0:
+ {
+ // Return if key without value.
+ if (parser->event != YP_ENULL && !parser->processed) {
+ parser->processed = true;
+ found = true;
+ {p++; goto _out; }
+ }
+ }
+ break;
+ case 1:
+ {
+ parser->line_count++;
+ parser->event = YP_ENULL;
+ parser->processed = false;
+ }
+ break;
+ case 2:
+ {
+ indent = 0;
+ id_pos = 0;
+ }
+ break;
+ case 3:
+ {
+ parser->data_len = 0;
+ }
+ break;
+ case 4:
+ {
+ if (parser->data_len >= sizeof(parser->data) - 1) {
+ return KNOT_ESPACE;
+ }
+ parser->data[parser->data_len++] = (*p);
+ }
+ break;
+ case 5:
+ {
+ parser->data_len--;
+ }
+ break;
+ case 6:
+ {
+ // Return if a value parsed.
+ parser->data[parser->data_len] = '\0';
+ parser->processed = true;
+ found = true;
+ {p++; goto _out; }
+ }
+ break;
+ case 7:
+ {
+ if (indent > 0 && parser->indent > 0 &&
+ indent != parser->indent) {
+ return KNOT_YP_EINVAL_INDENT;
+ }
+ parser->processed = false;
+ parser->key_len = 0;
+ parser->data_len = 0;
+ parser->event = YP_ENULL;
+ }
+ break;
+ case 8:
+ {
+ if (parser->key_len >= sizeof(parser->key) - 1) {
+ return KNOT_ESPACE;
+ }
+ parser->key[parser->key_len++] = (*p);
+ }
+ break;
+ case 9:
+ {
+ parser->key[parser->key_len] = '\0';
+ parser->indent = 0;
+ parser->id_pos = 0;
+ parser->event = YP_EKEY0;
+ }
+ break;
+ case 10:
+ {
+ parser->key[parser->key_len] = '\0';
+ parser->indent = indent;
+ parser->event = YP_EKEY1;
+ }
+ break;
+ case 11:
+ {
+ parser->key[parser->key_len] = '\0';
+ parser->indent = indent;
+ parser->id_pos = id_pos;
+ parser->event = YP_EID;
+ }
+ break;
+ case 12:
+ {
+ indent++;
+ }
+ break;
+ case 13:
+ {
+ id_pos++;
+ }
+ break;
+ case 14:
+ {
+ if (id_pos > 0 && parser->id_pos > 0 &&
+ id_pos != parser->id_pos) {
+ return KNOT_YP_EINVAL_INDENT;
+ }
+ parser->indent = 0;
+ }
+ break;
+ case 15:
+ {
+ switch ((*p)) {
+ case '\t':
+ return KNOT_YP_ECHAR_TAB;
+ default:
+ return KNOT_EPARSEFAIL;
+ }
+ }
+ break;
+ }
+ }
+
+_again:
+ if ( parser->cs == 0 )
+ goto _out;
+ if ( ++p != pe )
+ goto _resume;
+ _test_eof: {}
+ if ( p == eof )
+ {
+ const char *__acts = _yparser_actions + _yparser_eof_actions[ parser->cs];
+ unsigned int __nacts = (unsigned int) *__acts++;
+ while ( __nacts-- > 0 ) {
+ switch ( *__acts++ ) {
+ case 1:
+ {
+ parser->line_count++;
+ parser->event = YP_ENULL;
+ parser->processed = false;
+ }
+ break;
+ case 2:
+ {
+ indent = 0;
+ id_pos = 0;
+ }
+ break;
+ case 15:
+ {
+ switch ((*p)) {
+ case '\t':
+ return KNOT_YP_ECHAR_TAB;
+ default:
+ return KNOT_EPARSEFAIL;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ _out: {}
+ }
+
+
+ // Store the current parser position.
+ if (!parser->input.eof) {
+ parser->input.current = p;
+ } else {
+ parser->input.current = parser->input.end;
+ }
+
+ // Check for general parser error.
+ if (parser->cs == 0) {
+ return KNOT_EPARSEFAIL;
+ }
+
+ // Check if parsed an item.
+ if (found) {
+ return KNOT_EOK;
+ } else {
+ return KNOT_EFEWDATA;
+ }
+}
diff --git a/src/libknot/yparser/ypformat.c b/src/libknot/yparser/ypformat.c
new file mode 100644
index 0000000..3284924
--- /dev/null
+++ b/src/libknot/yparser/ypformat.c
@@ -0,0 +1,121 @@
+/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+
+#include "libknot/yparser/yptrafo.h"
+#include "libknot/attribute.h"
+#include "libknot/errcode.h"
+
+static int format_item(
+ const yp_item_t *item,
+ const uint8_t *data,
+ size_t data_len,
+ char *out,
+ size_t out_len,
+ yp_style_t style,
+ const char *prefix,
+ bool first_value,
+ bool last_value)
+{
+ if (item == NULL || out == NULL || prefix == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Format key part.
+ int ret = snprintf(out, out_len, "%s%s%s%s",
+ first_value ? prefix : "",
+ first_value ? item->name + 1 : "",
+ first_value ? ":" : "",
+ item->type == YP_TGRP ?
+ "\n" : (first_value && !last_value ? " [ " : " "));
+ if (ret < 0 || ret >= out_len) {
+ return KNOT_ESPACE;
+ }
+ out += ret;
+ out_len -= ret;
+
+ // Finish if group.
+ if (item->type == YP_TGRP) {
+ return KNOT_EOK;
+ }
+
+ // Format data part.
+ size_t aux_len = out_len;
+ ret = yp_item_to_txt(item, data, data_len, out, &aux_len, style);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ out += aux_len;
+ out_len -= aux_len;
+
+ // Format data end.
+ ret = snprintf(out, out_len, "%s%s",
+ last_value && !first_value ? " ]" : "",
+ last_value ? "\n" : ",");
+ if (ret < 0 || ret >= out_len) {
+ return KNOT_ESPACE;
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+int yp_format_key0(
+ const yp_item_t *item,
+ const uint8_t *data,
+ size_t data_len,
+ char *out,
+ size_t out_len,
+ yp_style_t style,
+ bool first_value,
+ bool last_value)
+{
+ return format_item(item, data, data_len, out, out_len, style, "",
+ first_value, last_value);
+}
+
+_public_
+int yp_format_id(
+ const yp_item_t *item,
+ const uint8_t *data,
+ size_t data_len,
+ char *out,
+ size_t out_len,
+ yp_style_t style)
+{
+ if (data == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ return format_item(item, data, data_len, out, out_len, style, " - ",
+ true, true);
+}
+
+_public_
+int yp_format_key1(
+ const yp_item_t *item,
+ const uint8_t *data,
+ size_t data_len,
+ char *out,
+ size_t out_len,
+ yp_style_t style,
+ bool first_value,
+ bool last_value)
+{
+ return format_item(item, data, data_len, out, out_len, style, " ",
+ first_value, last_value);
+}
diff --git a/src/libknot/yparser/ypformat.h b/src/libknot/yparser/ypformat.h
new file mode 100644
index 0000000..c1d21bd
--- /dev/null
+++ b/src/libknot/yparser/ypformat.h
@@ -0,0 +1,101 @@
+/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Tools for Yparser format creation.
+ *
+ * \addtogroup yparser
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/yparser/ypschema.h"
+
+/*!
+ * Formats key0 item.
+ *
+ * \param[in] item Schema item to format.
+ * \param[in] data Data to format.
+ * \param[in] data_len Data length.
+ * \param[out] out Output buffer.
+ * \param[in, out] out_len Output buffer length, output length.
+ * \param[in] style Value style.
+ * \param[in] first_value First value indication (multivalued support).
+ * \param[in] last_value Last value indication (multivalued support).
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int yp_format_key0(
+ const yp_item_t *item,
+ const uint8_t *data,
+ size_t data_len,
+ char *out,
+ size_t out_len,
+ yp_style_t style,
+ bool first_value,
+ bool last_value
+);
+
+/*!
+ * Formats identifier item.
+ *
+ * \param[in] item Schema item to format.
+ * \param[in] data Data to format.
+ * \param[in] data_len Data length.
+ * \param[out] out Output buffer.
+ * \param[in, out] out_len Output buffer length, output length.
+ * \param[in] style Value style.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int yp_format_id(
+ const yp_item_t *item,
+ const uint8_t *data,
+ size_t data_len,
+ char *out,
+ size_t out_len,
+ yp_style_t style
+);
+
+/*!
+ * Formats key1 item.
+ *
+ * \param[in] item Schema item to format.
+ * \param[in] data Data to format.
+ * \param[in] data_len Data length.
+ * \param[out] out Output buffer.
+ * \param[in, out] out_len Output buffer length, output length.
+ * \param[in] style Value style.
+ * \param[in] first_value First value indication (multivalued support).
+ * \param[in] last_value Last value indication (multivalued support).
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int yp_format_key1(
+ const yp_item_t *item,
+ const uint8_t *data,
+ size_t data_len,
+ char *out,
+ size_t out_len,
+ yp_style_t style,
+ bool first_value,
+ bool last_value
+);
+
+/*! @} */
diff --git a/src/libknot/yparser/ypschema.c b/src/libknot/yparser/ypschema.c
new file mode 100644
index 0000000..0ac5b57
--- /dev/null
+++ b/src/libknot/yparser/ypschema.c
@@ -0,0 +1,598 @@
+/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libknot/yparser/ypschema.h"
+#include "libknot/yparser/yptrafo.h"
+#include "libknot/attribute.h"
+#include "libknot/errcode.h"
+
+static size_t schema_count(
+ const yp_item_t *src)
+{
+ size_t count = 0;
+ for (const yp_item_t *item = src; item->name != NULL; item++) {
+ count++;
+ }
+
+ return count;
+}
+
+/*! Initializes the referenced item. */
+static int set_ref_item(
+ yp_item_t *dst,
+ const yp_item_t *schema)
+{
+ if (schema == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Get referenced section.
+ const yp_name_t *ref_name = dst->var.r.ref_name;
+ const yp_item_t *ref = yp_schema_find(ref_name, NULL, schema);
+ if (ref == NULL) {
+ return KNOT_YP_EINVAL_ITEM;
+ }
+ dst->var.r.ref = ref;
+
+ // Get referenced group if supported.
+ const yp_name_t *grp_ref_name = dst->var.r.grp_ref_name;
+ if (grp_ref_name != NULL) {
+ const yp_item_t *grp_ref = yp_schema_find(grp_ref_name, NULL, schema);
+ if (grp_ref == NULL) {
+ return KNOT_YP_EINVAL_ITEM;
+ }
+ dst->var.r.grp_ref = grp_ref;
+ }
+
+ return KNOT_EOK;
+}
+
+/*! Copies the sub_items list and initializes pointer to the identifier item. */
+static int set_grp_item(
+ yp_item_t *dst,
+ const yp_item_t *src,
+ const yp_item_t *schema)
+{
+ // Count subitems.
+ size_t count = schema_count(src->var.g.sub_items);
+
+ // Allocate space for subitems + terminal zero item.
+ size_t memsize = (count + 1) * sizeof(yp_item_t);
+ dst->sub_items = malloc(memsize);
+ if (dst->sub_items == NULL) {
+ return KNOT_ENOMEM;
+ }
+ memset(dst->sub_items, 0, memsize);
+
+ // Copy subitems.
+ for (size_t i = 0; i < count; i++) {
+ // The first item is an identifier if multi group.
+ if (i == 0 && (dst->flags & YP_FMULTI)) {
+ dst->var.g.id = &dst->sub_items[0];
+ }
+
+ // Copy sub-item.
+ dst->sub_items[i] = src->var.g.sub_items[i];
+
+ // Initialize sub-item.
+ int ret = KNOT_EOK;
+ switch (dst->sub_items[i].type) {
+ case YP_TREF:
+ ret = set_ref_item(dst->sub_items + i, schema);
+ break;
+ case YP_TGRP: // Deeper hierarchy is not supported.
+ ret = KNOT_ENOTSUP;
+ break;
+ default:
+ break;
+ }
+
+ // Set the parent item.
+ dst->sub_items[i].parent = dst;
+
+ if (ret != KNOT_EOK) {
+ free(dst->sub_items);
+ dst->sub_items = NULL;
+ return ret;
+ }
+ }
+
+ if (src->flags & YP_FALLOC) {
+ dst->var.g.sub_items = malloc(memsize);
+ if (dst->var.g.sub_items == NULL) {
+ free(dst->sub_items);
+ dst->sub_items = NULL;
+ return KNOT_ENOMEM;
+ }
+ memcpy((void *)dst->var.g.sub_items, src->var.g.sub_items, memsize);
+ }
+
+ return KNOT_EOK;
+}
+
+static int set_item(
+ yp_item_t *dst,
+ const yp_item_t *src,
+ const yp_item_t *schema)
+{
+ // Check maximal item name length.
+ if ((uint8_t)src->name[0] > YP_MAX_ITEM_NAME_LEN) {
+ return KNOT_ERANGE;
+ }
+
+ // Copy the static data.
+ *dst = *src;
+
+ // Copy item name into dynamic memory.
+ if (src->flags & YP_FALLOC) {
+ dst->name = malloc(src->name[0] + 2);
+ if (dst->name == NULL) {
+ return KNOT_ENOMEM;
+ }
+ memcpy((void *)dst->name, src->name, src->name[0] + 2);
+ }
+
+ int ret;
+
+ // Item type specific preparation.
+ switch (src->type) {
+ case YP_TREF:
+ ret = set_ref_item(dst, schema);
+ break;
+ case YP_TGRP:
+ ret = set_grp_item(dst, src, schema);
+ break;
+ default:
+ ret = KNOT_EOK;
+ }
+
+ if (ret != KNOT_EOK && src->flags & YP_FALLOC) {
+ free((void *)dst->name);
+ }
+
+ return ret;
+}
+
+static void unset_item(
+ yp_item_t *item)
+{
+ if (item->flags & YP_FALLOC) {
+ free((void *)item->name);
+ }
+ if (item->type & YP_TGRP) {
+ free(item->sub_items);
+ if (item->flags & YP_FALLOC) {
+ free((void *)item->var.g.sub_items);
+ }
+ }
+
+ memset(item, 0, sizeof(yp_item_t));
+}
+
+static int schema_copy(
+ yp_item_t *dst,
+ const yp_item_t *src,
+ const yp_item_t *schema)
+{
+ // Copy the schema.
+ for (int i = 0; src[i].name != NULL; i++) {
+ int ret = set_item(&dst[i], &src[i], schema);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+int yp_schema_copy(
+ yp_item_t **dst,
+ const yp_item_t *src)
+{
+ if (dst == NULL || src == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Allocate space for new schema (+ terminal NULL item).
+ size_t size = (schema_count(src) + 1) * sizeof(yp_item_t);
+ yp_item_t *out = malloc(size);
+ if (out == NULL) {
+ return KNOT_ENOMEM;
+ }
+ memset(out, 0, size);
+
+ // Copy the schema.
+ int ret = schema_copy(out, src, out);
+ if (ret != KNOT_EOK) {
+ free(out);
+ return ret;
+ }
+
+ *dst = out;
+
+ return KNOT_EOK;
+}
+
+_public_
+int yp_schema_merge(
+ yp_item_t **dst,
+ const yp_item_t *src1,
+ const yp_item_t *src2)
+{
+ if (dst == NULL || src1 == NULL || src2 == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ size_t count1 = schema_count(src1);
+ size_t count2 = schema_count(src2);
+
+ // Allocate space for new schema (+ terminal NULL item).
+ size_t size = (count1 + count2 + 1) * sizeof(yp_item_t);
+ yp_item_t *out = malloc(size);
+ if (out == NULL) {
+ return KNOT_ENOMEM;
+ }
+ memset(out, 0, size);
+
+ // Copy the first schema.
+ int ret = schema_copy(out, src1, out);
+ if (ret != KNOT_EOK) {
+ free(out);
+ return ret;
+ }
+
+ // Copy the second schema.
+ ret = schema_copy(out + count1, src2, out);
+ if (ret != KNOT_EOK) {
+ free(out);
+ return ret;
+ }
+
+ *dst = out;
+
+ return KNOT_EOK;
+}
+
+_public_
+void yp_schema_purge_dynamic(
+ yp_item_t *schema)
+{
+ if (schema == NULL) {
+ return;
+ }
+
+ for (yp_item_t *item = schema; item->name != NULL; item++) {
+ if (item->flags & YP_FALLOC) {
+ unset_item(item);
+ }
+ }
+}
+
+_public_
+void yp_schema_free(
+ yp_item_t *schema)
+{
+ if (schema == NULL) {
+ return;
+ }
+
+ for (yp_item_t *item = schema; item->name != NULL; item++) {
+ unset_item(item);
+ }
+ free(schema);
+}
+
+/*! Search the schema for an item with the given name. */
+static const yp_item_t* find_item(
+ const char *name,
+ size_t name_len,
+ const yp_item_t *schema)
+{
+ if (name == NULL || schema == NULL) {
+ return NULL;
+ }
+
+ for (const yp_item_t *item = schema; item->name != NULL; item++) {
+ if (item->name[0] != name_len) {
+ continue;
+ }
+ if (memcmp(item->name + 1, name, name_len) == 0) {
+ return item;
+ }
+ }
+
+ return NULL;
+}
+
+_public_
+const yp_item_t* yp_schema_find(
+ const yp_name_t *name,
+ const yp_name_t *parent_name,
+ const yp_item_t *schema)
+{
+ if (name == NULL || schema == NULL) {
+ return NULL;
+ }
+
+ if (parent_name == NULL) {
+ return find_item(name + 1, name[0], schema);
+ } else {
+ const yp_item_t *parent = find_item(parent_name + 1,
+ parent_name[0], schema);
+ if (parent == NULL) {
+ return NULL;
+ }
+ return find_item(name + 1, name[0], parent->sub_items);
+ }
+}
+
+_public_
+yp_check_ctx_t* yp_schema_check_init(
+ yp_item_t **schema)
+{
+ if (schema == NULL) {
+ return NULL;
+ }
+
+ yp_check_ctx_t *ctx = malloc(sizeof(yp_check_ctx_t));
+ if (ctx == NULL) {
+ return NULL;
+ }
+ memset(ctx, 0, sizeof(yp_check_ctx_t));
+
+ ctx->schema = schema;
+
+ return ctx;
+}
+
+static void reset_ctx(
+ yp_check_ctx_t *ctx,
+ size_t index)
+{
+ assert(index < YP_MAX_NODE_DEPTH);
+
+ yp_node_t *node = &ctx->nodes[index];
+
+ node->parent = (index > 0) ? &ctx->nodes[index - 1] : NULL;
+ node->item = NULL;
+ node->id_len = 0;
+ node->data_len = 0;
+
+ ctx->current = index;
+}
+
+static int check_item(
+ const char *key,
+ size_t key_len,
+ const char *data,
+ size_t data_len,
+ yp_check_ctx_t *ctx,
+ bool allow_key1_without_id)
+{
+ yp_node_t *node = &ctx->nodes[ctx->current];
+ yp_node_t *parent = node->parent;
+ bool is_id = false;
+
+ if (parent != NULL) {
+ // Check for invalid indentation.
+ if (parent->item == NULL) {
+ return KNOT_YP_EINVAL_INDENT;
+ }
+
+ // Check if valid group parent.
+ if (parent->item->type != YP_TGRP) {
+ return KNOT_YP_EINVAL_ITEM;
+ }
+
+ // Check if valid subitem.
+ node->item = find_item(key, key_len, parent->item->sub_items);
+ } else {
+ node->item = find_item(key, key_len, *ctx->schema);
+ }
+ if (node->item == NULL) {
+ return KNOT_YP_EINVAL_ITEM;
+ }
+
+ // Check if the parent requires id specification.
+ if (parent != NULL && parent->item->var.g.id != NULL) {
+ // Check if id.
+ if (node->item == parent->item->var.g.id) {
+ is_id = true;
+ // Move current to the parent.
+ --(ctx->current);
+ // Check for missing id.
+ } else if (parent->id_len == 0 && !allow_key1_without_id) {
+ return KNOT_YP_ENOID;
+ }
+ }
+
+ // Return if no data provided.
+ if (data == NULL) {
+ return KNOT_EOK;
+ }
+
+ // Group cannot have data.
+ if (data_len != 0 && node->item->type == YP_TGRP) {
+ return KNOT_YP_ENOTSUP_DATA;
+ }
+
+ // Convert item data to binary format.
+ const yp_item_t *item = (node->item->type != YP_TREF) ?
+ node->item : node->item->var.r.ref->var.g.id;
+ if (is_id) {
+ // Textual id must not be empty.
+ if (data_len == 0) {
+ return KNOT_YP_ENODATA;
+ }
+
+ parent->id_len = sizeof(((yp_node_t *)NULL)->id);
+ int ret = yp_item_to_bin(item, data, data_len, parent->id,
+ &parent->id_len);
+
+ // Binary id must not be empty.
+ if (ret == KNOT_EOK && parent->id_len == 0) {
+ return KNOT_YP_EINVAL_DATA;
+ }
+
+ return ret;
+ } else {
+ node->data_len = sizeof(((yp_node_t *)NULL)->data);
+ int ret = yp_item_to_bin(item, data, data_len, node->data,
+ &node->data_len);
+ return ret;
+ }
+}
+
+_public_
+int yp_schema_check_parser(
+ yp_check_ctx_t *ctx,
+ const yp_parser_t *parser)
+{
+ if (ctx == NULL || parser == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int ret;
+
+ switch (parser->event) {
+ case YP_EKEY0:
+ reset_ctx(ctx, 0);
+ ret = check_item(parser->key, parser->key_len, parser->data,
+ parser->data_len, ctx, false);
+ break;
+ case YP_EKEY1:
+ reset_ctx(ctx, 1);
+ ret = check_item(parser->key, parser->key_len, parser->data,
+ parser->data_len, ctx, false);
+ if (ret != KNOT_EOK) {
+ break;
+ }
+
+ // Check for KEY1 event with id item.
+ if (ctx->current != 1) {
+ return KNOT_YP_ENOTSUP_ID;
+ }
+
+ break;
+ case YP_EID:
+ reset_ctx(ctx, 1);
+ ret = check_item(parser->key, parser->key_len, parser->data,
+ parser->data_len, ctx, false);
+ if (ret != KNOT_EOK) {
+ break;
+ }
+
+ // Check for ID event with nonid item.
+ if (ctx->current != 0) {
+ return KNOT_YP_EINVAL_ID;
+ }
+
+ break;
+ default:
+ ret = KNOT_EPARSEFAIL;
+ break;
+ }
+
+ return ret;
+}
+
+_public_
+int yp_schema_check_str(
+ yp_check_ctx_t *ctx,
+ const char *key0,
+ const char *key1,
+ const char *id,
+ const char *data)
+{
+ if (ctx == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ size_t key0_len = (key0 != NULL) ? strlen(key0) : 0;
+ size_t key1_len = (key1 != NULL) ? strlen(key1) : 0;
+ size_t id_len = (id != NULL) ? strlen(id) : 0;
+ size_t data_len = (data != NULL) ? strlen(data) : 0;
+
+ // Key0 must always be non-empty.
+ if (key0_len == 0) {
+ return KNOT_YP_EINVAL_ITEM;
+ }
+
+ // Process key0.
+ reset_ctx(ctx, 0);
+ if (key1_len == 0) {
+ int ret = check_item(key0, key0_len, data, data_len, ctx, false);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ } else {
+ int ret = check_item(key0, key0_len, NULL, 0, ctx, false);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ // Process id.
+ if (id_len != 0) {
+ if (ctx->nodes[0].item->type != YP_TGRP ||
+ ctx->nodes[0].item->var.g.id == NULL) {
+ return KNOT_YP_ENOTSUP_ID;
+ }
+ const yp_name_t *name = ctx->nodes[0].item->var.g.id->name;
+
+ reset_ctx(ctx, 1);
+ int ret = check_item(name + 1, name[0], id, id_len, ctx, true);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Check for non-id item (should not happen).
+ assert(ctx->current == 0);
+
+ // Check for group id with data.
+ if (key1_len == 0 && data != NULL) {
+ return KNOT_YP_ENOTSUP_DATA;
+ }
+ }
+
+ // Process key1.
+ if (key1_len != 0) {
+ reset_ctx(ctx, 1);
+ int ret = check_item(key1, key1_len, data, data_len, ctx, true);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Check for id in key1 with extra data.
+ if (ctx->current != 1 && id_len != 0 && data != NULL) {
+ return KNOT_YP_ENOTSUP_DATA;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+void yp_schema_check_deinit(
+ yp_check_ctx_t* ctx)
+{
+ free(ctx);
+}
diff --git a/src/libknot/yparser/ypschema.h b/src/libknot/yparser/ypschema.h
new file mode 100644
index 0000000..57ced72
--- /dev/null
+++ b/src/libknot/yparser/ypschema.h
@@ -0,0 +1,354 @@
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Schema layer for Yparser.
+ *
+ * \addtogroup yparser
+ * @{
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stddef.h>
+
+#include "libknot/yparser/yparser.h"
+#include "libknot/lookup.h"
+
+struct wire_ctx;
+
+/*! Maximal length of item name. */
+#define YP_MAX_ITEM_NAME_LEN 64
+/*! Maximal length of binary identifier name (maximal dname length). */
+#define YP_MAX_ID_LEN 255
+/*! Maximal length of binary data (rough limit). */
+#define YP_MAX_DATA_LEN 32768
+/*! Integer item nil definition. */
+#define YP_NIL INT64_MIN
+/*! Maximal number of miscellaneous callbacks/pointers. */
+#define YP_MAX_MISC_COUNT 4
+/*! Maximal node stack depth. */
+#define YP_MAX_NODE_DEPTH 2
+
+#define YP_TXT_BIN_PARAMS struct wire_ctx *in, struct wire_ctx *out, const uint8_t *stop
+#define YP_BIN_TXT_PARAMS struct wire_ctx *in, struct wire_ctx *out
+
+/*! Helper macros for item variables definition. */
+#define YP_VNONE .var.i = { 0 }
+#define YP_VINT .var.i
+#define YP_VBOOL .var.b
+#define YP_VOPT .var.o
+#define YP_VSTR .var.s
+#define YP_VADDR .var.a
+#define YP_VNET .var.d
+#define YP_VDNAME .var.d
+#define YP_VHEX .var.d
+#define YP_VB64 .var.d
+#define YP_VDATA .var.d
+#define YP_VREF .var.r
+#define YP_VGRP .var.g
+
+/*! Schema item name is a char string with a leading byte (string length). */
+typedef char yp_name_t;
+
+/*! Schema item type. */
+typedef enum {
+ YP_TNONE = 0, /*!< Unspecified. */
+ YP_TINT, /*!< Integer. */
+ YP_TBOOL, /*!< Boolean. */
+ YP_TOPT, /*!< Option from the list. */
+ YP_TSTR, /*!< String. */
+ YP_THEX, /*!< String or hexadecimal string if "0x" prefix. */
+ YP_TADDR, /*!< Address (address[\@port] | UNIX path). */
+ YP_TNET, /*!< Network address range (address[/mask] | abs. UNIX path | address-address). */
+ YP_TDNAME, /*!< Domain name. */
+ YP_TB64, /*!< Base64 encoded string. */
+ YP_TDATA, /*!< Customized data. */
+ YP_TREF, /*!< Reference to another item. */
+ YP_TGRP, /*!< Group of sub-items. */
+} yp_type_t;
+
+/*! Schema item flags. */
+typedef enum {
+ YP_FNONE = 0, /*!< Unspecified. */
+ YP_FMULTI = 1 << 0, /*!< Multivalued item. */
+ YP_FALLOC = 1 << 1, /*!< Allocated item. */
+ YP_FUSR1 = 1 << 5, /*!< User-defined flag1. */
+ YP_FUSR2 = 1 << 6, /*!< User-defined flag2. */
+ YP_FUSR3 = 1 << 7, /*!< User-defined flag3. */
+ YP_FUSR4 = 1 << 8, /*!< User-defined flag4. */
+ YP_FUSR5 = 1 << 9, /*!< User-defined flag5. */
+ YP_FUSR6 = 1 << 10, /*!< User-defined flag6. */
+ YP_FUSR7 = 1 << 11, /*!< User-defined flag7. */
+ YP_FUSR8 = 1 << 12, /*!< User-defined flag8. */
+ YP_FUSR9 = 1 << 13, /*!< User-defined flag9. */
+ YP_FUSR10 = 1 << 14, /*!< User-defined flag10. */
+ YP_FUSR11 = 1 << 15, /*!< User-defined flag11. */
+ YP_FUSR12 = 1 << 16, /*!< User-defined flag12. */
+ YP_FUSR13 = 1 << 17, /*!< User-defined flag13. */
+ YP_FUSR14 = 1 << 18, /*!< User-defined flag14. */
+ YP_FUSR15 = 1 << 19, /*!< User-defined flag15. */
+ YP_FUSR16 = 1 << 20, /*!< User-defined flag16. */
+} yp_flag_t;
+
+/*! Schema item style. */
+typedef enum {
+ YP_SNONE = 0, /*!< Unspecified. */
+ YP_SSIZE = 1 << 0, /*!< Size unit (B, K, M, G) (in, out). */
+ YP_STIME = 1 << 1, /*!< Time unit (s, m, h, d) (in, out). */
+ YP_SUNIT = YP_SSIZE | YP_STIME, /*!< Unit (in, out). */
+ YP_SNOQUOTE = 1 << 2 /*!< Unquoted value (out). */
+} yp_style_t;
+
+typedef struct yp_item yp_item_t;
+
+/*! Schema item variables (type dependent). */
+typedef union {
+ /*! Integer variables. */
+ struct {
+ /*! Minimal value. */
+ int64_t min;
+ /*! Maximal value. */
+ int64_t max;
+ /*! Default value. */
+ int64_t dflt;
+ /*! Possible unit type. */
+ yp_style_t unit;
+ } i;
+ /*! Boolean variables. */
+ struct {
+ /*! Default value. */
+ bool dflt;
+ } b;
+ /*! Option variables. */
+ struct {
+ /*! List of options (maximal value is 255). */
+ struct knot_lookup const *opts;
+ /*! Default value. */
+ unsigned dflt;
+ } o;
+ /*! String variables. */
+ struct {
+ /*! Default value. */
+ char const *dflt;
+ } s;
+ /*! Address variables. */
+ struct {
+ /*! Default port (e.g. UDP). */
+ uint16_t dflt_port;
+ /*! Alternative default port (e.g. QUIC). */
+ uint16_t dflt_port_alt;
+ /*! Default socket. */
+ char const *dflt_socket;
+ } a;
+ /*! Customized data variables. */
+ struct {
+ /*! Length of default data. */
+ size_t dflt_len;
+ /*! Default data. */
+ uint8_t const *dflt;
+ /*! Text to binary transformation function. */
+ int (*to_bin)(YP_TXT_BIN_PARAMS);
+ /*! Binary to text transformation function. */
+ int (*to_txt)(YP_BIN_TXT_PARAMS);
+ } d;
+ /*! Reference variables. */
+ struct {
+ /*! Referenced section name. */
+ yp_name_t const *ref_name;
+ /*! Referenced group section name (optional). */
+ yp_name_t const *grp_ref_name;
+ /*! Referenced section item (dynamic value). */
+ yp_item_t const *ref;
+ /*! Referenced group section item (dynamic value). */
+ yp_item_t const *grp_ref;
+ } r;
+ /*! Group variables. */
+ struct {
+ /*! List of sub-items. */
+ yp_item_t const *sub_items;
+ /*! ID item of sub-items (dynamic value). */
+ yp_item_t const *id;
+ } g;
+} yp_var_t;
+
+/*! Schema item specification. */
+struct yp_item {
+ /*! Item name. */
+ const yp_name_t *name;
+ /*! Item type. */
+ yp_type_t type;
+ /*! Item parameters. */
+ yp_var_t var;
+ /*! Item flags. */
+ yp_flag_t flags;
+ /*! Arbitrary data/callbacks. */
+ const void *misc[YP_MAX_MISC_COUNT];
+ /*! Parent item. */
+ yp_item_t *parent;
+ /*! Item group subitems (name=NULL terminated array). */
+ yp_item_t *sub_items;
+};
+
+typedef struct yp_node yp_node_t;
+struct yp_node {
+ /*! Parent node. */
+ yp_node_t *parent;
+ /*! Node item descriptor. */
+ const yp_item_t *item;
+ /*! Current binary id length. */
+ size_t id_len;
+ /*! Current binary id. */
+ uint8_t id[YP_MAX_ID_LEN];
+ /*! Current item data length. */
+ size_t data_len;
+ /*! Current item data. */
+ uint8_t data[YP_MAX_DATA_LEN];
+};
+
+/*! Context parameters for check operations. */
+typedef struct {
+ /*! Used schema. */
+ yp_item_t **schema;
+ /*! Index of the current node. */
+ size_t current;
+ /*! Node stack. */
+ yp_node_t nodes[YP_MAX_NODE_DEPTH];
+} yp_check_ctx_t;
+
+/*!
+ * Copies the schema and reinitializes dynamic parameters.
+ *
+ * \param[out] dst New copy of the schema.
+ * \param[in] src Source schema.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int yp_schema_copy(
+ yp_item_t **dst,
+ const yp_item_t *src
+);
+
+/*!
+ * Merges two schemas.
+ *
+ * \param[out] dst Merged schema.
+ * \param[in] src1 Source schema1.
+ * \param[in] src2 Source schema2.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int yp_schema_merge(
+ yp_item_t **dst,
+ const yp_item_t *src1,
+ const yp_item_t *src2
+);
+
+/*!
+ * Purges dynamic items from the schema.
+ *
+ * \param[in] schema Schema to purge.
+ */
+void yp_schema_purge_dynamic(
+ yp_item_t *schema
+);
+
+/*!
+ * Deallocates the schema.
+ *
+ * \param[in] schema A schema returned by #yp_schema_copy().
+ */
+void yp_schema_free(
+ yp_item_t *schema
+);
+
+/*!
+ * Tries to find given parent_name/name in the schema.
+ *
+ * \param[in] name Name of the item.
+ * \param[in] parent_name Name of the parent item (NULL if no parent).
+ * \param[in] schema Schema.
+ *
+ * \return Item, NULL if not found or error.
+ */
+const yp_item_t* yp_schema_find(
+ const yp_name_t *name,
+ const yp_name_t *parent_name,
+ const yp_item_t *schema
+);
+
+/*!
+ * Prepares a context for item check against the schema.
+ *
+ * \param[in] schema Schema.
+ *
+ * \return Context, NULL if error.
+ */
+yp_check_ctx_t* yp_schema_check_init(
+ yp_item_t **schema
+);
+
+/*!
+ * Checks the current parser output against the schema.
+ *
+ * If the item is correct, context also contains binary value of the item.
+ *
+ * \param[in,out] ctx Check context.
+ * \param[in] parser Parser context.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int yp_schema_check_parser(
+ yp_check_ctx_t *ctx,
+ const yp_parser_t *parser
+);
+
+/*!
+ * Checks the string data against the schema.
+ *
+ * Description: key0[id].key1 data
+ *
+ * If the item is correct, context also contains binary value of the item.
+ *
+ * \param[in,out] ctx Check context.
+ * \param[in] key0 Key0 item name.
+ * \param[in] key1 Key1 item name.
+ * \param[in] id Item identifier.
+ * \param[in] data Item data (NULL means no data provided).
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int yp_schema_check_str(
+ yp_check_ctx_t *ctx,
+ const char *key0,
+ const char *key1,
+ const char *id,
+ const char *data
+);
+
+/*!
+ * Deallocates the context.
+ *
+ * \param[in,out] ctx Check context.
+ */
+void yp_schema_check_deinit(
+ yp_check_ctx_t *ctx
+);
+
+/*! @} */
diff --git a/src/libknot/yparser/yptrafo.c b/src/libknot/yparser/yptrafo.c
new file mode 100644
index 0000000..60b3717
--- /dev/null
+++ b/src/libknot/yparser/yptrafo.c
@@ -0,0 +1,1104 @@
+/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <arpa/inet.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libknot/yparser/yptrafo.h"
+#include "libknot/attribute.h"
+#include "libknot/consts.h"
+#include "libknot/dname.h"
+#include "contrib/base64.h"
+#include "contrib/ctype.h"
+#include "contrib/musl/inet_ntop.h"
+#include "contrib/sockaddr.h"
+#include "contrib/wire_ctx.h"
+
+enum {
+ UNIT_BYTE = 'B',
+ UNIT_KILO = 'K',
+ UNIT_MEGA = 'M',
+ UNIT_GIGA = 'G',
+ UNIT_SEC = 's',
+ UNIT_MIN = 'm',
+ UNIT_HOUR = 'h',
+ UNIT_DAY = 'd'
+};
+
+enum {
+ MULTI_BYTE = 1,
+ MULTI_KILO = 1024,
+ MULTI_MEGA = 1024 * 1024,
+ MULTI_GIGA = 1024 * 1024 * 1024,
+ MULTI_SEC = 1,
+ MULTI_MIN = 60,
+ MULTI_HOUR = 3600,
+ MULTI_DAY = 24 * 3600
+};
+
+static wire_ctx_t copy_in(
+ wire_ctx_t *in,
+ size_t in_len,
+ char *buf,
+ size_t buf_len)
+{
+ wire_ctx_t ctx = wire_ctx_init((uint8_t *)buf, buf_len);
+ wire_ctx_write(&ctx, in->position, in_len);
+ wire_ctx_skip(in, in_len);
+ // Write the terminator.
+ wire_ctx_write_u8(&ctx, '\0');
+ wire_ctx_skip(&ctx, -1);
+ return ctx;
+}
+
+_public_
+int yp_str_to_bin(
+ YP_TXT_BIN_PARAMS)
+{
+ YP_CHECK_PARAMS_BIN;
+
+ wire_ctx_write(out, in->position, YP_LEN);
+ wire_ctx_skip(in, YP_LEN);
+ // Write string terminator.
+ wire_ctx_write_u8(out, '\0');
+
+ YP_CHECK_RET;
+}
+
+_public_
+int yp_str_to_txt(
+ YP_BIN_TXT_PARAMS)
+{
+ YP_CHECK_PARAMS_TXT;
+
+ size_t len = strlen((char *)in->position) + 1;
+
+ wire_ctx_write(out, in->position, len);
+ wire_ctx_skip(in, len);
+ // Set the terminator as a current position.
+ wire_ctx_skip(out, -1);
+
+ YP_CHECK_RET;
+}
+
+_public_
+int yp_bool_to_bin(
+ YP_TXT_BIN_PARAMS)
+{
+ YP_CHECK_PARAMS_BIN;
+
+ if (strncasecmp((char *)in->position, "on", YP_LEN) == 0 ||
+ strncasecmp((char *)in->position, "true", YP_LEN) == 0) {
+ wire_ctx_write_u8(out, 1);
+ } else if (strncasecmp((char *)in->position, "off", YP_LEN) == 0 ||
+ strncasecmp((char *)in->position, "false", YP_LEN) == 0) {
+ wire_ctx_write_u8(out, 0);
+ } else {
+ return KNOT_EINVAL;
+ }
+
+ wire_ctx_skip(in, YP_LEN);
+
+ YP_CHECK_RET;
+}
+
+_public_
+int yp_bool_to_txt(
+ YP_BIN_TXT_PARAMS)
+{
+ YP_CHECK_PARAMS_TXT;
+
+ const char *value;
+
+ switch (wire_ctx_read_u8(in)) {
+ case 0:
+ value = "off";
+ break;
+ case 1:
+ value = "on";
+ break;
+ default:
+ return KNOT_EINVAL;
+ }
+
+ int ret = snprintf((char *)out->position, wire_ctx_available(out), "%s",
+ value);
+ if (ret <= 0 || ret >= wire_ctx_available(out)) {
+ return KNOT_ESPACE;
+ }
+ wire_ctx_skip(out, ret);
+
+ YP_CHECK_RET;
+}
+
+static int remove_unit(
+ int64_t *number,
+ char unit,
+ yp_style_t style)
+{
+ int64_t multiplier = 1;
+
+ // Get the multiplier for the unit.
+ if (style & YP_SSIZE) {
+ switch (unit) {
+ case UNIT_BYTE:
+ multiplier = MULTI_BYTE;
+ break;
+ case UNIT_KILO:
+ multiplier = MULTI_KILO;
+ break;
+ case UNIT_MEGA:
+ multiplier = MULTI_MEGA;
+ break;
+ case UNIT_GIGA:
+ multiplier = MULTI_GIGA;
+ break;
+ default:
+ return KNOT_EINVAL;
+ }
+ } else if (style & YP_STIME) {
+ switch (unit) {
+ case UNIT_SEC:
+ multiplier = MULTI_SEC;
+ break;
+ case UNIT_MIN:
+ multiplier = MULTI_MIN;
+ break;
+ case UNIT_HOUR:
+ multiplier = MULTI_HOUR;
+ break;
+ case UNIT_DAY:
+ multiplier = MULTI_DAY;
+ break;
+ default:
+ return KNOT_EINVAL;
+ }
+ } else {
+ return KNOT_EINVAL;
+ }
+
+ // Check for possible number overflow.
+ if (INT64_MAX / multiplier < (*number >= 0 ? *number : -*number)) {
+ return KNOT_ERANGE;
+ }
+
+ *number *= multiplier;
+
+ return KNOT_EOK;
+}
+
+_public_
+int yp_int_to_bin(
+ YP_TXT_BIN_PARAMS,
+ int64_t min,
+ int64_t max,
+ yp_style_t style)
+{
+ YP_CHECK_PARAMS_BIN;
+
+ // Copy input string to the buffer to limit strtoll overread.
+ char buf[32];
+ wire_ctx_t buf_ctx = copy_in(in, YP_LEN, buf, sizeof(buf));
+ if (buf_ctx.error != KNOT_EOK) {
+ return buf_ctx.error;
+ }
+
+ // Parse the number.
+ char *end;
+ errno = 0;
+ int64_t number = strtoll(buf, &end, 10);
+
+ // Check for number overflow.
+ if (errno == ERANGE && (number == LLONG_MAX || number == LLONG_MIN)) {
+ return KNOT_ERANGE;
+ }
+ // Check if the whole string is invalid.
+ if ((errno != 0 && number == 0) || end == buf) {
+ return KNOT_EINVAL;
+ }
+ // Check the rest of the string for a unit.
+ if (*end != '\0') {
+ // Check just for one-char rest.
+ if (*(end + 1) != '\0') {
+ return KNOT_EINVAL;
+ }
+
+ // Try to apply a unit on the number.
+ int ret = remove_unit(&number, *end, style);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ // Check for the result number overflow.
+ if (number < min || number > max) {
+ return KNOT_ERANGE;
+ }
+
+ // Write the result.
+ wire_ctx_write_u64(out, number);
+
+ YP_CHECK_RET;
+}
+
+static void add_unit(
+ int64_t *number,
+ char *unit,
+ yp_style_t style)
+{
+ int64_t multiplier = 1;
+ char basic_unit = '\0';
+ char new_unit = '\0';
+
+ // Get the multiplier for the unit.
+ if (style & YP_SSIZE) {
+ basic_unit = UNIT_BYTE;
+
+ if (*number < MULTI_KILO) {
+ multiplier = MULTI_BYTE;
+ new_unit = UNIT_BYTE;
+ } else if (*number < MULTI_MEGA) {
+ multiplier = MULTI_KILO;
+ new_unit = UNIT_KILO;
+ } else if (*number < MULTI_GIGA) {
+ multiplier = MULTI_MEGA;
+ new_unit = UNIT_MEGA;
+ } else {
+ multiplier = MULTI_GIGA;
+ new_unit = UNIT_GIGA;
+ }
+ } else if (style & YP_STIME) {
+ basic_unit = UNIT_SEC;
+
+ if (*number < MULTI_MIN) {
+ multiplier = MULTI_SEC;
+ new_unit = UNIT_SEC;
+ } else if (*number < MULTI_HOUR) {
+ multiplier = MULTI_MIN;
+ new_unit = UNIT_MIN;
+ } else if (*number < MULTI_DAY) {
+ multiplier = MULTI_HOUR;
+ new_unit = UNIT_HOUR;
+ } else {
+ multiplier = MULTI_DAY;
+ new_unit = UNIT_DAY;
+ }
+ }
+
+ // Check for unit application without any remainder.
+ if ((*number % multiplier) == 0) {
+ *number /= multiplier;
+ *unit = new_unit;
+ } else {
+ *unit = basic_unit;
+ }
+}
+
+_public_
+int yp_int_to_txt(
+ YP_BIN_TXT_PARAMS,
+ yp_style_t style)
+{
+ YP_CHECK_PARAMS_TXT;
+
+ char unit[2] = { '\0' };
+ int64_t number = wire_ctx_read_u64(in);
+ add_unit(&number, unit, style);
+
+ int ret = snprintf((char *)out->position, wire_ctx_available(out),
+ "%"PRId64"%s", number, unit);
+ if (ret <= 0 || ret >= wire_ctx_available(out)) {
+ return KNOT_ESPACE;
+ }
+ wire_ctx_skip(out, ret);
+
+ YP_CHECK_RET;
+}
+
+static uint8_t sock_type_guess(
+ const uint8_t *str,
+ size_t len)
+{
+ size_t dots = 0;
+ size_t semicolons = 0;
+ size_t digits = 0;
+
+ // Analyze the string.
+ for (size_t i = 0; i < len; i++) {
+ if (str[i] == '.') dots++;
+ else if (str[i] == ':') semicolons++;
+ else if (is_digit(str[i])) digits++;
+ }
+
+ // Guess socket type.
+ if (semicolons >= 1) {
+ return 6;
+ } else if (semicolons == 0 && dots == 3 && digits >= 3) {
+ return 4;
+ } else {
+ return 0;
+ }
+}
+
+_public_
+int yp_addr_noport_to_bin(
+ YP_TXT_BIN_PARAMS,
+ bool allow_unix)
+{
+ YP_CHECK_PARAMS_BIN;
+
+ struct in_addr addr4;
+ struct in6_addr addr6;
+
+ uint8_t type = sock_type_guess(in->position, YP_LEN);
+
+ // Copy address to the buffer to limit inet_pton overread.
+ char buf[INET6_ADDRSTRLEN];
+ if (type == 4 || type == 6) {
+ wire_ctx_t buf_ctx = copy_in(in, YP_LEN, buf, sizeof(buf));
+ if (buf_ctx.error != KNOT_EOK) {
+ return buf_ctx.error;
+ }
+ }
+
+ // Write address type.
+ wire_ctx_write_u8(out, type);
+
+ // Write address as such.
+ if (type == 4 && inet_pton(AF_INET, buf, &addr4) == 1) {
+ wire_ctx_write(out, (uint8_t *)&(addr4.s_addr),
+ sizeof(addr4.s_addr));
+ } else if (type == 6 && inet_pton(AF_INET6, buf, &addr6) == 1) {
+ wire_ctx_write(out, (uint8_t *)&(addr6.s6_addr),
+ sizeof(addr6.s6_addr));
+ } else if (type == 0 && allow_unix) {
+ int ret = yp_str_to_bin(in, out, stop);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ } else {
+ return KNOT_EINVAL;
+ }
+
+ YP_CHECK_RET;
+}
+
+_public_
+int yp_addr_noport_to_txt(
+ YP_BIN_TXT_PARAMS)
+{
+ YP_CHECK_PARAMS_TXT;
+
+ struct in_addr addr4;
+ struct in6_addr addr6;
+
+ int ret;
+
+ switch (wire_ctx_read_u8(in)) {
+ case 0:
+ ret = yp_str_to_txt(in, out);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ break;
+ case 4:
+ wire_ctx_read(in, &(addr4.s_addr), sizeof(addr4.s_addr));
+ if (knot_inet_ntop(AF_INET, &addr4, (char *)out->position,
+ wire_ctx_available(out)) == NULL) {
+ return KNOT_EINVAL;
+ }
+ wire_ctx_skip(out, strlen((char *)out->position));
+ break;
+ case 6:
+ wire_ctx_read(in, &(addr6.s6_addr), sizeof(addr6.s6_addr));
+ if (knot_inet_ntop(AF_INET6, &addr6, (char *)out->position,
+ wire_ctx_available(out)) == NULL) {
+ return KNOT_EINVAL;
+ }
+ wire_ctx_skip(out, strlen((char *)out->position));
+ break;
+ default:
+ return KNOT_EINVAL;
+ }
+
+ YP_CHECK_RET;
+}
+
+_public_
+int yp_addr_to_bin(
+ YP_TXT_BIN_PARAMS)
+{
+ YP_CHECK_PARAMS_BIN;
+
+ // Check for address@port separator.
+ const uint8_t *pos = (uint8_t *)strrchr((char *)in->position, '@');
+ // Ignore out-of-bounds result.
+ if (pos >= stop) {
+ pos = NULL;
+ }
+
+ // Store address type position.
+ uint8_t *type = out->position;
+
+ // Write the address without a port.
+ int ret = yp_addr_noport_to_bin(in, out, pos, true);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (pos != NULL) {
+ if (*type == 0) {
+ // Rewrite string terminator.
+ wire_ctx_skip(out, -1);
+ // Append the rest (separator and port) as a string.
+ ret = yp_str_to_bin(in, out, stop);
+ } else {
+ // Skip the separator.
+ wire_ctx_skip(in, sizeof(uint8_t));
+
+ // Write the port as a number.
+ ret = yp_int_to_bin(in, out, stop, 0, UINT16_MAX, YP_SNONE);
+ }
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ } else if (*type == 4 || *type == 6) {
+ wire_ctx_write_u64(out, (uint64_t)-1);
+ }
+
+ YP_CHECK_RET;
+}
+
+_public_
+int yp_addr_to_txt(
+ YP_BIN_TXT_PARAMS)
+{
+ YP_CHECK_PARAMS_TXT;
+
+ // Store address type position.
+ uint8_t *type = in->position;
+
+ // Write address.
+ int ret = yp_addr_noport_to_txt(in, out);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Write port.
+ if (*type == 4 || *type == 6) {
+ int64_t port = wire_ctx_read_u64(in);
+
+ if (port >= 0) {
+ // Write separator.
+ wire_ctx_write_u8(out, '@');
+
+ // Write port.
+ wire_ctx_skip(in, -sizeof(uint64_t));
+ ret = yp_int_to_txt(in, out, YP_SNONE);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+ }
+
+ YP_CHECK_RET;
+}
+
+_public_
+int yp_addr_range_to_bin(
+ YP_TXT_BIN_PARAMS)
+{
+ YP_CHECK_PARAMS_BIN;
+
+ // Format: 0 - single address, 1 - address prefix, 2 - address range.
+ uint8_t format = 0;
+
+ const bool unix_path = (in->position[0] == '/');
+ const uint8_t *pos = NULL;
+
+ if (!unix_path) {
+ // Check for the "addr/mask" format.
+ pos = (uint8_t *)strchr((char *)in->position, '/');
+ if (pos >= stop) {
+ pos = NULL;
+ }
+
+ if (pos != NULL) {
+ format = 1;
+ } else {
+ // Check for the "addr1-addr2" format.
+ pos = (uint8_t *)strchr((char *)in->position, '-');
+ if (pos >= stop) {
+ pos = NULL;
+ }
+ if (pos != NULL) {
+ format = 2;
+ }
+ }
+ }
+
+ // Store address1 type position.
+ uint8_t *type1 = out->position;
+
+ // Write the first address.
+ int ret = yp_addr_noport_to_bin(in, out, pos, unix_path);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ wire_ctx_write_u8(out, format);
+
+ switch (format) {
+ case 1:
+ // Skip the separator.
+ wire_ctx_skip(in, sizeof(uint8_t));
+
+ // Write the prefix length.
+ ret = yp_int_to_bin(in, out, stop, 0, (*type1 == 4) ? 32 : 128,
+ YP_SNONE);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ break;
+ case 2:
+ // Skip the separator.
+ wire_ctx_skip(in, sizeof(uint8_t));
+
+ // Store address2 type position.
+ uint8_t *type2 = out->position;
+
+ // Write the second address.
+ ret = yp_addr_noport_to_bin(in, out, stop, false);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Check for address mismatch.
+ if (*type1 != *type2) {
+ return KNOT_EINVAL;
+ }
+ break;
+ default:
+ break;
+ }
+
+ YP_CHECK_RET;
+}
+
+_public_
+int yp_addr_range_to_txt(
+ YP_BIN_TXT_PARAMS)
+{
+ YP_CHECK_PARAMS_TXT;
+
+ // Write the first address.
+ int ret = yp_addr_noport_to_txt(in, out);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ uint8_t format = wire_ctx_read_u8(in);
+
+ switch (format) {
+ case 1:
+ // Write the separator.
+ wire_ctx_write_u8(out, '/');
+
+ // Write the prefix length.
+ ret = yp_int_to_txt(in, out, YP_SNONE);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ break;
+ case 2:
+ // Write the separator.
+ wire_ctx_write_u8(out, '-');
+
+ // Write the second address.
+ ret = yp_addr_noport_to_txt(in, out);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ break;
+ default:
+ break;
+ }
+
+ YP_CHECK_RET;
+}
+
+_public_
+int yp_option_to_bin(
+ YP_TXT_BIN_PARAMS,
+ const knot_lookup_t *opts)
+{
+ YP_CHECK_PARAMS_BIN;
+
+ while (opts->name != NULL) {
+ if (YP_LEN == strlen(opts->name) &&
+ strncasecmp((char *)in->position, opts->name, YP_LEN) == 0) {
+ wire_ctx_write_u8(out, opts->id);
+ wire_ctx_skip(in, YP_LEN);
+ YP_CHECK_RET;
+ }
+ opts++;
+ }
+
+ return KNOT_EINVAL;
+}
+
+_public_
+int yp_option_to_txt(
+ YP_BIN_TXT_PARAMS,
+ const knot_lookup_t *opts)
+{
+ uint8_t id = wire_ctx_read_u8(in);
+
+ while (opts->name != NULL) {
+ if (id == opts->id) {
+ int ret = snprintf((char *)out->position,
+ wire_ctx_available(out), "%s",
+ opts->name);
+ if (ret <= 0 || ret >= wire_ctx_available(out)) {
+ return KNOT_ESPACE;
+ }
+ wire_ctx_skip(out, ret);
+ YP_CHECK_RET;
+ }
+ opts++;
+ }
+
+ return KNOT_EINVAL;
+}
+
+_public_
+int yp_dname_to_bin(
+ YP_TXT_BIN_PARAMS)
+{
+ YP_CHECK_PARAMS_BIN;
+
+ // Copy dname string to the buffer to limit dname_from_str overread.
+ knot_dname_txt_storage_t buf;
+ wire_ctx_t buf_ctx = copy_in(in, YP_LEN, buf, sizeof(buf));
+ if (buf_ctx.error != KNOT_EOK) {
+ return buf_ctx.error;
+ }
+
+ // Convert the dname.
+ if (knot_dname_from_str(out->position, buf, wire_ctx_available(out)) == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Check the result and count the length.
+ int ret = knot_dname_wire_check(out->position,
+ out->position + wire_ctx_available(out),
+ NULL);
+ if (ret <= 0) {
+ return KNOT_EINVAL;
+ }
+
+ // Convert the result to lower case.
+ knot_dname_to_lower(out->position);
+
+ wire_ctx_skip(out, ret);
+
+ YP_CHECK_RET;
+}
+
+_public_
+int yp_dname_to_txt(
+ YP_BIN_TXT_PARAMS)
+{
+ YP_CHECK_PARAMS_TXT;
+
+ if (knot_dname_to_str((char *)out->position, in->position,
+ wire_ctx_available(out)) == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ wire_ctx_skip(out, strlen((char *)out->position));
+
+ YP_CHECK_RET;
+}
+
+static int hex_to_num(char hex) {
+ if (hex >= '0' && hex <= '9') return hex - '0';
+ if (hex >= 'a' && hex <= 'f') return hex - 'a' + 10;
+ if (hex >= 'A' && hex <= 'F') return hex - 'A' + 10;
+ return -1;
+}
+
+_public_
+int yp_hex_to_bin(
+ YP_TXT_BIN_PARAMS)
+{
+ YP_CHECK_PARAMS_BIN;
+
+ // Check for hex notation (leading "0x").
+ if (wire_ctx_available(in) >= 2 &&
+ in->position[0] == '0' && in->position[1] == 'x') {
+ wire_ctx_skip(in, 2);
+
+ if (YP_LEN % 2 != 0) {
+ return KNOT_EINVAL;
+ }
+
+ // Write data length.
+ wire_ctx_write_u16(out, YP_LEN / 2);
+
+ // Decode hex string.
+ while (YP_LEN > 0) {
+ uint8_t buf[2] = { 0 };
+ wire_ctx_read(in, buf, sizeof(buf));
+
+ if (!is_xdigit(buf[0]) ||
+ !is_xdigit(buf[1])) {
+ return KNOT_EINVAL;
+ }
+
+ wire_ctx_write_u8(out, 16 * hex_to_num(buf[0]) +
+ hex_to_num(buf[1]));
+ }
+ } else {
+ // Write data length.
+ wire_ctx_write_u16(out, YP_LEN);
+
+ // Write textual string (without terminator).
+ wire_ctx_write(out, in->position, YP_LEN);
+ wire_ctx_skip(in, YP_LEN);
+ }
+
+ YP_CHECK_RET;
+}
+
+_public_
+int yp_hex_to_txt(
+ YP_BIN_TXT_PARAMS)
+{
+ YP_CHECK_PARAMS_TXT;
+
+ size_t len = wire_ctx_read_u16(in);
+
+ bool printable = true;
+
+ // Check for printable string.
+ for (size_t i = 0; i < len; i++) {
+ if (!is_print(in->position[i])) {
+ printable = false;
+ break;
+ }
+ }
+
+ if (printable) {
+ wire_ctx_write(out, in->position, len);
+ wire_ctx_skip(in, len);
+ } else {
+ const char *prefix = "0x";
+ const char *hex = "0123456789ABCDEF";
+
+ // Write hex prefix.
+ wire_ctx_write(out, (uint8_t *)prefix, strlen(prefix));
+
+ // Encode data to hex.
+ for (size_t i = 0; i < len; i++) {
+ uint8_t bin = wire_ctx_read_u8(in);
+ wire_ctx_write_u8(out, hex[bin / 16]);
+ wire_ctx_write_u8(out, hex[bin % 16]);
+ }
+ }
+
+ // Write the terminator.
+ wire_ctx_write_u8(out, '\0');
+ wire_ctx_skip(out, -1);
+
+ YP_CHECK_RET;
+}
+
+_public_
+int yp_base64_to_bin(
+ YP_TXT_BIN_PARAMS)
+{
+ YP_CHECK_PARAMS_BIN;
+
+ // Reserve some space for data length.
+ wire_ctx_skip(out, sizeof(uint16_t));
+
+ int ret = knot_base64_decode(in->position, YP_LEN, out->position,
+ wire_ctx_available(out));
+ if (ret < 0) {
+ return ret;
+ }
+ wire_ctx_skip(in, YP_LEN);
+
+ // Write the data length.
+ wire_ctx_skip(out, -sizeof(uint16_t));
+ wire_ctx_write_u16(out, ret);
+ wire_ctx_skip(out, ret);
+
+ YP_CHECK_RET;
+}
+
+_public_
+int yp_base64_to_txt(
+ YP_BIN_TXT_PARAMS)
+{
+ YP_CHECK_PARAMS_TXT;
+
+ // Read the data length.
+ uint16_t len = wire_ctx_read_u16(in);
+
+ int ret = knot_base64_encode(in->position, len, out->position,
+ wire_ctx_available(out));
+ if (ret < 0) {
+ return ret;
+ }
+ wire_ctx_skip(out, ret);
+
+ // Write the terminator.
+ wire_ctx_write_u8(out, '\0');
+ wire_ctx_skip(out, -1);
+
+ YP_CHECK_RET;
+}
+
+_public_
+int yp_item_to_bin(
+ const yp_item_t *item,
+ const char *txt,
+ size_t txt_len,
+ uint8_t *bin,
+ size_t *bin_len)
+{
+ if (item == NULL || txt == NULL || bin == NULL || bin_len == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ wire_ctx_t in = wire_ctx_init_const((const uint8_t *)txt, txt_len);
+ wire_ctx_t out = wire_ctx_init(bin, *bin_len);
+
+ int ret;
+ size_t ref_len;
+
+ switch (item->type) {
+ case YP_TINT:
+ ret = yp_int_to_bin(&in, &out, NULL, item->var.i.min,
+ item->var.i.max, item->var.i.unit);
+ break;
+ case YP_TBOOL:
+ ret = yp_bool_to_bin(&in, &out, NULL);
+ break;
+ case YP_TOPT:
+ ret = yp_option_to_bin(&in, &out, NULL, item->var.o.opts);
+ break;
+ case YP_TSTR:
+ ret = yp_str_to_bin(&in, &out, NULL);
+ break;
+ case YP_TADDR:
+ ret = yp_addr_to_bin(&in, &out, NULL);
+ break;
+ case YP_TNET:
+ ret = yp_addr_range_to_bin(&in, &out, NULL);
+ break;
+ case YP_TDNAME:
+ ret = yp_dname_to_bin(&in, &out, NULL);
+ break;
+ case YP_THEX:
+ ret = yp_hex_to_bin(&in, &out, NULL);
+ break;
+ case YP_TB64:
+ ret = yp_base64_to_bin(&in, &out, NULL);
+ break;
+ case YP_TDATA:
+ ret = item->var.d.to_bin(&in, &out, NULL);
+ break;
+ case YP_TREF:
+ ref_len = wire_ctx_available(&out);
+ ret = yp_item_to_bin(item->var.r.ref->var.g.id,
+ (char *)in.position, wire_ctx_available(&in),
+ out.position, &ref_len);
+ wire_ctx_skip(&out, ref_len);
+ break;
+ default:
+ ret = KNOT_EOK;
+ }
+
+ if (ret != KNOT_EOK) {
+ return ret;
+ } else if (in.error != KNOT_EOK) {
+ return in.error;
+ } else if (out.error != KNOT_EOK) {
+ return out.error;
+ }
+
+ *bin_len = wire_ctx_offset(&out);
+
+ return KNOT_EOK;
+}
+
+_public_
+int yp_item_to_txt(
+ const yp_item_t *item,
+ const uint8_t *bin,
+ size_t bin_len,
+ char *txt,
+ size_t *txt_len,
+ yp_style_t style)
+{
+ if (item == NULL || bin == NULL || txt == NULL || txt_len == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ wire_ctx_t in = wire_ctx_init_const(bin, bin_len);
+ wire_ctx_t out = wire_ctx_init((uint8_t *)txt, *txt_len);
+
+ // Write leading quote.
+ if (!(style & YP_SNOQUOTE)) {
+ wire_ctx_write_u8(&out, '"');
+ }
+
+ int ret;
+ size_t ref_len;
+
+ switch (item->type) {
+ case YP_TINT:
+ ret = yp_int_to_txt(&in, &out, item->var.i.unit & style);
+ break;
+ case YP_TBOOL:
+ ret = yp_bool_to_txt(&in, &out);
+ break;
+ case YP_TOPT:
+ ret = yp_option_to_txt(&in, &out, item->var.o.opts);
+ break;
+ case YP_TSTR:
+ ret = yp_str_to_txt(&in, &out);
+ break;
+ case YP_TADDR:
+ ret = yp_addr_to_txt(&in, &out);
+ break;
+ case YP_TNET:
+ ret = yp_addr_range_to_txt(&in, &out);
+ break;
+ case YP_TDNAME:
+ ret = yp_dname_to_txt(&in, &out);
+ break;
+ case YP_THEX:
+ ret = yp_hex_to_txt(&in, &out);
+ break;
+ case YP_TB64:
+ ret = yp_base64_to_txt(&in, &out);
+ break;
+ case YP_TDATA:
+ ret = item->var.d.to_txt(&in, &out);
+ break;
+ case YP_TREF:
+ ref_len = wire_ctx_available(&out);
+ ret = yp_item_to_txt(item->var.r.ref->var.g.id,
+ in.position, wire_ctx_available(&in),
+ (char *)out.position,
+ &ref_len, style | YP_SNOQUOTE);
+ wire_ctx_skip(&out, ref_len);
+ break;
+ default:
+ ret = KNOT_EOK;
+ }
+
+ // Write trailing quote.
+ if (!(style & YP_SNOQUOTE)) {
+ wire_ctx_write_u8(&out, '"');
+ }
+
+ // Write string terminator.
+ wire_ctx_write_u8(&out, '\0');
+ wire_ctx_skip(&out, -1);
+
+ if (ret != KNOT_EOK) {
+ return ret;
+ } else if (in.error != KNOT_EOK) {
+ return in.error;
+ } else if (out.error != KNOT_EOK) {
+ return out.error;
+ }
+
+ *txt_len = wire_ctx_offset(&out);
+
+ return KNOT_EOK;
+}
+
+_public_
+struct sockaddr_storage yp_addr_noport(
+ const uint8_t *data)
+{
+ struct sockaddr_storage ss = { AF_UNSPEC };
+
+ // Read address type.
+ uint8_t type = *data;
+ data += sizeof(type);
+
+ // Set address.
+ switch (type) {
+ case 0:
+ sockaddr_set(&ss, AF_UNIX, (char *)data, 0);
+ break;
+ case 4:
+ sockaddr_set_raw(&ss, AF_INET, data,
+ sizeof(((struct in_addr *)NULL)->s_addr));
+ break;
+ case 6:
+ sockaddr_set_raw(&ss, AF_INET6, data,
+ sizeof(((struct in6_addr *)NULL)->s6_addr));
+ break;
+ }
+
+ return ss;
+}
+
+_public_
+struct sockaddr_storage yp_addr(
+ const uint8_t *data,
+ bool *no_port)
+{
+ struct sockaddr_storage ss = yp_addr_noport(data);
+
+ size_t addr_len;
+
+ // Get binary address length.
+ switch (ss.ss_family) {
+ case AF_INET:
+ addr_len = sizeof(((struct in_addr *)NULL)->s_addr);
+ break;
+ case AF_INET6:
+ addr_len = sizeof(((struct in6_addr *)NULL)->s6_addr);
+ break;
+ default:
+ addr_len = 0;
+ *no_port = true;
+ }
+
+ if (addr_len > 0) {
+ int64_t port = knot_wire_read_u64(data + sizeof(uint8_t) + addr_len);
+ if (port >= 0) {
+ sockaddr_port_set(&ss, port);
+ *no_port = false;
+ } else {
+ *no_port = true;
+ }
+ }
+
+ return ss;
+}
diff --git a/src/libknot/yparser/yptrafo.h b/src/libknot/yparser/yptrafo.h
new file mode 100644
index 0000000..285ab98
--- /dev/null
+++ b/src/libknot/yparser/yptrafo.h
@@ -0,0 +1,311 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Value transformations for Yparser.
+ *
+ * \addtogroup yparser
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/yparser/ypschema.h"
+#include "libknot/lookup.h"
+#include "libknot/wire.h"
+
+/*!
+ * Transforms textual item value to binary form.
+ *
+ * \param[in] item Schema item to transform.
+ * \param[in] txt Value to transform.
+ * \param[in] txt_len Value length.
+ * \param[out] bin Output buffer.
+ * \param[in, out] bin_len Output buffer length, output length.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int yp_item_to_bin(
+ const yp_item_t *item,
+ const char *txt,
+ size_t txt_len,
+ uint8_t *bin,
+ size_t *bin_len
+);
+
+/*!
+ * Transforms binary item value to textual form.
+ *
+ * \param[in] item Schema item to transform.
+ * \param[in] bin Value to transform.
+ * \param[in] bin_len Value length.
+ * \param[out] txt Output buffer.
+ * \param[in, out] txt_len Output buffer length, output length.
+ * \param[in] style Value style.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int yp_item_to_txt(
+ const yp_item_t *item,
+ const uint8_t *bin,
+ size_t bin_len,
+ char *txt,
+ size_t *txt_len,
+ yp_style_t style
+);
+
+/*!
+ * Converts binary value to string pointer.
+ *
+ * \param[in] data Binary value to transform.
+ *
+ * \return String pointer.
+ */
+inline static const char* yp_str(
+ const uint8_t *data)
+{
+ return (const char *)data;
+}
+
+/*!
+ * Converts binary value to boolean value.
+ *
+ * \param[in] data Binary value to transform.
+ *
+ * \return Boolean value.
+ */
+inline static bool yp_bool(
+ const uint8_t *data)
+{
+ return (data[0] == 0) ? false : true;
+}
+
+/*!
+ * Converts binary value to integer value.
+ *
+ * \param[in] data Binary value to transform.
+ *
+ * \return Integer value.
+ */
+inline static int64_t yp_int(
+ const uint8_t *data)
+{
+ return (int64_t)knot_wire_read_u64(data);
+}
+
+/*!
+ * Converts binary value to address value.
+ *
+ * \param[in] data Binary value to transform.
+ *
+ * \return Address value.
+ */
+struct sockaddr_storage yp_addr_noport(
+ const uint8_t *data
+);
+
+/*!
+ * Converts binary value to address value with an optional port.
+ *
+ * \param[in] data Binary value to transform.
+ * \param[out] no_port No port indicator.
+ *
+ * \return Address value.
+ */
+struct sockaddr_storage yp_addr(
+ const uint8_t *data,
+ bool *no_port
+);
+
+/*!
+ * Converts binary value to option value.
+ *
+ * \param[in] data Binary value to transform.
+ *
+ * \return Unsigned value.
+ */
+inline static unsigned yp_opt(
+ const uint8_t *data)
+{
+ return (unsigned)data[0];
+}
+
+/*!
+ * Converts binary value to dname pointer.
+ *
+ * \param[in] data Binary value to transform.
+ *
+ * \return Dname pointer.
+ */
+inline static const uint8_t* yp_dname(
+ const uint8_t *data)
+{
+ return data;
+}
+
+/*!
+ * Converts binary value to data pointer.
+ *
+ * Applies to all data types with 2-byte length prefix (YP_THEX, YP_TB64).
+ *
+ * \param[in] data Binary value to transform.
+ *
+ * \return Data pointer.
+ */
+inline static const uint8_t* yp_bin(
+ const uint8_t *data)
+{
+ return data + 2;
+}
+
+/*!
+ * Gets binary value length.
+ *
+ * Applies to all data types with 2-byte length prefix (YP_THEX, YP_TB64).
+ *
+ * \param[in] data Binary value to transform.
+ *
+ * \return Data length.
+ */
+inline static size_t yp_bin_len(
+ const uint8_t *data)
+{
+ return knot_wire_read_u16(data);
+}
+
+/*!
+ * \brief Helper macros for conversion functions.
+ */
+
+#define YP_CHECK_CTX \
+ if (in->error != KNOT_EOK) { \
+ return in->error; \
+ } else if (out->error != KNOT_EOK) { \
+ return out->error; \
+ } \
+
+#define YP_CHECK_STOP \
+ if (stop != NULL) { \
+ assert(stop <= in->position + wire_ctx_available(in)); \
+ } else { \
+ stop = in->position + wire_ctx_available(in); \
+ }
+
+#define YP_LEN (stop - in->position)
+
+#define YP_CHECK_PARAMS_BIN \
+ YP_CHECK_CTX YP_CHECK_STOP
+
+#define YP_CHECK_PARAMS_TXT \
+ YP_CHECK_CTX
+
+#define YP_CHECK_RET \
+ YP_CHECK_CTX return KNOT_EOK;
+
+/*!
+ * \brief Conversion functions for basic types.
+ */
+
+int yp_str_to_bin(
+ YP_TXT_BIN_PARAMS
+);
+
+int yp_str_to_txt(
+ YP_BIN_TXT_PARAMS
+);
+
+int yp_bool_to_bin(
+ YP_TXT_BIN_PARAMS
+);
+
+int yp_bool_to_txt(
+ YP_BIN_TXT_PARAMS
+);
+
+int yp_int_to_bin(
+ YP_TXT_BIN_PARAMS,
+ int64_t min,
+ int64_t max,
+ yp_style_t style
+);
+
+int yp_int_to_txt(
+ YP_BIN_TXT_PARAMS,
+ yp_style_t style
+);
+
+int yp_addr_noport_to_bin(
+ YP_TXT_BIN_PARAMS,
+ bool allow_unix
+);
+
+int yp_addr_noport_to_txt(
+ YP_BIN_TXT_PARAMS
+);
+
+int yp_addr_to_bin(
+ YP_TXT_BIN_PARAMS
+);
+
+int yp_addr_to_txt(
+ YP_BIN_TXT_PARAMS
+);
+
+int yp_addr_range_to_bin(
+ YP_TXT_BIN_PARAMS
+);
+
+int yp_addr_range_to_txt(
+ YP_BIN_TXT_PARAMS
+);
+
+int yp_option_to_bin(
+ YP_TXT_BIN_PARAMS,
+ const struct knot_lookup *opts
+);
+
+int yp_option_to_txt(
+ YP_BIN_TXT_PARAMS,
+ const struct knot_lookup *opts
+);
+
+int yp_dname_to_bin(
+ YP_TXT_BIN_PARAMS
+);
+
+int yp_dname_to_txt(
+ YP_BIN_TXT_PARAMS
+);
+
+int yp_hex_to_bin(
+ YP_TXT_BIN_PARAMS
+);
+
+int yp_hex_to_txt(
+ YP_BIN_TXT_PARAMS
+);
+
+int yp_base64_to_bin(
+ YP_TXT_BIN_PARAMS
+);
+
+int yp_base64_to_txt(
+ YP_BIN_TXT_PARAMS
+);
+
+/*! @} */