From 8482de7808647ecaf28a9b9fc9625c8abee266f2 Mon Sep 17 00:00:00 2001
From: Daniel Salzman <daniel.salzman@nic.cz>
Date: Thu, 15 Jan 2015 18:50:51 +0100
Subject: [PATCH] libknot: add yparser

---
 Knot.files                              |  11 +
 scripts/update-parser.sh                |  45 +-
 src/Makefile.am                         |  12 +-
 src/libknot/errcode.c                   |  10 +
 src/libknot/errcode.h                   |  12 +-
 src/libknot/internal/yparser/yparser.c  | 242 ++++++++
 src/libknot/internal/yparser/yparser.h  | 153 +++++
 src/libknot/internal/yparser/ypbody.c   | 393 ++++++++++++
 src/libknot/internal/yparser/ypbody.h   |  46 ++
 src/libknot/internal/yparser/ypbody.rl  | 182 ++++++
 src/libknot/internal/yparser/ypformat.c | 122 ++++
 src/libknot/internal/yparser/ypformat.h | 101 ++++
 src/libknot/internal/yparser/ypscheme.c | 407 +++++++++++++
 src/libknot/internal/yparser/ypscheme.h | 267 ++++++++
 src/libknot/internal/yparser/yptrafo.c  | 771 ++++++++++++++++++++++++
 src/libknot/internal/yparser/yptrafo.h  | 169 ++++++
 16 files changed, 2928 insertions(+), 15 deletions(-)
 create mode 100644 src/libknot/internal/yparser/yparser.c
 create mode 100644 src/libknot/internal/yparser/yparser.h
 create mode 100644 src/libknot/internal/yparser/ypbody.c
 create mode 100644 src/libknot/internal/yparser/ypbody.h
 create mode 100644 src/libknot/internal/yparser/ypbody.rl
 create mode 100644 src/libknot/internal/yparser/ypformat.c
 create mode 100644 src/libknot/internal/yparser/ypformat.h
 create mode 100644 src/libknot/internal/yparser/ypscheme.c
 create mode 100644 src/libknot/internal/yparser/ypscheme.h
 create mode 100644 src/libknot/internal/yparser/yptrafo.c
 create mode 100644 src/libknot/internal/yparser/yptrafo.h

diff --git a/Knot.files b/Knot.files
index 2583937bd5..ee59fea3f2 100644
--- a/Knot.files
+++ b/Knot.files
@@ -237,6 +237,17 @@ src/libknot/internal/trie/murmurhash3.c
 src/libknot/internal/trie/murmurhash3.h
 src/libknot/internal/utils.c
 src/libknot/internal/utils.h
+src/libknot/internal/yparser/yparser.c
+src/libknot/internal/yparser/yparser.h
+src/libknot/internal/yparser/ypbody.c
+src/libknot/internal/yparser/ypbody.h
+src/libknot/internal/yparser/ypbody.rl
+src/libknot/internal/yparser/ypformat.c
+src/libknot/internal/yparser/ypformat.h
+src/libknot/internal/yparser/ypscheme.c
+src/libknot/internal/yparser/ypscheme.h
+src/libknot/internal/yparser/yptrafo.c
+src/libknot/internal/yparser/yptrafo.h
 src/libknot/libknot.h
 src/libknot/packet/compr.c
 src/libknot/packet/compr.h
diff --git a/scripts/update-parser.sh b/scripts/update-parser.sh
index 3acb1ef103..fcf012ca6f 100755
--- a/scripts/update-parser.sh
+++ b/scripts/update-parser.sh
@@ -1,24 +1,43 @@
 #!/bin/bash
 
+### ZSCANNER ###
+
 IN="./scanner.rl"
-OUT="./scanner.c"
+OUT_T0="./scanner.c.t0"
+OUT_G2="./scanner.c.g2"
 
 pushd ../src/zscanner/
 
-ragel -T0 -s -o $OUT $IN
-sed '/#line/d' $OUT > $OUT.t0
-rm $OUT
+# Generate slower/small zone parser.
+ragel -T0 -s -o $OUT_T0 $IN
+
+# Remove redundant comments and unused constants (clang warnings).
+sed -i '/#line/d' $OUT_T0
+sed -i '/static\ const\ int\ zone_scanner_en_/d' $OUT_T0
+sed -i '/zone_scanner_first_final/d' $OUT_T0
+
+# Generate fast/huge zone parser.
+ragel -G2 -s -o $OUT_G2 $IN
+
+# Remove redundant comments and unused constants (clang warnings).
+sed -i '/#line/d' $OUT_G2
+sed -i '/static\ const\ int\ zone_scanner_en_/d' $OUT_G2
+sed -i '/zone_scanner_first_final/d' $OUT_G2
+
+popd
+
+### YPARSER ###
+
+IN_Y="./ypbody.rl"
+OUT_Y="./ypbody.c"
 
-# Remove unused constants because of clang 3.4 warnings
-sed -e '/static\ const\ int\ zone_scanner_en_/d' -e '/zone_scanner_first_final/d' $OUT.t0 > ./tmp
-mv -f ./tmp $OUT.t0
+pushd ../src/libknot/internal/yparser/
 
-ragel -G2 -s -o $OUT $IN
-sed '/#line/d' $OUT > $OUT.g2
-rm $OUT
+# Generate yparser.
+ragel -T0 -s -o $OUT_Y $IN_Y
 
-# Remove unused constants because of clang 3.4 warnings
-sed -e '/static\ const\ int\ zone_scanner_en_/d' -e '/zone_scanner_first_final/d' $OUT.g2 > ./tmp
-mv -f ./tmp $OUT.g2
+# Remove redundant comments and unused constants (clang warnings).
+sed -i '/#line/d' $OUT_Y
+sed -i '/static\ const\ int\ yparser_/d' $OUT_Y
 
 popd
diff --git a/src/Makefile.am b/src/Makefile.am
index 798f0e9e95..6001400301 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -105,7 +105,12 @@ nobase_libknot_int_la_HEADERS =			\
 	libknot/internal/tolower.h		\
 	libknot/internal/trie/hat-trie.h	\
 	libknot/internal/trie/murmurhash3.h	\
-	libknot/internal/utils.h
+	libknot/internal/utils.h		\
+	libknot/internal/yparser/yparser.h	\
+	libknot/internal/yparser/ypbody.h	\
+	libknot/internal/yparser/ypformat.h	\
+	libknot/internal/yparser/ypscheme.h	\
+	libknot/internal/yparser/yptrafo.h
 
 # dynamic: libknot internal sources
 libknot_int_la_SOURCES =			\
@@ -130,6 +135,11 @@ libknot_int_la_SOURCES =			\
 	libknot/internal/trie/hat-trie.c	\
 	libknot/internal/trie/murmurhash3.c	\
 	libknot/internal/utils.c		\
+	libknot/internal/yparser/yparser.c	\
+	libknot/internal/yparser/ypbody.c	\
+	libknot/internal/yparser/ypformat.c	\
+	libknot/internal/yparser/ypscheme.c	\
+	libknot/internal/yparser/yptrafo.c	\
 	$(libknot_int_la_HEADERS)
 
 # dynamic: libknot headers
