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