diff --git a/src/libknot/errcode.c b/src/libknot/errcode.c
index 0f7aef2b3b..505d6f611d 100644
--- a/src/libknot/errcode.c
+++ b/src/libknot/errcode.c
@@ -80,6 +80,8 @@ const error_table_t error_messages[] = {
 	{ KNOT_ELIMIT,       "exceeded response rate limit" },
 	{ KNOT_EWRITABLE,    "file is not writable" },
 	{ KNOT_EOF,          "end of file" },
+	{ KNOT_ESYSTEM,      "system error" },
+	{ KNOT_EFILE,        "file error" },
 
 	/* Control states. */
 	{ KNOT_CTL_STOP,     "stopping server" },
@@ -124,6 +126,14 @@ const error_table_t error_messages[] = {
 	/* Dynamic backend errors. */
 	{ KNOT_DATABASE_ERROR, "unspecified database error" },
 
+	/* Yparser errors. */
+	{ KNOT_YP_EINVAL_ITEM,  "invalid item" },
+	{ KNOT_YP_EINVAL_DATA,  "invalid value" },
+	{ KNOT_YP_ENOTSUP_DATA, "value not supported" },
+	{ KNOT_YP_ENOTSUP_ID,   "identifier not supported" },
+	{ KNOT_YP_ENODATA,      "missing value" },
+	{ KNOT_YP_ENOID,        "missing identifier" },
+
 	{ KNOT_ERROR, NULL } /* Terminator */
 };
 
diff --git a/src/libknot/errcode.h b/src/libknot/errcode.h
index b14f54c93c..cd7e39512b 100644
--- a/src/libknot/errcode.h
+++ b/src/libknot/errcode.h
@@ -95,6 +95,8 @@ enum knot_error {
 	KNOT_ELIMIT,
 	KNOT_EWRITABLE,
 	KNOT_EOF,
+	KNOT_ESYSTEM,
+	KNOT_EFILE,
 
 	/* Control states. */
 	KNOT_CTL_STOP,
@@ -137,7 +139,15 @@ enum knot_error {
 	KNOT_NSEC3_ECOMPUTE_HASH,
 
 	/* Database backend. */
-	KNOT_DATABASE_ERROR
+	KNOT_DATABASE_ERROR,
+
+	/* Yparser errors. */
+	KNOT_YP_EINVAL_ITEM,
+	KNOT_YP_EINVAL_DATA,
+	KNOT_YP_ENOTSUP_DATA,
+	KNOT_YP_ENOTSUP_ID,
+	KNOT_YP_ENODATA,
+	KNOT_YP_ENOID
 };
 
 /*!
diff --git a/src/libknot/internal/yparser/yparser.c b/src/libknot/internal/yparser/yparser.c
new file mode 100644
index 0000000000..f8a250d6d8
--- /dev/null
+++ b/src/libknot/internal/yparser/yparser.c
@@ -0,0 +1,242 @@
+/*  Copyright (C) 2015 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <fcntl.h>
+#include <libgen.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "libknot/internal/yparser/yparser.h"
+#include "libknot/internal/yparser/ypbody.h"
+#include "libknot/errcode.h"
+
+/*! Maximal input file block size. */
+#define FILE_BLOCK_SIZE		5000000
+
+static void init_parser(
+	yp_parser_t *parser)
+{
+	memset(parser, 0, sizeof(*parser));
+
+	parser->cs = _start_state();
+	parser->file.path = strdup(".");
+	parser->file.descriptor = -1;
+	parser->line_count = 1;
+}
+
+static void purge_parser(
+	yp_parser_t *parser)
+{
+	free(parser->file.path);
+
+	// Cleanup the current file context if any.
+	if (parser->file.descriptor != -1) {
+		munmap((void *)parser->input.start,
+		       parser->input.end - parser->input.start);
+		free(parser->file.name);
+		close(parser->file.descriptor);
+	}
+}
+
+yp_parser_t* yp_create(
+	void)
+{
+	yp_parser_t *parser = malloc(sizeof(yp_parser_t));
+	if (parser == NULL) {
+		return NULL;
+	}
+
+	init_parser(parser);
+
+	return parser;
+}
+
+void yp_free(
+	yp_parser_t *parser)
+{
+	if (parser == NULL) {
+		return;
+	}
+
+	purge_parser(parser);
+	free(parser);
+}
+
+int yp_set_input_string(
+	yp_parser_t *parser,
+	const char *input,
+	size_t size)
+{
+	if (parser == NULL || input == NULL) {
+		return KNOT_EINVAL;
+	}
+
+	// Initialize parser.
+	purge_parser(parser);
+	init_parser(parser);
+
+	// Set the input string as a one block to parse.
+	parser->input.start   = input;
+	parser->input.current = input;
+	parser->input.end     = input + size;
+	parser->input.eof     = false;
+
+	return KNOT_EOK;
+}
+
+static int remap_file_block(
+	yp_parser_t *parser)
+{
+	// Compute the start of the following block.
+	size_t new_start = parser->file.block_end;
+	if (new_start >= parser->file.size) {
+		return KNOT_EOK;
+	}
+
+	// Compute the end of the following block.
+	size_t new_end = new_start + parser->file.block_size;
+	if (new_end > parser->file.size) {
+		new_end = parser->file.size;
+	}
+
+	size_t new_block_size = new_end - new_start;
+
+	// Unmap the current file block.
+	if (parser->input.start != NULL) {
+		munmap((void *)parser->input.start,
+		       parser->input.end - parser->input.start);
+	}
+
+	// Mmap the following file block.
+	char *data = mmap(0, new_block_size, PROT_READ, MAP_SHARED,
+	                  parser->file.descriptor, new_start);
+	if (data == MAP_FAILED) {
+		return KNOT_ENOMEM;
+	}
+
+	parser->file.block_end += new_block_size;
+
+	// Set new block limits.
+	parser->input.start   = data;
+	parser->input.end     = parser->input.start + new_block_size;
+	parser->input.current = parser->input.start;
+	parser->input.eof     = false;
+
+	return KNOT_EOK;
+}
+
+int yp_set_input_file(
+	yp_parser_t *parser,
+	const char *file_name)
+{
+	long page_size;
+	struct stat file_stat;
+
+	if (parser == NULL || file_name == NULL) {
+		return KNOT_EINVAL;
+	}
+
+	// Initialize parser.
+	purge_parser(parser);
+	init_parser(parser);
+
+	// Getting OS page size.
+	page_size = sysconf(_SC_PAGESIZE);
+	if (page_size <= 0) {
+		return KNOT_ESYSTEM;
+	}
+
+	// Try to open the file.
+	parser->file.descriptor = open(file_name, O_RDONLY);
+	if (parser->file.descriptor == -1) {
+		return KNOT_EFILE;
+	}
+
+	// Getting file information.
+	if (fstat(parser->file.descriptor, &file_stat) == -1) {
+		close(parser->file.descriptor);
+		return KNOT_EFILE;
+	}
+
+	// Check for directory.
+	if (S_ISDIR(file_stat.st_mode)) {
+		close(parser->file.descriptor);
+		return KNOT_EFILE;
+	}
+
+	// Get absolute path of the file.
+	char *full_name = realpath(file_name, NULL);
+	if (full_name != NULL) {
+		free(parser->file.path);
+		parser->file.path = strdup(dirname(full_name));
+		free(full_name);
+	} else {
+		close(parser->file.descriptor);
+		return KNOT_EFILE;
+	}
+
+	parser->file.name = strdup(file_name);
+	parser->file.size = file_stat.st_size;
+	// Block size adjustment to the multiple of page size.
+	parser->file.block_size = (FILE_BLOCK_SIZE / page_size) * page_size;
+	parser->file.block_end = 0;
+
+	return remap_file_block(parser);
+}
+
+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 current block.
+		if (parser->input.current == parser->input.end) {
+			// Remap the block if not end of input file.
+			if (parser->file.descriptor != -1 &&
+			    parser->file.block_end < parser->file.size) {
+				ret = remap_file_block(parser);
+			// Set the parser to final parsing.
+			} else if (!parser->input.eof) {
+				parser->input.eof = true;
+				ret = KNOT_EOK;
+			// End of parsing.
+			} else {
+				ret = KNOT_EOF;
+			}
+
+			if (ret != KNOT_EOK) {
+				return ret;
+			}
+		}
+
+		// Parse the next item.
+		ret = _parse(parser);
+	} while (ret == KNOT_EFEWDATA);
+
+	return ret;
+}
diff --git a/src/libknot/internal/yparser/yparser.h b/src/libknot/internal/yparser/yparser.h
new file mode 100644
index 0000000000..ed1a7e95d5
--- /dev/null
+++ b/src/libknot/internal/yparser/yparser.h
@@ -0,0 +1,153 @@
+/*  Copyright (C) 2015 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 <http://www.gnu.org/licenses/>.
+*/
+/*!
+ * \file
+ *
+ * Simple parser (Yparser) of a YAML-inspired data format.
+ *
+ * \addtogroup yparser
+ *
+ * @{
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/*! Maximal length of textual key value. */
+#define YP_MAX_TXT_KEY_LEN	128
+/*! Maximal length of textual data value. */
+#define YP_MAX_TXT_DATA_LEN	32768
+
+/*! 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;
+
+	/*! Parsing context for the current input block. */
+	struct {
+		/*! Start of the block. */
+		const char *start;
+		/*! Current parser postion. */
+		const char *current;
+		/*! End of the block. */
+		const char *end;
+		/*! Indication for the final block parsing. */
+		bool eof;
+	} input;
+
+	/*! Block context for file input. */
+	struct {
+		/*! Current working directory. */
+		char *path;
+		/*! File descriptor. */
+		int descriptor;
+		/*! File name. */
+		char *name;
+		/*! File size. */
+		size_t size;
+		/*! Used block size. */
+		size_t block_size;
+		/*! Position of the current block end. */
+		size_t block_end;
+	} 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];
+	/*! [out] Key length. */
+	size_t key_len;
+	/*! [out] Parsed data (zero terminated string). */
+	char data[YP_MAX_TXT_DATA_LEN];
+	/*! [out] Data length. */
+	size_t data_len;
+} yp_parser_t;
+
+/*!
+ * Allocates new parser.
+ *
+ * \return NULL if error.
+ */
+yp_parser_t* yp_create(
+	void
+);
+
+/*!
+ * Deallocates the parser.
+ *
+ * \param[in] parser Parser returned by #yp_create().
+ */
+void yp_free(
+	yp_parser_t *parser
+);
+
+/*!
+ * Sets the parser to parse given string.
+ *
+ * \param[in] parser Parser returned by #yp_create().
+ * \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 returned by #yp_create().
+ * \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 have more values, this function returns for each value. The item
+ * can also have no value.
+ *
+ * \param[in] parser Parser returned by #yp_create().
+ *
+ * \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/internal/yparser/ypbody.c b/src/libknot/internal/yparser/ypbody.c
new file mode 100644
index 0000000000..dcfe0656b0
--- /dev/null
+++ b/src/libknot/internal/yparser/ypbody.c
@@ -0,0 +1,393 @@
+
+/*  Copyright (C) 2015 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "libknot/internal/yparser/ypbody.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, 7, 1, 
+	8, 1, 9, 1, 10, 2, 1, 0, 
+	2, 2, 3, 2, 4, 0, 2, 5, 
+	6, 3, 1, 5, 6
+};
+
+static const unsigned char _yparser_key_offsets[] = {
+	0, 0, 12, 13, 22, 33, 37, 49, 
+	62, 67, 72, 77, 88, 92, 105, 117, 
+	129, 134, 140, 152, 164, 174, 185, 190, 
+	195, 206, 218
+};
+
+static const char _yparser_trans_keys[] = {
+	9, 10, 13, 32, 35, 45, 48, 57, 
+	65, 90, 97, 122, 10, 9, 13, 32, 
+	48, 57, 65, 90, 97, 122, 9, 13, 
+	32, 45, 58, 48, 57, 65, 90, 97, 
+	122, 9, 13, 32, 58, 9, 13, 32, 
+	33, 34, 92, 36, 43, 45, 90, 94, 
+	126, 9, 10, 13, 32, 33, 35, 92, 
+	36, 43, 45, 90, 94, 126, 9, 10, 
+	13, 32, 35, 9, 13, 34, 32, 126, 
+	9, 10, 13, 32, 35, 9, 13, 32, 
+	45, 58, 48, 57, 65, 90, 97, 122, 
+	9, 13, 32, 58, 9, 10, 13, 32, 
+	34, 35, 91, 33, 43, 45, 92, 94, 
+	126, 9, 10, 13, 32, 33, 35, 44, 
+	92, 36, 90, 94, 126, 9, 10, 13, 
+	32, 34, 35, 44, 92, 33, 90, 94, 
+	126, 9, 13, 34, 32, 126, 9, 10, 
+	13, 32, 35, 44, 9, 13, 32, 33, 
+	34, 92, 36, 43, 45, 90, 94, 126, 
+	9, 13, 32, 33, 34, 92, 36, 43, 
+	45, 90, 94, 126, 9, 13, 32, 33, 
+	44, 93, 36, 90, 92, 126, 9, 13, 
+	32, 33, 34, 44, 93, 36, 90, 92, 
+	126, 9, 13, 34, 32, 126, 9, 13, 
+	32, 44, 93, 9, 13, 32, 45, 58, 
+	48, 57, 65, 90, 97, 122, 9, 10, 
+	13, 32, 35, 45, 48, 57, 65, 90, 
+	97, 122, 9, 10, 13, 32, 35, 45, 
+	48, 57, 65, 90, 97, 122, 0
+};
+
+static const char _yparser_single_lengths[] = {
+	0, 6, 1, 3, 5, 4, 6, 7, 
+	5, 3, 5, 5, 4, 7, 8, 8, 
+	3, 6, 6, 6, 6, 7, 3, 5, 
+	5, 6, 6
+};
+
+static const char _yparser_range_lengths[] = {
+	0, 3, 0, 3, 3, 0, 3, 3, 
+	0, 1, 0, 3, 0, 3, 2, 2, 
+	1, 0, 3, 3, 2, 2, 1, 0, 
+	3, 3, 3
+};
+
+static const unsigned char _yparser_index_offsets[] = {
+	0, 0, 10, 12, 19, 28, 33, 43, 
+	54, 60, 65, 71, 80, 85, 96, 107, 
+	118, 123, 130, 140, 150, 159, 169, 174, 
+	180, 189, 199
+};
+
+static const char _yparser_indicies[] = {
+	1, 2, 1, 1, 3, 4, 5, 5, 
+	5, 0, 2, 3, 4, 4, 4, 6, 
+	6, 6, 0, 7, 7, 7, 8, 9, 
+	8, 8, 8, 0, 10, 10, 10, 11, 
+	0, 11, 11, 11, 12, 13, 12, 12, 
+	12, 12, 0, 14, 15, 14, 14, 16, 
+	17, 16, 16, 16, 16, 0, 18, 2, 
+	18, 18, 3, 0, 19, 19, 20, 19, 
+	0, 14, 15, 14, 14, 17, 0, 21, 
+	21, 21, 22, 23, 22, 22, 22, 0, 
+	24, 24, 24, 25, 0, 25, 2, 25, 
+	25, 27, 3, 28, 26, 26, 26, 0, 
+	29, 15, 29, 29, 30, 17, 31, 30, 
+	30, 30, 0, 32, 2, 32, 32, 27, 
+	3, 33, 26, 26, 26, 0, 34, 34, 
+	35, 34, 0, 29, 15, 29, 29, 17, 
+	31, 0, 33, 33, 33, 26, 27, 26, 
+	26, 26, 26, 0, 28, 28, 28, 36, 
+	37, 36, 36, 36, 36, 0, 38, 38, 
+	38, 39, 40, 14, 39, 39, 0, 41, 
+	41, 41, 36, 37, 28, 18, 36, 36, 
+	0, 42, 42, 43, 42, 0, 38, 38, 
+	38, 40, 14, 0, 44, 44, 44, 45, 
+	46, 45, 45, 45, 0, 1, 2, 1, 
+	1, 3, 4, 47, 47, 47, 0, 48, 
+	49, 48, 48, 50, 51, 52, 52, 52, 
+	0, 0
+};
+
+static const char _yparser_trans_targs[] = {
+	0, 1, 26, 2, 3, 24, 4, 5, 
+	4, 6, 5, 6, 7, 9, 8, 26, 
+	7, 2, 8, 9, 10, 12, 11, 13, 
+	12, 13, 14, 16, 19, 15, 14, 18, 
+	15, 18, 16, 17, 20, 22, 21, 20, 
+	19, 21, 22, 23, 12, 24, 13, 11, 
+	1, 26, 2, 3, 11
+};
+
+static const char _yparser_trans_actions[] = {
+	19, 0, 1, 0, 0, 30, 30, 17, 
+	11, 17, 0, 0, 24, 5, 9, 27, 
+	7, 9, 0, 7, 0, 13, 11, 13, 
+	0, 0, 24, 5, 0, 9, 7, 9, 
+	0, 0, 7, 0, 24, 5, 9, 7, 
+	9, 0, 7, 0, 15, 11, 15, 30, 
+	3, 21, 3, 3, 33
+};
+
+static const char _yparser_eof_actions[] = {
+	0, 19, 19, 19, 19, 19, 19, 19, 
+	19, 19, 19, 19, 19, 19, 19, 19, 
+	19, 19, 19, 19, 19, 19, 19, 19, 
+	19, 0, 3
+};
+
+
+
+
+
+int _parse(
+	yp_parser_t *parser)
+{
+	// Parser input limits (Ragel internals).
+	const char *p, *pe, *eof;
+
+	// 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 artifical 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:
+	{
+		parser->data_len = 0;
+	}
+	break;
+	case 3:
+	{
+		if (parser->data_len >= sizeof(parser->data)) {
+			return KNOT_ESPACE;
+		}
+		parser->data[parser->data_len++] = (*p);
+	}
+	break;
+	case 4:
+	{
+		// Return if a value parsed.
+		parser->data[parser->data_len] = '\0';
+		parser->processed = true;
+		found = true;
+		{p++; goto _out; }
+	}
+	break;
+	case 5:
+	{
+		parser->processed = false;
+		parser->key_len = 0;
+		parser->data_len = 0;
+		parser->event = YP_ENULL;
+	}
+	break;
+	case 6:
+	{
+		if (parser->key_len >= sizeof(parser->key)) {
+			return KNOT_ESPACE;
+		}
+		parser->key[parser->key_len++] = (*p);
+	}
+	break;
+	case 7:
+	{
+		parser->key[parser->key_len] = '\0';
+		parser->event = YP_EKEY0;
+	}
+	break;
+	case 8:
+	{
+		parser->key[parser->key_len] = '\0';
+		parser->event = YP_EKEY1;
+	}
+	break;
+	case 9:
+	{
+		parser->key[parser->key_len] = '\0';
+		parser->event = YP_EID;
+	}
+	break;
+	case 10:
+	{
+		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 10:
+	{
+		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;
+	}
+}
+
+int _start_state(
+	void)
+{
+	return 
+25
+;
+}
diff --git a/src/libknot/internal/yparser/ypbody.h b/src/libknot/internal/yparser/ypbody.h
new file mode 100644
index 0000000000..0396decf37
--- /dev/null
+++ b/src/libknot/internal/yparser/ypbody.h
@@ -0,0 +1,46 @@
+/*  Copyright (C) 2015 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 <http://www.gnu.org/licenses/>.
+*/
+/*!
+ * \file
+ *
+ * Yparser core interface for Ragel source.
+ *
+ * \addtogroup yparser
+ *
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/internal/yparser/yparser.h"
+
+/*!
+ * Gets the initial parser state.
+ */
+int _start_state(
+	void
+);
+
+/*!
+ * Executes the parser on the current input block.
+ *
+ * \param[in] parser A parser returned by #yp_create().
+ */
+int _parse(
+	yp_parser_t *parser
+);
+
+/*! @} */
diff --git a/src/libknot/internal/yparser/ypbody.rl b/src/libknot/internal/yparser/ypbody.rl
new file mode 100644
index 0000000000..d489b3ed60
--- /dev/null
+++ b/src/libknot/internal/yparser/ypbody.rl
@@ -0,0 +1,182 @@
+/*  Copyright (C) 2015 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "libknot/internal/yparser/ypbody.h"
+#include "libknot/errcode.h"
+
+%%{
+	machine yparser;
+
+	access parser->;
+
+	# Newline processing.
+	action _newline_init {
+		// Return if key without value.
+		if (parser->event != YP_ENULL && !parser->processed) {
+			parser->processed = true;
+			found = true;
+			fbreak;
+		}
+	}
+	action _newline {
+		parser->line_count++;
+		parser->event = YP_ENULL;
+		parser->processed = false;
+	}
+	newline_char = '\n' >_newline_init %_newline;
+
+	# Comment processing.
+	comment_char = '#';
+	comment      = comment_char . (^newline_char)*;
+
+	# White space processing.
+	sep_char = [ \t\r];
+	sep = sep_char+;
+
+	blank = (sep? :> comment?). newline_char;
+
+	# Data processing.
+	action _item_data_init {
+		parser->data_len = 0;
+	}
+	action _item_data {
+		if (parser->data_len >= sizeof(parser->data)) {
+			return KNOT_ESPACE;
+		}
+		parser->data[parser->data_len++] = fc;
+	}
+	action _item_data_exit {
+		// Return if a value parsed.
+		parser->data[parser->data_len] = '\0';
+		parser->processed = true;
+		found = true;
+		fbreak;
+	}
+	quote_char = '\"';
+	list_char  = [\[,\]];
+	data_char  =
+		(ascii - space - cntrl - quote_char - sep_char -
+		 comment_char - list_char
+		) $_item_data;
+	data_str_char =
+		(data_char | sep_char | comment_char | list_char
+		) $_item_data;
+	data_str = (quote_char . data_str_char* . quote_char);
+	item_data = (data_char+ | data_str) >_item_data_init %_item_data_exit;
+	item_data_plus =
+		item_data .
+		((sep | ',' | (sep . ',' . sep) | (sep . ',') | (',' . sep)) .
+		 item_data)*;
+	item_data_list =
+		('\[' . sep? . item_data_plus . sep? . '\]') | item_data_plus;
+
+	# Key processing.
+	action _key_init {
+		parser->processed = false;
+		parser->key_len = 0;
+		parser->data_len = 0;
+		parser->event = YP_ENULL;
+	}
+	action _key {
+		if (parser->key_len >= sizeof(parser->key)) {
+			return KNOT_ESPACE;
+		}
+		parser->key[parser->key_len++] = fc;
+	}
+	action _key0_exit {
+		parser->key[parser->key_len] = '\0';
+		parser->event = YP_EKEY0;
+	}
+	action _key1_exit {
+		parser->key[parser->key_len] = '\0';
+		parser->event = YP_EKEY1;
+	}
+	action _id_exit {
+		parser->key[parser->key_len] = '\0';
+		parser->event = YP_EID;
+	}
+	key_data_sep = sep? . ":" . sep?;
+	key_name = alnum . ("-" | alnum)*;
+	key0 =        key_name              >_key_init $_key %_key0_exit;
+	key1 = sep .  key_name              >_key_init $_key %_key1_exit;
+	id   = sep? . "-" . sep? . key_name >_key_init $_key %_id_exit;
+	item = (((key0 . key_data_sep . item_data_list?)
+	        |(key1 . key_data_sep . item_data_list?)
+	        |(id   . key_data_sep . item_data )
+	        ) . blank
+	       );
+
+	# Main processing loop.
+	action _error {
+		return KNOT_EPARSEFAIL;
+	}
+
+	main := (blank | item)* $!_error;
+}%%
+
+// Include parser static data (Ragel internals).
+%% write data;
+
+int _parse(
+	yp_parser_t *parser)
+{
+	// Parser input limits (Ragel internals).
+	const char *p, *pe, *eof;
+
+	// 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 artifical block with just one new line char.
+		p = "\n";
+		pe = p + 1;
+		eof = pe;
+	}
+
+	// Include parser body.
+	%% write exec;
+
+	// 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 == %%{ write error; }%%) {
+		return KNOT_EPARSEFAIL;
+	}
+
+	// Check if parsed an item.
+	if (found) {
+		return KNOT_EOK;
+	} else {
+		return KNOT_EFEWDATA;
+	}
+}
+
+int _start_state(
+	void)
+{
+	return %%{ write start; }%%;
+}
diff --git a/src/libknot/internal/yparser/ypformat.c b/src/libknot/internal/yparser/ypformat.c
new file mode 100644
index 0000000000..aa09267d1d
--- /dev/null
+++ b/src/libknot/internal/yparser/ypformat.c
@@ -0,0 +1,122 @@
+/*  Copyright (C) 2015 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "libknot/internal/yparser/yptrafo.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.
+	size_t total = 0;
+	int ret = snprintf(out, out_len, "%s%s%s%s",
+	                   first_value ? prefix : "",
+	                   first_value ? item->name + 1 : "",
+	                   first_value ? ":" : "",
+	                   data == NULL ? "\n" : (first_value && !last_value ?
+	                                          " [ " : " "));
+	if (ret < 0 || ret >= out_len) {
+		return KNOT_ESPACE;
+	}
+	total += ret;
+	out   += ret;
+	out_len -= ret;
+
+	// Finish if no data.
+	if (data == NULL) {
+		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;
+	}
+	total += aux_len;
+	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;
+}
+
+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);
+}
+
+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);
+}
+
+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)
+{
+	if (data == NULL) {
+		return KNOT_EINVAL;
+	}
+
+	return format_item(item, data, data_len, out, out_len, style, "    ",
+	                   first_value, last_value);
+}
diff --git a/src/libknot/internal/yparser/ypformat.h b/src/libknot/internal/yparser/ypformat.h
new file mode 100644
index 0000000000..00766918b9
--- /dev/null
+++ b/src/libknot/internal/yparser/ypformat.h
@@ -0,0 +1,101 @@
+/*  Copyright (C) 2015 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 <http://www.gnu.org/licenses/>.
+*/
+/*!
+ * \file
+ *
+ * Tools for Yparser format creation.
+ *
+ * \addtogroup yparser
+ *
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/internal/yparser/ypscheme.h"
+
+/*!
+ * Formats key0 item.
+ *
+ * \param[in] item Scheme 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 Scheme 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 Scheme 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/internal/yparser/ypscheme.c b/src/libknot/internal/yparser/ypscheme.c
new file mode 100644
index 0000000000..9ac4c790b1
--- /dev/null
+++ b/src/libknot/internal/yparser/ypscheme.c
@@ -0,0 +1,407 @@
+/*  Copyright (C) 2015 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "libknot/internal/yparser/ypscheme.h"
+#include "libknot/internal/yparser/yptrafo.h"
+#include "libknot/errcode.h"
+
+/*! Initializes the referenced item. */
+static int set_ref_item(
+	yp_item_t *dst,
+	const yp_item_t *scheme)
+{
+	if (scheme == NULL) {
+		return KNOT_EINVAL;
+	}
+
+	// Get reference category.
+	const yp_name_t *ref_name = dst->var.r.ref_name;
+	const yp_item_t *ref = yp_scheme_find(ref_name, NULL, scheme);
+	if (ref == NULL) {
+		return KNOT_YP_EINVAL_ITEM;
+	}
+
+	dst->var.r.ref = 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 *scheme)
+{
+	// Count subitems.
+	size_t num = 0;
+	while (src->var.g.sub_items[num].name != NULL) {
+		num++;
+	}
+
+	// Allocate space for subitems + terminal zero item.
+	size_t memsize = (num + 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 < num; 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, scheme);
+			break;
+		case YP_TGRP: // Deeper hierarchy is not supported.
+			ret = KNOT_ENOTSUP;
+			break;
+		default:
+			break;
+		}
+
+		if (ret != KNOT_EOK) {
+			free(dst->sub_items);
+			dst->sub_items = NULL;
+			return ret;
+		}
+	}
+
+	return KNOT_EOK;
+}
+
+static int set_item(
+	yp_item_t *dst,
+	const yp_item_t *src,
+	const yp_item_t *scheme)
+{
+	// 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;
+
+	// Item type specific preparation.
+	switch (src->type) {
+	case YP_TREF:
+		return set_ref_item(dst, scheme);
+	case YP_TGRP:
+		return set_grp_item(dst, src, scheme);
+	default:
+		return KNOT_EOK;
+	}
+}
+
+static void unset_item(
+	yp_item_t *item)
+{
+	if (item->sub_items != NULL) {
+		free(item->sub_items);
+	}
+
+	memset(item, 0, sizeof(yp_item_t));
+}
+
+int yp_scheme_copy(
+	yp_item_t **dst,
+	const yp_item_t *src)
+{
+	if (dst == NULL || src == NULL) {
+		return KNOT_EINVAL;
+	}
+
+	// Count scheme items.
+	size_t scheme_items = 0;
+	for (const yp_item_t *item = src; item->name != NULL; item++) {
+		scheme_items++;
+	}
+
+	// Allocate space for new scheme.
+	size_t size = (scheme_items + 1) * sizeof(yp_item_t);
+	*dst = malloc(size);
+	if (*dst == NULL) {
+		return KNOT_ENOMEM;
+	}
+	memset(*dst, 0, size);
+
+	// Copy the scheme.
+	for (int i = 0; i < scheme_items; i++) {
+		if (src[i].name == NULL) {
+			break;
+		}
+
+		int ret = set_item(*dst + i, src + i, *dst);
+		if (ret != KNOT_EOK) {
+			yp_scheme_free(*dst);
+			return ret;
+		}
+	}
+
+	return KNOT_EOK;
+}
+
+void yp_scheme_free(
+	yp_item_t *scheme)
+{
+	if (scheme == NULL) {
+		return;
+	}
+
+	for (yp_item_t *item = scheme; item->name != NULL; item++) {
+		unset_item(item);
+	}
+	free(scheme);
+}
+
+/*! Search the scheme 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 *scheme)
+{
+	if (name == NULL || scheme == NULL) {
+		return NULL;
+	}
+
+	for (const yp_item_t *item = scheme; item->name != NULL; item++) {
+		if (item->name[0] != name_len) {
+			continue;
+		}
+		if (memcmp(item->name + 1, name, name_len) == 0) {
+			return item;
+		}
+	}
+
+	return NULL;
+}
+
+const yp_item_t* yp_scheme_find(
+	const yp_name_t *name,
+	const yp_name_t *parent_name,
+	const yp_item_t *scheme)
+{
+	if (name == NULL || scheme == NULL) {
+		return NULL;
+	}
+
+	if (parent_name == NULL) {
+		return find_item(name + 1, name[0], scheme);
+	} else {
+		const yp_item_t *parent = find_item(parent_name + 1,
+		                                    parent_name[0], scheme);
+		if (parent == NULL) {
+			return NULL;
+		}
+		return find_item(name + 1, name[0], parent->sub_items);
+	}
+}
+
+yp_check_ctx_t* yp_scheme_check_init(
+	const yp_item_t *scheme)
+{
+	if (scheme == 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->scheme = scheme;
+
+	return ctx;
+}
+
+static int check_key0(
+	const char *key,
+	size_t key_len,
+	const char *data,
+	size_t data_len,
+	yp_check_ctx_t *ctx)
+{
+	const yp_item_t *key0 = find_item(key, key_len, ctx->scheme);
+	if (key0 == NULL) {
+		return KNOT_YP_EINVAL_ITEM;
+	}
+
+	// Group cannot have data.
+	if (key0->type == YP_TGRP && data_len != 0) {
+		return KNOT_YP_ENOTSUP_DATA;
+	}
+
+	ctx->key0 = key0;
+	ctx->key1 = NULL;
+	ctx->id_len = 0;
+	ctx->data_len = sizeof(((yp_check_ctx_t *)NULL)->data);
+
+	return yp_item_to_bin(key0, data, data_len, ctx->data, &ctx->data_len);
+}
+
+static int check_key1(
+	const char *key,
+	size_t key_len,
+	const char *data,
+	size_t data_len,
+	yp_check_ctx_t *ctx)
+{
+	// Sub-item must have a parent item.
+	if (ctx->key0 == NULL || ctx->key0->type != YP_TGRP) {
+		return KNOT_YP_EINVAL_ITEM;
+	}
+
+	const yp_item_t *key1 = find_item(key, key_len, ctx->key0->sub_items);
+	if (key1 == NULL) {
+		return KNOT_YP_EINVAL_ITEM;
+	}
+
+	// Sub-item must not be a group.
+	if (key1->type == YP_TGRP) {
+		return KNOT_YP_EINVAL_ITEM;
+	}
+
+	// Check if the group requires id specification.
+	if (ctx->key0->var.g.id != NULL) {
+		// Check if key1 is not id item.
+		if (key1 == ctx->key0->var.g.id) {
+			return KNOT_YP_EINVAL_ITEM;
+		}
+
+		if (ctx->id_len == 0) {
+			return KNOT_YP_ENOID;
+		}
+	// Check for id if not supported.
+	} else if (ctx->id_len > 0) {
+		return KNOT_YP_ENOTSUP_ID;
+	}
+
+	ctx->key1 = key1;
+	ctx->data_len = sizeof(((yp_check_ctx_t *)NULL)->data);
+
+	return yp_item_to_bin(key1, data, data_len, ctx->data, &ctx->data_len);
+}
+
+static int check_id(
+	const char *key,
+	size_t key_len,
+	const char *data,
+	size_t data_len,
+	yp_check_ctx_t *ctx)
+{
+	// Id must have a parent item.
+	if (ctx->key0 == NULL) {
+		return KNOT_YP_EINVAL_ITEM;
+	}
+
+	// Only group item can have id.
+	if (ctx->key0->type != YP_TGRP) {
+		return KNOT_YP_ENOTSUP_ID;
+	}
+
+	// Check group item without id support.
+	if (ctx->key0->var.g.id == NULL) {
+	       return KNOT_YP_ENOTSUP_ID;
+	}
+
+	const yp_item_t *id = find_item(key, key_len, ctx->key0->sub_items);
+	if (id == NULL) {
+		return KNOT_YP_EINVAL_ITEM;
+	}
+
+	// Id item must be the first one.
+	if (id != ctx->key0->var.g.id) {
+		return KNOT_YP_EINVAL_ITEM;
+	}
+
+	// Textual id must not be empty.
+	if (data_len == 0) {
+		return KNOT_YP_ENODATA;
+	}
+
+	ctx->key1 = ctx->key0->var.g.id;
+	ctx->data_len = 0;
+	ctx->id_len = sizeof(((yp_check_ctx_t *)NULL)->data);
+
+	int ret = yp_item_to_bin(ctx->key0->var.g.id, data, data_len, ctx->id,
+	                         &ctx->id_len);
+	if (ret != KNOT_EOK) {
+		return ret;
+	}
+
+	// Binary id must not be empty.
+	if (ctx->id_len == 0) {
+		return KNOT_YP_EINVAL_DATA;
+	}
+
+	return KNOT_EOK;
+}
+
+int yp_scheme_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:
+		ret = check_key0(parser->key, parser->key_len,
+		                 parser->data, parser->data_len, ctx);
+		break;
+	case YP_EKEY1:
+		ret = check_key1(parser->key, parser->key_len,
+		                 parser->data, parser->data_len, ctx);
+		break;
+	case YP_EID:
+		ret = check_id(parser->key, parser->key_len,
+		               parser->data, parser->data_len, ctx);
+		break;
+	default:
+		ret = KNOT_EPARSEFAIL;
+		break;
+	}
+
+	ctx->event = parser->event;
+
+	return ret;
+}
+
+void yp_scheme_check_deinit(
+	yp_check_ctx_t* ctx)
+{
+	free(ctx);
+}
diff --git a/src/libknot/internal/yparser/ypscheme.h b/src/libknot/internal/yparser/ypscheme.h
new file mode 100644
index 0000000000..6f86b26951
--- /dev/null
+++ b/src/libknot/internal/yparser/ypscheme.h
@@ -0,0 +1,267 @@
+/*  Copyright (C) 2015 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 <http://www.gnu.org/licenses/>.
+*/
+/*!
+ * \file
+ *
+ * Scheme layer for Yparser.
+ *
+ * \addtogroup yparser
+ *
+ * @{
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "libknot/internal/utils.h"
+#include "libknot/internal/yparser/yparser.h"
+
+/*! 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
+
+/*! 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_VDNAME	.var.d
+#define YP_VB64		.var.d
+#define YP_VDATA	.var.d
+#define YP_VREF		.var.r
+#define YP_VGRP		.var.g
+
+/*! Scheme item name is a char string with a leading byte (string length). */
+typedef char yp_name_t;
+
+/*! Scheme item type. */
+typedef enum {
+	YP_TNONE = 0, /*!< Unspecified. */
+	YP_TINT,      /*!< Integer. */
+	YP_TBOOL,     /*!< Boolean. */
+	YP_TOPT,      /*!< Option from the list. */
+	YP_TSTR,      /*!< String. */
+	YP_TADDR,     /*!< Address (address[@port]). */
+	YP_TNET,      /*!< Network (address[/mask]). */
+	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;
+
+/*! Scheme item flags. */
+typedef enum {
+	YP_FNONE  = 0,     /*!< Unspecified. */
+	YP_FMULTI = 1 << 0 /*!< Multivalued item. */
+} yp_flag_t;
+
+/*! Scheme 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;
+
+/*! Scheme 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;
+	/*! Boolen variables. */
+	struct {
+		/*! Default value. */
+		bool dflt;
+	} b;
+	/*! Option variables. */
+	struct {
+		/*! List of options (maximal value is 255). */
+		lookup_table_t const *opts;
+		/*! Default value. */
+		unsigned dflt;
+	} o;
+	/*! String variables. */
+	struct {
+		/*! Default value. */
+		char const *dflt;
+	} s;
+	/*! Address variables. */
+	struct {
+		/*! Default port. */
+		uint16_t dflt_port;
+		/*! 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)(char const *, size_t, uint8_t *, size_t *);
+		/*! Binary to text transformatio function. */
+		int (*to_txt)(uint8_t const *, size_t, char *, size_t *);
+	} d;
+	/*! Reference variables. */
+	struct {
+		/*! Referenced group name. */
+		yp_name_t const *ref_name;
+		/*! Referenced item (dynamic value). */
+		yp_item_t const *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;
+
+/*! Scheme 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[3];
+	/*! Item group subitems (name=NULL terminated array). */
+	yp_item_t *sub_items;
+};
+
+/*! Context parameters for check operations. */
+typedef struct {
+	/*! Used scheme. */
+	const yp_item_t *scheme;
+	/*! Current key0 item. */
+	const yp_item_t *key0;
+	/*! Current key1 item. */
+	const yp_item_t *key1;
+	/*! Current parser event. */
+	yp_event_t event;
+	/*! Current binary id. */
+	uint8_t id[YP_MAX_ID_LEN];
+	/*! Current binary id length. */
+	size_t id_len;
+	/*! Current item data. */
+	uint8_t data[YP_MAX_DATA_LEN];
+	/*! Current item data length. */
+	size_t data_len;
+} yp_check_ctx_t;
+
+/*!
+ * Copies the scheme and reinitializes dynamic parameters.
+ *
+ * \param[out] dst New copy of the scheme.
+ * \param[in] srt Source scheme.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int yp_scheme_copy(
+	yp_item_t **dst,
+	const yp_item_t *src
+);
+
+/*!
+ * Deallocates the scheme.
+ *
+ * \param[in] scheme A scheme returned by #yp_scheme_copy().
+ */
+void yp_scheme_free(
+	yp_item_t *scheme
+);
+
+/*!
+ * Tries to find given parent_name/name in the scheme.
+ *
+ * \param[in] name Name of the item.
+ * \param[in] parent_name Name of the parent item (NULL if no parent).
+ * \param[in] scheme Scheme.
+ *
+ * \return Item, NULL if not found or error.
+ */
+const yp_item_t* yp_scheme_find(
+	const yp_name_t *name,
+	const yp_name_t *parent_name,
+	const yp_item_t *scheme
+);
+
+/*!
+ * Prepares a context for item check against the scheme.
+ *
+ * \param[in] scheme Scheme.
+ *
+ * \return Context, NULL if error.
+ */
+yp_check_ctx_t* yp_scheme_check_init(
+	const yp_item_t *scheme
+);
+
+/*!
+ * Checks the current parser output against the scheme.
+ *
+ * If the item is correct, context also contains binary value of the item.
+ *
+ * \param[in,out] ctx New copy of the scheme.
+ * \param[in] parser Parser returned by #yp_create().
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int yp_scheme_check_parser(
+	yp_check_ctx_t *ctx,
+	const yp_parser_t *parser
+);
+
+/*!
+ * Deallocates the context.
+ *
+ * \param[in] ctx Context returned by #yp_scheme_check_init().
+ */
+void yp_scheme_check_deinit(
+	yp_check_ctx_t *ctx
+);
+
+// TODO: check from string.
+// TODO: scheme add/remove item.
+
+/*! @} */
diff --git a/src/libknot/internal/yparser/yptrafo.c b/src/libknot/internal/yparser/yptrafo.c
new file mode 100644
index 0000000000..7efb1e8baf
--- /dev/null
+++ b/src/libknot/internal/yparser/yptrafo.c
@@ -0,0 +1,771 @@
+/*  Copyright (C) 2015 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <arpa/inet.h>
+
+#include "libknot/internal/yparser/yptrafo.h"
+#include "libknot/internal/base64.h"
+#include "libknot/internal/sockaddr.h"
+#include "libknot/errcode.h"
+
+#define TXT_BIN char const *txt, size_t txt_len, uint8_t *bin, size_t *bin_len
+#define BIN_TXT uint8_t const *bin, size_t bin_len, char *txt, size_t *txt_len
+
+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 int yp_str_to_bin(
+	TXT_BIN)
+{
+	if (*bin_len <= txt_len) {
+		return KNOT_ESPACE;
+	}
+
+	memcpy(bin, txt, txt_len);
+	bin[txt_len] = '\0';
+	*bin_len = txt_len + 1;
+
+	return KNOT_EOK;
+}
+
+static int yp_str_to_txt(
+	BIN_TXT)
+{
+	if (*txt_len < bin_len) {
+		return KNOT_ESPACE;
+	}
+
+	memcpy(txt, bin, bin_len);
+	*txt_len = bin_len - 1;
+
+	return KNOT_EOK;
+}
+
+static int yp_bool_to_bin(
+	TXT_BIN)
+{
+	if (strcasecmp(txt, "on") == 0) {
+		bin[0] = '\0';
+		*bin_len = 1;
+		return KNOT_EOK;
+	} else if (strcasecmp(txt, "off") == 0) {
+		*bin_len = 0;
+		return KNOT_EOK;
+	}
+
+	return KNOT_EINVAL;
+}
+
+static int yp_bool_to_txt(
+	BIN_TXT)
+{
+	int ret;
+
+	switch (bin_len) {
+	case 0:
+		ret = snprintf(txt, *txt_len, "off");
+		if (ret <= 0 || ret >= *txt_len) {
+			return KNOT_ERANGE;
+		}
+		*txt_len = ret;
+		return KNOT_EOK;
+	case 1:
+		if (bin[0] == '\0') {
+			ret = snprintf(txt, *txt_len, "on");
+			if (ret <= 0 || ret >= *txt_len) {
+				return KNOT_ERANGE;
+			}
+			*txt_len = ret;
+			return KNOT_EOK;
+		}
+		break;
+	}
+
+	return KNOT_EINVAL;
+}
+
+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_ENOTSUP;
+		}
+	} 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_ENOTSUP;
+		}
+	} else {
+		return KNOT_ENOTSUP;
+	}
+
+	// Check for possible number overflow.
+	if (INT64_MAX / multiplier < (*number >= 0 ? *number : -*number)) {
+		return KNOT_ERANGE;
+	}
+
+	*number *= multiplier;
+
+	return KNOT_EOK;
+}
+
+static int yp_int_to_bin(
+	TXT_BIN,
+	int64_t min,
+	int64_t max,
+	uint8_t min_bytes,
+	yp_style_t style)
+{
+	char *end = (char *)txt;
+
+	int64_t number = strtoll(txt, &end, 10);
+
+	// Check if the whole string is invalid.
+	if (end == txt) {
+		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 the unit on the number.
+		if (remove_unit(&number, *end, style) != KNOT_EOK) {
+			return KNOT_EINVAL;
+		}
+	}
+
+	if (number < min || number > max) {
+		return KNOT_ERANGE;
+	}
+
+	// Convert the number to litte-endian byte order.
+	number = htole64(number);
+
+	// Store the result
+	memcpy(bin, &number, sizeof(number));
+	*bin_len = sizeof(number);
+
+	// Ignore trailing zeroes.
+	for (int i = 7; i >= min_bytes; i--) {
+		if (((uint8_t *)&number)[i] != 0) {
+			break;
+		}
+		(*bin_len)--;
+	}
+
+	return KNOT_EOK;
+}
+
+static void add_unit(
+	int64_t *number,
+	char *unit,
+	yp_style_t style)
+{
+	int64_t new_multi = 1;
+	int64_t new_unit = '\0';
+
+	if (*number == 0) {
+		return;
+	}
+
+	// Get the multiplier for the unit.
+	if (style & YP_SSIZE) {
+		if (*number < MULTI_KILO) {
+			new_multi = MULTI_BYTE;
+			new_unit = UNIT_BYTE;
+		} else if (*number < MULTI_MEGA) {
+			new_multi = MULTI_KILO;
+			new_unit = UNIT_KILO;
+		} else if (*number < MULTI_GIGA) {
+			new_multi = MULTI_MEGA;
+			new_unit = UNIT_MEGA;
+		} else {
+			new_multi = MULTI_GIGA;
+			new_unit = UNIT_GIGA;
+		}
+	} else if (style & YP_STIME) {
+		if (*number < MULTI_MIN) {
+			new_multi = MULTI_SEC;
+			new_unit = UNIT_SEC;
+		} else if (*number < MULTI_HOUR) {
+			new_multi = MULTI_MIN;
+			new_unit = UNIT_MIN;
+		} else if (*number < MULTI_DAY) {
+			new_multi = MULTI_HOUR;
+			new_unit = UNIT_HOUR;
+		} else {
+			new_multi = MULTI_DAY;
+			new_unit = UNIT_DAY;
+		}
+	}
+
+	if (new_unit != '\0' && (*number % new_multi) == 0) {
+		*number /= new_multi;
+		*unit = new_unit;
+	}
+}
+
+static int yp_int_to_txt(
+	BIN_TXT,
+	yp_style_t style)
+{
+	int64_t data = 0, number = 0;
+	char unit[2] = { '\0' };
+
+	memcpy(&data, bin, bin_len);
+	number = le64toh(data);
+
+	add_unit(&number, unit, style);
+
+	int ret = snprintf(txt, *txt_len, "%"PRId64"%s", number, unit);
+	if (ret <= 0 || ret >= *txt_len) {
+		return KNOT_ERANGE;
+	}
+	*txt_len = ret;
+
+	return KNOT_EOK;
+}
+
+static int addr_to_bin(
+	TXT_BIN,
+	bool allow_unix)
+{
+	struct in_addr  addr4;
+	struct in6_addr addr6;
+
+	uint8_t type;
+	size_t addr_len;
+	const void *addr;
+
+	if (inet_pton(AF_INET, txt, &addr4) == 1) {
+		type = 4;
+		addr_len = sizeof(addr4.s_addr);
+		addr = &(addr4.s_addr);
+	} else if (inet_pton(AF_INET6, txt, &addr6) == 1) {
+		type = 6;
+		addr_len = sizeof(addr6.s6_addr);
+		addr = &(addr6.s6_addr);
+	} else if (allow_unix && txt_len > 0) {
+		type = 0;
+		addr_len = txt_len;
+		addr = txt;
+	} else {
+		return KNOT_EINVAL;
+	}
+
+	if (*bin_len < sizeof(type) + addr_len) {
+		return KNOT_ESPACE;
+	}
+
+	*bin = type;
+	memcpy(bin + sizeof(type), addr, addr_len);
+	*bin_len = sizeof(type) + addr_len;
+
+	return KNOT_EOK;
+}
+
+static int addr_to_txt(
+	BIN_TXT)
+{
+	struct in_addr  addr4;
+	struct in6_addr addr6;
+
+	uint8_t type = *bin;
+	bin += sizeof(type);
+	bin_len -= sizeof(type);
+
+	int ret;
+	switch (type) {
+	case 0:
+		ret = snprintf(txt, *txt_len, "%.*s", (int)bin_len, bin);
+		if (ret <= 0 || ret >= *txt_len) {
+			return KNOT_ESPACE;
+		}
+		break;
+	case 4:
+		if (bin_len != sizeof(addr4.s_addr)) {
+			return KNOT_EINVAL;
+		}
+		memcpy(&(addr4.s_addr), bin, bin_len);
+		if (inet_ntop(AF_INET, &addr4, txt, *txt_len) == NULL) {
+			return KNOT_ESPACE;
+		}
+		break;
+	case 6:
+		if (bin_len != sizeof(addr6.s6_addr)) {
+			return KNOT_EINVAL;
+		}
+		memcpy(&(addr6.s6_addr), bin, bin_len);
+		if (inet_ntop(AF_INET6, &addr6, txt, *txt_len) == NULL) {
+			return KNOT_ESPACE;
+		}
+		break;
+	default:
+		return KNOT_EINVAL;
+	}
+
+	*txt_len = strlen(txt);
+
+	return KNOT_EOK;
+}
+
+static int yp_addr_to_bin(
+	TXT_BIN,
+	bool net)
+{
+	// Check for separator.
+	char *pos = index(txt, net ? '/' : '@');
+	if (pos == NULL) {
+		int ret = addr_to_bin(txt, txt_len, bin, bin_len, !net);
+		if (ret != KNOT_EOK) {
+			return ret;
+		}
+	} else {
+		size_t txt_addr_len = pos - txt;
+		char *addr = strndup(txt, txt_addr_len);
+		if (addr == NULL) {
+			return KNOT_ENOMEM;
+		}
+
+		// Address part.
+		size_t bin_addr_len = *bin_len;
+		int ret = addr_to_bin(addr, txt_addr_len, bin, &bin_addr_len,
+		                      false);
+		if (ret != KNOT_EOK) {
+			free(addr);
+			return ret;
+		}
+
+		// Set maximal port/prefix length.
+		uint8_t type = *bin;
+		size_t max_num;
+		if (net) {
+			if (type == 4) {
+				max_num = 32;
+			} else {
+				max_num = 128;
+			}
+		} else {
+			max_num = UINT16_MAX;
+		}
+
+		txt += txt_addr_len + sizeof(char);
+		bin += bin_addr_len;
+
+		// Port/prefix length part.
+		size_t bin_num_len = *bin_len - bin_addr_len;
+		ret = yp_int_to_bin(txt, txt_len - txt_addr_len - 1,
+		                    bin, &bin_num_len, 0, max_num,
+		                    net ? sizeof(uint8_t) : sizeof(uint16_t),
+		                    YP_SNONE);
+		if (ret != KNOT_EOK) {
+			free(addr);
+			return ret;
+		}
+
+		free(addr);
+
+		*bin_len = bin_addr_len + bin_num_len;
+	}
+
+	return KNOT_EOK;
+}
+
+static int yp_addr_to_txt(
+	BIN_TXT,
+	bool net)
+{
+	// Set binary address length.
+	uint8_t type = *bin;
+	size_t bin_addr_len = sizeof(type);
+	switch (type) {
+	case 0:
+		bin_addr_len += bin_len - sizeof(type);
+		break;
+	case 4:
+		bin_addr_len += sizeof(((struct in_addr *)NULL)->s_addr);
+		break;
+	case 6:
+		bin_addr_len += sizeof(((struct in6_addr *)NULL)->s6_addr);
+		break;
+	default:
+		return KNOT_EINVAL;
+	}
+
+	// Write address.
+	size_t txt_addr_len = *txt_len;
+	int ret = addr_to_txt(bin, bin_addr_len, txt, &txt_addr_len);
+	if (ret != KNOT_EOK) {
+		return ret;
+	}
+	bin_len -= bin_addr_len;
+	bin += bin_addr_len;
+	txt += txt_addr_len;
+
+	if (bin_len == 0) {
+		*txt_len = txt_addr_len;
+		return KNOT_EOK;
+	}
+
+	// Write separator.
+	char *sep = net ? "/" :"@";
+	size_t txt_sep_len = *txt_len - txt_addr_len;
+	ret = yp_str_to_txt((uint8_t *)sep, 2, txt, &txt_sep_len);
+	if (ret != KNOT_EOK) {
+		return ret;
+	}
+	txt += txt_sep_len;
+
+	// Write port/prefix length.
+	size_t txt_num_len = *txt_len - txt_addr_len - txt_sep_len;
+	ret = yp_int_to_txt(bin, bin_len, txt, &txt_num_len, YP_SNONE);
+	if (ret != KNOT_EOK) {
+		return ret;
+	}
+
+	*txt_len = txt_addr_len + txt_sep_len + txt_num_len;
+
+	return KNOT_EOK;
+}
+
+static int yp_option_to_bin(
+	TXT_BIN,
+	const lookup_table_t *opts)
+{
+	while (opts->name != NULL) {
+		if (strcasecmp(txt, opts->name) == 0) {
+			bin[0] = opts->id;
+			*bin_len = 1;
+			return KNOT_EOK;
+		}
+		opts++;
+	}
+
+	return KNOT_EINVAL;
+}
+
+static int yp_option_to_txt(
+	BIN_TXT,
+	const lookup_table_t *opts)
+{
+	while (opts->name != NULL) {
+		if (bin[0] == opts->id) {
+			int ret = snprintf(txt, *txt_len, "%s", opts->name);
+			if (ret <= 0 || ret >= *txt_len) {
+				return KNOT_ERANGE;
+			}
+			*txt_len = ret;
+			return KNOT_EOK;
+		}
+		opts++;
+	}
+
+	return KNOT_EINVAL;
+}
+
+static int yp_base64_to_bin(
+	TXT_BIN)
+{
+	int ret = base64_decode((uint8_t *)txt, txt_len, bin, *bin_len);
+	if (ret < 0) {
+		return ret;
+	}
+
+	*bin_len = ret;
+
+	return KNOT_EOK;
+}
+
+static int yp_base64_to_txt(
+	BIN_TXT)
+{
+	int ret = base64_encode(bin, bin_len, (uint8_t *)txt, *txt_len);
+	if (ret < 0) {
+		return ret;
+	}
+
+	if (ret >= *txt_len) {
+		return KNOT_ESPACE;
+	}
+	*txt_len = ret;
+	txt[*txt_len] = '\0';
+
+	return KNOT_EOK;
+}
+
+static int yp_dname_to_bin(
+	TXT_BIN)
+{
+	knot_dname_t *dname = knot_dname_from_str(bin, txt, *bin_len);
+	if (dname == NULL) {
+		return KNOT_EINVAL;
+	}
+
+	int ret = knot_dname_wire_check(bin, bin + *bin_len, NULL);
+	if (ret <= 0) {
+		return KNOT_EINVAL;
+	}
+	*bin_len = ret;
+
+	ret = knot_dname_to_lower(bin);
+	if (ret != KNOT_EOK) {
+		return KNOT_EINVAL;
+	}
+
+	return KNOT_EOK;
+}
+
+static int yp_dname_to_txt(
+	BIN_TXT)
+{
+	char *name = knot_dname_to_str(txt, bin, *txt_len);
+	if (name == NULL) {
+		return KNOT_EINVAL;
+	}
+
+	*txt_len = strlen(txt);
+
+	return KNOT_EOK;
+}
+
+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;
+	}
+
+	switch (item->type) {
+	case YP_TINT:
+		return yp_int_to_bin(txt, txt_len, bin, bin_len, item->var.i.min,
+		                     item->var.i.max, 0, item->var.i.unit);
+	case YP_TBOOL:
+		return yp_bool_to_bin(txt, txt_len, bin, bin_len);
+	case YP_TOPT:
+		return yp_option_to_bin(txt, txt_len, bin, bin_len,
+		                        item->var.o.opts);
+	case YP_TSTR:
+		return yp_str_to_bin(txt, txt_len, bin, bin_len);
+	case YP_TADDR:
+		return yp_addr_to_bin(txt, txt_len, bin, bin_len, false);
+	case YP_TNET:
+		return yp_addr_to_bin(txt, txt_len, bin, bin_len, true);
+	case YP_TDNAME:
+		return yp_dname_to_bin(txt, txt_len, bin, bin_len);
+	case YP_TB64:
+		return yp_base64_to_bin(txt, txt_len, bin, bin_len);
+	case YP_TDATA:
+		return item->var.d.to_bin(txt, txt_len, bin, bin_len);
+	case YP_TREF:
+		return yp_item_to_bin(item->var.r.ref->var.g.id, txt, txt_len,
+		                      bin, bin_len);
+	default:
+		*bin_len = 0;
+		return KNOT_EOK;
+	}
+}
+
+static int yp_item_to_txt_unquoted(
+	const yp_item_t *item,
+	const uint8_t *bin,
+	size_t bin_len,
+	char *txt,
+	size_t *txt_len,
+	yp_style_t style)
+{
+	switch (item->type) {
+	case YP_TINT:
+		return yp_int_to_txt(bin, bin_len, txt, txt_len,
+		                     item->var.i.unit & style);
+	case YP_TBOOL:
+		return yp_bool_to_txt(bin, bin_len, txt, txt_len);
+	case YP_TOPT:
+		return yp_option_to_txt(bin, bin_len, txt, txt_len,
+		                        item->var.o.opts);
+	case YP_TSTR:
+		return yp_str_to_txt(bin, bin_len, txt, txt_len);
+	case YP_TADDR:
+		return yp_addr_to_txt(bin, bin_len, txt, txt_len, false);
+	case YP_TNET:
+		return yp_addr_to_txt(bin, bin_len, txt, txt_len, true);
+	case YP_TDNAME:
+		return yp_dname_to_txt(bin, bin_len, txt, txt_len);
+	case YP_TB64:
+		return yp_base64_to_txt(bin, bin_len, txt, txt_len);
+	case YP_TDATA:
+		return item->var.d.to_txt(bin, bin_len, txt, txt_len);
+	case YP_TREF:
+		return yp_item_to_txt(item->var.r.ref->var.g.id, bin, bin_len,
+		                      txt, txt_len, style | YP_SNOQUOTE);
+	default:
+		*txt_len = 0;
+		return KNOT_EOK;
+	}
+}
+
+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;
+	}
+
+	// Print unquoted item value.
+	if (style & YP_SNOQUOTE) {
+		return yp_item_to_txt_unquoted(item, bin, bin_len, txt, txt_len,
+		                               style);
+	}
+
+	size_t out_len = 0;
+
+	// Print leading quote.
+	if (*txt_len < 1) {
+		return KNOT_ESPACE;
+	}
+	*(txt++) = '\"';
+	out_len += sizeof(char);
+
+	// Print unquoted item value.
+	size_t len = *txt_len - out_len;
+	int ret = yp_item_to_txt_unquoted(item, bin, bin_len, txt, &len, style);
+	if (ret != KNOT_EOK) {
+		return ret;
+	}
+	txt += len;
+	out_len += len;
+
+	// Print trailing quote.
+	if (*txt_len - out_len < 2) {
+		return KNOT_ESPACE;
+	}
+	*(txt++) = '\"';
+	out_len += sizeof(char);
+
+	// Print string terminator.
+	*txt = '\0';
+	*txt_len = out_len;
+
+	return KNOT_EOK;
+}
+
+struct sockaddr_storage yp_addr(
+	const uint8_t *data,
+	size_t data_len,
+	int *num)
+{
+	struct sockaddr_storage ss = { AF_UNSPEC };
+
+	uint8_t type = *data;
+	data += sizeof(type);
+	data_len -= sizeof(type);
+
+	// Set binary address length.
+	int family;
+	size_t bin_addr_len;
+	switch (type) {
+	case 0:
+		family = AF_UNIX;
+		bin_addr_len = data_len;
+		break;
+	case 4:
+		family = AF_INET;
+		bin_addr_len = sizeof(((struct in_addr *)NULL)->s_addr);
+		break;
+	case 6:
+		family = AF_INET6;
+		bin_addr_len = sizeof(((struct in6_addr *)NULL)->s6_addr);
+		break;
+	default:
+		*num = -1;
+		return ss;
+	}
+
+	sockaddr_set_raw(&ss, family, data, bin_addr_len);
+	data += bin_addr_len;
+	data_len -= bin_addr_len;
+
+	*num = (data_len == 0) ? -1 : yp_int(data, data_len);
+
+	return ss;
+}
diff --git a/src/libknot/internal/yparser/yptrafo.h b/src/libknot/internal/yparser/yptrafo.h
new file mode 100644
index 0000000000..43f82845cb
--- /dev/null
+++ b/src/libknot/internal/yparser/yptrafo.h
@@ -0,0 +1,169 @@
+/*  Copyright (C) 2015 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 <http://www.gnu.org/licenses/>.
+*/
+/*!
+ * \file
+ *
+ * Value transformations for Yparser.
+ *
+ * \addtogroup yparser
+ *
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/internal/yparser/ypscheme.h"
+#include "libknot/internal/endian.h"
+#include "libknot/dname.h"
+
+/*!
+ * Transforms textual item value to binary form.
+ *
+ * \param[in] item Scheme 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 Scheme 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 integer value.
+ *
+ * \param[in] data Binary value to to transform.
+ * \param[in] data_len Length of the value.
+ *
+ * \return Integer value.
+ */
+inline static int64_t yp_int(
+	const uint8_t *data,
+	size_t data_len)
+{
+	int64_t num = 0;
+	memcpy(&num, data, data_len);
+	return le64toh(num);
+}
+
+/*!
+ * Converts binary value to boolean value.
+ *
+ * \param[in] data_len Length of the value.
+ *
+ * \return Boolean value.
+ */
+inline static bool yp_bool(
+	size_t data_len)
+{
+	return (data_len > 0) ? true : false;
+}
+
+/*!
+ * Converts binary value to option value.
+ *
+ * \param[in] data Binary value to to transform.
+ *
+ * \return Unsigned value.
+ */
+inline static unsigned yp_opt(
+	const uint8_t *data)
+{
+	return (unsigned)data[0];
+}
+
+/*!
+ * Converts binary value to string pointer.
+ *
+ * \param[in] data Binary value to to transform.
+ *
+ * \return String ointer.
+ */
+inline static const char* yp_str(
+	const uint8_t *data)
+{
+	return (const char *)data;
+}
+
+/*!
+ * Converts binary value to address value with port/mask.
+ *
+ * \param[in] data Binary value to to transform.
+ * \param[in] data_len Length of the value.
+ * \param[out] num Possible port/prefix value.
+ *
+ * \return Address value.
+ */
+struct sockaddr_storage yp_addr(
+	const uint8_t *data,
+	size_t data_len,
+	int *num
+);
+
+/*!
+ * Converts binary value to dname pointer.
+ *
+ * \param[in] data Binary value to to transform.
+ *
+ * \return Dname pointer.
+ */
+inline static const knot_dname_t* yp_dname(
+	const uint8_t *data)
+{
+	return (const knot_dname_t *)data;
+}
+
+/*!
+ * Converts binary value to data pointer.
+ *
+ * \param[in] data Binary value to to transform.
+ *
+ * \return Data pointer.
+ */
+inline static const uint8_t* yp_data(
+	const uint8_t *data)
+{
+	return data;
+}
+
+/*! @} */
-- 
GitLab