From 94cdf36635e59bc32981781d4c8a724426d10a9b Mon Sep 17 00:00:00 2001 From: Jeroen Koekkoek Date: Thu, 14 Dec 2023 17:26:02 +0100 Subject: [PATCH] Simplify parser interface and code Exposing type and field information makes little sense. Applications are required to interpret wire representation for handling queries. Unknown types can simply be handled generically. Removing the information simplifies the interface and code. Fixes #98. --- .github/workflows/build-test.yml | 2 +- CMakeLists.txt | 7 +- include/zone.h | 230 +- include/zone/attributes.h | 18 - scripts/algorithm-hash.c | 78 + scripts/certificate-hash.c | 72 + src/attributes.h | 40 + src/bench.c | 69 +- src/diagnostic.h | 2 +- src/fallback/base16.h | 191 -- src/fallback/bench.c | 10 +- src/fallback/bits.h | 8 +- src/fallback/eui.h | 111 - src/fallback/name.h | 59 +- src/fallback/nsap.h | 74 - src/fallback/parser.c | 31 +- src/fallback/scanner.h | 200 +- src/fallback/text.h | 131 +- src/fallback/type.h | 409 ---- src/generic/apl.h | 2 +- src/generic/base16.h | 310 +++ src/{fallback => generic}/base32.h | 50 +- src/{fallback => generic}/base64.h | 120 +- src/generic/caa.h | 112 +- src/generic/cert.h | 132 ++ src/generic/dnssec.h | 179 ++ src/{fallback => generic}/endian.h.in | 0 src/generic/eui.h | 67 + src/generic/format.h | 364 +++ src/generic/gpos.h | 78 +- src/generic/ilnp64.h | 28 +- src/{fallback => generic}/ip4.h | 34 +- src/generic/ip6.h | 30 +- src/generic/loc.h | 20 +- src/generic/name.h | 75 +- src/generic/nsap.h | 63 + src/generic/nsec.h | 90 +- src/generic/number.h | 231 +- src/generic/nxt.h | 56 +- src/generic/parser.h | 957 ++++++++ src/generic/scanner.h | 199 +- src/generic/svcb.h | 526 +++-- src/generic/text.h | 139 +- src/{fallback => generic}/time.h | 40 +- src/generic/ttl.h | 168 +- src/generic/type.h | 202 ++ src/generic/types.h | 3151 +++++++++++++++++++++++++ src/generic/wks.h | 8 +- src/haswell/base32.h | 31 +- src/haswell/bench.c | 8 +- src/haswell/parser.c | 25 +- src/haswell/simd.h | 36 +- src/lexer.h | 296 --- src/log.c | 76 - src/log.h | 52 - src/parser.h | 321 --- src/table.h | 44 - src/types.h | 3087 ------------------------ src/westmere/base32.h | 31 +- src/westmere/bench.c | 8 +- src/westmere/ip4.h | 31 +- src/westmere/parser.c | 25 +- src/westmere/simd.h | 40 +- src/westmere/time.h | 34 +- src/westmere/type.h | 347 +-- src/zone.c | 143 +- tests/base32.c | 7 +- tests/include.c | 4 +- tests/ip4.c | 4 +- tests/svcb.c | 4 +- tests/time.c | 6 +- tests/types.c | 4 +- 72 files changed, 7049 insertions(+), 6788 deletions(-) create mode 100644 scripts/algorithm-hash.c create mode 100644 scripts/certificate-hash.c create mode 100644 src/attributes.h delete mode 100644 src/fallback/base16.h delete mode 100644 src/fallback/eui.h delete mode 100644 src/fallback/nsap.h delete mode 100644 src/fallback/type.h create mode 100644 src/generic/base16.h rename src/{fallback => generic}/base32.h (67%) rename src/{fallback => generic}/base64.h (94%) create mode 100644 src/generic/cert.h create mode 100644 src/generic/dnssec.h rename src/{fallback => generic}/endian.h.in (100%) create mode 100644 src/generic/eui.h create mode 100644 src/generic/format.h rename src/{fallback => generic}/ip4.h (70%) create mode 100644 src/generic/nsap.h create mode 100644 src/generic/parser.h rename src/{fallback => generic}/time.h (66%) create mode 100644 src/generic/type.h create mode 100644 src/generic/types.h delete mode 100644 src/lexer.h delete mode 100644 src/log.c delete mode 100644 src/log.h delete mode 100644 src/parser.h delete mode 100644 src/table.h delete mode 100644 src/types.h diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 07e6905..ed6fd29 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -87,4 +87,4 @@ jobs: set -e -x cd build ctest -j 4 --output-on-failure -T test -C ${BUILD_TYPE:-RelWithDebInfo} - ZONE_TARGET=fallback ctest -j 4 --output-on-failure -T test -C ${BUILD_TYPE:-RelWithDebInfo} + ZONE_KERNEL=fallback ctest -j 4 --output-on-failure -T test -C ${BUILD_TYPE:-RelWithDebInfo} diff --git a/CMakeLists.txt b/CMakeLists.txt index e7b51ab..eb4d0aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -145,9 +145,7 @@ target_include_directories( $) target_sources(zone PRIVATE - src/zone.c - src/log.c - src/fallback/parser.c) + src/zone.c src/fallback/parser.c) add_executable(zone-bench src/bench.c src/fallback/bench.c) target_include_directories( @@ -227,7 +225,8 @@ foreach(match ${matches}) endforeach() file(GENERATE OUTPUT config.h CONTENT "${template}") -configure_file(src/fallback/endian.h.in fallback/endian.h @ONLY) +# FIXME: we'll need to port this over to autoconf as well +configure_file(src/generic/endian.h.in generic/endian.h @ONLY) if(BUILD_TESTING) diff --git a/include/zone.h b/include/zone.h index 850aab4..4fdd548 100644 --- a/include/zone.h +++ b/include/zone.h @@ -1,5 +1,5 @@ /* - * zone.h -- (DNS) zone parser + * zone.h -- (DNS) presentation format parser * * Copyright (c) 2022-2023, NLnet Labs. All rights reserved. * @@ -40,6 +40,8 @@ extern "C" { #define ZONE_CH (3u) /** Hesiod @rfc{1035} */ #define ZONE_HS (4u) +/** Any (QCLASS) @rfc{1035} */ +#define ZONE_ANY (255u) /** @} */ /** @@ -133,11 +135,11 @@ extern "C" { #define ZONE_NSEC (47u) /** DNS Public Key @rfc{4034} @rfc{3755} */ #define ZONE_DNSKEY (48u) -/** DHCID [RFC4701] */ +/** DHCID @rfc{4701} */ #define ZONE_DHCID (49u) -/** NSEC3 [RFC5155] */ +/** NSEC3 @rfc{5155} */ #define ZONE_NSEC3 (50u) -/** NSEC3PARAM [RFC5155] */ +/** NSEC3PARAM @rfc{5155} */ #define ZONE_NSEC3PARAM (51u) /** TLSA @rfc{6698} */ #define ZONE_TLSA (52u) @@ -183,156 +185,9 @@ extern "C" { #define ZONE_DLV (32769u) /** @} */ -typedef struct zone_string zone_string_t; -struct zone_string { - size_t length; - const char *data; -}; - -// FIXME: probably best to rename this to mnemonic to stay with DNS terminology? -typedef struct zone_symbol zone_symbol_t; -struct zone_symbol { - struct { - char data[24]; // zero padded for convenient vectorized comparison - size_t length; - } key; - uint32_t value; -}; - -typedef struct zone_table zone_table_t; -struct zone_table { - size_t length; - const zone_symbol_t *symbols; // sorted for use with bsearch -}; - -/** - * @brief Type of value defined by field - * - * Fields are defined by their binary representation, NOT their textual - * representation. e.g. time-to-live and timestamp fields are encoded as - * 32-bit integers on the wire. @ref type_qualifiers are used to complement - * the type information. e.g. @ref ZONE_TTL and @ref ZONE_TIME can be used to - * provide extra information regarding the aforementioned types. - */ -typedef enum { - ZONE_INT8, - ZONE_INT16, - ZONE_INT32, - ZONE_IP4, - ZONE_IP6, - ZONE_NAME, - ZONE_STRING, - // (B)inary (L)arge (Ob)ject. Inspired by relational database terminology. - // Must be last. - ZONE_BLOB, - ZONE_ILNP64, - // hex fields - // ZONE_EUI48 (ZONE_HEX6?) - // ZONE_EUI64 (ZONE_HEX8?) - // miscellaneous fields - ZONE_SVC_PARAM, /**< SVCB service parameter */ - ZONE_TYPE_BITMAP, /**< NSEC type bitmap */ - ZONE_SERVICE_BITMAP /**< WKS service bitmap */ -} zone_type_t; - -/** - * @defgroup type_qualifiers Type qualifiers - * - * Type qualifiers provide additional information for RDATA fields. Types - * indicate the binary representation of an RDATA field, qualifier(s) can be - * used to communicate semantics. e.g. a time-to-live is presented on the - * wire as a 32-bit integer, ZONE_TTL can be used to signal the field - * represents a time-to-live value. - * - * @note Some types allow for more than one qualifier to be specified, hence - * each qualifier is assigned a separate bit. - * - * @{ - */ -/** - * @brief Type code (#ZONE_INT16) - * - * Type codes may appear in text by name or generic type notation @rfc{3597}. - */ -#define ZONE_TYPE (1u << 0) -/** - * @brief Class code (#ZONE_INT16) - * - * Class codes may appear in text by name or generic class notation @rfc{3597}. - */ -#define ZONE_CLASS (1u << 1) -/** - * @brief Time-to-live (TTL) (#ZONE_INT32) - * - * Time-to-live values may appear in text as numeric value (seconds) or in - * "1h2m3s" notation (@e extension). - */ -#define ZONE_TTL (1u << 2) -/** - * @brief Timestamp (#ZONE_INT32) - * - * Timestamps must be presented in text in "YYYYMMDDHHmmSS" notation. - */ -#define ZONE_TIME (1u << 3) -/** @brief Text representation is base16 (#ZONE_STRING or #ZONE_BLOB) */ -#define ZONE_BASE16 (1u << 4) -/** @brief Text representation is base32 (#ZONE_BLOB) */ -#define ZONE_BASE32 (1u << 5) -/** @brief Text representation is base64 (#ZONE_BLOB) */ -#define ZONE_BASE64 (1u << 6) -/** @brief Name is compressed (#ZONE_NAME) */ -#define ZONE_COMPRESSED (1u << 7) -/** @brief Name represents a mailbox (#ZONE_NAME) */ -#define ZONE_MAILBOX (1u << 8) -/** @brief Name is converted to lower case for DNSSEC validation (#ZONE_NAME) */ -#define ZONE_LOWER_CASE (1u << 9) -/** @brief Optional (#ZONE_NAME) */ -#define ZONE_OPTIONAL (1u << 10) -/** - * @brief May occur multiple times (#ZONE_STRING or #ZONE_SVC_PARAM) - * - * Field may occur multiple times. e.g. #ZONE_STRING in #ZONE_TXT or - * #ZONE_SVC_PARAM in #ZONE_SVCB. Sequences must be the last field in the - * record. - */ -#define ZONE_SEQUENCE (1u << 11) - -#define ZONE_CAA_TAG (1u << 12) -/** @} */ - -typedef struct zone_field_info zone_field_info_t; -struct zone_field_info { - zone_string_t name; - uint32_t type; - uint32_t qualifiers; - zone_table_t symbols; -}; - -/** - * @defgroup options Type options - * @brief Options for record types - * - * @{ - */ -#define ZONE_ANY (1<<2) -#define ZONE_EXPERIMENTAL (1<<3) -#define ZONE_OBSOLETE (1<<4) -/** @} */ - -typedef struct zone_type_info zone_type_info_t; -struct zone_type_info { - zone_symbol_t name; - uint32_t options; - struct { - size_t length; - const zone_field_info_t *fields; - } rdata; -}; - #define ZONE_BLOCK_SIZE (64) #define ZONE_WINDOW_SIZE (256 * ZONE_BLOCK_SIZE) // 16KB - // tape capacity must be large enough to hold every token from a single // worst-case read (e.g. 64 consecutive line feeds). in practice a single // block will never contain 64 tokens, therefore, to optimize throughput, @@ -350,10 +205,10 @@ struct zone_name_buffer { uint8_t octets[ ZONE_NAME_SIZE + ZONE_PADDING_SIZE ]; }; +// FIXME: explain need for NSEC padding typedef struct zone_rdata_buffer zone_rdata_buffer_t; struct zone_rdata_buffer { - size_t length; /**< Length of RDATA stored in buffer */ - uint8_t octets[ ZONE_RDATA_SIZE + 4096 /* nsec padding */ ]; + uint8_t octets[ ZONE_RDATA_SIZE + 4096 /* NSEC padding */ ]; }; // @private @@ -425,9 +280,6 @@ struct zone_parser; typedef void(*zone_log_t)( zone_parser_t *, - const char *, // file - size_t, // line - const char *, // function uint32_t, // category const char *, // message void *); // user data @@ -435,42 +287,23 @@ typedef void(*zone_log_t)( /** * @brief Write error message to active log handler. * - * @note Direct use is discouraged. Use of #ZONE_LOG instead. + * The zone parser operates on a per-record base and therefore cannot detect + * errors that span records. e.g. SOA records being specified more than once. + * The user may print a message using the active log handler, keeping the + * error message format consistent. * * @param[in] parser Zone parser - * @param[in] file Name of source file - * @param[in] line Line number in source file - * @param[in] function Name of function * @param[in] category Log category * @param[in] format Format string compatible with printf * @param[in] ... Variadic arguments corresponding to #format */ ZONE_EXPORT void zone_log( zone_parser_t *parser, - const char *file, - size_t line, - const char *function, uint32_t category, const char *format, ...) -zone_nonnull((1,2,4,6)) -zone_format_printf(6,7); - -/** - * @brief Write log message to active log handler. - * - * The zone parser operates on a per-record base and therefore cannot detect - * errors that span records. e.g. SOA records being specified more than once. - * The user may print a message using the active log handler, keeping the - * error message format consistent. - * - * @param[in] parser Zone parser - * @param[in] category Log category - * @param[in] format Format string compatible with printf - * @param[in] ... Variadic arguments corresponding to @ref format - */ -#define ZONE_LOG(parser, category, ...) \ - zone_log(parser, __FILE__, __LINE__, __func__, category, __VA_ARGS__) +zone_nonnull((1,3)) +zone_format_printf(3,4); typedef struct zone_name zone_name_t; struct zone_name { @@ -478,12 +311,11 @@ struct zone_name { uint8_t *octets; }; -// invoked for each record (host order). header (owner, type, class and ttl) +// invoked for each resource record (host order). header (owner, type, class and ttl) // fields are passed individually for convenience. rdata fields can be visited // individually by means of the iterator -typedef int32_t(*zone_add_t)( +typedef int32_t(*zone_accept_t)( zone_parser_t *, - const zone_type_info_t *, // type information const zone_name_t *, // owner (length + octets) uint16_t, // type uint16_t, // class @@ -493,16 +325,17 @@ typedef int32_t(*zone_add_t)( void *); // user data typedef struct { - /** Lax mode of operation. */ + /** Non-strict mode of operation. */ /** Authoritative servers may choose to be more lenient when operating as - as a secondary as data may have been transferred over AXFR/IXFR that + a secondary as data may have been transferred over AXFR/IXFR that would have triggered an error otherwise. */ - bool secondary; + bool non_strict; /** Disable $INCLUDE directive. */ /** Useful in setups where untrusted input may be offered. */ bool no_includes; /** Enable 1h2m3s notations for TTLS. */ bool pretty_ttls; + // FIXME: require origin to be in wire format? (#115) const char *origin; uint32_t default_ttl; uint16_t default_class; @@ -512,13 +345,10 @@ typedef struct { custom callback was specified. */ uint32_t categories; /** Callback used to write out log messages. */ - zone_log_t write; + zone_log_t callback; } log; struct { - zone_add_t add; - // FIXME: more callbacks to be added at a later stage to support efficient - // (de)serialization of AXFR/IXFR in text representation. - //zone_delete_t remove; + zone_accept_t callback; } accept; } zone_options_t; @@ -568,21 +398,21 @@ struct zone_parser { /** Success */ #define ZONE_SUCCESS (0) /** Syntax error */ -#define ZONE_SYNTAX_ERROR (-1) +#define ZONE_SYNTAX_ERROR (-256) // (-1 << 8) /** Semantic error */ -#define ZONE_SEMANTIC_ERROR (-2) +#define ZONE_SEMANTIC_ERROR (-512) // (-2 << 8) /** Operation failed due to lack of memory */ -#define ZONE_OUT_OF_MEMORY (-3) +#define ZONE_OUT_OF_MEMORY (-768) // (-3 << 8) /** Bad parameter value */ -#define ZONE_BAD_PARAMETER (-4) +#define ZONE_BAD_PARAMETER (-1024) // (-4 << 8) /** Error reading zone file */ -#define ZONE_IO_ERROR (-5) +#define ZONE_READ_ERROR (-1280) // (-5 << 8) /** Control directive or support for record type is not implemented */ -#define ZONE_NOT_IMPLEMENTED (-6) +#define ZONE_NOT_IMPLEMENTED (-1536) // (-6 << 8) /** Specified file does not exist */ -#define ZONE_NOT_A_FILE (-6) +#define ZONE_NOT_A_FILE (-1792) // (-7 << 8) /** Access to specified file is not allowed */ -#define ZONE_NOT_PERMITTED (-7) +#define ZONE_NOT_PERMITTED (-2048) // (-8 << 8) /** @} */ /** diff --git a/include/zone/attributes.h b/include/zone/attributes.h index 03fd5b2..0a2c110 100644 --- a/include/zone/attributes.h +++ b/include/zone/attributes.h @@ -43,27 +43,9 @@ #define zone_nonnull_all zone_attribute((__nonnull__)) #if _MSC_VER -# define zone_really_inline __forceinline -# define zone_never_inline __declspec(noinline) -# define zone_warn_unused_result - -# define zone_likely(params) (params) -# define zone_unlikely(params) (params) - # define zone_format(params) # define zone_format_printf(string_index, first_to_check) #else // _MSC_VER -# define zone_really_inline inline zone_attribute((always_inline)) -# define zone_never_inline zone_attribute((noinline)) -# if zone_has_attribute(warn_unused_result) -# define zone_warn_unused_result zone_attribute((warn_unused_result)) -# else -# define zone_warn_unused_result -# endif - -# define zone_likely(params) __builtin_expect(!!(params), 1) -# define zone_unlikely(params) __builtin_expect(!!(params), 0) - # if zone_has_attribute(format) # define zone_format(params) zone_attribute((__format__ params)) # if __MINGW32__ diff --git a/scripts/algorithm-hash.c b/scripts/algorithm-hash.c new file mode 100644 index 0000000..aac1063 --- /dev/null +++ b/scripts/algorithm-hash.c @@ -0,0 +1,78 @@ +/* + * hash.c -- Calculate perfect hash for DNSSEC algorithms + * + * Copyright (c) 2023, NLnet Labs. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include +#include +#include +#include +#include + +typedef struct tuple tuple_t; +struct tuple { + char name[24]; + uint8_t code; +}; + +// // https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml +static const tuple_t algorithms[] = { + { "RSAMD5", 1 }, + { "DH", 2 }, + { "DSA", 3 }, + { "ECC", 4 }, + { "RSASHA1", 5 }, + { "DSA-NSEC-SHA1", 6 }, + { "RSASHA1-NSEC3-SHA1", 7 }, + { "RSASHA256", 8 }, + { "RSASHA512", 10 }, + { "ECC-GOST", 12 }, + { "ECDSAP256SHA256", 13 }, + { "ECDSAP384SHA384", 14 }, + { "INDIRECT", 252 }, + { "PRIVATEDNS", 253 }, + { "PRIVATEOID", 254 } +}; + +const uint64_t original_magic = 29874llu; + +static uint8_t hash(uint64_t magic, uint64_t value) +{ + uint32_t value32 = ((value >> 32) ^ value); + return (value32 * magic) >> 32; +} + +int main(int argc, char *argv[]) +{ + const size_t n = sizeof(algorithms)/sizeof(algorithms[0]); + for (uint64_t magic = original_magic; magic < UINT64_MAX; magic++) { + size_t i; + uint16_t keys[256] = { 0 }; + for (i=0; i < n; i++) { + uint64_t value; + memcpy(&value, algorithms[i].name, 8); + + uint8_t key = hash(magic, value); + if (keys[key & 0xf]) + break; + keys[key & 0xf] = 1; + } + + if (i == n) { + printf("i: %zu, magic: %" PRIu64 "\n", i, magic); + for (i=0; i < n; i++) { + uint64_t value; + memcpy(&value, algorithms[i].name, 8); + uint8_t key = hash(magic, value); + printf("%s: %" PRIu8 " (%" PRIu16 ")\n", algorithms[i].name, key & 0xf, algorithms[i].code); + } + //print_table(magic); + return 0; + } + } + + printf("no magic value\n"); + return 1; +} diff --git a/scripts/certificate-hash.c b/scripts/certificate-hash.c new file mode 100644 index 0000000..2cfcfdc --- /dev/null +++ b/scripts/certificate-hash.c @@ -0,0 +1,72 @@ +/* + * certificate-hash.c -- Calculate perfect hash for certificate algorithms + * + * Copyright (c) 2023, NLnet Labs. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include +#include +#include +#include +#include + +typedef struct tuple tuple_t; +struct tuple { + char name[8]; + uint8_t code; +}; +i +// https://www.iana.org/assignments/cert-rr-types/cert-rr-types.xhtml +static const tuple_t algorithms[] = { + { "PKIX", 1 }, + { "SPKI", 2 }, + { "PGP", 3 }, + { "IPKIX", 4 }, + { "ISPKI", 5 }, + { "IPGP", 6 }, + { "ACPKIX", 7 }, + { "IACPKIX", 8 }, + { "OID", 254 }, + { "URI", 253 } +}; + +const uint64_t original_magic = 98112llu; + +static uint8_t hash(uint64_t magic, uint64_t value) +{ + uint32_t value32 = ((value >> 32) ^ value); + return (value32 * magic) >> 32; +} + +int main(int argc, char *argv[]) +{ + const size_t n = sizeof(algorithms)/sizeof(algorithms[0]); + for (uint64_t magic = original_magic; magic < UINT64_MAX; magic++) { + size_t i; + uint16_t keys[256] = { 0 }; + for (i=0; i < n; i++) { + uint64_t value; + memcpy(&value, algorithms[i].name, 8); + + uint8_t key = hash(magic, value); + if (keys[key & 0xf]) + break; + keys[key & 0xf] = 1; + } + + if (i == n) { + printf("i: %zu, magic: %" PRIu64 "\n", i, magic); + for (i=0; i < n; i++) { + uint64_t value; + memcpy(&value, algorithms[i].name, 8); + uint8_t key = hash(magic, value); + printf("%s: %" PRIu8 " (%" PRIu16 ")\n", algorithms[i].name, key & 0xf, algorithms[i].code); + } + return 0; + } + } + + printf("no magic value\n"); + return 1; +} diff --git a/src/attributes.h b/src/attributes.h new file mode 100644 index 0000000..358adf9 --- /dev/null +++ b/src/attributes.h @@ -0,0 +1,40 @@ +/* + * attributes.h -- internal compiler attribute abstractions + * + * Copyright (c) 2023, NLnet Labs. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ +#include "zone/attributes.h" + +#ifndef ATTRIBUTES_H +#define ATTRIBUTES_H + +#define nonnull(params) zone_nonnull(params) +#define nonnull_all zone_nonnull_all + +#if _MSC_VER +# define really_inline __forceinline +# define never_inline __declspec(noinline) +# define warn_unused_result + +# define likely(params) (params) +# define unlikely(params) (params) + +# define zone_format(params) +# define zone_format_printf(string_index, first_to_check) +#else // _MSC_VER +# define really_inline inline zone_attribute((always_inline)) +# define never_inline zone_attribute((noinline)) +# if zone_has_attribute(warn_unused_result) +# define warn_unused_result zone_attribute((warn_unused_result)) +# else +# define warn_unused_result +# endif + +# define likely(params) __builtin_expect(!!(params), 1) +# define unlikely(params) __builtin_expect(!!(params), 0) +#endif + +#endif // ATTRIBUTES_H diff --git a/src/bench.c b/src/bench.c index ea4e9e6..f0f1dab 100644 --- a/src/bench.c +++ b/src/bench.c @@ -1,5 +1,5 @@ /* - * bench.c -- some useful comment + * bench.c -- simple scanner/parser benchmarking tool * * Copyright (c) 2023, NLnet Labs. All rights reserved. * @@ -20,6 +20,7 @@ #include "zone.h" #include "config.h" #include "isadetection.h" +#include "attributes.h" #include "diagnostic.h" #if _WIN32 @@ -27,6 +28,8 @@ #define strncasecmp(s1, s2, n) _strnicmp(s1, s2, n) #endif +typedef zone_parser_t parser_t; + #if HAVE_HASWELL extern int32_t zone_bench_haswell_lex(zone_parser_t *, size_t *); extern int32_t zone_haswell_parse(zone_parser_t *); @@ -40,15 +43,15 @@ extern int32_t zone_westmere_parse(zone_parser_t *); extern int32_t zone_bench_fallback_lex(zone_parser_t *, size_t *); extern int32_t zone_fallback_parse(zone_parser_t *); -typedef struct target target_t; -struct target { +typedef struct kernel kernel_t; +struct kernel { const char *name; uint32_t instruction_set; int32_t (*bench_lex)(zone_parser_t *, size_t *); int32_t (*parse)(zone_parser_t *); }; -static const target_t targets[] = { +static const kernel_t kernels[] = { #if HAVE_HASWELL { "haswell", AVX2, &zone_bench_haswell_lex, &zone_haswell_parse }, #endif @@ -68,12 +71,12 @@ extern int32_t zone_open( extern void zone_close( zone_parser_t *); -static int32_t bench_lex(zone_parser_t *parser, const target_t *target) +static int32_t bench_lex(zone_parser_t *parser, const kernel_t *kernel) { size_t tokens = 0; int32_t result; - if ((result = target->bench_lex(parser, &tokens)) < 0) + if ((result = kernel->bench_lex(parser, &tokens)) < 0) return result; printf("Lexed %zu tokens\n", tokens); @@ -81,8 +84,7 @@ static int32_t bench_lex(zone_parser_t *parser, const target_t *target) } static int32_t bench_accept( - zone_parser_t *parser, - const zone_type_info_t *info, + parser_t *parser, const zone_name_t *owner, uint16_t type, uint16_t class, @@ -92,7 +94,6 @@ static int32_t bench_accept( void *user_data) { (void)parser; - (void)info; (void)owner; (void)type; (void)class; @@ -103,13 +104,13 @@ static int32_t bench_accept( return ZONE_SUCCESS; } -static int32_t bench_parse(zone_parser_t *parser, const target_t *target) +static int32_t bench_parse(zone_parser_t *parser, const kernel_t *kernel) { size_t records = 0; int32_t result; parser->user_data = &records; - result = target->parse(parser); + result = kernel->parse(parser); printf("Parsed %zu records\n", records); return result; @@ -118,32 +119,32 @@ static int32_t bench_parse(zone_parser_t *parser, const target_t *target) diagnostic_push() msvc_diagnostic_ignored(4996) -static const target_t *select_target(const char *name) +static const kernel_t *select_kernel(const char *name) { - const size_t n = sizeof(targets)/sizeof(targets[0]); + const size_t n = sizeof(kernels)/sizeof(kernels[0]); const uint32_t supported = detect_supported_architectures(); - const target_t *target = NULL; + const kernel_t *kernel = NULL; - if ((!name || !*name) && !(name = getenv("ZONE_TARGET"))) { - for (size_t i=0; !target && i < n; i++) { - if (targets[i].instruction_set & supported) - target = &targets[i]; + if ((!name || !*name) && !(name = getenv("ZONE_KERNEL"))) { + for (size_t i=0; !kernel && i < n; i++) { + if (kernel[i].instruction_set & supported) + kernel = &kernels[i]; } - assert(target != NULL); + assert(kernel != NULL); } else { - for (size_t i=0; !target && i < n; i++) { - if (strcasecmp(name, targets[i].name) == 0) - target = &targets[i]; + for (size_t i=0; !kernel && i < n; i++) { + if (strcasecmp(name, kernels[i].name) == 0) + kernel = &kernels[i]; } - if (!target || (target->instruction_set && !(target->instruction_set & supported))) { + if (!kernel || (kernel->instruction_set && !(kernel->instruction_set & supported))) { fprintf(stderr, "Target %s is unavailable\n", name); return NULL; } } - printf("Selected target %s\n", target->name); - return target; + printf("Selected target %s\n", kernel->name); + return kernel; } diagnostic_pop() @@ -157,12 +158,12 @@ static void help(const char *program) " -h Display available options.\n" " -t target Select target (default:%s)\n" "\n" - "Targets:\n"; + "Kernels:\n"; - printf(format, program, targets[0].name); + printf(format, program, kernels[0].name); - for (size_t i=0, n=sizeof(targets)/sizeof(targets[0]); i < n; i++) - printf(" %s\n", targets[i].name); + for (size_t i=0, n=sizeof(kernels)/sizeof(kernels[0]); i < n; i++) + printf(" %s\n", kernels[i].name); } static void usage(const char *program) @@ -195,7 +196,7 @@ int main(int argc, char *argv[]) if (optind > argc || argc - optind < 2) usage(program); - int32_t (*bench)(zone_parser_t *, const target_t *) = 0; + int32_t (*bench)(zone_parser_t *, const kernel_t *) = 0; if (strcasecmp(argv[optind], "lex") == 0) bench = &bench_lex; else if (strcasecmp(argv[optind], "parse") == 0) @@ -203,8 +204,8 @@ int main(int argc, char *argv[]) else usage(program); - const target_t *target; - if (!(target = select_target(name))) + const kernel_t *kernel; + if (!(kernel = select_kernel(name))) exit(EXIT_FAILURE); zone_parser_t parser = { 0 }; @@ -213,14 +214,14 @@ int main(int argc, char *argv[]) zone_rdata_buffer_t rdata; zone_buffers_t buffers = { 1, &owner, &rdata }; - options.accept.add = &bench_accept; + options.accept.callback = &bench_accept; options.origin = "."; options.default_ttl = 3600; options.default_class = ZONE_IN; if (zone_open(&parser, &options, &buffers, argv[argc-1], NULL) < 0) exit(EXIT_FAILURE); - if (bench(&parser, target) < 0) + if (bench(&parser, kernel) < 0) exit(EXIT_FAILURE); zone_close(&parser); diff --git a/src/diagnostic.h b/src/diagnostic.h index feb84e5..026eacb 100644 --- a/src/diagnostic.h +++ b/src/diagnostic.h @@ -1,5 +1,5 @@ /* - * diagnostic.h -- some useful comment + * diagnostic.h -- compiler diagnostic abstractions * * Copyright (c) 2022-2023, NLnet Labs. All rights reserved. * diff --git a/src/fallback/base16.h b/src/fallback/base16.h deleted file mode 100644 index 6238c26..0000000 --- a/src/fallback/base16.h +++ /dev/null @@ -1,191 +0,0 @@ -/* - * base16.h -- some useful comment - * - * Copyright (c) 2022, NLnet Labs. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - * - */ -#ifndef BASE16_H -#define BASE16_H - -// hexadecimal characters decode to 0x00 - 0x0f -// delimiting characters decode to 0x80 -// non-delimiters, non-hexadecimal characters decode to 0x90 - -// eui48 and eui64 depend on this table too -static const uint8_t b16rmap[256] = { - // end-of-file (0x00) - 0x80, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // 0x00 - 0x07 - // tab (0x09), line feed (0x0a), carriage return (0x0d) - 0x90, 0x80, 0x80, 0x90, 0x90, 0x80, 0x90, 0x90, // 0x08 - 0x0f - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // 0x10 - 0x17 - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // 0x18 - 0x1f - // space (0x20), quote (0x22) - 0x80, 0x90, 0x80, 0x90, 0x90, 0x90, 0x90, 0x90, // 0x20 - 0x27 - // left paren (0x28), right paren (0x29) - 0x80, 0x80, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // 0x28 - 0x2f - // digits - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 0x30 - 0x37 - // semicolon (0x3b) - 0x08, 0x09, 0x90, 0x80, 0x90, 0x90, 0x90, 0x90, // 0x38 - 0x3f - // upper case - 0x90, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x90, // 0x40 - 0x47 - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // 0x48 - 0x4f - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // 0x50 - 0x57 - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // 0x58 - 0x5f - // lower case - 0x90, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x90, // 0x60 - 0x67 - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // 0x68 - 0x6f - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // 0x70 - 0x77 - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // 0x78 - 0x7f - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // 0x80 - 0x87 - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // 0x88 - 0x8f - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // 0x90 - 0x97 - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // 0x98 - 0x9f - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // 0xa0 - 0xa7 - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // 0xa8 - 0xaf - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // 0xb0 - 0xb7 - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // 0xb8 - 0xbf - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // 0xc0 - 0xc7 - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // 0xc8 - 0xcf - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // 0xd0 - 0xd7 - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // 0xd8 - 0xdf - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // 0xe0 - 0xe7 - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // 0xe8 - 0xef - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // 0xf0 - 0xf7 - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // 0xf8 - 0xff -}; - -// FIXME: RFC3597 section 5 states each word of data must contain an even -// number of hexadecimal digits. The same is not true for DS records. -// RFC4043 section 5.3 merely states whitespace is allowed within the -// hexadecimal text. Words containing an uneven number of hexadecimal -// digits are impractical, but supported (BIND supports uneven -// sequences) -zone_nonnull_all -static zone_really_inline int32_t parse_base16_sequence( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - token_t *token) -{ - int32_t r; - - if ((r = have_contiguous(parser, type, field, token)) < 0) - return r; - - uint8_t x0 = 0x80, x1 = 0x80; - uint8_t *w = &parser->rdata->octets[parser->rdata->length]; - const uint8_t *ws = w, *we = &parser->rdata->octets[ZONE_RDATA_SIZE]; - const char *p; - - do { - p = token->data; - if (zone_unlikely(!(x0 & 0x80))) { - x1 = b16rmap[(uint8_t)p[0]]; - if (x1 & 0x80) - SYNTAX_ERROR(parser, "Invalid %s in %s record", NAME(field), TNAME(type)); - w[0] = (uint8_t)((x0 << 4) | x1); - w += 1; p += 1; - } - - while (w < we) { - x0 = b16rmap[ (uint8_t)(p[0]) ]; - x1 = b16rmap[ (uint8_t)(p[1]) ]; - if ((x0 | x1) & 0x80) - break; - w[0] = (uint8_t)((x0 << 4) | x1); - w += 1; p += 2; - } - - if (x0 == 0x90 || (x0 != 0x80 && x1 != 0x80)) - SYNTAX_ERROR(parser, "Invalid %s in %s record", NAME(field), TNAME(type)); - - lex(parser, token); - } while (token->code == CONTIGUOUS && w < we); - - if (w - ws == 1 || w >= we || x0 != 0x80) - SYNTAX_ERROR(parser, "Invalid %s in %s record", NAME(field), TNAME(type)); - - if ((r = have_delimiter(parser, type, token)) < 0) - return r; - - parser->rdata->length += (size_t)(w - ws); - return ZONE_BLOB; -} - -zone_nonnull_all -static zone_really_inline int32_t parse_base16( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token) -{ - int32_t r; - - if ((r = have_contiguous(parser, type, field, token)) < 0) - return r; - - const char *p = token->data; - uint8_t x0 = 0x80, x1 = 0x80; - uint8_t *ws = &parser->rdata->octets[parser->rdata->length], *w = ws; - const uint8_t *we = &parser->rdata->octets[ZONE_RDATA_SIZE]; - - while (w < we) { - x0 = b16rmap[ (uint8_t)(p[0]) ]; - x1 = b16rmap[ (uint8_t)(p[1]) ]; - if ((x0 | x1) & 0x80) - break; - w[0] = (uint8_t)((x0 << 4) | x1); - w += 1; p += 2; - } - - if (w - ws == 1 || w >= we || x0 != 0x80) - SYNTAX_ERROR(parser, "Invalid %s in %s record", NAME(field), TNAME(type)); - - parser->rdata->length += (size_t)(w - ws); - return 0; -} - -zone_nonnull_all -static zone_really_inline int32_t parse_salt( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token) -{ - int32_t r; - - if ((r = have_contiguous(parser, type, field, token)) < 0) - return r; - - const char *p = token->data; - - if (p[0] == '-' && !is_contiguous((uint8_t)p[1])) { - parser->rdata->octets[parser->rdata->length++] = 0; - return ZONE_STRING; - } - - uint8_t x0 = 0x80, x1 = 0x80; - uint8_t *ws = &parser->rdata->octets[parser->rdata->length], *w = ws + 1; - const uint8_t *we = w + 255; - - while (w < we) { - x0 = b16rmap[ (uint8_t)(p[0]) ]; - x1 = b16rmap[ (uint8_t)(p[1]) ]; - if ((x0 | x1) & 0x80) - break; - w[0] = (uint8_t)((x0 << 4) | x1); - w += 1; p += 2; - } - - if (w - ws == 1 || w >= we || x0 != 0x80) - SYNTAX_ERROR(parser, "Invalid %s in %s record", NAME(field), TNAME(type)); - - *ws = (uint8_t)((w - ws) - 1); - parser->rdata->length += (size_t)(w - ws); - return ZONE_STRING; -} - -#endif // BASE16_H diff --git a/src/fallback/bench.c b/src/fallback/bench.c index 039fadb..c741a99 100644 --- a/src/fallback/bench.c +++ b/src/fallback/bench.c @@ -7,23 +7,23 @@ * */ #include "zone.h" +#include "attributes.h" #include "diagnostic.h" -#include "log.h" -#include "lexer.h" +#include "generic/parser.h" #include "fallback/scanner.h" diagnostic_push() clang_diagnostic_ignored(missing-prototypes) -int32_t zone_bench_fallback_lex(zone_parser_t *parser, size_t *tokens) +int32_t zone_bench_fallback_lex(parser_t *parser, size_t *tokens) { token_t token; (*tokens) = 0; - lex(parser, &token); + take(parser, &token); while (token.code > 0) { (*tokens)++; - lex(parser, &token); + take(parser, &token); } return token.code ? -1 : 0; diff --git a/src/fallback/bits.h b/src/fallback/bits.h index f7f174f..31857a5 100644 --- a/src/fallback/bits.h +++ b/src/fallback/bits.h @@ -12,7 +12,7 @@ #if _MSC_VER #include -static zone_really_inline uint64_t trailing_zeroes(uint64_t mask) +static really_inline uint64_t trailing_zeroes(uint64_t mask) { unsigned long index; if (_BitScanForward64(&index, mask)) @@ -21,7 +21,7 @@ static zone_really_inline uint64_t trailing_zeroes(uint64_t mask) return 64; } -static zone_really_inline uint64_t leading_zeroes(uint64_t mask) +static really_inline uint64_t leading_zeroes(uint64_t mask) { unsigned long index; if (_BitScanReverse64(&index, mask)) @@ -30,12 +30,12 @@ static zone_really_inline uint64_t leading_zeroes(uint64_t mask) return 64; } #else -static zone_really_inline uint64_t trailing_zeroes(uint64_t mask) +static really_inline uint64_t trailing_zeroes(uint64_t mask) { return (uint64_t)__builtin_ctzll(mask); } -static zone_really_inline uint64_t leading_zeroes(uint64_t mask) +static really_inline uint64_t leading_zeroes(uint64_t mask) { return (uint64_t)__builtin_clzll(mask); } diff --git a/src/fallback/eui.h b/src/fallback/eui.h deleted file mode 100644 index 97881a8..0000000 --- a/src/fallback/eui.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * eui48.h -- some useful comment - * - * Copyright (c) 2023, NLnet Labs. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - * - */ -#ifndef EUI_H -#define EUI_H - -// RFC7043 section 3.2: xx-xx-xx-xx-xx-xx -zone_nonnull_all -static zone_really_inline int32_t parse_eui48( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token) -{ - uint8_t c, x[12]; - uint8_t *w = parser->rdata->octets + parser->rdata->length; - const uint8_t *p = (uint8_t *)token->data; - - c = (p[ 2] == '-') & (p[ 5] == '-') & - (p[ 8] == '-') & (p[11] == '-') & - (p[14] == '-'); - if (!c) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - - x[ 0] = b16rmap[p[ 0]]; - x[ 1] = b16rmap[p[ 1]]; - x[ 2] = b16rmap[p[ 3]]; - x[ 3] = b16rmap[p[ 4]]; - x[ 4] = b16rmap[p[ 6]]; - x[ 5] = b16rmap[p[ 7]]; - x[ 6] = b16rmap[p[ 9]]; - x[ 7] = b16rmap[p[10]]; - x[ 8] = b16rmap[p[12]]; - x[ 9] = b16rmap[p[13]]; - x[10] = b16rmap[p[15]]; - x[11] = b16rmap[p[16]]; - - c = x[ 0] | x[ 1] | x[ 2] | x[ 3] | x[ 4] | x[ 5] | - x[ 6] | x[ 7] | x[ 8] | x[ 9] | x[10] | x[11]; - if (c & 0x80) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - - w[0] = (uint8_t)((x[ 0] << 4) | x[ 1]); - w[1] = (uint8_t)((x[ 2] << 4) | x[ 3]); - w[2] = (uint8_t)((x[ 4] << 4) | x[ 5]); - w[3] = (uint8_t)((x[ 6] << 4) | x[ 7]); - w[4] = (uint8_t)((x[ 8] << 4) | x[ 9]); - w[5] = (uint8_t)((x[10] << 4) | x[11]); - - parser->rdata->length += 6; - return ZONE_EUI48; -} - -// RFC7043 section 4.2, require xx-xx-xx-xx-xx-xx-xx-xx -zone_nonnull_all -static zone_really_inline int32_t parse_eui64( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token) -{ - uint8_t c, x[16]; - uint8_t *w = parser->rdata->octets + parser->rdata->length; - const uint8_t *p = (uint8_t *)token->data; - - c = (p[ 2] == '-') & (p[ 5] == '-') & (p[ 8] == '-') & (p[11] == '-') & - (p[14] == '-') & (p[17] == '-') & (p[20] == '-'); - if (!c) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - - x[ 0] = b16rmap[p[ 0]]; - x[ 1] = b16rmap[p[ 1]]; - x[ 2] = b16rmap[p[ 3]]; - x[ 3] = b16rmap[p[ 4]]; - x[ 4] = b16rmap[p[ 6]]; - x[ 5] = b16rmap[p[ 7]]; - x[ 6] = b16rmap[p[ 9]]; - x[ 7] = b16rmap[p[10]]; - x[ 8] = b16rmap[p[12]]; - x[ 9] = b16rmap[p[13]]; - x[10] = b16rmap[p[15]]; - x[11] = b16rmap[p[16]]; - x[12] = b16rmap[p[18]]; - x[13] = b16rmap[p[19]]; - x[14] = b16rmap[p[21]]; - x[15] = b16rmap[p[22]]; - - c = x[ 0] | x[ 1] | x[ 2] | x[ 3] | x[ 4] | x[ 5] | x[ 6] | x[ 7] | - x[ 8] | x[ 9] | x[10] | x[11] | x[12] | x[13] | x[14] | x[15]; - if (c & 0x80) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - - w[0] = (uint8_t)((x[ 0] << 4) | x[ 1]); - w[1] = (uint8_t)((x[ 2] << 4) | x[ 3]); - w[2] = (uint8_t)((x[ 4] << 4) | x[ 5]); - w[3] = (uint8_t)((x[ 6] << 4) | x[ 7]); - w[4] = (uint8_t)((x[ 8] << 4) | x[ 9]); - w[5] = (uint8_t)((x[10] << 4) | x[11]); - w[6] = (uint8_t)((x[12] << 4) | x[13]); - w[7] = (uint8_t)((x[14] << 4) | x[15]); - - parser->rdata->length += 8; - return ZONE_EUI64; -} - -#endif // EUI_H diff --git a/src/fallback/name.h b/src/fallback/name.h index 3d5c383..dbcc5ff 100644 --- a/src/fallback/name.h +++ b/src/fallback/name.h @@ -1,5 +1,5 @@ /* - * name.h -- some useful comment + * name.h -- domain name parser * * Copyright (c) 2022-2023, NLnet Labs. All rights reserved. * @@ -9,23 +9,21 @@ #ifndef NAME_H #define NAME_H -zone_nonnull_all -static zone_really_inline int32_t scan_name( - zone_parser_t *parser, - const token_t *token, +nonnull_all +static really_inline int32_t scan_name( + const char *data, + size_t length, uint8_t octets[255 + ZONE_BLOCK_SIZE], size_t *lengthp) { uint8_t *l = octets, *w = octets + 1; const uint8_t *we = octets + 255; - const char *t = token->data, *te = t + token->length; - - (void)parser; + const char *t = data, *te = t + length; l[0] = 0; if (*t == '.') - return (*lengthp = token->length) == 1 ? 0 : -1; + return (*lengthp = length) == 1 ? 0 : -1; while ((t < te) & (w < we)) { *w = (uint8_t)*t; @@ -57,47 +55,4 @@ static zone_really_inline int32_t scan_name( return *l != 0; } -zone_nonnull_all -static zone_really_inline int32_t parse_name( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token) -{ - int32_t r; - size_t n = 0; - uint8_t *o = &parser->rdata->octets[parser->rdata->length]; - - if (zone_likely(token->code == CONTIGUOUS)) { - // a freestanding "@" denotes the current origin - if (token->data[0] == '@' && token->length > 1) - goto relative; - r = scan_name(parser, token, o, &n); - if (r == 0) - return (void)(parser->rdata->length += n), ZONE_NAME; - if (r > 0) - goto relative; - } else if (token->code == QUOTED) { - if (token->length == 0) - goto invalid; - r = scan_name(parser, token, o, &n); - if (r == 0) - return (void)(parser->rdata->length += n), ZONE_NAME; - if (r > 0) - goto relative; - } else { - return have_string(parser, type, field, token); - } - -invalid: - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - -relative: - if (n > 255 - parser->file->origin.length) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - memcpy(o+n, parser->file->origin.octets, parser->file->origin.length); - parser->rdata->length += n + parser->file->origin.length; - return ZONE_NAME; -} - #endif // NAME_H diff --git a/src/fallback/nsap.h b/src/fallback/nsap.h deleted file mode 100644 index f2f21d6..0000000 --- a/src/fallback/nsap.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * nsap.h -- some useful comment - * - * Copyright (c) 2023, NLnet Labs. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - * - */ -#ifndef NSAP_H -#define NSAP_H - -// https://datatracker.ietf.org/doc/html/rfc1706 (historic) - -zone_nonnull_all -static zone_really_inline int32_t parse_nsap( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token) -{ - int32_t r; - - if ((r = have_contiguous(parser, type, field, token)) < 0) - return r; - - const char *p = token->data; - - // RFC1706 section 7 - // NSAP format is "0x" (i.e., a zero followed by an 'x' character) followed - // by a variable length string of hex characters (0 to 9, a to f). The hex - // string is case-insensitive. "."s (i.e., periods) may be inserted in the - // hex string anywhere after "0x" for readability. The "."s have no - // significance other than for readability and are not propagated in the - // protocol (e.g., queries or zone transfers). - - if (p[0] != '0' || (p[1] & 0xdf) != 'X') - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - - p += 2; - - uint8_t x0 = 0x80, x1 = 0x80; - uint8_t *w = &parser->rdata->octets[parser->rdata->length]; - const uint8_t *ws = w, *we = &parser->rdata->octets[ZONE_RDATA_SIZE]; - - while (w < we) { - x0 = b16rmap[(uint8_t)p[0]]; - x1 = b16rmap[(uint8_t)p[1]]; - if (!((x0 | x1) & 0x80)) { - w[0] = (uint8_t)((x0 << 4) | x1); - w += 1; p += 2; - } else { - while (p[0] == '.') - (void)(p += 1), x0 = b16rmap[(uint8_t)p[0]]; - if (x0 & 0x80) - break; - p += 1; - x1 = b16rmap[(uint8_t)p[0]]; - while (p[0] == '.') - (void)(p += 1), x1 = b16rmap[(uint8_t)p[0]]; - if (x1 == 0x90) - break; - w[0] = (uint8_t)((x0 << 4) | x1); - w += 1; p += 1; - } - } - - if (w == ws || w >= we || x0 != 0x80) - SYNTAX_ERROR(parser, "Invalid %s in %s record", NAME(field), TNAME(type)); - - parser->rdata->length += (size_t)(w - ws); - return ZONE_BLOB; -} - -#endif // NSAP_H diff --git a/src/fallback/parser.c b/src/fallback/parser.c index 1facd4f..e3fe2ae 100644 --- a/src/fallback/parser.c +++ b/src/fallback/parser.c @@ -7,42 +7,43 @@ * */ #include "zone.h" +#include "attributes.h" #include "diagnostic.h" -#include "log.h" -#include "fallback/endian.h" +#include "generic/endian.h" #include "fallback/bits.h" -#include "lexer.h" -#include "table.h" +#include "generic/parser.h" #include "fallback/scanner.h" #include "generic/number.h" #include "generic/ttl.h" -#include "fallback/time.h" +#include "generic/time.h" #include "fallback/text.h" #include "fallback/name.h" -#include "fallback/ip4.h" +#include "generic/ip4.h" #include "generic/ip6.h" -#include "fallback/base16.h" -#include "fallback/base32.h" -#include "fallback/base64.h" +#include "generic/base16.h" +#include "generic/base32.h" +#include "generic/base64.h" #include "generic/nsec.h" #include "generic/nxt.h" #include "generic/caa.h" #include "generic/ilnp64.h" -#include "fallback/eui.h" -#include "fallback/nsap.h" +#include "generic/eui.h" +#include "generic/nsap.h" #include "generic/wks.h" #include "generic/loc.h" #include "generic/gpos.h" #include "generic/apl.h" #include "generic/svcb.h" -#include "types.h" -#include "fallback/type.h" -#include "parser.h" +#include "generic/cert.h" +#include "generic/dnssec.h" +#include "generic/types.h" +#include "generic/type.h" +#include "generic/format.h" diagnostic_push() clang_diagnostic_ignored(missing-prototypes) -int32_t zone_fallback_parse(zone_parser_t *parser) +int32_t zone_fallback_parse(parser_t *parser) { return parse(parser); } diff --git a/src/fallback/scanner.h b/src/fallback/scanner.h index 9e3ddbd..3d88728 100644 --- a/src/fallback/scanner.h +++ b/src/fallback/scanner.h @@ -13,12 +13,12 @@ #include #include -zone_nonnull_all -static zone_really_inline const char *scan_comment( - zone_parser_t *parser, const char *start, const char *end) +nonnull_all +static really_inline const char *scan_comment( + parser_t *parser, const char *start, const char *end) { while (start < end) { - if (zone_unlikely(*start == '\n')) + if (unlikely(*start == '\n')) return start; start += 1; } @@ -27,9 +27,9 @@ static zone_really_inline const char *scan_comment( return end; } -zone_nonnull_all -static zone_really_inline const char *scan_quoted( - zone_parser_t *parser, const char *start, const char *end) +nonnull_all +static really_inline const char *scan_quoted( + parser_t *parser, const char *start, const char *end) { while (start < end) { if (*start == '\\') { @@ -52,13 +52,13 @@ static zone_really_inline const char *scan_quoted( return end; } -zone_nonnull_all -static zone_really_inline const char *scan_contiguous( - zone_parser_t *parser, const char *start, const char *end) +nonnull_all +static really_inline const char *scan_contiguous( + parser_t *parser, const char *start, const char *end) { while (start < end) { - if (zone_likely(is_contiguous((uint8_t)*start))) { - if (zone_likely(*start != '\\')) { + if (likely(classify[ (uint8_t)*start ] == CONTIGUOUS)) { + if (likely(*start != '\\')) { start += 1; } else { parser->file->lines.tail[0] += *(start + 1) == '\n'; @@ -76,9 +76,9 @@ static zone_really_inline const char *scan_contiguous( return end; } -zone_nonnull_all -static zone_really_inline void scan( - zone_parser_t *parser, const char *start, const char *end) +nonnull_all +static really_inline void scan( + parser_t *parser, const char *start, const char *end) { if (parser->file->state.is_escaped) { parser->file->state.is_escaped = 0; @@ -97,7 +97,7 @@ static zone_really_inline void scan( } while (start < end) { - const int32_t code = contiguous[(uint8_t)*start]; + const int32_t code = classify[(uint8_t)*start]; if (code == BLANK) { start++; } else if (code == CONTIGUOUS) { @@ -125,148 +125,44 @@ static zone_really_inline void scan( } } -zone_nonnull_all -static zone_never_inline void step(zone_parser_t *parser, token_t *token) +nonnull_all +warn_unused_result +static really_inline int32_t reindex(parser_t *parser) { - bool start_of_line = false; - const char *data_limit, **tape_limit; - - // start of line is initially true - if (parser->file->fields.tail == parser->file->fields.tape) - start_of_line = true; - else if (parser->file->fields.tail[-1][0] == '\n') - start_of_line = !is_blank((uint8_t)parser->file->fields.tail[-1][1]); - - // restore deferred line count - parser->file->lines.tape[0] = parser->file->lines.tail[0]; - parser->file->lines.head = parser->file->lines.tape; - parser->file->lines.tail = parser->file->lines.tape; - // restore (possibly) deferred field - parser->file->fields.tape[0] = parser->file->fields.tail[1]; - parser->file->fields.head = parser->file->fields.tape; - parser->file->fields.tail = parser->file->fields.tape; - if (parser->file->fields.tape[0]) - parser->file->fields.tail++; - // delimiters are never deferred - parser->file->delimiters.head = parser->file->delimiters.tape; - parser->file->delimiters.tail = parser->file->delimiters.tape; - -shuffle: - // refill if required - if (parser->file->end_of_file == ZONE_HAVE_DATA) { - int32_t code; - const char *data; - if (parser->file->fields.head[0]) - data = parser->file->fields.head[0]; - else - data = parser->file->buffer.data + parser->file->buffer.index; - parser->file->fields.head[0] = parser->file->buffer.data; - const size_t length = - (size_t)((parser->file->buffer.data+parser->file->buffer.length) - data); - const size_t index = - (size_t)((parser->file->buffer.data+parser->file->buffer.index) - data); - memmove(parser->file->buffer.data, data, length); - parser->file->buffer.length = length; - parser->file->buffer.index = index; - parser->file->buffer.data[length] = '\0'; - if ((code = refill(parser)) < 0) - DEFER_ERROR(parser, token, code); - } - - data_limit = parser->file->buffer.data + parser->file->buffer.length; - tape_limit = parser->file->fields.tape + ZONE_TAPE_SIZE; - for (;;) { - const char *data = parser->file->buffer.data + parser->file->buffer.index; - if (data_limit - data < ZONE_BLOCK_SIZE) - break; - if (tape_limit - parser->file->fields.tail < ZONE_BLOCK_SIZE) - goto terminate; - scan(parser, data, data + ZONE_BLOCK_SIZE); - parser->file->buffer.index += ZONE_BLOCK_SIZE; - } - - const size_t length = parser->file->buffer.length - parser->file->buffer.index; - assert(length <= ZONE_BLOCK_SIZE); - if (parser->file->end_of_file == ZONE_HAVE_DATA) - goto terminate; - if (length > (size_t)(tape_limit - parser->file->fields.tail)) - goto terminate; - - const char *data = &parser->file->buffer.data[parser->file->buffer.index]; - scan(parser, data, data + length); - parser->file->buffer.index += length; - parser->file->end_of_file = ZONE_NO_MORE_DATA; + assert(parser->file->buffer.index <= parser->file->buffer.length); + size_t left = parser->file->buffer.length - parser->file->buffer.index; + const char *data = parser->file->buffer.data + parser->file->buffer.index; + const char **tape = parser->file->fields.tail; + const char **tape_limit = parser->file->fields.tape + ZONE_TAPE_SIZE; + + if (left >= ZONE_BLOCK_SIZE) { + const char *data_limit = parser->file->buffer.data + + (parser->file->buffer.length - ZONE_BLOCK_SIZE); + while (data <= data_limit && ((uintptr_t)tape_limit - (uintptr_t)tape) >= ZONE_BLOCK_SIZE) { + scan(parser, data, data + ZONE_BLOCK_SIZE); + parser->file->buffer.index += ZONE_BLOCK_SIZE; + data += ZONE_BLOCK_SIZE; + tape = parser->file->fields.tail; + } -terminate: - // make sure tape contains no partial tokens - if (parser->file->end_of_file == ZONE_NO_MORE_DATA) { - parser->file->fields.tail[1] = NULL; - } else if (parser->file->state.follows_contiguous || parser->file->state.in_quoted) { - parser->file->fields.tail[0] = parser->file->fields.tail[-1]; - parser->file->fields.tail--; - } else { - parser->file->fields.tail[1] = NULL; + assert(parser->file->buffer.index <= parser->file->buffer.length); + left = parser->file->buffer.length - parser->file->buffer.index; } - parser->file->fields.tail[0] = data_limit; - parser->file->delimiters.tail[0] = data_limit; - if (parser->file->fields.head[0] == parser->file->buffer.data) - parser->file->start_of_line = start_of_line; - else - parser->file->start_of_line = false; - - for (;;) { - data = *parser->file->fields.head; - token->data = data; - token->code = (int32_t)contiguous[ (uint8_t)*data ]; - // end-of-file is idempotent - parser->file->fields.head += (*data != '\0'); - if (zone_likely(token->code == CONTIGUOUS)) { - const char *delimiter = *parser->file->delimiters.head++; - assert(delimiter > token->data); - token->length = (size_t)(delimiter - token->data); - return; - } else if (token->code == LINE_FEED) { - if (zone_unlikely(token->data == line_feed)) - parser->file->span += *parser->file->lines.head++; - parser->file->span++; - if (parser->file->grouped) - continue; - parser->file->line += parser->file->span; - parser->file->span = 0; - parser->file->start_of_line = !is_blank((uint8_t)*(token->data+1)); - return; - } else if (token->code == QUOTED) { - const char *delimiter = *parser->file->delimiters.head++; - token->data++; - assert(delimiter > token->data); - token->length = (size_t)(delimiter - token->data); - return; - } else if (token->code == END_OF_FILE) { - zone_file_t *file; - - if (parser->file->end_of_file != ZONE_NO_MORE_DATA) - goto shuffle; - if (parser->file->grouped) - DEFER_SYNTAX_ERROR(parser, token, "Missing closing brace"); - if (!parser->file->includer) - return; - file = parser->file; - parser->file = parser->file->includer; - parser->owner = &parser->file->owner; - zone_close_file(parser, file); - return; - } else if (token->code == LEFT_PAREN) { - if (parser->file->grouped) - DEFER_SYNTAX_ERROR(parser, token, "Nested opening brace"); - parser->file->grouped = true; - } else { - assert(token->code == RIGHT_PAREN); - if (!parser->file->grouped) - DEFER_SYNTAX_ERROR(parser, token, "Missing opening brace"); - parser->file->grouped = false; + // only scan partial blocks after reading all data + if (parser->file->end_of_file) { + assert(left < ZONE_BLOCK_SIZE); + if (!left) { + parser->file->end_of_file = ZONE_NO_MORE_DATA; + } else if (((uintptr_t)tape_limit - (uintptr_t)tape) >= left) { + scan(parser, data, data + left); + parser->file->end_of_file = ZONE_NO_MORE_DATA; + parser->file->buffer.index += left; + parser->file->state.follows_contiguous = 0; } } + + return (parser->file->state.follows_contiguous | parser->file->state.in_quoted) != 0; } #endif // SCANNER_H diff --git a/src/fallback/text.h b/src/fallback/text.h index 3509eee..6afa8f4 100644 --- a/src/fallback/text.h +++ b/src/fallback/text.h @@ -1,5 +1,5 @@ /* - * text.h -- fallback parser for strings + * text.h -- fallback string parser * * Copyright (c) 2022-2023, NLnet Labs. All rights reserved. * @@ -9,8 +9,8 @@ #ifndef TEXT_H #define TEXT_H -zone_nonnull_all -static zone_really_inline uint32_t unescape(const char *text, uint8_t *wire) +nonnull_all +static really_inline uint32_t unescape(const char *text, uint8_t *wire) { uint8_t d[3]; @@ -26,89 +26,78 @@ static zone_really_inline uint32_t unescape(const char *text, uint8_t *wire) } } -zone_nonnull_all -static zone_really_inline int32_t parse_string( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, +nonnull_all +static really_inline int32_t parse_text_inner( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, const token_t *token) { - uint8_t *w = &parser->rdata->octets[parser->rdata->length + 1]; - const uint8_t *ws = w - 1, *we = w + 255; - const char *t = token->data, *te = t + token->length; + uint32_t skip; + const char *data = token->data, *limit = token->data + token->length; - // FIXME: SWAR can possibly applied to improve performance and copy - // eight bytes as opposed to one - while ((t < te) & (w < we)) { - *w = (uint8_t)*t; - if (zone_unlikely(*t == '\\')) { - uint32_t o; - if (!(o = unescape(t, w))) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - w += 1; t += o; - } else { - w += 1; t += 1; + if ((uintptr_t)rdata->limit - (uintptr_t)rdata->octets >= token->length) { + while (data < limit) { + *rdata->octets = (uint8_t)*data; + if (likely(*data != '\\')) + (void)(rdata->octets += 1), data += 1; + else if (!(skip = unescape(data, rdata->octets))) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + else + (void)(rdata->octets += 1), data += skip; } - } - - if (t != te || w >= we) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - parser->rdata->octets[parser->rdata->length] = (uint8_t)((w - ws) - 1); - parser->rdata->length += (size_t)(w - ws); - return ZONE_STRING; -} - -zone_nonnull_all -static zone_really_inline int32_t parse_text_internal( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token) -{ - uint8_t *w = &parser->rdata->octets[parser->rdata->length]; - const uint8_t *ws = w, *we = &parser->rdata->octets[ZONE_RDATA_SIZE]; - const char *t = token->data, *te = t + token->length; - while ((t < te) & (w < we)) { - *w = (uint8_t)*t; - if (zone_unlikely(*t == '\\')) { - uint32_t o; - if (!(o = unescape(t, w))) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - w += 1; t += o; - } else { - w += 1; t += 1; + if (data != limit) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + return 0; + } else { + while (data < limit && rdata->octets < rdata->limit) { + *rdata->octets = (uint8_t)*data; + if (likely(*data != '\\')) + (void)(rdata->octets += 1), data += 1; + else if (!(skip = unescape(data, rdata->octets))) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + else + (void)(rdata->octets += 1), data += skip; } - } - if (t != te || w >= we) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - parser->rdata->length += (size_t)(w - ws); - return ZONE_BLOB; + if (data != limit || rdata->octets >= rdata->limit) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + return 0; + } } -zone_nonnull_all -static zone_really_inline int32_t parse_quoted_text( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, +nonnull_all +static really_inline int32_t parse_string( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, const token_t *token) { - if (zone_likely(token->code & QUOTED)) - return parse_text_internal(parser, type, field, token); - return have_quoted(parser, type, field, token); + int32_t code; + uint8_t *octets = rdata->octets; + uint8_t *limit = rdata->limit; + if ((uintptr_t)rdata->limit - (uintptr_t)rdata->octets > 1 + 255) + rdata->limit = rdata->octets + 1 + 255; + rdata->octets += 1; + + code = parse_text_inner(parser, type, field, rdata, token); + *octets = (uint8_t)((uintptr_t)rdata->octets - (uintptr_t)octets) - 1; + rdata->limit = limit; + return code; } -zone_nonnull_all -static zone_really_inline int32_t parse_text( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, +nonnull_all +static really_inline int32_t parse_text( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, const token_t *token) { - if (zone_likely(token->code & (CONTIGUOUS|QUOTED))) - return parse_text_internal(parser, type, field, token); - return have_string(parser, type, field, token); + return parse_text_inner(parser, type, field, rdata, token); } #endif // TEXT_H diff --git a/src/fallback/type.h b/src/fallback/type.h deleted file mode 100644 index ea2858a..0000000 --- a/src/fallback/type.h +++ /dev/null @@ -1,409 +0,0 @@ -/* - * type.h -- some useful comment - * - * Copyright (c) 2023, NLnet Labs. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - * - */ -#ifndef TYPE_H -#define TYPE_H - -#include -#if _WIN32 -#define strncasecmp(s1, s2, n) _strnicmp(s1, s2, n) -#else -#include -#endif - -static const uint8_t type_code_page[256] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00 - 0x07 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x08 - 0x0f - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x10 - 0x17 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x18 - 0x1f - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20 - 0x27 - // hyphen - 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, // 0x28 - 0x2f - // digits - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // 0x30 - 0x37 - 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x38 - 0x3f - // letters (upper case) - 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, // 0x40 - 0x47 - 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, // 0x48 - 0x4f - 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, // 0x50 - 0x57 - 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x58 - 0x5f - // letters (lower case) - 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, // 0x60 - 0x67 - 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, // 0x68 - 0x6f - 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, // 0x70 - 0x77 - 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x78 - 0x7f - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x80 - 0x87 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x88 - 0x8f - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x90 - 0x97 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x98 - 0x9f - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xa0 - 0xa7 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xa8 - 0xaf - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xb0 - 0xb7 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xb8 - 0xbf - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xc0 - 0xc7 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xc8 - 0xcf - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xd0 - 0xd7 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xd8 - 0xdf - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xe0 - 0xe7 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xe8 - 0xef - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xf0 - 0xf7 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xf8 - 0xff -}; - -zone_nonnull_all -static zone_really_inline int32_t maybe_type( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token, - uint16_t *code, - const zone_symbol_t **symbol, - uint16_t key) -{ - (void)parser; - (void)type; - (void)field; - - const zone_symbol_t *s = &types[key].info.name; - - if (strncasecmp(token->data, s->key.data, s->key.length) != 0 || - contiguous[ (uint8_t)token->data[ s->key.length ] ] == CONTIGUOUS) - return 0; - - *symbol = s; - *code = (uint16_t)s->value; - return ZONE_TYPE; -} - -zone_nonnull_all -static zone_really_inline int32_t maybe_class( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token, - uint16_t *code, - const zone_symbol_t **symbol, - uint16_t key) -{ - (void)parser; - (void)type; - (void)field; - - const zone_symbol_t *s = &classes[key].name; - if (strncasecmp(token->data, s->key.data, s->key.length) != 0 || - contiguous[ (uint8_t)token->data[ s->key.length ] ] == CONTIGUOUS) - return 0; - - *symbol = s; - *code = (uint16_t)s->value; - return ZONE_CLASS; -} - -zone_nonnull_all -static zone_really_inline int32_t find_type_or_class( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token, - uint16_t *code, - const zone_symbol_t **symbol) -{ - const char *p = token->data; - -#define MAYBE_TYPE(key) \ - return maybe_type(parser, type, field, token, code, symbol, key) -#define MAYBE_CLASS(key) \ - return maybe_class(parser, type, field, token, code, symbol, key) - - switch (type_code_page[ (uint8_t)p[0] ]) { - case 'A': - switch (type_code_page[ (uint8_t)p[1] ]) { - case 0 : MAYBE_TYPE(ZONE_A); - case 'A': MAYBE_TYPE(ZONE_AAAA); - case 'F': MAYBE_TYPE(ZONE_AFSDB); - case 'P': MAYBE_TYPE(ZONE_APL); - case '6': MAYBE_TYPE(ZONE_A6); - case 'V': MAYBE_TYPE(ZONE_AVC); - } - break; - case 'C': - switch (type_code_page[ (uint8_t)p[1] ]) { - case 'N': MAYBE_TYPE(ZONE_CNAME); - case 'D': - switch (type_code_page[ (uint8_t)p[2] ]) { - case 'S': MAYBE_TYPE(ZONE_CDS); - case 'N': MAYBE_TYPE(ZONE_CDNSKEY); - } - break; - case 'H': MAYBE_CLASS(ZONE_CH); - case 'A': MAYBE_TYPE(ZONE_CAA); - case 'E': MAYBE_TYPE(ZONE_CERT); - case 'S': - switch (type_code_page[ (uint8_t)p[2] ]) { - case 0 : MAYBE_CLASS(ZONE_CS); - case 'Y': MAYBE_TYPE(ZONE_CSYNC); - } - } - break; - case 'D': - switch (type_code_page[ (uint8_t)p[1] ]) { - case 'N': - switch (type_code_page[ (uint8_t)p[2] ]) { - case 'A': MAYBE_TYPE(ZONE_DNAME); - case 'S': MAYBE_TYPE(ZONE_DNSKEY); - } - break; - case 'S': MAYBE_TYPE(ZONE_DS); - case 'H': MAYBE_TYPE(ZONE_DHCID); - case 'L': MAYBE_TYPE(259); - } - break; - case 'E': - switch (type_code_page[ (uint8_t)p[3] ]) { - case '4': MAYBE_TYPE(ZONE_EUI48); - case '6': MAYBE_TYPE(ZONE_EUI64); - } - break; - case 'G': MAYBE_TYPE(ZONE_GPOS); - case 'H': - switch (type_code_page[ (uint8_t)p[2] ]) { - case 'T': MAYBE_TYPE(ZONE_HTTPS); - case 'N': MAYBE_TYPE(ZONE_HINFO); - case 'P': MAYBE_TYPE(ZONE_HIP); - case 0 : MAYBE_CLASS(ZONE_HS); - } - break; - case 'I': - switch (type_code_page[ (uint8_t)p[1] ]) { - case 'N': MAYBE_CLASS(ZONE_IN); - case 'P': MAYBE_TYPE(ZONE_IPSECKEY); - case 'S': MAYBE_TYPE(ZONE_ISDN); - } - break; - case 'K': - switch (type_code_page[ (uint8_t)p[1] ] ) { - case 'E': MAYBE_TYPE(ZONE_KEY); - case 'X': MAYBE_TYPE(ZONE_KX); - } - break; - case 'L': - switch (type_code_page[ (uint8_t)p[1] ]) { - case '3': MAYBE_TYPE(ZONE_L32); - case '6': MAYBE_TYPE(ZONE_L64); - case 'O': MAYBE_TYPE(ZONE_LOC); - case 'P': MAYBE_TYPE(ZONE_LP); - } - break; - case 'M': - switch (type_code_page[ (uint8_t)p[1] ]) { - case 'X': MAYBE_TYPE(ZONE_MX); - case 'B': MAYBE_TYPE(ZONE_MB); - case 'D': MAYBE_TYPE(ZONE_MD); - case 'F': MAYBE_TYPE(ZONE_MF); - case 'G': MAYBE_TYPE(ZONE_MG); - case 'I': MAYBE_TYPE(ZONE_MINFO); - case 'R': MAYBE_TYPE(ZONE_MR); - } - break; - case 'N': - switch (type_code_page[ (uint8_t)p[1] ]) { - case 'S': - switch (type_code_page[ (uint8_t)p[2] ]) { - case 0 : MAYBE_TYPE(ZONE_NS); - case 'E': - switch (type_code_page[ (uint8_t)p[4] ]) { - case 0 : MAYBE_TYPE(ZONE_NSEC); - case '3': - switch (type_code_page[ (uint8_t)p[5] ]) { - case 0 : MAYBE_TYPE(ZONE_NSEC3); - case 'P': MAYBE_TYPE(ZONE_NSEC3PARAM); - } - } - break; - case 'A': - switch (type_code_page[ (uint8_t)p[4] ]) { - case 0 : MAYBE_TYPE(ZONE_NSAP); - case '-': MAYBE_TYPE(ZONE_NSAP_PTR); - } - } - break; - case 'A': MAYBE_TYPE(ZONE_NAPTR); - case 'I': MAYBE_TYPE(ZONE_NID); - case 'X': MAYBE_TYPE(ZONE_NXT); - } - break; - case 'O': MAYBE_TYPE(ZONE_OPENPGPKEY); - case 'P': - switch (type_code_page[ (uint8_t)p[1] ]) { - case 'T': MAYBE_TYPE(ZONE_PTR); - case 'X': MAYBE_TYPE(ZONE_PX); - } - break; - case 'R': - switch (type_code_page[ (uint8_t)p[1] ]) { - case 'R': MAYBE_TYPE(ZONE_RRSIG); - case 'P': MAYBE_TYPE(ZONE_RP); - case 'T': MAYBE_TYPE(ZONE_RT); - } - break; - case 'S': - switch (type_code_page[ (uint8_t)p[1] ]) { - case 'O': MAYBE_TYPE(ZONE_SOA); - case 'R': MAYBE_TYPE(ZONE_SRV); - case 'I': MAYBE_TYPE(ZONE_SIG); - case 'M': MAYBE_TYPE(ZONE_SMIMEA); - case 'P': MAYBE_TYPE(ZONE_SPF); - case 'S': MAYBE_TYPE(ZONE_SSHFP); - case 'V': MAYBE_TYPE(ZONE_SVCB); - } - break; - case 'T': - switch (type_code_page[ (uint8_t)p[1] ]) { - case 'X': MAYBE_TYPE(ZONE_TXT); - case 'L': MAYBE_TYPE(ZONE_TLSA); - } - break; - case 'U': MAYBE_TYPE(ZONE_URI); - case 'W': MAYBE_TYPE(ZONE_WKS); - case 'X': MAYBE_TYPE(ZONE_X25); - case 'Z': MAYBE_TYPE(ZONE_ZONEMD); - } - -#undef MAYBE_TYPE -#undef MAYBE_CLASS - return 0; -} - -zone_nonnull_all -static zone_really_inline int32_t scan_generic_type( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token, - uint16_t *code, - const zone_symbol_t **symbol) -{ - const char *ps = token->data + 4, *p = ps; - uint64_t n = 0; - for (;; p++) { - const uint64_t d = (uint8_t)*p - '0'; - if (d > 9) - break; - n = n * 10 + d; - } - - if (!n || n > 65535 || p - ps > 5 || is_contiguous((uint8_t)*p)) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - - *code = (uint16_t)n; - if (*code <= 258) - *symbol = &types[*code].info.name; - else if (*code == ZONE_DLV) - *symbol = &types[259].info.name; - else - *symbol = &types[0].info.name; - return ZONE_TYPE; -} - -zone_nonnull_all -static zone_really_inline int32_t scan_generic_class( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token, - uint16_t *code, - const zone_symbol_t **symbol) -{ - const char *ps = token->data + 5, *p = ps; - uint64_t n = 0; - for (;; p++) { - const uint64_t d = (uint8_t)*p - '0'; - if (d > 9) - break; - n = n * 10 + d; - } - - if (!n || n > 65535 || p - ps >= 5 || is_contiguous((uint8_t)*p)) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - - *code = (uint16_t)n; - if (*code <= 4) - *symbol = &classes[*code].name; - else - *symbol = &classes[0].name; - return ZONE_CLASS; -} - -zone_nonnull_all -static zone_really_inline int32_t scan_type_or_class( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token, - uint16_t *code, - const zone_symbol_t **symbol) -{ - int32_t r; - - if ((r = have_contiguous(parser, type, field, token)) < 0) - return r; - if ((r = find_type_or_class(parser, type, field, token, code, symbol))) - return r; - - if (strncasecmp(token->data, "TYPE", 4) == 0) - return scan_generic_type(parser, type, field, token, code, symbol); - else if (strncasecmp(token->data, "CLASS", 5) == 0) - return scan_generic_class(parser, type, field, token, code, symbol); - - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); -} - -zone_nonnull_all -static zone_really_inline int32_t scan_type( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token, - uint16_t *code, - const zone_symbol_t **symbol) -{ - int32_t r; - - if ((r = have_contiguous(parser, type, field, token)) < 0) - return r; - if ((r = find_type_or_class(parser, type, field, token, code, symbol)) == ZONE_TYPE) - return r; - - if (strncasecmp(token->data, "TYPE", 4) == 0) - return scan_generic_type(parser, type, field, token, code, symbol); - - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); -} - -zone_nonnull_all -static zone_really_inline int32_t parse_type( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token) -{ - int32_t r; - uint16_t c = 0; - const zone_symbol_t *s; - - if ((r = have_contiguous(parser, type, field, token)) < 0) - return r; - - scan_type(parser, type, field, token, &c, &s); - c = htobe16(c); - memcpy(&parser->rdata->octets[parser->rdata->length], &c, sizeof(c)); - parser->rdata->length += sizeof(c); - return ZONE_TYPE; -} - -#endif // TYPE_H diff --git a/src/generic/apl.h b/src/generic/apl.h index 9443063..255c9c7 100644 --- a/src/generic/apl.h +++ b/src/generic/apl.h @@ -9,7 +9,7 @@ #ifndef APL_H #define APL_H -static zone_really_inline int32_t scan_apl( +static really_inline int32_t scan_apl( const char *text, size_t length, uint8_t *octets, size_t size) { uint8_t negate = text[0] == '!'; diff --git a/src/generic/base16.h b/src/generic/base16.h new file mode 100644 index 0000000..ae2ec6d --- /dev/null +++ b/src/generic/base16.h @@ -0,0 +1,310 @@ +/* + * base16.h -- Fast Base16 stream decoder + * + * Copyright (c) 2005-2016, Nick Galbreath. + * Copyright (c) 2022, NLnet Labs. All rights reserved. + * + * SPDX-License-Identifier: BSD-2-Clause + * + */ +#ifndef BASE16_H +#define BASE16_H + +// adaptation of base16 decoder by Nick Galbreath to operate similar to the +// base64 stream decoder by Alfred Klomp. +// https://github.com/client9/stringencoders +// https://github.com/aklomp/base64 + +struct base16_state { + int eof; + int bytes; + unsigned char carry; +}; + +#define BASE16_EOF 1 + +#if BYTE_ORDER == LITTLE_ENDIAN + +static const uint32_t base16_table_dec_32bit_d0[256] = { + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 0, 16, 32, 48, 64, 80, 96, 112, 128, 144, 256, 256, + 256, 256, 256, 256, 256, 160, 176, 192, 208, 224, 240, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 160, 176, 192, 208, 224, 240, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256 +}; + +static const uint32_t base16_table_dec_32bit_d1[256] = { + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 256, 256, + 256, 256, 256, 256, 256, 10, 11, 12, 13, 14, 15, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 10, 11, 12, 13, 14, 15, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256 +}; + +#elif BYTE_ORDER == BIG_ENDIAN + +#error "generate table please!" + +#else + +#error "not implemented" + +#endif + +static really_inline int +base16_dec_loop_generic_32_inner( + const uint8_t **s, uint8_t **o, size_t *rounds) +{ + const uint32_t val1 = base16_table_dec_32bit_d0[(*s)[0]] + | base16_table_dec_32bit_d1[(*s)[1]]; + const uint32_t val2 = base16_table_dec_32bit_d0[(*s)[2]] + | base16_table_dec_32bit_d1[(*s)[3]]; + + if (val1 > 0xff || val2 > 0xff) + return 0; + + (*o)[0] = (uint8_t)val1; + (*o)[1] = (uint8_t)val2; + + *s += 4; + *o += 2; + *rounds -= 1; + + return 1; +} + +static really_inline void +base16_dec_loop_generic_32( + const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen) +{ + if (*slen < 4) + return; + + // some comment on the how and what... + size_t rounds = (*slen - 4) / 4; + + *slen -= rounds * 4; // 4 bytes consumed per round + *olen += rounds * 2; // 2 bytes produced per round + + do { + if (rounds >= 8) { + if (base16_dec_loop_generic_32_inner(s, o, &rounds) && + base16_dec_loop_generic_32_inner(s, o, &rounds) && + base16_dec_loop_generic_32_inner(s, o, &rounds) && + base16_dec_loop_generic_32_inner(s, o, &rounds) && + base16_dec_loop_generic_32_inner(s, o, &rounds) && + base16_dec_loop_generic_32_inner(s, o, &rounds) && + base16_dec_loop_generic_32_inner(s, o, &rounds) && + base16_dec_loop_generic_32_inner(s, o, &rounds)) + continue; + break; + } + if (rounds >= 4) { + if (base16_dec_loop_generic_32_inner(s, o, &rounds) && + base16_dec_loop_generic_32_inner(s, o, &rounds) && + base16_dec_loop_generic_32_inner(s, o, &rounds) && + base16_dec_loop_generic_32_inner(s, o, &rounds)) + continue; + break; + } + if (rounds >= 2) { + if (base16_dec_loop_generic_32_inner(s, o, &rounds) && + base16_dec_loop_generic_32_inner(s, o, &rounds)) + continue; + break; + } + base16_dec_loop_generic_32_inner(s, o, &rounds); + break; + } while (rounds > 0); + + // Adjust for any rounds that were skipped: + *slen += rounds * 4; + *olen -= rounds * 2; +} + +nonnull((1,2,4,5)) +static really_inline int base16_stream_decode( + struct base16_state *state, + const char *src, + size_t srclen, + uint8_t *out, + size_t *outlen) +{ + int ret = 0; + const uint8_t *s = (const uint8_t *) src; + uint8_t *o = (uint8_t *) out; + uint32_t q; + + // Use local temporaries to avoid cache thrashing: + size_t olen = 0; + size_t slen = srclen; + struct base16_state st; + st.eof = state->eof; + st.bytes = state->bytes; + st.carry = state->carry; + + if (st.eof) { + *outlen = 0; + return ret; + } + + // Duff's device again: + switch (st.bytes) + { + for (;;) + { + case 0: + base16_dec_loop_generic_32(&s, &slen, &o, &olen); + if (slen-- == 0) { + ret = 1; + break; + } + if ((q = base16_table_dec_32bit_d0[*s++]) >= 255) { + st.eof = BASE16_EOF; + break; + } + st.carry = (uint8_t)q; + st.bytes = 1; + + // fallthrough + + case 1: + if (slen-- == 0) { + ret = 1; + break; + } + if ((q = base16_table_dec_32bit_d1[*s++]) >= 255) { + st.eof = BASE16_EOF; + break; + } + *o++ = st.carry | (uint8_t)q; + st.carry = 0; + st.bytes = 0; + olen++; + } + } + + state->eof = st.eof; + state->bytes = st.bytes; + state->carry = st.carry; + *outlen = olen; + return ret; +} + +nonnull((1,3,4)) +static really_inline int base16_decode( + const char *src, size_t srclen, uint8_t *out, size_t *outlen) +{ + struct base16_state state = { 0 }; + return base16_stream_decode(&state, src, srclen, out, outlen) & !state.bytes; +} + +// FIXME: RFC3597 section 5 states each word of data must contain an even +// number of hexadecimal digits. The same is not true for DS records. +// RFC4043 section 5.3 merely states whitespace is allowed within the +// hexadecimal text. Words containing an uneven number of hexadecimal +// digits are impractical, but supported (BIND supports uneven +// sequences) +nonnull_all +static really_inline int32_t parse_base16_sequence( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, + token_t *token) +{ + if (is_contiguous(token)) { + struct base16_state state = { 0 }; + + do { + size_t length = (token->length + 1) / 2; + if ((uintptr_t)rdata->limit - (uintptr_t)rdata->octets < length) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + if (!base16_stream_decode(&state, token->data, token->length, rdata->octets, &length)) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + rdata->octets += length; + take(parser, token); + } while (is_contiguous(token)); + + if (state.bytes) + *rdata->octets++ = state.carry; + } + + return have_delimiter(parser, type, token); +} + +nonnull_all +static really_inline int32_t parse_base16( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, + const token_t *token) +{ + size_t length = token->length / 2; + if ((uintptr_t)rdata->limit - (uintptr_t)rdata->octets < length) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + if (!base16_decode(token->data, token->length, rdata->octets, &length)) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + rdata->octets += length; + return 0; +} + +nonnull_all +static really_inline int32_t parse_salt( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, + const token_t *token) +{ + if (token->length == 1 && token->data[0] == '-') + return (void)(*rdata->octets++ = 0), 0; + + size_t length = token->length / 2; + uint8_t *octets = rdata->octets++; + // FIXME: not quite right yet! we must not exceed 255 octets! + if ((uintptr_t)rdata->limit - (uintptr_t)rdata->octets < length) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + if (!base16_decode(token->data, token->length, rdata->octets, &length)) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + *octets = (uint8_t)length; + rdata->octets += length; + return 0; +} + +#endif // BASE16_H diff --git a/src/fallback/base32.h b/src/generic/base32.h similarity index 67% rename from src/fallback/base32.h rename to src/generic/base32.h index 12829b6..cfcdecd 100644 --- a/src/fallback/base32.h +++ b/src/generic/base32.h @@ -1,5 +1,5 @@ /* - * base32.h -- some useful comment + * base32.h -- Base32 decoder * * Copyright (c) 2022, NLnet Labs. All rights reserved. * @@ -45,22 +45,17 @@ static const uint8_t b32rmap[256] = { }; static const uint8_t b32rmap_special = 0xf0; -static const uint8_t b32rmap_end = 0xfd; -static const uint8_t b32rmap_space = 0xfe; -zone_nonnull_all -static zone_really_inline int32_t parse_base32( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, +nonnull_all +static really_inline int32_t parse_base32( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *item, + rdata_t *rdata, const token_t *token) { - int32_t r; uint32_t state = 0; - if ((r = have_contiguous(parser, type, field, token)) < 0) - return r; - const char *p = token->data; for (;; p++) { const uint8_t ofs = b32rmap[(uint8_t)*p]; @@ -70,48 +65,47 @@ static zone_really_inline int32_t parse_base32( switch (state) { case 0: - parser->rdata->octets[parser->rdata->length ] = (uint8_t)(ofs << 3); + *rdata->octets = (uint8_t)(ofs << 3); state = 1; break; case 1: - parser->rdata->octets[parser->rdata->length++] |= (uint8_t)(ofs >> 2); - parser->rdata->octets[parser->rdata->length ] = (uint8_t)(ofs << 6); + *rdata->octets++ |= (uint8_t)(ofs >> 2); + *rdata->octets = (uint8_t)(ofs << 6); state = 2; break; case 2: - parser->rdata->octets[parser->rdata->length ] |= (uint8_t)(ofs << 1); + *rdata->octets |= (uint8_t)(ofs << 1); state = 3; break; case 3: - parser->rdata->octets[parser->rdata->length++] |= (uint8_t)(ofs >> 4); - parser->rdata->octets[parser->rdata->length ] = (uint8_t)(ofs << 4); + *rdata->octets++ |= (uint8_t)(ofs >> 4); + *rdata->octets = (uint8_t)(ofs << 4); state = 4; break; case 4: - parser->rdata->octets[parser->rdata->length++] |= (uint8_t)(ofs >> 1); - parser->rdata->octets[parser->rdata->length ] = (uint8_t)(ofs << 7); + *rdata->octets++ |= (uint8_t)(ofs >> 1); + *rdata->octets = (uint8_t)(ofs << 7); state = 5; break; case 5: - parser->rdata->octets[parser->rdata->length ] |= (uint8_t)(ofs << 2); + *rdata->octets |= (uint8_t)(ofs << 2); state = 6; break; case 6: - parser->rdata->octets[parser->rdata->length++] |= (uint8_t)(ofs >> 3); - parser->rdata->octets[parser->rdata->length ] = (uint8_t)(ofs << 5); + *rdata->octets++ |= (uint8_t)(ofs >> 3); + *rdata->octets = (uint8_t)(ofs << 5); state = 7; break; case 7: - parser->rdata->octets[parser->rdata->length++] |= ofs; + *rdata->octets++ |= ofs; state = 0; break; } } - if (contiguous[ (uint8_t)*p ] == CONTIGUOUS) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - - return ZONE_STRING; + if (p != token->data + token->length) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(item), NAME(type)); + return 0; } #endif // BASE32_H diff --git a/src/fallback/base64.h b/src/generic/base64.h similarity index 94% rename from src/fallback/base64.h rename to src/generic/base64.h index 868e0e4..084cd23 100644 --- a/src/fallback/base64.h +++ b/src/generic/base64.h @@ -13,7 +13,7 @@ #ifndef BASE64_H #define BASE64_H -// Slightly modified version of https://github.com/aklomp/base64 +// Modified version of https://github.com/aklomp/base64 struct base64_state { int eof; @@ -32,13 +32,6 @@ struct base64_state { // End-of-file when stream end has been reached or invalid input provided: #define BASE64_EOF 2 -static const uint8_t -base64_table_enc_6bit[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789" - "+/"; - // In the lookup table below, note that the value for '=' (character 61) is // 254, not 255. This character is used for in-band signaling of the end of // the datastream, and we will use that later. The characters A-Z, a-z, 0-9 @@ -451,9 +444,9 @@ const uint32_t base64_table_dec_32bit_d3[256] = { # error "byte order unknown" -#endif // ZONE_LITTLE_ENDIAN +#endif // LITTLE_ENDIAN -static inline int +static really_inline int dec_loop_generic_32_inner (const uint8_t **s, uint8_t **o, size_t *rounds) { const uint32_t str @@ -484,7 +477,7 @@ dec_loop_generic_32_inner (const uint8_t **s, uint8_t **o, size_t *rounds) return 1; } -static inline void +static really_inline void dec_loop_generic_32 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen) { if (*slen < 8) { @@ -540,8 +533,8 @@ dec_loop_generic_32 (const uint8_t **s, size_t *slen, uint8_t **o, size_t *olen) *olen -= rounds * 3; } -zone_nonnull((1,2,4,5)) -static int base64_stream_decode( +nonnull((1,2,4,5)) +static really_inline int base64_stream_decode( struct base64_state *state, const char *src, size_t srclen, @@ -679,70 +672,61 @@ static int base64_stream_decode( return ret; } -zone_nonnull_all -static zone_really_inline int32_t parse_base64_sequence( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - token_t *token) +nonnull((1,3,4)) +static really_inline int base64_decode( + const char *src, + size_t srclen, + uint8_t *out, + size_t *outlen) { - int32_t r; struct base64_state state = { 0 }; + return base64_stream_decode(&state, src, srclen, out, outlen) & !state.bytes; +} - if ((r = have_contiguous(parser, type, field, token)) < 0) - return r; - - // implement length check(s) better! - do { - size_t length; - if (token->length / 4 > (ZONE_RDATA_SIZE - parser->rdata->length) / 3) - SYNTAX_ERROR(parser, "maximum size exceeded!"); - int success = base64_stream_decode( - &state, - token->data, - token->length, - parser->rdata->octets + parser->rdata->length, - &length); - if (!success) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - parser->rdata->length += length; - lex(parser, token); - } while (token->code == CONTIGUOUS); - - // if we're still waiting on input, it's an error here - if (state.bytes) - SYNTAX_ERROR(parser, "invalid base64 sequence!"); +nonnull_all +static really_inline int32_t parse_base64_sequence( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *item, + rdata_t *rdata, + token_t *token) +{ + if (is_contiguous(token)) { + struct base64_state state = { 0 }; + + do { + size_t length = token->length / 4; + if (((uintptr_t)rdata->limit - (uintptr_t)rdata->octets) / 3 < length) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(item), NAME(type)); + if (!base64_stream_decode(&state, token->data, token->length, rdata->octets, &length)) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(item), NAME(type)); + rdata->octets += length; + take(parser, token); + } while (is_contiguous(token)); + + // incomplete base64 sequence + if (state.bytes) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(item), NAME(type)); + } return have_delimiter(parser, type, token); } -zone_nonnull_all -static zone_really_inline int32_t parse_base64( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, +nonnull_all +static really_inline int32_t parse_base64( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *item, + rdata_t *rdata, const token_t *token) { - int32_t r; - struct base64_state state = { 0 }; - - if ((r = have_contiguous(parser, type, field, token)) < 0) - return r; - - // check we have enough room in the output buffer!!! - size_t length; - if (token->length / 4 > (ZONE_RDATA_SIZE - parser->rdata->length) / 3) - SYNTAX_ERROR(parser, "maximum size exceeded"); - int success = base64_stream_decode( - &state, token->data, token->length, parser->rdata->octets + parser->rdata->length, &length); - if (!success) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - parser->rdata->length += length; - - if (state.bytes) - SYNTAX_ERROR(parser, "invalid base64 sequence!"); - - return 0; + size_t length = token->length / 4; + if (((uintptr_t)rdata->limit - (uintptr_t)rdata->octets) / 3 < length) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(item), NAME(type)); + if (!base64_decode(token->data, token->length, rdata->octets, &length)) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(item), NAME(type)); + rdata->octets += length; + return 0; } #endif // BASE64_H diff --git a/src/generic/caa.h b/src/generic/caa.h index 5af9ebe..0677f17 100644 --- a/src/generic/caa.h +++ b/src/generic/caa.h @@ -1,5 +1,5 @@ /* - * caa.h -- some useful comment + * caa.h -- CAA (RFC8659) parser * * Copyright (c) 2023, NLnet Labs. All rights reserved. * @@ -9,71 +9,73 @@ #ifndef CAA_H #define CAA_H -// FIXME: eligable for vectorization -zone_nonnull_all -static zone_really_inline int32_t parse_caa_tag( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, +static const uint8_t bad_caa_chars[256] = { + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x00 - 0x07 + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x08 - 0x0f + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x10 - 0x17 + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x18 - 0x10f + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x20 - 0x27 + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x28 - 0x2f + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x30 - 0x37 + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x38 - 0x3f + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x40 - 0x47 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x48 - 0x4f + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x50 - 0x57 + 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x58 - 0x5f + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x60 - 0x67 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x68 - 0x6f + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x70 - 0x77 + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x78 - 0x7f + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x80 - 0x87 + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x88 - 0x8f + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x90 - 0x97 + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x98 - 0x9f + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xa0 - 0xa7 + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xa8 - 0xaf + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xb0 - 0xb7 + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xb8 - 0xbf + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xc0 - 0xc7 + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xc8 - 0xcf + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xd0 - 0xd7 + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xd8 - 0xdf + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xe0 - 0xe7 + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xe0 - 0xe7 + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xf8 - 0xff + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xf8 - 0xff +}; + +nonnull_all +static really_inline int32_t parse_caa_tag( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, const token_t *token) { - int32_t r; - // RFC8659 section 4.1 // https://datatracker.ietf.org/doc/html/rfc8659 // - // Tags MAY contain ASCII characters "a" through "z", "A" through "Z", and - // the numbers 0 through 9. Tags MUST NOT contain any other characters. - // Matching of tags is case insensitive. - // - // Tags submitted for registration by IANA MUST NOT contain any characters - // other than the (lowercase) ASCII characters "a" through "z" and the - // numbers 0 through 9. - // - // Tags registered by IANA + // Certification Authority Restriction Properties registered by IANA // https://www.iana.org/assignments/pkix-parameters/pkix-parameters.xhtml - // - // issue - // issuewild - // iodef - // auth - // path - // policy - // contactemail - // contactphone - // issuevmc - - // Tags are meant to be written as . - if ((r = have_contiguous(parser, type, field, token)) < 0) - return r; - uint8_t *w = &parser->rdata->octets[parser->rdata->length + 1]; - const uint8_t *ws = w, *we = w + 255; - const char *t = token->data; + if (token->length > 255) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + *rdata->octets++ = (uint8_t)token->length; - while (w < we) { - const uint8_t c = (uint8_t)*t; - if ((c >= 'A' && c <= 'Z') || - (c >= 'a' && c <= 'z') || - (c >= '0' && c <= '9')) - { - w[0] = c; - w += 1; t += 1; - } else if (contiguous[c] != CONTIGUOUS) { - break; - } else { - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - } + uint32_t bad_chars = 0; + for (size_t count=0; count < token->length; count++) { + const uint8_t octet = (uint8_t)token->data[count]; + *rdata->octets++ = octet; + bad_chars |= bad_caa_chars[octet]; } - // FIXME: if an uppercase character is found, ensure it is not one of the - // tags registered by IANA. + // Tags MAY contain ASCII characters "a" through "z", "A" through "Z", + // and the numbers 0 through 9. Tags MUST NOT contain any other + // characters. Matching of tags is case insensitive. + if (bad_chars) + SEMANTIC_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); - if (w >= we) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - parser->rdata->octets[parser->rdata->length] = (uint8_t)(w - ws); - parser->rdata->length += (size_t)(w - ws) + 1; - return ZONE_STRING; + return 0; } #endif // CAA_H diff --git a/src/generic/cert.h b/src/generic/cert.h new file mode 100644 index 0000000..06f72ed --- /dev/null +++ b/src/generic/cert.h @@ -0,0 +1,132 @@ +/* + * cert.h + * + * Copyright (c) 2023, NLnet Labs. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ +#ifndef CERT_H +#define CERT_H + +// https://www.iana.org/assignments/cert-rr-types/cert-rr-types.xhtml + +typedef struct certificate_type certificate_type_t; +struct certificate_type { + struct { + char name[8]; + size_t length; + } key; + uint16_t value; +}; + +#define BAD_CERTIFICATE_TYPE(value) \ + { { "", 0 }, 0 } +#define CERTIFICATE_TYPE(name, value) \ + { { name, sizeof(name) - 1 }, value } + +static const certificate_type_t certificate_types[] = { + BAD_CERTIFICATE_TYPE(0), + CERTIFICATE_TYPE("PKIX", 1), + CERTIFICATE_TYPE("SPKI", 2), + CERTIFICATE_TYPE("PGP", 3), + CERTIFICATE_TYPE("IPKIX", 4), + CERTIFICATE_TYPE("ISPKI", 5), + CERTIFICATE_TYPE("IPGP", 6), + CERTIFICATE_TYPE("ACPKIX", 7), + CERTIFICATE_TYPE("IACPKIX", 8), + CERTIFICATE_TYPE("URI", 253), + CERTIFICATE_TYPE("OID", 254), +}; + +static const certificate_type_t *certificate_type_map[16] = { + &certificate_types[5], // ISPKI (0) + &certificate_types[0], + &certificate_types[0], + &certificate_types[0], + &certificate_types[0], + &certificate_types[0], + &certificate_types[10], // OID (6) + &certificate_types[0], + &certificate_types[3], // PGP (8) + &certificate_types[4], // IPKIX (9) + &certificate_types[2], // SPKI (10) + &certificate_types[1], // PKIX (11) + &certificate_types[8], // IACPKIX (12) + &certificate_types[9], // URI (13) + &certificate_types[6], // IPGP (14) + &certificate_types[7] // ACPKIX (15) +}; + +// magic value generated using certificate-hash.c +static uint8_t certificate_hash(uint64_t value) +{ + uint32_t value32 = (uint32_t)((value >> 32) ^ value); + return (uint8_t)((value32 * 98112ull) >> 32) & 0xf; +} + +nonnull_all +static really_inline int32_t parse_certificate_type( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, + const token_t *token) +{ + static const int8_t zero_masks[48] = { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }; + + uint32_t number = (uint8_t)token->data[0] - '0'; + + if (number > 9) { + uint64_t input; + memcpy(&input, token->data, 8); + static const uint64_t letter_mask = 0x4040404040404040llu; + const size_t length = token->length; + // convert to upper case + input &= input & ~((input & letter_mask) >> 1); + // zero out non-relevant bytes + uint64_t zero_mask; + memcpy(&zero_mask, &zero_masks[32 - (token->length & 0xf)], 8); + input &= zero_mask; + const uint8_t index = certificate_hash(input); + assert(index < 16); + const certificate_type_t *certificate_type = certificate_type_map[index]; + uint64_t name; + memcpy(&name, certificate_type->key.name, 8); + uint16_t value = certificate_type->value; + if (unlikely((input != name) & (length != certificate_type->key.length) & (value != 0))) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + value = htobe16(value); + memcpy(rdata->octets, &value, 2); + rdata->octets += 2; + return 0; + } + + bool leading_zero = (number == 0) & (token->length > 1); + size_t length = 1; + + for (; length < token->length; length++) { + const uint8_t digit = (uint8_t)token->data[length] - '0'; + if (digit > 9) + break; + number = number * 10 + digit; + } + + if (number > 65535 || length > 5 || length != token->length || leading_zero) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + + uint16_t value = (uint16_t)number; + value = htobe16(value); + memcpy(rdata->octets, &value, 2); + rdata->octets += 2; + return 0; +} + +#endif // CERT_H diff --git a/src/generic/dnssec.h b/src/generic/dnssec.h new file mode 100644 index 0000000..5cef2f5 --- /dev/null +++ b/src/generic/dnssec.h @@ -0,0 +1,179 @@ +/** + * dnssec.h + * + * Copyright (c) 2023, NLnet Labs. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ +#ifndef DNSSEC_H +#define DNSSEC_H + +// https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml + +typedef struct algorithm algorithm_t; +struct algorithm { + struct { + char name[24]; + size_t length; + } key; + uint8_t value; +}; + +#define BAD_ALGORITHM(value) \ + { { "", 0 }, 0 } +#define ALGORITHM(name, value) \ + { { name, sizeof(name) - 1 }, value } + +static const algorithm_t algorithms[32] = { + BAD_ALGORITHM(0), + ALGORITHM("RSAMD5", 1), + ALGORITHM("DH", 2), + ALGORITHM("DSA", 3), + ALGORITHM("ECC", 4), + ALGORITHM("RSASHA1", 5), + ALGORITHM("DSA-NSEC-SHA1", 6), + ALGORITHM("RSASHA1-NSEC3-SHA1", 7), + ALGORITHM("RSASHA256", 8), + BAD_ALGORITHM(9), + ALGORITHM("RSASHA512", 10), + BAD_ALGORITHM(11), + ALGORITHM("ECC-GOST", 12), + ALGORITHM("ECDSAP256SHA256", 13), + ALGORITHM("ECDSAP384SHA384", 14), + BAD_ALGORITHM(15), + ALGORITHM("INDIRECT", 252), + ALGORITHM("PRIVATEDNS", 253), + ALGORITHM("PRIVATEOID", 254), +}; + +static const struct { + const algorithm_t *algorithm; + uint8_t mask[24]; +} algorithm_hash_map[16] = { + { &algorithms[2], // DH (0) + { 0xdf, 0xdf, 0 } }, + { &algorithms[10], // RSASHA512 (1) + { 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xff, 0xff, + 0xff, 0 } }, + { &algorithms[7], // RSASHA1-NSEC3-SHA1 (2) + { 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xff, 0xff, + 0xdf, 0xdf, 0xdf, 0xdf, 0xff, 0xff, 0xdf, 0xdf, + 0xdf, 0xff, 0 } }, + { &algorithms[8], // RSASHA256 (3) + { 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xff, 0xff, + 0xff, 0 } }, + { &algorithms[13], // ECDSAP256SHA256 (4) + { 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xff, 0xff, + 0xff, 0xdf, 0xdf, 0xdf, 0xff, 0xff, 0xff, 0 } }, + { &algorithms[0], // unknown + { 0 } }, + { &algorithms[6], // DSA-NSEC-SHA1 (6) + { 0xdf, 0xdf, 0xdf, 0xff, 0xdf, 0xdf, 0xdf, 0xdf, + 0xff, 0xdf, 0xdf, 0xdf, 0xff, 0 } }, + { &algorithms[1], // RSAMD5 (7) + { 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xff, 0 } }, + { &algorithms[5], // RSASHA1 (8) + { 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xff, 0 } }, + { &algorithms[17], // PRIVATEDNS (9) + { 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, + 0xdf, 0xdf, 0 } }, + { &algorithms[18], // PRIVATEOID (10) + { 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, + 0xdf, 0xdf, 0 } }, + { &algorithms[16], // INDIRECT (11) + { 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, + 0 } }, + { &algorithms[14], // ECDSAP384SHA384 (12) + { 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xff, 0xff, + 0xff, 0xdf, 0xdf, 0xdf, 0xff, 0xff, 0xff, 0 } }, + { &algorithms[3], // DSA (13) + { 0xdf, 0xdf, 0xdf, 0 } }, + { &algorithms[4], // ECC (14) + { 0xdf, 0xdf, 0xdf, 0 } }, + { &algorithms[12], // ECC-GHOST (15) + { 0xdf, 0xdf, 0xdf, 0xff, 0xdf, 0xdf, 0xdf, 0xdf, + 0 } } +}; + +#undef UNKNOWN_ALGORITHM +#undef ALGORITHM + +// magic value generated using algorithm-hash.c +static uint8_t algorithm_hash(uint64_t value) +{ + uint32_t value32 = (uint32_t)((value >> 32) ^ value); + return (uint8_t)((value32 * 29874llu) >> 32) & 0xf; +} + +nonnull_all +warn_unused_result +static really_inline int32_t parse_algorithm_type( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, + const token_t *token) +{ + static const int8_t zero_masks[48] = { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }; + + uint32_t number = (uint8_t)token->data[0] - '0'; + + if (unlikely(number > 9)) { + uint64_t input; + memcpy(&input, token->data, 8); + const uint64_t letter_mask = 0x4040404040404040llu; + // convert to upper case + input &= input & ~((input & letter_mask) >> 1); + // zero out non-relevant bytes + uint64_t zero_mask; + memcpy(&zero_mask, &zero_masks[32 - (token->length & 0x1f)], 8); + input &= zero_mask; + const uint8_t index = algorithm_hash(input); + assert(index < 16); + const algorithm_t *algorithm = algorithm_hash_map[index].algorithm; + uint64_t matches, mask, name; + // compare bytes 0-7 + memcpy(&name, algorithm->key.name, 8); + matches = input == name; + // compare bytes 8-15 + memcpy(&input, token->data + 8, 8); + memcpy(&mask, algorithm_hash_map[index].mask + 8, 8); + memcpy(&name, algorithm->key.name + 8, 8); + matches &= (input & mask) == name; + // compare bytes 16-23 + memcpy(&input, token->data + 16, 8); + memcpy(&mask, algorithm_hash_map[index].mask + 16, 8); + memcpy(&name, algorithm->key.name + 16, 8); + matches &= (input & mask) == name; + if (!(matches && (token->length == algorithm->key.length) && number > 0)) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + *rdata->octets++ = (uint8_t)algorithm->value; + return 0; + } + + bool leading_zero = (number == 0) & (token->length > 1); + size_t length = 1; + + for (; length < token->length; length++) { + const uint8_t digit = (uint8_t)token->data[length] - '0'; + if (digit > 9) + break; + number = number * 10 + digit; + } + + *rdata->octets++ = (uint8_t)number; + if (number > 255 || length > 3 || length != token->length || leading_zero) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + + return 0; +} + +#endif // DNSSEC_H diff --git a/src/fallback/endian.h.in b/src/generic/endian.h.in similarity index 100% rename from src/fallback/endian.h.in rename to src/generic/endian.h.in diff --git a/src/generic/eui.h b/src/generic/eui.h new file mode 100644 index 0000000..ffdbd13 --- /dev/null +++ b/src/generic/eui.h @@ -0,0 +1,67 @@ +/* + * eui.h -- EUI-48 and EUI-64 (RFC7043) parser + * + * Copyright (c) 2023, NLnet Labs. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ +#ifndef EUI_H +#define EUI_H + +nonnull((1,2)) +static really_inline int +eui_base16_dec_loop_generic_32_inner(const uint8_t *s, uint8_t *o, int dash) +{ + const uint32_t val1 = base16_table_dec_32bit_d0[s[0]] + | base16_table_dec_32bit_d1[s[1]]; + const uint32_t val2 = base16_table_dec_32bit_d0[s[3]] + | base16_table_dec_32bit_d1[s[4]]; + + if (val1 > 0xff || val2 > 0xff || (dash ^ ((s[2] == '-') ^ (s[5] == '-')))) + return 0; + + o[0] = (uint8_t)val1; + o[1] = (uint8_t)val2; + + return 1; +} + +// RFC7043 section 3.2, require xx-xx-xx-xx-xx-xx +nonnull_all +static really_inline int32_t parse_eui48( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, + const token_t *token) +{ + const uint8_t *input = (const uint8_t *)token->data; + if (token->length == 17 && + eui_base16_dec_loop_generic_32_inner(input, rdata->octets, 0) && + eui_base16_dec_loop_generic_32_inner(input+6, rdata->octets+2, 0) && + eui_base16_dec_loop_generic_32_inner(input+12, rdata->octets+4, 1)) + return (void)(rdata->octets += 6), 0; + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); +} + +// RFC7043 section 4.2, require xx-xx-xx-xx-xx-xx-xx-xx +nonnull_all +static really_inline int32_t parse_eui64( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, + const token_t *token) +{ + const uint8_t *input = (const uint8_t *)token->data; + if (token->length == 23 && + eui_base16_dec_loop_generic_32_inner(input, rdata->octets, 0) && + eui_base16_dec_loop_generic_32_inner(input+6, rdata->octets+2, 0) && + eui_base16_dec_loop_generic_32_inner(input+12, rdata->octets+4, 0) && + eui_base16_dec_loop_generic_32_inner(input+18, rdata->octets+6, 1)) + return (void)(rdata->octets += 8), 0; + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); +} + +#endif // EUI_H diff --git a/src/generic/format.h b/src/generic/format.h new file mode 100644 index 0000000..0ae81fe --- /dev/null +++ b/src/generic/format.h @@ -0,0 +1,364 @@ +/* + * format.h + * + * Copyright (c) 2022, NLnet Labs. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ +#ifndef FORMAT_H +#define FORMAT_H + +#define FIELDS(fields) \ + { (sizeof(fields)/sizeof(fields[0])), fields } + +#define FIELD(name) \ + { { { name, sizeof(name) - 1 } } } + +#define ENTRY(name, fields) \ + { { { name, sizeof(name) - 1 }, 0 }, 0, false, false, fields, 0, 0 } + +nonnull_all +static really_inline int32_t parse_type( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, + const token_t *token) +{ + uint16_t code; + const mnemonic_t *mnemonic; + + if (scan_type(token->data, token->length, &code, &mnemonic) != 1) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + code = htobe16(code); + memcpy(rdata->octets, &code, 2); + rdata->octets += 2; + return 0; +} + +nonnull_all +static really_inline int32_t parse_name( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, + const token_t *token) +{ + size_t length = 0; + + if (likely(is_contiguous(token))) { + // a freestanding "@" denotes the current origin + if (token->length == 1 && token->data[0] == '@') + goto relative; + switch (scan_name(token->data, token->length, rdata->octets, &length)) { + case 0: + rdata->octets += length; + return 0; + case 1: + goto relative; + } + } else if (is_quoted(token)) { + if (token->length == 0) + goto invalid; + switch (scan_name(token->data, token->length, rdata->octets, &length)) { + case 0: + rdata->octets += length; + return 0; + case 1: + goto relative; + } + } + +invalid: + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + +relative: + if (length > 255 - parser->file->origin.length) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + memcpy(rdata->octets + length, parser->file->origin.octets, parser->file->origin.length); + rdata->octets += length + parser->file->origin.length; + return 0; +} + +nonnull_all +static really_inline int32_t parse_owner( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + const token_t *token) +{ + size_t length = 0; + uint8_t *octets = parser->file->owner.octets; + + if (likely(is_contiguous(token))) { + // a freestanding "@" denotes the origin + if (token->length == 1 && token->data[0] == '@') + goto relative; + switch (scan_name(token->data, token->length, octets, &length)) { + case 0: + parser->file->owner.length = length; + parser->owner = &parser->file->owner; + return 0; + case 1: + goto relative; + } + } else { + assert(is_quoted(token)); + if (token->length == 0) + goto invalid; + switch (scan_name(token->data, token->length, octets, &length)) { + case 0: + parser->file->owner.length = length; + parser->owner = &parser->file->owner; + return 0; + case 1: + goto relative; + } + } + +invalid: + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + +relative: + if (length > 255 - parser->file->origin.length) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + memcpy(octets+length, parser->file->origin.octets, parser->file->origin.length); + parser->file->owner.length = length + parser->file->origin.length; + parser->owner = &parser->file->owner; + return 0; +} + +nonnull_all +static really_inline int32_t parse_rr( + parser_t *parser, token_t *token) +{ + static const rdata_info_t fields[] = { + FIELD("OWNER"), + FIELD("TYPE"), + FIELD("CLASS"), + FIELD("TTL") + }; + + static const type_info_t rr = ENTRY("RR", FIELDS(fields)); + + int32_t code; + const type_info_t *descriptor; + rdata_t rdata = { parser->rdata->octets, parser->rdata->octets + 65535 }; + + if (parser->file->start_of_line) { + if ((code = have_contiguous_or_quoted(parser, &rr, &fields[0], token)) < 0) + return code; + if ((code = parse_owner(parser, &rr, &fields[0], token)) < 0) + return code; + if ((code = take_contiguous(parser, &rr, &fields[1], token)) < 0) + return code; + } else { + if ((code = have_contiguous(parser, &rr, &fields[1], token)) < 0) + return code; + } + + const mnemonic_t *mnemonic; + + if ((uint8_t)token->data[0] - '0' < 10) { + if (!scan_ttl(token->data, token->length, parser->options.pretty_ttls, &parser->file->last_ttl)) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[3]), NAME(&rr)); + if (parser->file->last_ttl & (1u << 31)) + SEMANTIC_ERROR(parser, "Invalid %s in %s", NAME(&fields[3]), NAME(&rr)); + goto class_or_type; + } else { + switch (scan_type_or_class(token->data, token->length, &parser->file->last_type, &mnemonic)) { + case 1: + goto rdata; + case 2: + parser->file->last_class = parser->file->last_type; + goto ttl_or_type; + default: + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[1]), NAME(&rr)); + } + } + +ttl_or_type: + if ((code = take_contiguous(parser, &rr, &fields[1], token)) < 0) + return code; + if ((uint8_t)token->data[0] - '0' < 10) { + if (!scan_ttl(token->data, token->length, parser->options.pretty_ttls, &parser->file->last_ttl)) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[3]), NAME(&rr)); + if (parser->file->last_ttl & (1u << 31)) + SEMANTIC_ERROR(parser, "Invalid %s in %s", NAME(&fields[3]), NAME(&rr)); + goto type; + } else { + if (unlikely(scan_type(token->data, token->length, &parser->file->last_type, &mnemonic) != 1)) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[1]), NAME(&rr)); + goto rdata; + } + +class_or_type: + if ((code = take_contiguous(parser, &rr, &fields[1], token)) < 0) + return code; + switch (scan_type_or_class(token->data, token->length, &parser->file->last_type, &mnemonic)) { + case 1: + goto rdata; + case 2: + parser->file->last_class = parser->file->last_type; + goto type; + default: + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[0]), NAME(&rr)); + } + +type: + if ((code = take_contiguous(parser, &rr, &fields[1], token)) < 0) + return code; + if (unlikely(scan_type(token->data, token->length, &parser->file->last_type, &mnemonic) != 1)) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[1]), NAME(&rr)); + +rdata: + descriptor = (const type_info_t *)mnemonic; + + // RFC3597 + // parse generic rdata if rdata starts with "\\#" + take(parser, token); + if (likely(token->data[0] != '\\')) + return descriptor->parse(parser, descriptor, &rdata, token); + else if (is_contiguous(token) && strncmp(token->data, "\\#", token->length) == 0) + return parse_generic_rdata(parser, descriptor, &rdata, token); + else + return descriptor->parse(parser, descriptor, &rdata, token); +} + +// RFC1035 section 5.1 +// $INCLUDE [] [] +nonnull_all +static really_inline int32_t parse_dollar_include( + parser_t *parser, token_t *token) +{ + static const rdata_info_t fields[] = { + FIELD("file-name"), + FIELD("domain-name") + }; + + static const type_info_t include = ENTRY("$INCLUDE", FIELDS(fields)); + + if (parser->options.no_includes) + NOT_PERMITTED(parser, "%s is disabled", NAME(&include)); + + int32_t code; + file_t *file; + if ((code = take_quoted_or_contiguous(parser, &include, &fields[0], token)) < 0) + return code; + if ((code = zone_open_file(parser, token->data, token->length, &file)) < 0) + return code; + + name_buffer_t name; + const name_buffer_t *origin = &parser->file->origin; + + // $INCLUDE directive MAY specify an origin + take(parser, token); + if (is_contiguous_or_quoted(token)) { + if (scan_name(token->data, token->length, name.octets, &name.length) != 0) { + zone_close_file(parser, file); + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[1]), NAME(&include)); + } + origin = &name; + take(parser, token); + } + + // store the current owner to restore later if necessary + file_t *includer; + includer = parser->file; + includer->owner = *parser->owner; + file->includer = includer; + file->owner = *origin; + file->origin = *origin; + file->last_type = 0; + file->last_class = includer->last_class; + file->last_ttl = includer->last_ttl; + file->line = 1; + + if (!is_delimiter(token)) { + zone_close_file(parser, file); + return have_delimiter(parser, &include, token); + } + + // check for recursive includes + do { + if (strcmp(includer->path, file->path) != 0) + continue; + zone_close_file(parser, file); + SYNTAX_ERROR(parser, "Circular include in %s", NAME(&include)); + } while ((includer = includer->includer)); + + parser->file = file; + return 0; +} + +// RFC1035 section 5.1 +// $ORIGIN [] +nonnull_all +static inline int32_t parse_dollar_origin( + parser_t *parser, token_t *token) +{ + static const rdata_info_t fields[] = { FIELD("name") }; + static const type_info_t origin = ENTRY("$ORIGIN", FIELDS(fields)); + int32_t code; + + if ((code = take_contiguous_or_quoted(parser, &origin, &fields[0], token)) < 0) + return code; + if (scan_name(token->data, token->length, parser->file->origin.octets, &parser->file->origin.length) != 0) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[0]), NAME(&origin)); + + return take_delimiter(parser, &origin, token); +} + +// RFC2308 section 4 +// $TTL [] +nonnull_all +static really_inline int32_t parse_dollar_ttl( + parser_t *parser, token_t *token) +{ + static const rdata_info_t fields[] = { FIELD("ttl") }; + static const type_info_t ttl = ENTRY("$TTL", FIELDS(fields)); + int32_t code; + + if ((code = take_contiguous(parser, &ttl, &fields[0], token)) < 0) + return code; + if (!scan_ttl(token->data, token->length, parser->options.pretty_ttls, &parser->file->default_ttl)) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[0]), NAME(&ttl)); + if (parser->file->default_ttl & (1u << 31)) + SEMANTIC_ERROR(parser, "Invalid %s in %s", NAME(&fields[0]), NAME(&ttl)); + + parser->file->last_ttl = parser->file->default_ttl; + + return take_delimiter(parser, &ttl, token); +} + +static inline int32_t parse(parser_t *parser) +{ + int32_t code = 0; + token_t token; + + while (code >= 0) { + take(parser, &token); + if (likely(is_contiguous(&token))) { + if (!parser->file->start_of_line || token.data[0] != '$') + code = parse_rr(parser, &token); + else if (token.length == 4 && memcmp(token.data, "$TTL", 4) == 0) + code = parse_dollar_ttl(parser, &token); + else if (token.length == 7 && memcmp(token.data, "$ORIGIN", 7) == 0) + code = parse_dollar_origin(parser, &token); + else if (token.length == 8 && memcmp(token.data, "$INCLUDE", 8) == 0) + code = parse_dollar_include(parser, &token); + else + code = parse_rr(parser, &token); + } else if (is_quoted(&token)) { + code = parse_rr(parser, &token); + } else if (is_end_of_file(&token)) { + if (parser->file->end_of_file == ZONE_NO_MORE_DATA) + break; + } + } + + return code; +} + +#endif // FORMAT_H diff --git a/src/generic/gpos.h b/src/generic/gpos.h index 58bad0b..910e598 100644 --- a/src/generic/gpos.h +++ b/src/generic/gpos.h @@ -9,11 +9,12 @@ #ifndef GPOS_H #define GPOS_H -zone_nonnull_all -static zone_really_inline int32_t parse_latitude( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, +nonnull_all +static really_inline int32_t parse_latitude( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, const token_t *token) { const char *text = token->data + (token->data[0] == '-'); @@ -49,13 +50,13 @@ static zone_really_inline int32_t parse_latitude( if (text[1] != '.') text += 1; else - for (text += 2; 9u > (uint8_t)((uint8_t)text[0] - '0'); text++) ; + for (text += 2; 10u > (uint8_t)((uint8_t)text[0] - '0'); text++) ; break; case 0x0b: // 0b1011 ("dd.d") if (text[2] != '.') text += 2; else - for (text += 3; 9u > (uint8_t)((uint8_t)text[0] - '0'); text++) ; + for (text += 3; 10u > (uint8_t)((uint8_t)text[0] - '0'); text++) ; break; default: goto bad_latitude; @@ -64,22 +65,20 @@ static zone_really_inline int32_t parse_latitude( if (text != token->data + token->length) goto bad_latitude; - parser->rdata->octets[parser->rdata->length] = (uint8_t)token->length; - memcpy(parser->rdata->octets + parser->rdata->length + 1, token->data, token->length); - parser->rdata->length += token->length + 1; + *rdata->octets = (uint8_t)token->length; + memcpy(rdata->octets + 1, token->data, token->length); + rdata->octets += 1 + token->length; return 0; - bad_latitude: - if (token->code != CONTIGUOUS) - return have_contiguous(parser, type, field, token); - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); } -zone_nonnull_all -static zone_really_inline int32_t parse_longitude( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, +nonnull_all +static really_inline int32_t parse_longitude( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, const token_t *token) { const char *text = token->data + (token->data[0] == '-'); @@ -105,7 +104,7 @@ static zone_really_inline int32_t parse_longitude( case 0x09: // 0b01001 ("d..d.") case 0x19: // 0b11001 ("d..dd") text += 1; - break; + break; case 0x03: // 0b00011 ("dd...") case 0x13: // 0b10011 ("dd..d") degrees = digits[0] * 10 + digits[1]; @@ -127,7 +126,7 @@ static zone_really_inline int32_t parse_longitude( if (text[1] != '.') text += 1; else - for (text += 2; 9u > (uint8_t)((uint8_t)text[0] - '0'); text++) ; + for (text += 2; 10u > (uint8_t)((uint8_t)text[0] - '0'); text++) ; break; case 0x0b: // 0b01011 ("dd.d.") case 0x1b: // 0b11011 ("dd.dd") @@ -138,7 +137,7 @@ static zone_really_inline int32_t parse_longitude( if (text[2] != '.') text += 2; else - for (text += 3; 9u > (uint8_t)((uint8_t)text[0] - '0'); text++) ; + for (text += 3; 10u > (uint8_t)((uint8_t)text[0] - '0'); text++) ; break; case 0x17: // 0b10111 ("ddd.d") // ensure no leading zero and range is between -180 and 180 @@ -148,7 +147,7 @@ static zone_really_inline int32_t parse_longitude( if (text[3] != '.') text += 3; else - for (text += 4; 9u > (uint8_t)((uint8_t)text[0] - '0'); text++) ; + for (text += 4; 10u > (uint8_t)((uint8_t)text[0] - '0'); text++) ; break; default: goto bad_longitude; @@ -157,29 +156,26 @@ static zone_really_inline int32_t parse_longitude( if (text != token->data + token->length) goto bad_longitude; - parser->rdata->octets[parser->rdata->length] = (uint8_t)token->length; - memcpy(parser->rdata->octets + parser->rdata->length + 1, token->data, token->length); - parser->rdata->length += token->length + 1; + *rdata->octets = (uint8_t)token->length; + memcpy(rdata->octets + 1, token->data, token->length); + rdata->octets += 1 + token->length; return 0; - bad_longitude: - if (token->code != CONTIGUOUS) - return have_contiguous(parser, type, field, token); - - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); } -zone_nonnull_all -static zone_really_inline int32_t parse_altitude( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, +nonnull_all +static really_inline int32_t parse_altitude( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, const token_t *token) { const char *text = token->data; if (token->length > 255) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); for (; 9u > (uint8_t)((uint8_t)*text - '0'); text++) ; @@ -187,11 +183,11 @@ static zone_really_inline int32_t parse_altitude( for (text++; 9u > (uint8_t)((uint8_t)*text - '0'); text++) ; if (text != token->data + token->length) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); - parser->rdata->octets[parser->rdata->length] = (uint8_t)token->length; - memcpy(parser->rdata->octets + parser->rdata->length + 1, token->data, token->length); - parser->rdata->length += token->length + 1; + *rdata->octets = (uint8_t)token->length; + memcpy(rdata->octets + 1, token->data, token->length); + rdata->octets += 1 + token->length; return 0; } diff --git a/src/generic/ilnp64.h b/src/generic/ilnp64.h index 5cb2c22..64e3475 100644 --- a/src/generic/ilnp64.h +++ b/src/generic/ilnp64.h @@ -1,5 +1,5 @@ /* - * ilnp64.h -- some useful comment + * ilnp64.h -- 64-bit Locator (RFC6742 section 2.3) parser * * Copyright (c) 2023, NLnet Labs. All rights reserved. * @@ -11,18 +11,14 @@ // FIXME: very likely eligable for vectorization (or optimization even), but // gains are small as the type is not frequently used -zone_nonnull_all -static zone_really_inline int32_t parse_ilnp64( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, +nonnull_all +static really_inline int32_t parse_ilnp64( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, const token_t *token) { - int32_t r; - - if ((r = have_contiguous(parser, type, field, token)) < 0) - return r; - uint16_t a[4] = { 0, 0, 0, 0 }; size_t n = 0; const char *p = token->data, *g = p; @@ -48,15 +44,15 @@ static zone_really_inline int32_t parse_ilnp64( } } - if (n != 3 || p == g || p - g > 4 || contiguous[(uint8_t)*p] == CONTIGUOUS) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); + if (n != 3 || p == g || p - g > 4 || classify[(uint8_t)*p] == CONTIGUOUS) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); a[0] = htobe16(a[0]); a[1] = htobe16(a[1]); a[2] = htobe16(a[2]); a[3] = htobe16(a[3]); - memcpy(parser->rdata->octets+parser->rdata->length, a, 8); - parser->rdata->length += 8; - return ZONE_ILNP64; + memcpy(rdata->octets, a, 8); + rdata->octets += 8; + return 0; } #endif // ILNP64_H diff --git a/src/fallback/ip4.h b/src/generic/ip4.h similarity index 70% rename from src/fallback/ip4.h rename to src/generic/ip4.h index c4f70f2..56f7d33 100644 --- a/src/fallback/ip4.h +++ b/src/generic/ip4.h @@ -9,8 +9,8 @@ #ifndef IP4_H #define IP4_H -zone_nonnull_all -static zone_really_inline int32_t scan_ip4( +nonnull_all +static really_inline int32_t scan_ip4( const char *text, uint8_t *wire, size_t *length) { const char *start = text; @@ -44,19 +44,15 @@ static zone_really_inline int32_t scan_ip4( return 4; } -zone_nonnull_all -static zone_really_inline int32_t parse_ip4( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - token_t *token) +nonnull_all +static really_inline int32_t parse_ip4( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *item, + rdata_t *rdata, + const token_t *token) { - int32_t r; - - if ((r = have_contiguous(parser, type, field, token)) < 0) - return r; - - uint8_t *o = &parser->rdata->octets[parser->rdata->length]; + uint8_t *o = rdata->octets; const uint8_t *os = o; uint64_t n = 0; const char *p = token->data; @@ -69,7 +65,7 @@ static zone_really_inline int32_t parse_ip4( n = n * 10 + (uint8_t)d; } else { if (!(p - ps) || p - ps > 3 || n < m[(p - ps)] || n > 255 || o - os > 3) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(item), NAME(type)); ps = p + 1; *o++ = (uint8_t)n; if (*p != '.') @@ -78,11 +74,11 @@ static zone_really_inline int32_t parse_ip4( } } - if (is_contiguous((uint8_t)*p) || o - os != 4) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); + if (p != token->data + token->length || o - os != 4) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(item), NAME(type)); - parser->rdata->length += 4; - return ZONE_IP4; + rdata->octets += 4; + return 0; } #endif // IP4_H diff --git a/src/generic/ip6.h b/src/generic/ip6.h index 72a036d..5634aff 100644 --- a/src/generic/ip6.h +++ b/src/generic/ip6.h @@ -170,8 +170,8 @@ inet_pton6(const char *src, uint8_t *dst) return (int)(src - start); } -zone_nonnull_all -static zone_really_inline int32_t scan_ip6( +nonnull_all +static really_inline int32_t scan_ip6( const char *text, uint8_t *wire, size_t *length) { int len = inet_pton6(text, wire); @@ -181,24 +181,20 @@ static zone_really_inline int32_t scan_ip6( return 16; } -zone_nonnull_all -static zone_really_inline int32_t parse_ip6( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - token_t *token) +nonnull_all +static really_inline int32_t parse_ip6( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *item, + rdata_t *rdata, + const token_t *token) { - int32_t r; - - if ((r = have_contiguous(parser, type, field, token)) < 0) - return r; - - if (inet_pton6(token->data, &parser->rdata->octets[parser->rdata->length]) != -1) { - parser->rdata->length += 16; - return ZONE_IP6; + if (inet_pton6(token->data, rdata->octets) != -1) { + rdata->octets += 16; + return 0; } - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(item), NAME(type)); } #endif // IP6_H diff --git a/src/generic/loc.h b/src/generic/loc.h index abcdd12..59ad13c 100644 --- a/src/generic/loc.h +++ b/src/generic/loc.h @@ -9,8 +9,8 @@ #ifndef LOC_H #define LOC_H -zone_nonnull_all -static zone_really_inline int32_t scan_degrees( +nonnull_all +static really_inline int32_t scan_degrees( const char *text, size_t length, uint32_t *degrees) { uint8_t digits[3]; @@ -44,8 +44,8 @@ static zone_really_inline int32_t scan_degrees( } } -zone_nonnull_all -static zone_really_inline int64_t scan_minutes( +nonnull_all +static really_inline int64_t scan_minutes( const char *text, size_t length, uint32_t *minutes) { uint8_t digits[2]; @@ -69,8 +69,8 @@ static zone_really_inline int64_t scan_minutes( } } -zone_nonnull_all -static zone_really_inline int64_t scan_seconds( +nonnull_all +static really_inline int64_t scan_seconds( const char *text, size_t length, uint32_t *seconds) { uint8_t digits[3]; @@ -124,8 +124,8 @@ static zone_really_inline int64_t scan_seconds( } } -zone_nonnull((1,3)) -static zone_really_inline int32_t scan_altitude( +nonnull((1,3)) +static really_inline int32_t scan_altitude( const char *text, size_t length, uint32_t *altitude) { uint64_t negative = 0, limit = 11, maximum = 4284967295llu; @@ -182,8 +182,8 @@ static zone_really_inline int32_t scan_altitude( } // converts ascii size/precision X * 10**Y(cm) to 0xXY -zone_nonnull((1,3)) -static zone_really_inline int32_t scan_precision( +nonnull((1,3)) +static really_inline int32_t scan_precision( const char *text, size_t length, uint8_t *scientific) { uint64_t meters = 0, centimeters; diff --git a/src/generic/name.h b/src/generic/name.h index 940d773..141b373 100644 --- a/src/generic/name.h +++ b/src/generic/name.h @@ -1,5 +1,5 @@ /* - * name.h -- some useful comment + * name.h -- domain name parser * * Copyright (c) 2022-2023, NLnet Labs. All rights reserved. * @@ -15,8 +15,8 @@ struct name_block { uint64_t dots; }; -zone_nonnull_all -static zone_really_inline void copy_name_block( +nonnull_all +static really_inline void copy_name_block( name_block_t *block, const char *text, uint8_t *wire) { simd_8x32_t input; @@ -26,23 +26,18 @@ static zone_really_inline void copy_name_block( block->dots = simd_find_8x32(&input, '.'); } -#define likely(...) zone_likely(__VA_ARGS__) -#define unlikely(...) zone_unlikely(__VA_ARGS__) - -zone_nonnull_all -static zone_really_inline int32_t scan_name( - zone_parser_t *parser, - const token_t *token, +nonnull_all +static really_inline int32_t scan_name( + const char *data, + size_t tlength, uint8_t octets[255 + ZONE_BLOCK_SIZE], size_t *lengthp) { uint64_t label = 0; - const char *text = token->data; + const char *text = data; uint8_t *wire = octets + 1; name_block_t block; - (void)parser; - octets[0] = 0; // real world domain names quickly exceed 16 octets (www.example.com is @@ -50,10 +45,10 @@ static zone_really_inline int32_t scan_name( // octets. encode in 32-byte blocks. copy_name_block(&block, text, wire); - uint64_t count = 32, length = 0, base = 0, left = token->length; + uint64_t count = 32, length = 0, base = 0, left = tlength; uint64_t carry = 0; - if (token->length < 32) - count = token->length; + if (tlength < 32) + count = tlength; uint64_t mask = (1llu << count) - 1u; // check for escape sequences @@ -62,7 +57,7 @@ static zone_really_inline int32_t scan_name( // check for root, i.e. "." if (unlikely(block.dots & 1llu)) - return ((*lengthp = token->length) == 1 ? 0 : -1); + return ((*lengthp = tlength) == 1 ? 0 : -1); length = count; block.dots &= mask; @@ -156,50 +151,4 @@ static zone_really_inline int32_t scan_name( return carry == 0; } -#undef likely -#undef unlikely - -zone_nonnull_all -static zone_really_inline int32_t parse_name( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token) -{ - int32_t r; - size_t n = 0; - uint8_t *o = &parser->rdata->octets[parser->rdata->length]; - - if (zone_likely(token->code == CONTIGUOUS)) { - // a freestanding "@" denotes the current origin - if (token->data[0] == '@' && token->length == 1) - goto relative; - r = scan_name(parser, token, o, &n); - if (r == 0) - return (void)(parser->rdata->length += n), ZONE_NAME; - if (r > 0) - goto relative; - } else if (token->code == QUOTED) { - if (token->length == 0) - goto invalid; - r = scan_name(parser, token, o, &n); - if (r == 0) - return (void)(parser->rdata->length += n), ZONE_NAME; - if (r > 0) - goto relative; - } else { - return have_string(parser, type, field, token); - } - -invalid: - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - -relative: - if (n > 255 - parser->file->origin.length) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - memcpy(o+n, parser->file->origin.octets, parser->file->origin.length); - parser->rdata->length += n + parser->file->origin.length; - return ZONE_NAME; -} - #endif // NAME_H diff --git a/src/generic/nsap.h b/src/generic/nsap.h new file mode 100644 index 0000000..9499486 --- /dev/null +++ b/src/generic/nsap.h @@ -0,0 +1,63 @@ +/* + * nsap.h -- NSAP (RFC1706) parser + * + * Copyright (c) 2023, NLnet Labs. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ +#ifndef NSAP_H +#define NSAP_H + +// https://datatracker.ietf.org/doc/html/rfc1706 (historic) + +nonnull_all +static really_inline int32_t parse_nsap( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, + const token_t *token) +{ + const uint8_t *data = (const uint8_t *)token->data; + + // RFC1706 section 7 + // NSAP format is "0x" (i.e., a zero followed by an 'x' character) followed + // by a variable length string of hex characters (0 to 9, a to f). The hex + // string is case-insensitive. "."s (i.e., periods) may be inserted in the + // hex string anywhere after "0x" for readability. The "."s have no + // significance other than for readability and are not propagated in the + // protocol (e.g., queries or zone transfers). + if (unlikely((data[0] == '0') ^ ((data[1] & 0xdf) == 'X'))) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + + data += 2; + + while (rdata->octets < rdata->limit) { + uint32_t d0 = base16_table_dec_32bit_d0[data[0]]; + uint32_t d1 = base16_table_dec_32bit_d1[data[1]]; + if ((d0 | d1) > 0xff) { + while (*data == '.') data++; + d0 = base16_table_dec_32bit_d0[data[0]]; + if (d0 > 0xff) + break; + data += 1; + while (*data == '.') data++; + d1 = base16_table_dec_32bit_d1[data[0]]; + if (d1 > 0xff) + goto bad_sequence; + data += 1; + } else { + data += 2; + } + *rdata->octets++ = (uint8_t)(d0 | d1); + } + + if (rdata->octets <= rdata->limit && data == (uint8_t *)token->data + token->length) + return 0; + +bad_sequence: + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); +} + +#endif // NSAP_H diff --git a/src/generic/nsec.h b/src/generic/nsec.h index 0d4e777..52619b2 100644 --- a/src/generic/nsec.h +++ b/src/generic/nsec.h @@ -1,5 +1,5 @@ /* - * nsec.h -- parse NSEC (RFC4043) rdata in (DNS) zone files + * nsec.h -- NSEC (RFC4043) parser * * Copyright (c) 2022-2023, NLnet Labs. All rights reserved. * @@ -9,63 +9,61 @@ #ifndef NSEC_H #define NSEC_H -zone_nonnull_all -static zone_really_inline int32_t scan_type( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token, - uint16_t *code, - const zone_symbol_t **symbol); +nonnull_all +static really_inline int32_t scan_type( + const char *, size_t, uint16_t *, const mnemonic_t **); -typedef uint8_t zone_nsec_t[256 + 2]; +typedef uint8_t nsec_t[256 + 2]; -zone_nonnull_all -static zone_really_inline int32_t parse_nsec( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, +nonnull_all +static really_inline int32_t parse_nsec( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, token_t *token) { - uint16_t code; - const zone_symbol_t *symbol; + if (likely(is_contiguous(token))) { + nsec_t *bitmap = (void *)rdata->octets; + //assert(parser->rdata->octets + // FIXME: convert to static assert + //assert(parser->rdata->length < sizeof(parser->rdata) - (256 * (256 + 2))); - uint8_t *octets = &parser->rdata->octets[parser->rdata->length]; - zone_nsec_t *bitmap = (void *)octets; - // FIXME: convert to static assert - assert(parser->rdata->length < sizeof(parser->rdata) - (256 * (256 + 2))); + uint32_t highest_window = 0; + uint32_t windows[256] = { 0 }; - uint32_t highest_window = 0; - uint32_t windows[256] = { 0 }; + do { + uint16_t code; + const mnemonic_t *mnemonic; - do { - scan_type(parser, type, field, token, &code, &symbol); + if (scan_type(token->data, token->length, &code, &mnemonic) != 1) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); - const uint8_t bit = (uint8_t)(code % 256); - const uint8_t window = code / 256; - const uint8_t block = bit / 8; + const uint8_t bit = (uint8_t)(code % 256); + const uint8_t window = code / 256; + const uint8_t block = bit / 8; - if (!windows[window]) - memset(bitmap[window], 0, sizeof(bitmap[window])); - if (window > highest_window) - highest_window = window; - windows[window] |= 1 << block; - bitmap[window][2 + block] |= (1 << (7 - bit % 8)); - lex(parser, token); - } while (token->code == CONTIGUOUS); + if (!windows[window]) + memset(bitmap[window], 0, sizeof(bitmap[window])); + if (window > highest_window) + highest_window = window; + windows[window] |= 1 << block; + bitmap[window][2 + block] |= (1 << (7 - bit % 8)); + take(parser, token); + } while (is_contiguous(token)); - for (uint32_t window = 0; window <= highest_window; window++) { - if (!windows[window]) - continue; - const uint8_t blocks = (uint8_t)(64 - leading_zeroes(windows[window])); - memmove(&octets[0], &bitmap[window], 2 + blocks); - octets[0] = (uint8_t)window; - octets[1] = blocks; - octets += 2 + blocks; + for (uint32_t window = 0; window <= highest_window; window++) { + if (!windows[window]) + continue; + const uint8_t blocks = (uint8_t)(64 - leading_zeroes(windows[window])); + memmove(rdata->octets, &bitmap[window], 2 + blocks); + rdata->octets[0] = (uint8_t)window; + rdata->octets[1] = blocks; + rdata->octets += 2 + blocks; + } } - parser->rdata->length += (uintptr_t)octets - (uintptr_t)bitmap; - return ZONE_TYPE_BITMAP; + return have_delimiter(parser, type, token); } #endif // NSEC_H diff --git a/src/generic/number.h b/src/generic/number.h index d71bd0e..a1e9403 100644 --- a/src/generic/number.h +++ b/src/generic/number.h @@ -1,5 +1,5 @@ /* - * number.h -- some useful comment + * number.h -- integer parsing routines * * Copyright (c) 2022-2023, NLnet Labs. All rights reserved. * @@ -9,169 +9,120 @@ #ifndef NUMBER_H #define NUMBER_H -// FIXME: remove in favor of specialized functions, much easier -zone_nonnull_all -static zone_really_inline int32_t parse_symbol8( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token) +nonnull((1,3)) +static really_inline int32_t scan_int8( + const char *data, size_t length, uint8_t *number) { - int32_t r; - - if ((r = have_contiguous(parser, type, field, token)) < 0) - return r; - - // FIXME: implement generic number scanning - uint64_t n = 0; - const char *p = token->data; - for (;; p++) { - const uint64_t d = (uint8_t)*p - '0'; - if (d > 9) - break; - n = n * 10 + d; - } + uint32_t sum = (uint8_t)data[0] - '0'; + + if (sum > 9 || length > 3) + return 0; - // FIXME: replace with simple length check - if (is_contiguous((uint8_t)*p)) { - const zone_symbol_t *s; - if (!(s = lookup_symbol(&field->symbols, token))) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - n = (uint8_t)s->value; - } else { - if (n > UINT8_MAX || p - token->data > 3) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); + uint32_t non_zero = (sum != 0) | (length == 1); + + for (size_t count=1; count < length; count++) { + const uint8_t digit = (uint8_t)data[count] - '0'; + sum = sum * 10 + digit; + if (digit > 9) + return 0; } - parser->rdata->octets[parser->rdata->length] = (uint8_t)n; - parser->rdata->length += sizeof(uint8_t); - return ZONE_INT8; + *number = (uint8_t)sum; + return sum <= 255u && non_zero; } -zone_nonnull_all -static zone_really_inline int32_t parse_symbol16( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token) +nonnull((1,3)) +static really_inline int32_t scan_int16( + const char *data, size_t length, uint16_t *number) { - int32_t r; - - if ((r = have_contiguous(parser, type, field, token)) < 0) - return r; - - uint64_t n = 0; - const char *p = token->data; - for (;; p++) { - const uint64_t d = (uint8_t)*p - '0'; - if (d > 9) - break; - n = n * 10 + d; - } + uint32_t sum = (uint8_t)data[0] - '0'; - if (is_contiguous((uint8_t)*p)) { - const zone_symbol_t *s; - if (!(s = lookup_symbol(&field->symbols, token))) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - n = (uint16_t)s->value; - } else { - if (n > UINT16_MAX || p - token->data > 5) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); + if (sum > 9 || length > 5) + return 0; + + uint32_t non_zero = (sum != 0) | (length == 1); + + for (size_t count=1; count < length; count++) { + const uint8_t digit = (uint8_t)data[count] - '0'; + sum = sum * 10 + digit; + if (digit > 9) + return 0; } - uint16_t n16 = htobe16((uint16_t)n); - memcpy(&parser->rdata->octets[parser->rdata->length], &n16, sizeof(n16)); - parser->rdata->length += sizeof(n16); - return ZONE_INT16; + *number = (uint16_t)sum; + return sum <= 65535u && non_zero; } -zone_nonnull_all -static zone_really_inline int32_t parse_int8( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token) +nonnull((1,3)) +static really_inline int32_t scan_int32( + const char *data, size_t length, uint32_t *number) { - int32_t r; - - if ((r = have_contiguous(parser, type, field, token)) < 0) - return r; - - uint64_t n = 0; - const char *p = token->data; - for (;; p++) { - const uint64_t d = (uint8_t)*p - '0'; - if (d > 9) - break; - n = n * 10 + d; - } + uint64_t sum = (uint8_t)data[0] - '0'; - if (n > UINT8_MAX || p - token->data > 3 || is_contiguous((uint8_t)*p)) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); + if (sum > 9 || length > 10) + return 0; - parser->rdata->octets[parser->rdata->length] = (uint8_t)n; - parser->rdata->length += sizeof(uint8_t); - return ZONE_INT8; -} + uint32_t non_zero = (sum != 0) | (length == 1); -zone_nonnull_all -static zone_really_inline int32_t parse_int16( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token) -{ - int32_t r; - - if ((r = have_contiguous(parser, type, field, token)) < 0) - return r; - - uint64_t n = 0; - const char *p = token->data; - for (;; p++) { - const uint64_t d = (uint8_t)*p - '0'; - if (d > 9) - break; - n = n * 10 + d; + for (size_t count=1; count < length; count++) { + const uint8_t digit = (uint8_t)data[count] - '0'; + sum = sum * 10 + digit; + if (digit > 9) + return 0; } - if (n > UINT16_MAX || p - token->data > 5 || is_contiguous((uint8_t)*p)) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - - uint16_t n16 = htobe16((uint16_t)n); - memcpy(&parser->rdata->octets[parser->rdata->length], &n16, sizeof(n16)); - parser->rdata->length += sizeof(n16); - return ZONE_INT16; + *number = (uint32_t)sum; + return sum <= 4294967295u && non_zero; } -zone_nonnull_all -static zone_really_inline int32_t parse_int32( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, +nonnull_all +static really_inline int32_t parse_int8( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, const token_t *token) { - int32_t r; - - if ((r = have_contiguous(parser, type, field, token)) < 0) - return r; - - uint64_t n = 0; - const char *p = token->data; - for (;; p++) { - const uint64_t d = (uint8_t)*p - '0'; - if (d > 9) - break; - n = n * 10 + d; - } + uint8_t number; + if (!scan_int8(token->data, token->length, &number)) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + memcpy(rdata->octets, &number, 1); + *rdata->octets++ = number; + return 0; +} - if (n > UINT32_MAX || p - token->data > 10 || is_contiguous((uint8_t)*p)) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); +nonnull_all +static really_inline int32_t parse_int16( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, + const token_t *token) +{ + uint16_t number; + if (!scan_int16(token->data, token->length, &number)) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + number = htobe16(number); + memcpy(rdata->octets, &number, 2); + rdata->octets += 2; + return 0; +} - const uint32_t n32 = htobe32((uint32_t)n); - memcpy(&parser->rdata->octets[parser->rdata->length], &n32, sizeof(n32)); - parser->rdata->length += sizeof(n32); - return ZONE_INT32; +nonnull_all +static really_inline int32_t parse_int32( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, + const token_t *token) +{ + uint32_t number; + if (!scan_int32(token->data, token->length, &number)) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + number = htobe32(number); + memcpy(rdata->octets, &number, 4); + rdata->octets += 4; + return 0; } #endif // NUMBER_H diff --git a/src/generic/nxt.h b/src/generic/nxt.h index 270eb7f..26873f1 100644 --- a/src/generic/nxt.h +++ b/src/generic/nxt.h @@ -1,5 +1,5 @@ /* - * nxt.h + * nxt.h - NXT (RFC2535) parser * * Copyright (c) 2023, NLnet Labs. All rights reserved. * @@ -9,50 +9,48 @@ #ifndef NXT_H #define NXT_H -zone_nonnull_all -static zone_really_inline int32_t scan_type( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token, +nonnull_all +static really_inline int32_t scan_type( + const char *data, + size_t length, uint16_t *code, - const zone_symbol_t **symbol); - -zone_nonnull_all -static zone_really_inline int32_t parse_nxt( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, + const mnemonic_t **mnemonic); + +nonnull_all +static really_inline int32_t parse_nxt( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, token_t *token) { uint16_t code; - const zone_symbol_t *symbol; + const mnemonic_t *mnemonic; - if (token->code == CONTIGUOUS) { - scan_type(parser, type, field, token, &code, &symbol); - int32_t result; + if (is_contiguous(token)) { + if (scan_type(token->data, token->length, &code, &mnemonic) != 1) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); uint8_t bit = (uint8_t)(code % 8); uint8_t block = (uint8_t)(code / 8), highest_block = block; - uint8_t *octets = &parser->rdata->octets[parser->rdata->length]; - memset(octets, 0, block + 1); - octets[block] = (uint8_t)(1 << (7 - bit)); + memset(rdata->octets, 0, block + 1); + rdata->octets[block] = (uint8_t)(1 << (7 - bit)); - lex(parser, token); - while (token->code == CONTIGUOUS) { - if ((result = scan_type(parser, type, field, token, &code, &symbol)) < 0) - return result; + take(parser, token); + while (is_contiguous(token)) { + if (scan_type(token->data, token->length, &code, &mnemonic) != 1) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); bit = (uint8_t)(code % 8); block = (uint8_t)(code / 8); if (block > highest_block) { - memset(&octets[highest_block+1], 0, block - highest_block); + memset(&rdata->octets[highest_block+1], 0, block - highest_block); highest_block = block; } - octets[block] |= 1 << (7 - bit); - lex(parser, token); + rdata->octets[block] |= 1 << (7 - bit); + take(parser, token); } - parser->rdata->length += highest_block + 1; + rdata->octets += highest_block + 1; } return have_delimiter(parser, type, token); diff --git a/src/generic/parser.h b/src/generic/parser.h new file mode 100644 index 0000000..660a938 --- /dev/null +++ b/src/generic/parser.h @@ -0,0 +1,957 @@ +/* + * parser.h -- base parser definitions + * + * Copyright (c) 2023, NLnet Labs. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ +#ifndef PARSER_H +#define PARSER_H + +#include +#include +#include +#include + +#if _WIN32 +# define strncasecmp(s1, s2, n) _strnicmp((s1), (s2), (n)) +#else +# include +#endif + +typedef zone_parser_t parser_t; // convenience +typedef zone_file_t file_t; +typedef zone_name_buffer_t name_buffer_t; +typedef zone_rdata_buffer_t rdata_buffer_t; + +typedef struct token token_t; +struct token { + int32_t code; + const char *data; + size_t length; +}; + +// view of current RDATA buffer +typedef struct rdata rdata_t; +struct rdata { + uint8_t *octets; + uint8_t *limit; +}; + +typedef struct string string_t; +struct string { + const char *data; + size_t length; +}; + +typedef struct mnemonic mnemonic_t; +struct mnemonic { + struct { + char data[16]; + size_t length; + } key; + uint32_t value; +}; + +#define NAME(info) ((info)->name.key.data) + + +typedef struct svc_param_info svc_param_info_t; +struct svc_param_info; + +typedef struct rdata_info rdata_info_t; +struct rdata_info; + +typedef struct type_info type_info_t; +struct type_info; + + +typedef int32_t (*parse_svc_param_t)( + parser_t *, + const type_info_t *, + const rdata_info_t *, + uint16_t, + const svc_param_info_t *, + rdata_t *, + const token_t *); + +struct svc_param_info { + mnemonic_t name; + bool has_value; + parse_svc_param_t parse, parse_non_strict; +}; + +struct rdata_info { + struct { string_t key; } name; // convenience +}; + +typedef struct class_info class_info_t; +struct class_info { + mnemonic_t name; +}; + +typedef int32_t (*check_rr_t)( + parser_t *, + const type_info_t *, + const rdata_t *); + +typedef int32_t (*parse_rdata_t)( + parser_t *, + const type_info_t *, + rdata_t *, + token_t *); + +struct type_info { + mnemonic_t name; + uint16_t defined_in; + bool is_obsolete; + bool is_experimental; + struct { + size_t length; + const rdata_info_t *fields; + } rdata; + check_rr_t check; + parse_rdata_t parse; +}; + + +#define END_OF_FILE (0) +#define CONTIGUOUS (1<<0) +#define QUOTED (1<<1) +#define LINE_FEED (1<<2) +#define LEFT_PAREN (1<<3) +#define RIGHT_PAREN (1<<4) +#define BLANK (1<<6) +#define COMMENT (1<<7) + +static const uint8_t classify[256] = { + // 0x00 = "\0" + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x00 - 0x07 + // 0x09 = "\t", 0x0a = "\n", 0x0d = "\r" + 0x01, 0x40, 0x04, 0x01, 0x01, 0x40, 0x01, 0x01, // 0x08 - 0x0f + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x10 - 0x17 + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x18 - 0x1f + // 0x20 = " ", 0x22 = "\"" + 0x40, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x20 - 0x27 + // 0x28 = "(", 0x29 = ")" + 0x08, 0x10, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x28 - 0x2f + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x30 - 0x37 + // 0x3b = ";" + 0x01, 0x01, 0x01, 0x80, 0x01, 0x01, 0x01, 0x01, // 0x38 - 0x3f + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x40 - 0x47 + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x48 - 0x4f + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x50 - 0x57 + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x58 - 0x5f + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x60 - 0x67 + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x68 - 0x6f + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x70 - 0x77 + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x78 - 0x7f + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x80 - 0x87 + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x88 - 0x8f + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x90 - 0x97 + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x98 - 0x9f + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xa0 - 0xa7 + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xa8 - 0xaf + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xb0 - 0xb7 + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xb8 - 0xbf + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xc0 - 0xc7 + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xc8 - 0xcf + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xd0 - 0xd7 + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xd8 - 0xdf + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xe0 - 0xe7 + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xe8 - 0xef + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xf8 - 0xf7 + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 // 0xf8 - 0xff +}; + + + +// special constant to mark line feeds with additional line count. i.e. CRLF +// within text. line feeds have no special meaning other than terminating the +// record and require no further processing +static const char line_feed[ZONE_BLOCK_SIZE] = { '\n', '\0' }; + +// special constant used as data on errors +static const char end_of_file[ZONE_BLOCK_SIZE] = { '\0' }; + +extern int32_t zone_open_file( + parser_t *, const char *path, size_t length, zone_file_t **); + +extern void zone_close_file( + parser_t *, zone_file_t *); + +extern void zone_vlog(parser_t *, uint32_t, const char *, va_list); + +nonnull((1)) +static really_inline void defer_error(token_t *token, int32_t code) +{ + token->code = code; + token->data = end_of_file; + token->length = 0; +} + +nonnull((1,3)) +warn_unused_result +static never_inline int32_t raise_error( + parser_t *parser, int32_t code, const char *format, ...) +{ + va_list arguments; + uint32_t category = ZONE_ERROR; + if (code == ZONE_SEMANTIC_ERROR && parser->options.non_strict) + category = ZONE_WARNING; + va_start(arguments, format); + zone_vlog(parser, category, format, arguments); + va_end(arguments); + if (category == ZONE_WARNING) + return 0; + return code; +} + +#define RAISE_ERROR(parser, code, ...) \ + do { \ + return raise_error((parser), (code), __VA_ARGS__); \ + } while (0) + +#define OUT_OF_MEMORY(parser, ...) \ + RAISE_ERROR((parser), ZONE_OUT_OF_MEMORY, __VA_ARGS__) +#define READ_ERROR(parser, ...) \ + RAISE_ERROR((parser), ZONE_READ_ERROR, __VA_ARGS__) +#define NOT_IMPLEMENTED(parser, ...) \ + RAISE_ERROR((parser), ZONE_NOT_IMPLEMENTED, __VA_ARGS__) +#define NOT_PERMITTED(parser, ...) \ + RAISE_ERROR((parser), ZONE_NOT_PERMITTED, __VA_ARGS__) + +// semantic errors in zone files are special as a secondary may choose +// to report, but otherwise ignore them. e.g. a TTL with the MSB set. cases +// where the data can be presented in wire format but is otherwise considered +// invalid. e.g. a TTL is limited to 32-bits, values that require more bits +// are invalid without exception, but secondaries may choose to accept values +// with the MSB set in order to update the zone +#define SEMANTIC_ERROR(parser, ...) \ + do { \ + if (raise_error((parser), ZONE_SEMANTIC_ERROR, __VA_ARGS__)) \ + return ZONE_SEMANTIC_ERROR; \ + } while (0) + + +nonnull_all +warn_unused_result +static really_inline int32_t refill(parser_t *parser) +{ + // refill if possible (i.e. not if string or if file is empty) + if (parser->file->end_of_file != ZONE_HAVE_DATA) + return 0; + + // move unread data to start of buffer + char *data = parser->file->buffer.data + parser->file->buffer.index; + // account for non-terminated character-strings + if (*parser->file->fields.head) + data = (char *)*parser->file->fields.head; + + *parser->file->fields.head = parser->file->buffer.data; + // account for unread data left in buffer + size_t length = (size_t) + ((parser->file->buffer.data + parser->file->buffer.length) - data); + // account for non-terminated character-string left in buffer + assert((parser->file->buffer.data + parser->file->buffer.index) >= data); + size_t index = (size_t) + ((parser->file->buffer.data + parser->file->buffer.index) - data); + memmove(parser->file->buffer.data, data, length); + parser->file->buffer.length = length; + parser->file->buffer.index = index; + parser->file->buffer.data[length] = '\0'; + + // allocate extra space if required + if (parser->file->buffer.length == parser->file->buffer.size) { + size_t size = parser->file->buffer.size + ZONE_WINDOW_SIZE; + if (!(data = realloc(data, size + 1))) + OUT_OF_MEMORY(parser, "Cannot increase buffer size to %zu", size); + parser->file->buffer.size = size; + parser->file->buffer.data = data; + } + + size_t count = fread(parser->file->buffer.data + parser->file->buffer.length, + sizeof(parser->file->buffer.data[0]), + parser->file->buffer.size - parser->file->buffer.length, + parser->file->handle); + + if (!count && ferror(parser->file->handle)) + READ_ERROR(parser, "Cannot refill buffer"); + + // always null-terminate for terminating token + parser->file->buffer.length += (size_t)count; + parser->file->buffer.data[parser->file->buffer.length] = '\0'; + parser->file->end_of_file = feof(parser->file->handle) != 0; + return 0; +} + +nonnull_all +warn_unused_result +static really_inline int32_t reindex(parser_t *parser); + +// do not invoke directly +nonnull_all +warn_unused_result +static really_inline int32_t advance(parser_t *parser) +{ + int32_t code; + + // save embedded line count (quoted or escaped newlines) + parser->file->lines.tape[0] = parser->file->lines.tail[0]; + parser->file->lines.head = parser->file->lines.tape; + parser->file->lines.tail = parser->file->lines.tape; + // restore non-terminated token (partial quoted or contiguous) + parser->file->fields.tape[0] = parser->file->fields.tail[1]; + parser->file->fields.head = parser->file->fields.tape; + parser->file->fields.tail = + parser->file->fields.tape + (!!parser->file->fields.tape[0]); + // reset delimiters + parser->file->delimiters.head = parser->file->delimiters.tape; + parser->file->delimiters.tail = parser->file->delimiters.tape; + + if ((code = refill(parser)) < 0) + return code; + + if (reindex(parser)) { + // save non-terminated token + parser->file->fields.tail[0] = parser->file->fields.tail[-1]; + parser->file->fields.tail--; + } else { + parser->file->fields.tail[1] = NULL; + } + + // FIXME: if tail is still equal to tape, refill immediately?! + + // terminate (end of buffer is null-terminated) + parser->file->fields.tail[0] = + parser->file->buffer.data + parser->file->buffer.length; + parser->file->delimiters.tail[0] = + parser->file->buffer.data + parser->file->buffer.length; + // start-of-line must be false if start of tape is not start of buffer + if (*parser->file->fields.head != parser->file->buffer.data) + parser->file->start_of_line = false; + //parser->file->start_of_line = start_of_line; + return 0; +} + +nonnull_all +warn_unused_result +static really_inline bool is_contiguous(const token_t *token) +{ + return token->code == CONTIGUOUS; +} + +nonnull_all +warn_unused_result +static really_inline bool is_quoted(const token_t *token) +{ + return token->code == QUOTED; +} + +nonnull_all +warn_unused_result +static really_inline bool is_contiguous_or_quoted(const token_t *token) +{ + return (token->code == CONTIGUOUS || token->code == QUOTED); +} + +nonnull_all +warn_unused_result +static really_inline bool is_delimiter(const token_t *token) +{ + return (token->code == LINE_FEED || token->code == END_OF_FILE); +} + +nonnull_all +warn_unused_result +static really_inline bool is_end_of_file(const token_t *token) +{ + return token->code == 0; +} + + +#define SYNTAX_ERROR(parser, token, ...) \ + do { \ + zone_log((parser), ZONE_ERROR, __VA_ARGS__); \ + defer_error((token), ZONE_SYNTAX_ERROR); \ + return; \ + } while (0) + +#define ERROR(parser, token, code) \ + do { \ + defer_error(token, code); \ + return; \ + } while (0) + + +nonnull_all +static never_inline void maybe_take(parser_t *parser, token_t *token) +{ + for (;;) { + token->data = *parser->file->fields.head; + token->code = (int32_t)classify[ (uint8_t)**parser->file->fields.head ]; + if (likely(token->code == CONTIGUOUS)) { + assert(*parser->file->delimiters.head > *parser->file->fields.head); + token->length = (uintptr_t)*parser->file->delimiters.head - + (uintptr_t)*parser->file->fields.head; + parser->file->fields.head++; + parser->file->delimiters.head++; + return; + } else if (token->code == LINE_FEED) { + if (unlikely(token->data == line_feed)) + parser->file->span += *parser->file->lines.head++; + parser->file->span++; + parser->file->fields.head++; + if (unlikely(parser->file->grouped)) + continue; + parser->file->line += parser->file->span; + parser->file->span = 0; + parser->file->start_of_line = classify[ (uint8_t)*(token->data+1) ] != BLANK; + token->length = 1; + return; + } else if (token->code == QUOTED) { + assert(*parser->file->delimiters.head > *parser->file->fields.head); + token->data++; + token->length = ((uintptr_t)*parser->file->delimiters.head - + (uintptr_t)*parser->file->fields.head) - 1; + parser->file->fields.head++; + parser->file->delimiters.head++; + return; + } else if (token->code == END_OF_FILE) { + int32_t code; + if (parser->file->end_of_file == ZONE_NO_MORE_DATA) { + if (parser->file->grouped) + SYNTAX_ERROR(parser, token, "Missing closing brace"); + token->data = end_of_file; + token->length = 1; + if (!parser->file->includer) + return; + file_t *file = parser->file; + parser->file = parser->file->includer; + parser->owner = &parser->file->owner; + zone_close_file(parser, file); + return; + } else if (unlikely((code = advance(parser)) < 0)) { + ERROR(parser, token, code); + } + } else if (token->code == LEFT_PAREN) { + if (unlikely(parser->file->grouped)) + SYNTAX_ERROR(parser, token, "Nested opening brace"); + parser->file->grouped = true; + parser->file->fields.head++; + } else { + assert(token->code == RIGHT_PAREN); + if (unlikely(!parser->file->grouped)) + SYNTAX_ERROR(parser, token, "Missing opening brace"); + parser->file->grouped = false; + parser->file->fields.head++; + } + } +} + +nonnull_all +static really_inline void take(parser_t *parser, token_t *token) +{ + for (;;) { + token->data = *parser->file->fields.head; + token->code = (int32_t)classify[ (uint8_t)**parser->file->fields.head ]; + if (likely(token->code == CONTIGUOUS)) { + assert(*parser->file->delimiters.head > *parser->file->fields.head); + token->length = (uintptr_t)*parser->file->delimiters.head - + (uintptr_t)*parser->file->fields.head; + parser->file->fields.head++; + parser->file->delimiters.head++; + return; + } else if (token->code == LINE_FEED) { + if (unlikely(token->data == line_feed)) + parser->file->span += *parser->file->lines.head++; + parser->file->span++; + parser->file->fields.head++; + if (unlikely(parser->file->grouped)) + continue; + parser->file->line += parser->file->span; + parser->file->span = 0; + parser->file->start_of_line = classify[ (uint8_t)*(token->data+1) ] != BLANK; + token->length = 1; + return; + } else if (token->code == QUOTED) { + assert(*parser->file->delimiters.head > *parser->file->fields.head); + token->data++; + token->length = ((uintptr_t)*parser->file->delimiters.head - + (uintptr_t)*parser->file->fields.head) - 1; + parser->file->fields.head++; + parser->file->delimiters.head++; + return; + } else { + maybe_take(parser, token); + return; + } + } +} + +#undef SYNTAX_ERROR +#undef ERROR + +// token sequence is predictable. fields typically require a specific type, +// except names, strings and SvcParams. even then, names are typically not +// quoted and strings (or text) are typically quoted. implement specialized +// tape accessors for performance and reduction in binary size. + +#define SYNTAX_ERROR(parser, token, ...) \ + do { \ + zone_log((parser), ZONE_ERROR, __VA_ARGS__); \ + defer_error((token), ZONE_SYNTAX_ERROR); \ + return ZONE_SYNTAX_ERROR; \ + } while (0) + +#define ERROR(parser, token, code) \ + do { \ + defer_error((token), (code)); \ + return (code); \ + } while (0) + +nonnull_all +warn_unused_result +static never_inline int32_t dont_have_contiguous( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + token_t *token) +{ + if (token->code < 0) + return token->code; + assert(token->code != CONTIGUOUS); + if (token->code == QUOTED) + SYNTAX_ERROR(parser, token, "Invalid %s in %s", NAME(field), NAME(type)); + assert(token->code == END_OF_FILE || token->code == LINE_FEED); + SYNTAX_ERROR(parser, token, "Missing %s in %s", NAME(field), NAME(type)); +} + +nonnull_all +warn_unused_result +static really_inline int32_t have_contiguous( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + token_t *token) +{ + if (unlikely(token->code != CONTIGUOUS)) + return dont_have_contiguous(parser, type, field, token); + return 0; +} + +nonnull_all +warn_unused_result +static never_inline int32_t maybe_take_contiguous( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + token_t *token) +{ + int32_t code; + + assert(token->code != CONTIGUOUS); + + for (;;) { + if (likely(token->code == CONTIGUOUS)) { + assert(*parser->file->delimiters.head > *parser->file->fields.head); + token->length = (uintptr_t)*parser->file->delimiters.head - + (uintptr_t)*parser->file->fields.head; + parser->file->fields.head++; + parser->file->delimiters.head++; + return 0; + } else if (token->code == END_OF_FILE) { + if (parser->file->end_of_file == ZONE_NO_MORE_DATA) + SYNTAX_ERROR(parser, token, "Missing %s in %s", NAME(field), NAME(type)); + if ((code = advance(parser)) < 0) + ERROR(parser, token, code); + } else if (token->code == QUOTED) { + SYNTAX_ERROR(parser, token, "Invalid %s in %s", NAME(field), NAME(type)); + } else if (token->code == LEFT_PAREN) { + if (parser->file->grouped) + SYNTAX_ERROR(parser, token, "Nested opening brace"); + parser->file->grouped = true; + parser->file->fields.head++; + } else if (token->code == RIGHT_PAREN) { + if (!parser->file->grouped) + SYNTAX_ERROR(parser, token, "Missing opening brace"); + parser->file->grouped = false; + parser->file->fields.head++; + } else if (token->code == LINE_FEED) { + if (token->data == line_feed) + parser->file->span += *parser->file->lines.head++; + parser->file->span++; + if (!parser->file->grouped) + SYNTAX_ERROR(parser, token, "Missing %s in %s", NAME(field), NAME(type)); + parser->file->fields.head++; + } + token->data = *parser->file->fields.head; + token->code = (int32_t)classify[ (uint8_t)**parser->file->fields.head ]; + } +} + +nonnull_all +warn_unused_result +static really_inline int32_t take_contiguous( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + token_t *token) +{ + token->data = *parser->file->fields.head; + token->code = (int32_t)classify[ (uint8_t)**parser->file->fields.head ]; + if (unlikely(token->code != CONTIGUOUS)) + return maybe_take_contiguous(parser, type, field, token); + assert(*parser->file->delimiters.head > *parser->file->fields.head); + token->length = (uintptr_t)*parser->file->delimiters.head - + (uintptr_t)*parser->file->fields.head; + parser->file->fields.head++; + parser->file->delimiters.head++; + return 0; +} + +nonnull_all +warn_unused_result +static never_inline int32_t dont_have_quoted( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + token_t *token) +{ + if (token->code < 0) + return token->code; + assert(token->code != QUOTED); + if (token->code == CONTIGUOUS) + SYNTAX_ERROR(parser, token, "Invalid %s in %s", NAME(field), NAME(type)); + assert(token->code == END_OF_FILE || token->code == LINE_FEED); + SYNTAX_ERROR(parser, token, "Missing %s in %s", NAME(field), NAME(type)); +} + +nonnull_all +warn_unused_result +static really_inline int32_t have_quoted( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + token_t *token) +{ + if (unlikely(token->code != QUOTED)) + return dont_have_quoted(parser, type, field, token); + return 0; +} + +nonnull_all +warn_unused_result +static never_inline int32_t maybe_take_quoted( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + token_t *token) +{ + int32_t code; + + assert(token->code != QUOTED); + + for (;;) { + if (likely(token->code == QUOTED)) { + assert(*parser->file->fields.head > *parser->file->delimiters.head); + token->data++; + token->length = (uintptr_t)*parser->file->delimiters.head - + (uintptr_t)*parser->file->fields.head; + parser->file->fields.head++; + parser->file->delimiters.head++; + return 0; + } else if (token->code == END_OF_FILE) { + if (parser->file->end_of_file == ZONE_NO_MORE_DATA) + SYNTAX_ERROR(parser, token, "Missing %s in %s", NAME(field), NAME(type)); + if ((code = advance(parser)) < 0) + ERROR(parser, token, code); + } else if (token->code == CONTIGUOUS) { + SYNTAX_ERROR(parser, token, "Invalid %s in %s", NAME(field), NAME(type)); + } else if (token->code == LEFT_PAREN) { + if (parser->file->grouped) + SYNTAX_ERROR(parser, token, "Nested opening brace"); + parser->file->grouped = true; + parser->file->fields.head++; + } else if (token->code == RIGHT_PAREN) { + if (!parser->file->grouped) + SYNTAX_ERROR(parser, token, "Missing opening brace"); + parser->file->grouped = false; + parser->file->fields.head++; + } else if (token->code == LINE_FEED) { + if (token->data == line_feed) + parser->file->span += *parser->file->lines.head++; + parser->file->span++; + if (!parser->file->grouped) + SYNTAX_ERROR(parser, token, "Missing %s in %s", NAME(field), NAME(type)); + parser->file->fields.head++; + } else { + assert(token->code < 0); + return token->code; + } + token->data = *parser->file->fields.head; + token->code = (int32_t)classify[ (uint8_t)**parser->file->fields.head ]; + } +} + +nonnull_all +warn_unused_result +static really_inline int32_t take_quoted( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + token_t *token) +{ + token->data = *parser->file->fields.head; + token->code = (int32_t)classify[ (uint8_t)**parser->file->fields.head ]; + if (unlikely((token->code != QUOTED))) + return maybe_take_quoted(parser, type, field, token); + assert(*parser->file->delimiters.head > *parser->file->fields.head); + token->data++; + token->length = ((uintptr_t)*parser->file->delimiters.head - + (uintptr_t)*parser->file->fields.head) - 1; + parser->file->fields.head++; + parser->file->delimiters.head++; + return 0; +} + +nonnull_all +warn_unused_result +static never_inline int32_t dont_have_contiguous_or_quoted( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + token_t *token) +{ + if (token->code == QUOTED || token->code < 0) + return token->code; + assert(token->code == END_OF_FILE || token->code == LINE_FEED); + SYNTAX_ERROR(parser, token, "Missing %s in %s", NAME(field), NAME(type)); +} + +nonnull_all +warn_unused_result +static really_inline int32_t have_contiguous_or_quoted( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + token_t *token) +{ + if (unlikely(token->code != CONTIGUOUS)) + return dont_have_contiguous_or_quoted(parser, type, field, token); + return 0; +} + +nonnull_all +warn_unused_result +static never_inline int32_t maybe_take_contiguous_or_quoted( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + token_t *token) +{ + int32_t code; + + for (;;) { + if (likely(token->code == CONTIGUOUS)) { + assert(*parser->file->delimiters.head > *parser->file->fields.head); + token->length = (uintptr_t)*parser->file->delimiters.head - + (uintptr_t)*parser->file->fields.head; + parser->file->fields.head++; + parser->file->delimiters.head++; + return 0; + } else if (token->code == QUOTED) { + assert(*parser->file->delimiters.head > *parser->file->fields.head); + token->data++; + token->length = ((uintptr_t)*parser->file->delimiters.head - + (uintptr_t)*parser->file->fields.head) - 1; + parser->file->fields.head++; + parser->file->delimiters.head++; + return 0; + } else if (token->code == END_OF_FILE) { + if (parser->file->end_of_file == ZONE_NO_MORE_DATA) + SYNTAX_ERROR(parser, token, "Missing %s in %s", NAME(field), NAME(type)); + if ((code = advance(parser)) < 0) + ERROR(parser, token, code); + } else if (token->code == LEFT_PAREN) { + if (parser->file->grouped) + SYNTAX_ERROR(parser, token, "Nested opening brace"); + parser->file->grouped = true; + parser->file->fields.head++; + } else if (token->code == RIGHT_PAREN) { + if (!parser->file->grouped) + SYNTAX_ERROR(parser, token, "Missing opening brace"); + parser->file->grouped = false; + parser->file->fields.head++; + } else if (token->code == LINE_FEED) { + if (token->data == line_feed) + parser->file->span += *parser->file->lines.head++; + parser->file->span++; + if (!parser->file->grouped) + SYNTAX_ERROR(parser, token, "Missing %s in %s", NAME(field), NAME(type)); + parser->file->fields.head++; + } + token->data = *parser->file->fields.head; + token->code = (int32_t)classify[ (uint8_t)**parser->file->fields.head ]; + } +} + +nonnull_all +warn_unused_result +static really_inline int32_t take_contiguous_or_quoted( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + token_t *token) +{ + token->data = *parser->file->fields.head; + token->code = (int32_t)classify[ (uint8_t)**parser->file->fields.head ]; + if (likely(token->code == CONTIGUOUS)) { + assert(*parser->file->delimiters.head > *parser->file->fields.head); + token->length = (uintptr_t)*parser->file->delimiters.head - + (uintptr_t)*parser->file->fields.head; + parser->file->fields.head++; + parser->file->delimiters.head++; + return 0; + } else { + return maybe_take_contiguous_or_quoted(parser, type, field, token); + } +} + +nonnull_all +warn_unused_result +static really_inline int32_t take_quoted_or_contiguous( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + token_t *token) +{ + token->data = *parser->file->fields.head; + token->code = (int32_t)classify[ (uint8_t)**parser->file->fields.head ]; + if (likely(token->code == QUOTED)) { + assert(*parser->file->delimiters.head > *parser->file->fields.head); + token->data++; + token->length = ((uintptr_t)*parser->file->delimiters.head - + (uintptr_t)*parser->file->fields.head) - 1; + parser->file->fields.head++; + parser->file->delimiters.head++; + return 0; + } else { + return maybe_take_contiguous_or_quoted(parser, type, field, token); + } +} + +diagnostic_push() +clang_diagnostic_ignored(unused-function) +gcc_diagnostic_ignored(unused-function) + +nonnull_all +warn_unused_result +static never_inline int32_t dont_have_delimiter( + parser_t *parser, const type_info_t *type, token_t *token) +{ + if (token->code == END_OF_FILE || token->code < 0) + return token->code; + assert(token->code == CONTIGUOUS || token->code == QUOTED); + SYNTAX_ERROR(parser, token, "Trailing data in %s", NAME(type)); +} + +nonnull_all +warn_unused_result +static never_inline int32_t have_delimiter( + parser_t *parser, const type_info_t *type, token_t *token) +{ + if (unlikely(token->code != LINE_FEED)) + return dont_have_delimiter(parser, type, token); + return 0; +} + +diagnostic_pop() + +nonnull_all +warn_unused_result +static never_inline int32_t maybe_take_delimiter( + parser_t *parser, const type_info_t *type, token_t *token) +{ + int32_t code; + + for (;;) { + if (likely(token->code == LINE_FEED)) { + if (unlikely(token->data == line_feed)) + parser->file->span += *parser->file->lines.head++; + if (unlikely(parser->file->grouped)) { + parser->file->span++; + parser->file->fields.head++; + } else { + token->length = 1; + parser->file->line += 1 + parser->file->span; + parser->file->span = 0; + parser->file->start_of_line = classify[ (uint8_t)*(token->data+1) ] != BLANK; + return 0; + } + } else if (token->code == END_OF_FILE) { + if (parser->file->end_of_file == ZONE_NO_MORE_DATA) { + if (parser->file->grouped) + SYNTAX_ERROR(parser, token, "Missing closing brace"); + token->data = end_of_file; + token->length = 1; + if (!parser->file->includer) + return 0; + file_t *file = parser->file; + parser->file = parser->file->includer; + parser->owner = &parser->file->owner; + zone_close_file(parser, file); + return 0; + } + + if ((code = advance(parser)) < 0) + ERROR(parser, token, code); + } else if (token->code == LEFT_PAREN) { + if (parser->file->grouped) + SYNTAX_ERROR(parser, token, "Nested opening brace"); + parser->file->grouped = true; + parser->file->fields.head++; + } else if (token->code == RIGHT_PAREN) { + if (!parser->file->grouped) + SYNTAX_ERROR(parser, token, "Missing opening brace"); + parser->file->grouped = false; + parser->file->fields.head++; + } else { + assert(token->code == CONTIGUOUS || token->code == QUOTED); + SYNTAX_ERROR(parser, token, "Trailing data in %s", NAME(type)); + } + token->data = *parser->file->fields.head; + token->code = (int32_t)classify[ (uint8_t)**parser->file->fields.head ]; + } +} + +nonnull_all +warn_unused_result +static really_inline int32_t take_delimiter( + parser_t *parser, const type_info_t *type, token_t *token) +{ + token->data = *parser->file->fields.head; + token->code = (int32_t)classify[ (uint8_t)**parser->file->fields.head ]; + if (likely(token->code == LINE_FEED)) { + if (unlikely(parser->file->grouped || token->data == line_feed)) + parser->file->span += *parser->file->lines.head++; + token->length = 1; + parser->file->line = 1 + parser->file->span; + parser->file->span = 0; + parser->file->start_of_line = classify[ (uint8_t)*(*parser->file->fields.head+1) ] != BLANK; + parser->file->fields.head++; + return 0; + } else { + return maybe_take_delimiter(parser, type, token); + } +} + +#undef SYNTAX_ERROR +#undef ERROR + +// define SYNTAX_ERROR for the rest of the code base +#define SYNTAX_ERROR(parser, ...) \ + RAISE_ERROR((parser), ZONE_SYNTAX_ERROR, __VA_ARGS__) + +#endif // PARSER_H diff --git a/src/generic/scanner.h b/src/generic/scanner.h index 3176fbe..711963e 100644 --- a/src/generic/scanner.h +++ b/src/generic/scanner.h @@ -13,12 +13,9 @@ #include #include -#include "zone.h" -#include "log.h" - // Copied from simdjson under the terms of The 3-Clause BSD License. // Copyright (c) 2018-2023 The simdjson authors -static inline uint64_t find_escaped( +static really_inline uint64_t find_escaped( uint64_t backslash, uint64_t *is_escaped) { backslash &= ~ *is_escaped; @@ -43,7 +40,7 @@ static inline uint64_t find_escaped( // includes a semicolon (or newline for that matter) and/or a comment region // includes one (or more) quote characters. also, for comments, only newlines // directly following a non-escaped, non-quoted semicolon must be included -static inline void find_delimiters( +static really_inline void find_delimiters( uint64_t quotes, uint64_t semicolons, uint64_t newlines, @@ -144,7 +141,7 @@ struct block { uint64_t special; }; -static zone_really_inline void scan(zone_parser_t *parser, block_t *block) +static really_inline void scan(parser_t *parser, block_t *block) { // escaped newlines are classified as contiguous. however, escape sequences // have no meaning in comments and newlines, escaped or not, have no @@ -191,7 +188,7 @@ static zone_really_inline void scan(zone_parser_t *parser, block_t *block) follows(block->contiguous, &parser->file->state.follows_contiguous); } -static zone_really_inline void tokenize(zone_parser_t *parser, const block_t *block, uint64_t clear) +static really_inline void write_indexes(parser_t *parser, const block_t *block, uint64_t clear) { uint64_t fields = (block->contiguous & ~block->follows_contiguous) | (block->quoted & block->in_quoted) | @@ -225,7 +222,7 @@ static zone_really_inline void tokenize(zone_parser_t *parser, const block_t *bl // character strings. edge case, but must be supported and handled in the // scanner for ease of use and to accommodate for parallel processing in the // parser. escaped newlines may have been present in the last block - if (zone_unlikely(parser->file->lines.tail[0] || (newline & in_string))) { + if (unlikely(parser->file->lines.tail[0] || (newline & in_string))) { // FIXME: test logic properly, likely eligable for simplification for (count=0; count < field_count; count++) { const uint64_t field = -fields & fields; @@ -260,7 +257,7 @@ static zone_really_inline void tokenize(zone_parser_t *parser, const block_t *bl delimiters = clear_lowest_bit(delimiters); } - if (zone_unlikely(count > 6)) { + if (unlikely(count > 6)) { for (uint64_t i=6; i < 12; i++) { parser->file->fields.tail[i] = base + trailing_zeroes(fields); parser->file->delimiters.tail[i] = base + trailing_zeroes(delimiters); @@ -268,7 +265,7 @@ static zone_really_inline void tokenize(zone_parser_t *parser, const block_t *bl delimiters = clear_lowest_bit(delimiters); } - if (zone_unlikely(count > 12)) { + if (unlikely(count > 12)) { for (uint64_t i=12; i < count; i++) { parser->file->fields.tail[i] = base + trailing_zeroes(fields); parser->file->delimiters.tail[i] = base + trailing_zeroes(delimiters); @@ -283,156 +280,54 @@ static zone_really_inline void tokenize(zone_parser_t *parser, const block_t *bl } } -zone_nonnull_all -static zone_never_inline void step(zone_parser_t *parser, token_t *token) +nonnull_all +warn_unused_result +static really_inline int32_t reindex(parser_t *parser) { block_t block = { 0 }; - bool start_of_line = false; - const char *data_limit, **tape_limit; - - // start of line is initially true - if (parser->file->fields.tail == parser->file->fields.tape) - start_of_line = true; - else if (parser->file->fields.tail[-1][0] == '\n') - start_of_line = !is_blank((uint8_t)parser->file->fields.tail[-1][1]); - - // restore deferred line count - parser->file->lines.tape[0] = parser->file->lines.tail[0]; - parser->file->lines.head = parser->file->lines.tape; - parser->file->lines.tail = parser->file->lines.tape; - // restore (possibly) deferred field - parser->file->fields.tape[0] = parser->file->fields.tail[1]; - parser->file->fields.head = parser->file->fields.tape; - parser->file->fields.tail = parser->file->fields.tape; - if (parser->file->fields.tape[0]) - parser->file->fields.tail++; - // delimiters are never deferred - parser->file->delimiters.head = parser->file->delimiters.tape; - parser->file->delimiters.tail = parser->file->delimiters.tape; - -shuffle: - if (parser->file->end_of_file == ZONE_HAVE_DATA) { - int32_t code; - const char *start; - if (parser->file->fields.head[0]) - start = parser->file->fields.head[0]; - else - start = parser->file->buffer.data + parser->file->buffer.index; - parser->file->fields.head[0] = parser->file->buffer.data; - const size_t length = - (size_t)((parser->file->buffer.data+parser->file->buffer.length) - start); - const size_t index = - (size_t)((parser->file->buffer.data+parser->file->buffer.index) - start); - memmove(parser->file->buffer.data, start, length); - parser->file->buffer.length = length; - parser->file->buffer.index = index; - parser->file->buffer.data[length] = '\0'; - if ((code = refill(parser)) < 0) - DEFER_ERROR(parser, token, code); - } - data_limit = parser->file->buffer.data + parser->file->buffer.length; - tape_limit = parser->file->fields.tape + ZONE_TAPE_SIZE; - for (;;) { - const char *data = parser->file->buffer.data + parser->file->buffer.index; - if (data_limit - data < ZONE_BLOCK_SIZE) - break; - if (tape_limit - parser->file->fields.tail < ZONE_BLOCK_SIZE) - goto terminate; - simd_loadu_8x64(&block.input, (const uint8_t *)data); - scan(parser, &block); - tokenize(parser, &block, 0); - parser->file->buffer.index += ZONE_BLOCK_SIZE; - } + assert(parser->file->buffer.index <= parser->file->buffer.length); + size_t left = parser->file->buffer.length - parser->file->buffer.index; + const char *data = parser->file->buffer.data + parser->file->buffer.index; + const char **tape = parser->file->fields.tail; + const char **tape_limit = parser->file->fields.tape + ZONE_TAPE_SIZE; + + if (left >= ZONE_BLOCK_SIZE) { + const char *data_limit = parser->file->buffer.data + + (parser->file->buffer.length - ZONE_BLOCK_SIZE); + while (data <= data_limit && ((uintptr_t)tape_limit - (uintptr_t)tape) >= ZONE_BLOCK_SIZE) { + simd_loadu_8x64(&block.input, (const uint8_t *)data); + scan(parser, &block); + write_indexes(parser, &block, 0); + parser->file->buffer.index += ZONE_BLOCK_SIZE; + data += ZONE_BLOCK_SIZE; + tape = parser->file->fields.tail; + } - const size_t length = parser->file->buffer.length - parser->file->buffer.index; - assert(length <= ZONE_BLOCK_SIZE); - if (parser->file->end_of_file == ZONE_HAVE_DATA) - goto terminate; - if (length > (size_t)(tape_limit - parser->file->fields.tail)) - goto terminate; - - uint8_t buffer[ZONE_BLOCK_SIZE] = { 0 }; - memcpy(buffer, &parser->file->buffer.data[parser->file->buffer.index], length); - const uint64_t clear = ~((1llu << length) - 1); - simd_loadu_8x64(&block.input, buffer); - scan(parser, &block); - //block.starts &= ~clear; - block.contiguous &= ~clear; - tokenize(parser, &block, clear); - parser->file->buffer.index += length; - parser->file->end_of_file = ZONE_NO_MORE_DATA; - -terminate: - // make sure tape contains no partial tokens - if ((uint64_t)((int64_t)(block.contiguous | block.in_quoted) >> 63)) { - parser->file->fields.tail[0] = parser->file->fields.tail[-1]; - parser->file->fields.tail--; - } else { - parser->file->fields.tail[1] = NULL; + assert(parser->file->buffer.index <= parser->file->buffer.length); + left = parser->file->buffer.length - parser->file->buffer.index; } - parser->file->fields.tail[0] = data_limit; - parser->file->delimiters.tail[0] = data_limit; - if (parser->file->fields.head[0] == parser->file->buffer.data) - parser->file->start_of_line = start_of_line; - else - parser->file->start_of_line = false; - - for (;;) { - const char *data = parser->file->fields.head[0]; - token->data = data; - token->code = (int32_t)contiguous[ (uint8_t)*data ]; - // end-of-file is idempotent - parser->file->fields.head += (*token->data != '\0'); - if (zone_likely(token->code == CONTIGUOUS)) { - const char *delimiter = *parser->file->delimiters.head++; - assert(delimiter > token->data); - token->length = (size_t)(delimiter - token->data); - return; - } else if (token->code == LINE_FEED) { - if (zone_unlikely(token->data == line_feed)) - parser->file->span += *parser->file->lines.head++; - parser->file->span++; - if (parser->file->grouped) - continue; - parser->file->line += parser->file->span; - parser->file->span = 0; - parser->file->start_of_line = !is_blank((uint8_t)*(token->data+1)); - token->length = 1; - return; - } else if (token->code == QUOTED) { - const char *delimiter = *parser->file->delimiters.head++; - token->data++; - assert(delimiter >= token->data); - token->length = (size_t)(delimiter - token->data); - return; - } else if (token->code == END_OF_FILE) { - zone_file_t *file; - - if (parser->file->end_of_file != ZONE_NO_MORE_DATA) - goto shuffle; - if (parser->file->grouped) - DEFER_SYNTAX_ERROR(parser, token, "Missing closing brace"); - if (!parser->file->includer) - return; - file = parser->file; - parser->file = parser->file->includer; - parser->owner = &parser->file->owner; - zone_close_file(parser, file); - token->length = 1; - return; - } else if (token->code == LEFT_PAREN) { - if (parser->file->grouped) - DEFER_SYNTAX_ERROR(parser, token, "Nested opening brace"); - parser->file->grouped = true; - } else { - assert(token->code == RIGHT_PAREN); - if (!parser->file->grouped) - DEFER_SYNTAX_ERROR(parser, token, "Missing opening brace"); - parser->file->grouped = false; + // only scan partial blocks after reading all data + if (parser->file->end_of_file) { + assert(left < ZONE_BLOCK_SIZE); + if (!left) { + parser->file->end_of_file = ZONE_NO_MORE_DATA; + } else if (((uintptr_t)tape_limit - (uintptr_t)tape) >= left) { + // input is required to be padded, but may contain garbage + uint8_t buffer[ZONE_BLOCK_SIZE] = { 0 }; + memcpy(buffer, data, left); + const uint64_t clear = ~((1llu << left) - 1); + simd_loadu_8x64(&block.input, buffer); + scan(parser, &block); + block.contiguous &= ~clear; + write_indexes(parser, &block, clear); + parser->file->end_of_file = ZONE_NO_MORE_DATA; + parser->file->buffer.index += left; } } + + return (uint64_t)((int64_t)(block.contiguous | block.in_quoted) >> 63) != 0; } #endif // SCANNER_H diff --git a/src/generic/svcb.h b/src/generic/svcb.h index 11947df..8b6d848 100644 --- a/src/generic/svcb.h +++ b/src/generic/svcb.h @@ -1,5 +1,5 @@ /* - * svcb.h -- svcb parser + * svcb.h -- svcb (RFC9460) parser * * Copyright (c) 2022-2023, NLnet Labs. All rights reserved. * @@ -9,31 +9,6 @@ #ifndef SVCB_H #define SVCB_H -typedef struct svc_param_info svc_param_info_t; -struct svc_param_info; - -// FIXME: move, to be used by all parse functions -typedef struct rdata rdata_t; -struct rdata { - uint8_t *octets; - uint8_t *limit; -}; - -typedef int32_t (*svc_param_parse_t)( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - uint16_t key, - const svc_param_info_t *svc_param_info, - rdata_t *rdata, - const token_t *); - -struct svc_param_info { - zone_symbol_t name; - bool value; - svc_param_parse_t parse, parse_strict; -}; - // RFC9460 section 7.1: // The "alpn" and "no-default-alpn" SvcParamKeys together indicate the set // of Application-Layer Protocol Negotiation (ALPN) protocol identifiers @@ -57,11 +32,11 @@ struct svc_param_info { // ... A value-list parser that splits on "," and prohibits items // containing "\"" is sufficient to comply with all requirements in // this document. ... -zone_nonnull_all +nonnull_all static int32_t parse_alpn( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, uint16_t key, const svc_param_info_t *param, rdata_t *rdata, @@ -72,7 +47,7 @@ static int32_t parse_alpn( uint8_t *octet = rdata->octets + 1; uint8_t *limit = rdata->octets + 1 + token->length; if (limit > rdata->limit) - SYNTAX_ERROR(parser, "Invalid alpn in %s", TNAME(type)); + SYNTAX_ERROR(parser, "Invalid alpn in %s", NAME(type)); memcpy(octet, token->data, token->length); @@ -83,35 +58,35 @@ static int32_t parse_alpn( for (; octet < limit; octet++) { // FIXME: SIMD and possibly SWAR can easily be used to improve if (*octet == '\\') - SYNTAX_ERROR(parser, "Invalid alpn in %s", TNAME(type)); + SYNTAX_ERROR(parser, "Invalid alpn in %s", NAME(type)); if (*octet != ',') continue; assert(separator < octet); const size_t length = ((uintptr_t)octet - (uintptr_t)separator) - 1; if (length == 0) - SYNTAX_ERROR(parser, "Invalid alpn in %s", TNAME(type)); + SYNTAX_ERROR(parser, "Invalid alpn in %s", NAME(type)); if (length > 255) - SYNTAX_ERROR(parser, "Invalid alpn in %s", TNAME(type)); + SYNTAX_ERROR(parser, "Invalid alpn in %s", NAME(type)); *separator = (uint8_t)length; separator = octet; } const size_t length = ((uintptr_t)octet - (uintptr_t)separator) - 1; if (length == 0) - SYNTAX_ERROR(parser, "Invalid alpn in %s", TNAME(type)); + SYNTAX_ERROR(parser, "Invalid alpn in %s", NAME(type)); if (length > 255) - SYNTAX_ERROR(parser, "Invalid alpn in %s", TNAME(type)); + SYNTAX_ERROR(parser, "Invalid alpn in %s", NAME(type)); *separator = (uint8_t)length; rdata->octets = limit; return 0; } -zone_nonnull_all +nonnull_all static int32_t parse_port( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, uint16_t key, const svc_param_info_t *param, rdata_t *rdata, @@ -124,7 +99,7 @@ static int32_t parse_port( (void)param; if (!token->length || token->length > 5) - SYNTAX_ERROR(parser, "Invalid port in %s", TNAME(type)); + SYNTAX_ERROR(parser, "Invalid port in %s", NAME(type)); uint64_t number = 0; for (;; data++) { @@ -140,17 +115,17 @@ static int32_t parse_port( rdata->octets += 2; if (rdata->octets > rdata->limit) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); if (data != token->data + token->length || number > 65535) - SYNTAX_ERROR(parser, "Invalid port in %s", TNAME(type)); + SYNTAX_ERROR(parser, "Invalid port in %s", NAME(type)); return 0; } -zone_nonnull_all +nonnull_all static int32_t parse_ipv4hint( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, uint16_t key, const svc_param_info_t *param, rdata_t *rdata, @@ -164,29 +139,29 @@ static int32_t parse_ipv4hint( (void)param; if (scan_ip4(t, rdata->octets, &n) == -1) - SYNTAX_ERROR(parser, "Invalid ipv4hint in %s", TNAME(type)); + SYNTAX_ERROR(parser, "Invalid ipv4hint in %s", NAME(type)); rdata->octets += 4; t += n; while (*t == ',') { if (rdata->octets > rdata->limit) - SYNTAX_ERROR(parser, "Invalid ipv4hint in %s", TNAME(type)); + SYNTAX_ERROR(parser, "Invalid ipv4hint in %s", NAME(type)); if (scan_ip4(t + 1, rdata->octets, &n) == -1) - SYNTAX_ERROR(parser, "Invalid ipv4hint in %s", TNAME(type)); + SYNTAX_ERROR(parser, "Invalid ipv4hint in %s", NAME(type)); rdata->octets += 4; t += n + 1; } if (t != te || rdata->octets > rdata->limit) - SYNTAX_ERROR(parser, "Invalid ipv4hint in %s", TNAME(type)); + SYNTAX_ERROR(parser, "Invalid ipv4hint in %s", NAME(type)); return 0; } -zone_nonnull_all +nonnull_all static int32_t parse_ech( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, uint16_t key, const svc_param_info_t *param, rdata_t *rdata, @@ -205,20 +180,20 @@ static int32_t parse_ech( struct base64_state state = { 0 }; if (!base64_stream_decode( &state, token->data, token->length, rdata->octets, &length)) - SYNTAX_ERROR(parser, "Invalid ech in %s", TNAME(type)); + SYNTAX_ERROR(parser, "Invalid ech in %s", NAME(type)); rdata->octets += length; if (state.bytes) - SYNTAX_ERROR(parser, "Invalid ech in %s", TNAME(type)); + SYNTAX_ERROR(parser, "Invalid ech in %s", NAME(type)); return 0; } -zone_nonnull_all +nonnull_all static int32_t parse_ipv6hint( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, uint16_t key, const svc_param_info_t *param, rdata_t *rdata, @@ -232,21 +207,21 @@ static int32_t parse_ipv6hint( (void)param; if (scan_ip6(t, rdata->octets, &n) == -1) - SYNTAX_ERROR(parser, "Invalid ipv6hint in %s", TNAME(type)); + SYNTAX_ERROR(parser, "Invalid ipv6hint in %s", NAME(type)); rdata->octets += 16; t += n; while (*t == ',') { if (rdata->octets >= rdata->limit) - SYNTAX_ERROR(parser, "Invalid ipv6hint in %s", TNAME(type)); + SYNTAX_ERROR(parser, "Invalid ipv6hint in %s", NAME(type)); if (scan_ip6(t + 1, rdata->octets, &n) == -1) - SYNTAX_ERROR(parser, "Invalid ipv6hint in %s", TNAME(type)); + SYNTAX_ERROR(parser, "Invalid ipv6hint in %s", NAME(type)); rdata->octets += 16; t += n + 1; } if (t != te || rdata->octets > rdata->limit) - SYNTAX_ERROR(parser, "Invalid ipv6hint in %s", TNAME(type)); + SYNTAX_ERROR(parser, "Invalid ipv6hint in %s", NAME(type)); return 0; } @@ -254,11 +229,11 @@ static int32_t parse_ipv6hint( // "dohpath" is a single-valued SvcParamKey whose value (in both // presentation format and wire format) MUST be a URI Template in // relative form ([RFC6570], Section 1.1) encoded in UTF-8 [RFC3629]. -zone_nonnull_all -static int32_t parse_dohpath_strict( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, +nonnull_all +static int32_t parse_dohpath( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, uint16_t key, const svc_param_info_t *param, rdata_t *rdata, @@ -276,7 +251,7 @@ static int32_t parse_dohpath_strict( if (*t == '\\') { uint32_t o; if (!(o = unescape(t, rdata->octets))) - SYNTAX_ERROR(parser, "Invalid dohpath in %s", TNAME(type)); + SYNTAX_ERROR(parser, "Invalid dohpath in %s", NAME(type)); rdata->octets += 1; t += o; } else { rdata->octets += 1; t += 1; @@ -290,15 +265,15 @@ static int32_t parse_dohpath_strict( // FIXME: implement if (t != te || rdata->octets >= rdata->limit) - SYNTAX_ERROR(parser, "Invalid dohpath in %s", TNAME(type)); + SYNTAX_ERROR(parser, "Invalid dohpath in %s", NAME(type)); return 0; } -zone_nonnull_all -static int32_t parse_dohpath( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, +nonnull_all +static int32_t parse_dohpath_non_strict( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, uint16_t key, const svc_param_info_t *param, rdata_t *rdata, @@ -316,7 +291,7 @@ static int32_t parse_dohpath( if (*t == '\\') { uint32_t o; if (!(o = unescape(t, rdata->octets))) - SYNTAX_ERROR(parser, "Invalid dohpath in %s", TNAME(type)); + SYNTAX_ERROR(parser, "Invalid dohpath in %s", NAME(type)); rdata->octets += 1; t += o; } else { rdata->octets += 1; t += 1; @@ -324,15 +299,15 @@ static int32_t parse_dohpath( } if (t != te || rdata->octets >= rdata->limit) - SYNTAX_ERROR(parser, "Invalid dohpath in %s", TNAME(type)); + SYNTAX_ERROR(parser, "Invalid dohpath in %s", NAME(type)); return 0; } -zone_nonnull_all -static zone_never_inline int32_t parse_unknown( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, +nonnull_all +static int32_t parse_unknown( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, uint16_t key, const svc_param_info_t *param, rdata_t *rdata, @@ -348,7 +323,7 @@ static zone_never_inline int32_t parse_unknown( if (*t == '\\') { uint32_t o; if (!(o = unescape(t, rdata->octets))) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); rdata->octets += 1; t += o; } else { rdata->octets += 1; t += 1; @@ -356,7 +331,7 @@ static zone_never_inline int32_t parse_unknown( } if (t != te || rdata->octets >= rdata->limit) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); return 0; } @@ -389,31 +364,31 @@ static zone_never_inline int32_t parse_unknown( #define SVC_PARAM_KEY_INVALID_KEY (65535u) /** @} */ -zone_nonnull_all -static int32_t parse_mandatory_strict( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, +nonnull_all +static int32_t parse_mandatory_non_strict( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, uint16_t key, const svc_param_info_t *svc_param, rdata_t *rdata, const token_t *token); -zone_nonnull_all +nonnull_all static int32_t parse_mandatory( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, uint16_t key, const svc_param_info_t *svc_param, rdata_t *rdata, const token_t *token); -#define SVC_PARAM(name, key, value, parse, parse_sorted) \ - { { { name, sizeof(name) - 1 }, key }, value, parse, parse_sorted } +#define SVC_PARAM(name, key, value, parse, parse_non_strict) \ + { { { name, sizeof(name) - 1 }, key }, value, parse, parse_non_strict } static const svc_param_info_t svc_params[] = { - SVC_PARAM("mandatory", 0u, true, parse_mandatory, parse_mandatory_strict), + SVC_PARAM("mandatory", 0u, true, parse_mandatory, parse_mandatory_non_strict), SVC_PARAM("alpn", 1u, true, parse_alpn, parse_alpn), // RFC9460 section 7.1.1: // For "no-default-alpn", the presentation and wire format values MUST be @@ -427,7 +402,7 @@ static const svc_param_info_t svc_params[] = { // RFC9461 section 5: // If the "alpn" SvcParam indicates support for HTTP, "dohpath" MUST be // present. - SVC_PARAM("dohpath", 7u, true, parse_dohpath, parse_dohpath_strict), + SVC_PARAM("dohpath", 7u, true, parse_dohpath, parse_dohpath_non_strict), SVC_PARAM("ohttp", 8u, false, 0, 0), }; @@ -436,30 +411,37 @@ static const svc_param_info_t unknown_svc_param = #undef SVC_PARAM -zone_nonnull_all -static zone_really_inline size_t scan_unknown_svc_param_key( +nonnull_all +static really_inline size_t scan_unknown_svc_param_key( const char *data, uint16_t *key, const svc_param_info_t **param) { - size_t length = 0; - uint64_t number = 0; + size_t length = 4; + uint32_t number = (uint8_t)data[3] - '0'; + + if (number > 9) + return 0; + + uint32_t leading_zero = number == 0; + for (;; length++) { - const uint64_t digit = (uint8_t)data[length] - '0'; + const uint32_t digit = (uint8_t)data[length] - '0'; if (digit > 9) break; number = number * 10 + digit; } - if (!length || length > 4) + leading_zero &= length > 4; + if (leading_zero || length > 3 + 5) return 0; if (number < (sizeof(svc_params) / sizeof(svc_params[0]))) return (void)(*param = &svc_params[(*key = (uint16_t)number)]), length; if (number < 65535) - return (void)(*key = (uint16_t)number), (void)(*param = &unknown_svc_param), length + 3; + return (void)(*key = (uint16_t)number), (void)(*param = &unknown_svc_param), length; return 0; } -zone_nonnull_all -static zone_really_inline size_t scan_svc_param( +nonnull_all +static really_inline size_t scan_svc_param( const char *data, uint16_t *key, const svc_param_info_t **param) { // draft-ietf-dnsop-svcb-https-12 section 2.1: @@ -486,13 +468,13 @@ static zone_really_inline size_t scan_svc_param( else if (memcmp(data, "ohttp", 5) == 0) return (void)(*param = &svc_params[(*key = SVC_PARAM_KEY_OHTTP)]), 5; else if (memcmp(data, "key", 0) == 0) - return scan_unknown_svc_param_key(data + 3, key, param); + return scan_unknown_svc_param_key(data, key, param); else return 0; } -zone_nonnull_all -static zone_really_inline size_t scan_svc_param_key( +nonnull_all +static really_inline size_t scan_svc_param_key( const char *data, uint16_t *key) { // FIXME: improve implementation @@ -500,17 +482,18 @@ static zone_really_inline size_t scan_svc_param_key( return scan_svc_param(data, key, ¶m); } -zone_nonnull_all -static zone_really_inline int32_t parse_mandatory_strict( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, +nonnull_all +static int32_t parse_mandatory( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, uint16_t key, const svc_param_info_t *param, rdata_t *rdata, const token_t *token) { (void)field; + (void)param; // RFC9460 section 8: // The presentation value SHALL be a comma-seperatred list of one or more @@ -521,7 +504,7 @@ static zone_really_inline int32_t parse_mandatory_strict( size_t skip; if (!(skip = scan_svc_param_key(data, &key))) - SYNTAX_ERROR(parser, "Invalid %s in %s", TNAME(param), TNAME(type)); + SYNTAX_ERROR(parser, "Invalid mandatory in %s", NAME(type)); highest_key = key; key = htobe16(key); @@ -531,7 +514,7 @@ static zone_really_inline int32_t parse_mandatory_strict( while (*data == ',' && rdata->octets < rdata->limit) { if (!(skip = scan_svc_param_key(data + 1, &key))) - SYNTAX_ERROR(parser, "Invalid mandatory of %s", TNAME(type)); + SYNTAX_ERROR(parser, "Invalid mandatory of %s", NAME(type)); data += skip + 1; if (key > highest_key) { highest_key = key; @@ -555,7 +538,7 @@ static zone_really_inline int32_t parse_mandatory_strict( // RFC9460 section 8: // Keys MAY appear in any order, but MUST NOT appear more than once. if (key == smaller_key) - SYNTAX_ERROR(parser, "Duplicate key in mandatory of %s", TNAME(type)); + SYNTAX_ERROR(parser, "Duplicate key in mandatory of %s", NAME(type)); assert(key < smaller_key); uint16_t length = (uint16_t)(rdata->octets - octets); memmove(octets + 2, octets, length); @@ -566,102 +549,223 @@ static zone_really_inline int32_t parse_mandatory_strict( } if (rdata->octets >= rdata->limit) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); if (data != token->data + token->length) - SYNTAX_ERROR(parser, "..."); + SYNTAX_ERROR(parser, "Invalid mandatory in %s", NAME(type)); return 0; } -zone_nonnull_all -static zone_really_inline int32_t parse_mandatory( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, +nonnull_all +static int32_t parse_mandatory_non_strict( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, uint16_t key, const svc_param_info_t *param, rdata_t *rdata, const token_t *token) { - size_t skip; - const char *data = token->data; - (void)field; + // RFC9460 section 8: + // The presentation value SHALL be a comma-seperatred list of one or more + // valid SvcParamKeys, ... + bool out_of_order = false; + int32_t highest_key = -1; + const uint8_t *whence = rdata->octets; + const char *data = token->data; + size_t skip; + if (!(skip = scan_svc_param_key(data, &key))) - SYNTAX_ERROR(parser, "Invalid key in %s of %s", TNAME(param), TNAME(type)); + SYNTAX_ERROR(parser, "Invalid key in %s of %s", NAME(param), NAME(type)); memcpy(rdata->octets, &key, 2); rdata->octets += 2; data += skip; while (*data == ',' && rdata->octets < rdata->limit) { if (!(skip = scan_svc_param_key(data + 1, &key))) - SYNTAX_ERROR(parser, "Invalid key in %s of %s", TNAME(param), TNAME(type)); + SYNTAX_ERROR(parser, "Invalid key in %s of %s", NAME(param), NAME(type)); + + if ((int32_t)key <= highest_key) { + // RFC9460 section 8: + // In wire format, the keys are represented by their numeric values in + // network byte order, concatenated in ascending order. + const uint8_t *octets = whence; + uint16_t smaller_key = 0; + while (octets < rdata->octets) { + memcpy(&smaller_key, octets, sizeof(smaller_key)); + smaller_key = be16toh(smaller_key); + if (key < smaller_key) + break; + octets += 2; + } + assert(octets < rdata->octets); + // RFC9460 section 8: + // Keys MAY appear in any order, but MUST NOT appear more than once. + if (key == smaller_key) + SEMANTIC_ERROR(parser, "Duplicate key in mandatory of %s", NAME(type)); + assert(key < smaller_key); + out_of_order = true; + } + data += skip + 1; + key = htobe16(key); memcpy(rdata->octets, &key, 2); rdata->octets += 2; } + if (out_of_order) + SEMANTIC_ERROR(parser, "Out of order keys in mandatory of %s", NAME(type)); if (rdata->octets >= rdata->limit - 2) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); if (data != token->data + token->length) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); return 0; } +nonnull_all +static int32_t parse_svc_params_non_strict( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, + token_t *token) +{ + bool out_of_order = false; + int32_t code, highest_key = -1; + const uint16_t zero = 0; + const uint8_t *whence = rdata->octets; + + while (is_contiguous(token)) { + size_t skip; + uint16_t key; + const svc_param_info_t *param; + + if (!(skip = scan_svc_param(token->data, &key, ¶m))) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + assert(param); + + if ((int32_t)key <= highest_key) { + const uint8_t *octets = whence; + uint16_t smaller_key = 65535; + out_of_order = true; + + while (octets < rdata->octets) { + memcpy(&smaller_key, octets, sizeof(smaller_key)); + smaller_key = be16toh(smaller_key); + if (key <= smaller_key) + break; + uint16_t length; + memcpy(&length, octets + 2, sizeof(length)); + length = be16toh(length); + octets += length + 4; + } + + assert(octets < rdata->octets); + if (key == smaller_key) + SEMANTIC_ERROR(parser, "Duplicate key in %s", NAME(type)); + } + + switch ((token->data[skip] == '=') + (param->has_value << 1)) { + case 1: // void parameter with value + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + case 0: // void parameter without value + case 2: // parameter without value + if (skip != token->length) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + key = htobe16(key); + memcpy(rdata->octets, &key, sizeof(key)); + memcpy(rdata->octets+2, &zero, sizeof(zero)); + rdata->octets += 4; + break; + case 3: // parameter with value + skip += 1; + // quoted value, separate token + if (token->data[skip] != '"') + (void)(token->data += skip), token->length -= skip; + else if ((code = take_quoted(parser, type, field, token)) < 0) + return 0; + { + uint8_t *octets = rdata->octets; + rdata->octets += 4; + code = param->parse_non_strict( + parser, type, field, key, param, rdata, token); + if (code) + return code; + uint16_t length = (uint16_t)(rdata->octets - octets) - 4; + key = htobe16(key); + length = htobe16(length); + memcpy(octets, &key, sizeof(key)); + memcpy(octets+2, &length, sizeof(length)); + } + break; + } + + take(parser, token); + } + + if (out_of_order) + SEMANTIC_ERROR(parser, "Out of order parameters in %s", NAME(type)); + + return have_delimiter(parser, type, token); +} + // https://www.iana.org/assignments/dns-svcb/dns-svcb.xhtml -zone_nonnull_all -static zone_really_inline int32_t parse_svc_params_strict( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - // rdata_t rdata, +nonnull_all +static really_inline int32_t parse_svc_params( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, token_t *token) { + // propagate data as-is if secondary + if (parser->options.non_strict) + return parse_svc_params_non_strict(parser, type, field, rdata, token); + const uint16_t zero = 0; int32_t code, highest_key = -1; - uint8_t *whence = parser->rdata->octets + parser->rdata->length; - rdata_t rdata = { parser->rdata->octets + parser->rdata->length, - parser->rdata->octets + ZONE_RDATA_SIZE }; + uint8_t *whence = rdata->octets; - while (token->code == CONTIGUOUS) { + while (is_contiguous(token)) { size_t skip; uint16_t key; const svc_param_info_t *param; if (!(skip = scan_svc_param(token->data, &key, ¶m))) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); assert(param); if (key > highest_key) { highest_key = key; - switch ((token->data[skip] == '=') + (param->value << 1)) { + switch ((token->data[skip] == '=') | (param->has_value << 1)) { case 1: // void parameter with value - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); case 0: // void parameter without value case 2: // parameter without optional value if (skip != token->length) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); key = htobe16(key); - memcpy(rdata.octets, &key, sizeof(key)); - memcpy(rdata.octets+2, &zero, sizeof(zero)); - rdata.octets += 4; + memcpy(rdata->octets, &key, sizeof(key)); + memcpy(rdata->octets+2, &zero, sizeof(zero)); + rdata->octets += 4; break; case 3: // parameter with value skip += 1; // quoted parameter, separate token - if (token->data[skip] == '"') - lex(parser, token); - else + if (token->data[skip] != '"') (void)(token->data += skip), token->length -= skip; + else if ((code = take_quoted(parser, type, field, token)) < 0) + return code; { - uint8_t *octets = rdata.octets; - rdata.octets += 4; - code = param->parse_strict( - parser, type, field, key, param, &rdata, token); + uint8_t *octets = rdata->octets; + rdata->octets += 4; + code = param->parse( + parser, type, field, key, param, rdata, token); if (code < 0) return code; - uint16_t length = (uint16_t)(rdata.octets - octets) - 4; + uint16_t length = (uint16_t)(rdata->octets - octets) - 4; key = htobe16(key); length = htobe16(length); memcpy(octets, &key, sizeof(key)); @@ -673,7 +777,7 @@ static zone_really_inline int32_t parse_svc_params_strict( uint8_t *octets = whence; uint16_t smaller_key = 65535; - while (octets < rdata.octets) { + while (octets < rdata->octets) { memcpy(&smaller_key, octets, sizeof(smaller_key)); smaller_key = be16toh(smaller_key); if (key <= smaller_key) @@ -684,28 +788,28 @@ static zone_really_inline int32_t parse_svc_params_strict( octets += length + 4; } - assert(octets < rdata.octets); + assert(octets < rdata->octets); if (key == smaller_key) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); - switch ((token->data[skip] == '=') + (param->value << 1)) { + switch ((token->data[skip] == '=') + (param->has_value << 1)) { case 1: // void parameter with value - SYNTAX_ERROR(parser, "foobar1"); + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); case 0: // void parameter without value case 2: // parameter without value key = htobe16(key); - memmove(octets + 4, octets, (uintptr_t)rdata.octets - (uintptr_t)octets); + memmove(octets + 4, octets, (uintptr_t)rdata->octets - (uintptr_t)octets); memcpy(octets, &key, sizeof(key)); memcpy(octets+2, &zero, sizeof(zero)); - rdata.octets += 4; + rdata->octets += 4; break; case 3: // parameter with value skip += 1; // quoted parameter, separate token - if (token->data[skip] == '"') - lex(parser, token); - else + if (token->data[skip] != '"') (void)(token->data += skip), token->length -= skip; + else if ((code = take_quoted(parser, type, field, token)) < 0) + return code; { uint16_t length; rdata_t param_rdata; @@ -714,20 +818,20 @@ static zone_really_inline int32_t parse_svc_params_strict( // // move existing data to end of the buffer and reset limit to // avoid allocating memory - assert(rdata.octets - octets < ZONE_RDATA_SIZE); - length = (uint16_t)(rdata.octets - octets); + assert(rdata->octets - octets < ZONE_RDATA_SIZE); + length = (uint16_t)(rdata->octets - octets); param_rdata.octets = octets + 4u; param_rdata.limit = parser->rdata->octets + (ZONE_RDATA_SIZE - length); // move data PADDING_SIZE past limit to ensure SIMD operatations // do not overwrite existing data memmove(param_rdata.limit + ZONE_PADDING_SIZE, octets, length); - code = param->parse_strict( + code = param->parse( parser, type, field, key, param, ¶m_rdata, token); if (code) return code; assert(param_rdata.octets < param_rdata.limit); memmove(param_rdata.octets, param_rdata.limit + ZONE_PADDING_SIZE, length); - rdata.octets = param_rdata.octets + length; + rdata->octets = param_rdata.octets + length; length = (uint16_t)(param_rdata.octets - octets) - 4u; key = htobe16(key); length = htobe16(length); @@ -738,84 +842,10 @@ static zone_really_inline int32_t parse_svc_params_strict( } } - lex(parser, token); - } - - // FIXME: check all keys specified in mandatory are actually specified!!!! - - // FIXME: remove once all parsers use rdata_t - parser->rdata->length = (uintptr_t)rdata.octets - (uintptr_t)parser->rdata->octets; - - return have_delimiter(parser, type, token); -} - -zone_nonnull_all -static zone_really_inline int32_t parse_svc_params( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - // rdata_t *rdata, - token_t *token) -{ - // propagate data as-is if secondary - if (!parser->options.secondary) - return parse_svc_params_strict(parser, type, field, token); - - int32_t code; - const uint16_t zero = 0; - rdata_t rdata = { - parser->rdata->octets + parser->rdata->length, - parser->rdata->octets + 65535 }; - - while (token->code == CONTIGUOUS) { - size_t skip; - uint16_t key; - const svc_param_info_t *param; - - if (!(skip = scan_svc_param(token->data, &key, ¶m))) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - assert(param); - - switch ((token->data[skip] == '=') + (param->value << 1)) { - case 1: // void parameter with value - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - case 0: // void parameter without value - case 2: // parameter without value - if (skip != token->length) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - key = htobe16(key); - memcpy(rdata.octets, &key, sizeof(key)); - memcpy(rdata.octets+2, &zero, sizeof(zero)); - rdata.octets += 4; - break; - case 3: // parameter with value - skip += 1; - // quoted value, separate token - if (token->data[skip] == '"') - lex(parser, token); - else - (void)(token->data += skip), token->length -= skip; - { - uint8_t *octets = rdata.octets; - rdata.octets += 4; - code = param->parse( - parser, type, field, key, param, &rdata, token); - if (code) - return code; - uint16_t length = (uint16_t)(rdata.octets - octets) - 4; - key = htobe16(key); - length = htobe16(length); - memcpy(octets, &key, sizeof(key)); - memcpy(octets+2, &length, sizeof(length)); - } - break; - } - - lex(parser, token); + take(parser, token); } - // FIXME: remove once all parsers use rdata_t - parser->rdata->length = (uintptr_t)rdata.octets - (uintptr_t)parser->rdata->octets; + // FIXME: check keys specified in mandatory are actually specified! return have_delimiter(parser, type, token); } diff --git a/src/generic/text.h b/src/generic/text.h index 0b33f30..85aed9f 100644 --- a/src/generic/text.h +++ b/src/generic/text.h @@ -1,5 +1,5 @@ /* - * text.h -- some useful comment + * text.h -- string parser * * Copyright (c) 2022-2023, NLnet Labs. All rights reserved. * @@ -9,8 +9,8 @@ #ifndef TEXT_H #define TEXT_H -zone_nonnull_all -static zone_really_inline uint32_t unescape(const char *text, uint8_t *wire) +nonnull_all +static really_inline uint32_t unescape(const char *text, uint8_t *wire) { uint8_t d[3]; uint32_t o; @@ -33,8 +33,8 @@ struct string_block { uint64_t backslashes; }; -zone_nonnull_all -static zone_really_inline void copy_string_block( +nonnull_all +static really_inline void copy_string_block( string_block_t *block, const char *text, uint8_t *wire) { simd_8x32_t input; @@ -43,121 +43,70 @@ static zone_really_inline void copy_string_block( block->backslashes = simd_find_8x32(&input, '\\'); } -zone_nonnull_all -static zone_really_inline int32_t parse_string_internal( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, +nonnull_all +static really_inline int32_t parse_text_inner( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, const token_t *token) { string_block_t b; - uint8_t *w = &parser->rdata->octets[parser->rdata->length + 1]; - const uint8_t *ws = w - 1, *we = w + 255; const char *t = token->data, *te = t + token->length; uint64_t left = token->length; - while ((t < te) & (w < we)) { - copy_string_block(&b, t, w); + while ((t < te) & (rdata->octets < rdata->limit)) { + copy_string_block(&b, t, rdata->octets); uint64_t n = 32; if (left < 32) n = left; uint64_t mask = (1llu << n) - 1; - if (b.backslashes & mask) { + if (unlikely(b.backslashes & mask)) { n = trailing_zeroes(b.backslashes); - w += n; t += n; - if (!(n = unescape(t, w))) - SEMANTIC_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - w += 1; t += n; + rdata->octets += n; t += n; + if (!(n = unescape(t, rdata->octets))) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + rdata->octets += 1; t += n; } else { - w += n; t += n; + rdata->octets += n; t += n; } } - if (w >= we) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - parser->rdata->octets[parser->rdata->length] = (uint8_t)((w - ws) - 1); - parser->rdata->length += (size_t)(w - ws); - return ZONE_STRING; + if (rdata->octets >= rdata->limit) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + return 0; } -zone_nonnull_all -static zone_really_inline int32_t parse_string( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, +nonnull_all +static really_inline int32_t parse_string( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, const token_t *token) { - int32_t r; - - if ((r = have_string(parser, type, field, token)) < 0) - return r; - return parse_string_internal(parser, type, field, token); + int32_t code; + uint8_t *octets = rdata->octets, *limit = rdata->limit; + if (rdata->limit - rdata->octets > 1 + 255) + rdata->limit = rdata->octets + 1 + 255; + rdata->octets += 1; + + code = parse_text_inner(parser, type, field, rdata, token); + *octets = (uint8_t)((rdata->octets - octets) - 1); + rdata->limit = limit; + return code; } -zone_nonnull_all -static zone_really_inline int32_t parse_text_internal( +nonnull_all +static really_inline int32_t parse_text( zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, const token_t *token) { - string_block_t b; - uint8_t *w = &parser->rdata->octets[parser->rdata->length]; - const uint8_t *ws = w, *we = &parser->rdata->octets[ZONE_RDATA_SIZE]; - const char *t = token->data, *te = t + token->length; - uint64_t left = token->length; - - while ((t < te) & (w < we)) { - copy_string_block(&b, t, w); - uint64_t n = 32; - if (left < 32) - n = left; - uint64_t mask = (1llu << n) - 1; - - if (zone_unlikely(b.backslashes & mask)) { - n = trailing_zeroes(b.backslashes); - w += n; t += n; - if (!(n = unescape(t, w))) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - w += 1; t += n; - } else { - w += n; t += n; - } - } - - if (w >= we) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - parser->rdata->length += (size_t)(w - ws); - return ZONE_BLOB; -} - -zone_nonnull_all -static zone_really_inline int32_t parse_quoted_text( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token) -{ - int32_t r; - - if ((r = have_quoted(parser, type, field, token)) < 0) - return r; - return parse_text_internal(parser, type, field, token); -} - -zone_nonnull_all -static zone_really_inline int32_t parse_text( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token) -{ - int32_t r; - - if ((r = have_string(parser, type, field, token)) < 0) - return r; - return parse_text_internal(parser, type, field, token); + return parse_text_inner(parser, type, field, rdata, token); } #endif // TEXT_H diff --git a/src/fallback/time.h b/src/generic/time.h similarity index 66% rename from src/fallback/time.h rename to src/generic/time.h index 198be86..1df4927 100644 --- a/src/fallback/time.h +++ b/src/generic/time.h @@ -1,5 +1,5 @@ /* - * time.h -- parse_time function + * time.h -- timestamp parser * * Copyright (c) 2022-2023, NLnet Labs. All rights reserved. * @@ -28,28 +28,24 @@ static uint64_t leap_days(uint64_t y1, uint64_t y2) return (y2/4 - y1/4) - (y2/100 - y1/100) + (y2/400 - y1/400); } -zone_nonnull_all -static zone_really_inline int32_t parse_time( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - token_t *token) +nonnull_all +static really_inline int32_t parse_time( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *item, + rdata_t *rdata, + const token_t *token) { - int32_t r; - - if ((r = have_contiguous(parser, type, field, token)) < 0) - return r; - uint64_t d[14]; // YYYYmmddHHMMSS const char *p = token->data; for (int i = 0; i < 14; i++) { d[i] = (uint8_t)p[i] - '0'; if (d[i] > 9) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(item), NAME(type)); } - if (contiguous[ (uint8_t)p[14] ] == CONTIGUOUS) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); + if (token->length != 14) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(item), NAME(type)); // code adapted from Python 2.4.1 sources (Lib/calendar.py) const uint64_t year = (d[0] * 1000) + (d[1] * 100) + (d[2] * 10) + d[3]; @@ -60,17 +56,17 @@ static zone_really_inline int32_t parse_time( const uint64_t sec = (d[12] * 10) + d[13]; if (year < 1970 || year > 2106) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(item), NAME(type)); uint64_t leap_year = is_leap_year(year); uint64_t days = 365 * (year - 1970) + leap_days(1970, year); if (!mon || mon > 12) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(item), NAME(type)); if (!mday || mday > days_in_month[mon] + (leap_year & (mon == 2))) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(item), NAME(type)); if (hour > 23 || min > 59 || sec > 59) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(item), NAME(type)); days += days_to_month[mon]; days += (mon > 2) & leap_year; @@ -81,9 +77,9 @@ static zone_really_inline int32_t parse_time( const uint64_t seconds = minutes * 60 + sec; uint32_t time = htobe32((uint32_t)seconds); - memcpy(&parser->rdata->octets[parser->rdata->length], &time, sizeof(time)); - parser->rdata->length += sizeof(time); - return ZONE_INT32; + memcpy(rdata->octets, &time, sizeof(time)); + rdata->octets += 4; + return 0; } #endif // TIME_H diff --git a/src/generic/ttl.h b/src/generic/ttl.h index 8084b6a..53367e0 100644 --- a/src/generic/ttl.h +++ b/src/generic/ttl.h @@ -1,5 +1,5 @@ /* - * ttl.h -- some useful comment + * ttl.h -- Time to Live (TTL) parser * * Copyright (c) 2022-2023, NLnet Labs. All rights reserved. * @@ -10,7 +10,7 @@ #define TTL_H // [sS] = 1, [mM] = 60, [hH] = 60*60, [dD] = 24*60*60, [wW] = 7*24*60*60 -static const uint32_t units[256] = { +static const uint32_t ttl_units[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00 - 0x0f 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x10 - 0x1f 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x20 - 0x2f @@ -29,117 +29,89 @@ static const uint32_t units[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 0xf0 - 0xff }; -zone_nonnull_all -static zone_really_inline int32_t scan_ttl( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - token_t *token, - uint32_t *seconds) +nonnull_all +static really_inline int32_t scan_ttl( + const char *data, size_t length, bool allow_units, uint32_t *ttl) { - int32_t r; - uint64_t t = 0, m = parser->options.secondary ? UINT32_MAX : INT32_MAX; - - if ((r = have_contiguous(parser, type, field, token)) < 0) - return r; - - const char *p = token->data; - for (;; p++) { - const uint64_t d = (uint8_t)*p - '0'; - if (d > 9) - break; - t = t * 10 + d; - } - - if (zone_likely(contiguous[ (uint8_t)*p ] != CONTIGUOUS)) { - // FIXME: comment RFC2308 msb - if (t > m || !t || p - token->data > 10) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - if (t & (1llu << 31)) - SEMANTIC_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - *seconds = (uint32_t)t; - return ZONE_TTL; - } else if (p == token->data || !parser->options.pretty_ttls) { - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - } - - uint64_t n = t, u = 0, f = 0; - enum { NUMBER, UNIT } s = UNIT; - - for (t = 0; ; p++) { - const uint64_t d = (uint8_t)*p - '0'; - - if (s == NUMBER) { - if (d <= 9) { - n = n * 10 + d; - } else if (!(u = units[ (uint8_t)*p ])) { - break; + if (scan_int32(data, length, ttl)) + return 1; + if (!allow_units) + return 0; + + uint64_t sum = 0, number = (uint8_t)data[0] - '0'; + // ttls must start with a number. e.g. 1h not h1 + if (number > 9) + return 0; + + uint64_t unit = 0, last_unit = 0; + enum { NUMBER, UNIT } state = NUMBER; + + for (size_t count = 1; count < length; count++) { + const uint64_t digit = (uint8_t)data[count] - '0'; + + if (state == NUMBER) { + if (digit < 10) { + number = number * 10 + digit; + if (number > UINT32_MAX) + return 0; + } else if (!(unit = ttl_units[ (uint8_t)data[count] ])) { + return 0; // units must not be repeated e.g. 1m1m - } else if (u == f) { - SYNTAX_ERROR(parser, "Invalid %s in %s, reuse of unit %c", - NAME(field), TNAME(type), *p); + } else if (unit == last_unit) { + return 0; // greater units must precede smaller units. e.g. 1m1s, not 1s1m - } else if (u < f) { - SYNTAX_ERROR(parser, "Invalid %s in %s, unit %c follows smaller unit", - NAME(field), TNAME(type), *p); + } else if (unit < last_unit) { + return 0; } else { - f = u; - n = n * u; - s = UNIT; + if (UINT32_MAX / unit < number) + return 0; + number *= unit; + if (UINT32_MAX - sum < number) + return 0; + last_unit = unit; + sum += number; + number = 0; + state = UNIT; } - - if (n > m) - SYNTAX_ERROR(parser, "Invalid %s in %s", - NAME(field), TNAME(type)); - } else if (s == UNIT) { + } else if (state == UNIT) { // units must be followed by a number. e.g. 1h30m, not 1hh - if (d > 9) - SYNTAX_ERROR(parser, "Invalid %s in %s, non-digit follows unit", - NAME(field), TNAME(type)); + if (digit > 9) + return 0; // units must not be followed by a number if smallest unit, // i.e. seconds, was previously specified - if (f == 1) - SYNTAX_ERROR(parser, "Invalid %s in %s, digit follows unit s", - NAME(field), TNAME(type)); - t = t + n; - n = d; - s = NUMBER; - - if (t > m) - SYNTAX_ERROR(parser, "Invalid %s in %s", - NAME(field), TNAME(type)); + if (last_unit == 1) + return 0; + number = digit; + state = NUMBER; } } - if (zone_unlikely(contiguous[ (uint8_t)*p ] != CONTIGUOUS)) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - - t = t + n; - if (t > m || !t) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - if (t & (1llu << 31)) - SEMANTIC_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); + if (UINT32_MAX - sum < number) + return 0; - *seconds = (uint32_t)t; - return ZONE_TTL; + sum += number; + *ttl = (uint32_t)sum; + return 1; } -zone_nonnull_all -static zone_really_inline int32_t parse_ttl( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - token_t *token) +nonnull_all +static really_inline int32_t parse_ttl( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, + const token_t *token) { - int32_t r; - uint32_t t = 0; - - if ((r = scan_ttl(parser, type, field, token, &t)) < 0) - return r; - t = htobe32(t); - memcpy(&parser->rdata->octets[parser->rdata->length], &t, sizeof(t)); - parser->rdata->length += sizeof(t); - return ZONE_TTL; + uint32_t ttl; + if (!scan_ttl(token->data, token->length, parser->options.pretty_ttls, &ttl)) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + // FIXME: comment RFC2308 msb + if (ttl & (1u << 31)) + SEMANTIC_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + ttl = htobe32(ttl); + memcpy(rdata->octets, &ttl, sizeof(ttl)); + rdata->octets += 4; + return 0; } #endif // TTL_H diff --git a/src/generic/type.h b/src/generic/type.h new file mode 100644 index 0000000..6bfae48 --- /dev/null +++ b/src/generic/type.h @@ -0,0 +1,202 @@ +/* + * type.h -- RRTYPE parser + * + * Copyright (c) 2023, NLnet Labs. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ +#ifndef TYPE_H +#define TYPE_H + +#define V(code) { &(types[0].name), 0 } +#define T(code) { &(types[code].name), 1 } +#define C(code) { &(classes[code].name), 2 } + +// map hash to type or class descriptor (generated using hash.c) +static const struct { + const mnemonic_t *mnemonic; + int32_t code; +} types_and_classes[256] = { + V(0), V(0), V(0), V(0), V(0), T(44), V(0), T(3), + V(0), V(0), V(0), V(0), T(11), V(0), T(42), V(0), + V(0), V(0), V(0), V(0), V(0), T(62), V(0), V(0), + V(0), T(99), T(25), V(0), T(53), V(0), V(0), V(0), + V(0), V(0), V(0), V(0), T(50), V(0), V(0), V(0), + V(0), T(39), V(0), T(21), V(0), T(5), V(0), V(0), + V(0), V(0), V(0), V(0), V(0), T(1), V(0), V(0), + C(1), V(0), T(105), T(49), V(0), T(59), V(0), T(29), + V(0), T(20), V(0), T(6), V(0), V(0), V(0), C(3), + V(0), T(63), V(0), V(0), V(0), C(2), T(43), T(37), + V(0), C(4), V(0), V(0), T(45), T(104), T(2), V(0), + T(23), T(55), V(0), T(24), V(0), V(0), V(0), V(0), + V(0), V(0), V(0), T(7), V(0), V(0), V(0), T(12), + V(0), V(0), T(60), V(0), V(0), T(36), T(10), T(15), + V(0), T(26), V(0), V(0), T(19), V(0), V(0), V(0), + V(0), V(0), V(0), T(65), V(0), T(8), V(0), T(108), + V(0), T(38), V(0), T(9), V(0), V(0), V(0), V(0), + V(0), V(0), V(0), V(0), T(46), V(0), V(0), V(0), + V(0), V(0), V(0), V(0), V(0), V(0), T(27), T(48), + V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0), + V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0), + V(0), V(0), T(28), T(4), T(51), V(0), V(0), T(30), + V(0), T(106), V(0), V(0), T(16), T(64), V(0), V(0), + V(0), V(0), T(257), V(0), V(0), V(0), V(0), V(0), + T(256), V(0), V(0), V(0), V(0), T(22), V(0), V(0), + V(0), T(33), V(0), T(61), V(0), T(52), V(0), V(0), + T(259), V(0), V(0), V(0), T(14), V(0), V(0), V(0), + T(13), V(0), V(0), V(0), V(0), V(0), T(107), V(0), + V(0), T(18), V(0), T(17), V(0), V(0), T(35), V(0), + V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0), + V(0), V(0), V(0), V(0), T(258), V(0), V(0), T(109), + V(0), V(0), V(0), V(0), V(0), V(0), T(47), V(0) +}; + +#undef V +#undef T +#undef C + +nonnull_all +static really_inline int32_t scan_generic_type( + const char *data, size_t length, uint16_t *code, const mnemonic_t **mnemonic) +{ + if (scan_int16(data + 4, length - 4, code) == 0) + return -1; + if (*code <= 4) + *mnemonic = &types[*code].name; + else + *mnemonic = &types[0].name; + return 1; +} + +nonnull_all +static really_inline int32_t scan_generic_class( + const char *data, size_t length, uint16_t *code, const mnemonic_t **mnemonic) +{ + if (scan_int16(data + 5, length - 5, code) == 0) + return -1; + if (*code < 4) + *mnemonic = &classes[*code].name; + else + *mnemonic = &classes[0].name; + return 2; +} + +#if BYTE_ORDER == LITTLE_ENDIAN +# define TYPE (0x45505954llu) +# define TYPE_MASK (0xffffffffllu) +# define CLASS (0x5353414c43llu) +# define CLASS_MASK (0xffffffffffllu) +#else +# define TYPE (0x5459504500000000llu) +# define TYPE_MASK (0xffffffff00000000llu) +# define CLASS (0x434c415353000000llu) +# define CLASS_MASK (0xffffffffff000000llu) +#endif + +static const int8_t zero_masks[48] = { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static really_inline uint8_t hash(uint64_t prefix) +{ + uint32_t value = (uint32_t)((prefix >> 32) ^ prefix); + // magic value is generated using hash.c, rerun when adding types + return (uint8_t)((value * 3523264710ull) >> 32); +} + +nonnull_all +static really_inline int32_t scan_type_or_class( + const char *data, size_t length, uint16_t *code, const mnemonic_t **mnemonic) +{ + uint64_t input0, input1; + static const uint64_t letter_mask = 0x40404040404040llu; + + // safe, input is padded + memcpy(&input0, data, 8); + memcpy(&input1, data + 8, 8); + + // convert to upper case + input0 = input0 & ~((input0 & letter_mask) >> 1); + input1 = input1 & ~((input1 & letter_mask) >> 1); + + length &= 0x1f; + const uint8_t *zero_mask = (const uint8_t *)&zero_masks[32 - (length & 0x1f)]; + uint64_t zero_mask0, zero_mask1; + + // sanitize input + memcpy(&zero_mask0, zero_mask, 8); + memcpy(&zero_mask1, zero_mask + 8, 8); + + input0 &= zero_mask0; + input1 &= zero_mask1; + + const uint8_t index = hash(input0); + *code = (uint16_t)types_and_classes[index].mnemonic->value; + *mnemonic = types_and_classes[index].mnemonic; + + uint64_t name0, name1; + memcpy(&name0, (*mnemonic)->key.data, 8); + memcpy(&name1, (*mnemonic)->key.data + 8, 8); + + if (likely(((input0 ^ name0) | (input1 ^ name1)) == 0) && *code) + return types_and_classes[index].code; + else if ((input0 & TYPE_MASK) == TYPE) + return scan_generic_type(data, length, code, mnemonic); + else if ((input0 & CLASS_MASK) == CLASS) + return scan_generic_class(data, length, code, mnemonic); + return 0; +} + +nonnull_all +static really_inline int32_t scan_type( + const char *data, size_t length, uint16_t *code, const mnemonic_t **mnemonic) +{ + uint64_t input0, input1; + static const uint64_t letter_mask = 0x40404040404040llu; + + // safe, input is padded + memcpy(&input0, data, 8); + memcpy(&input1, data + 8, 8); + + // convert to upper case + input0 = input0 & ~((input0 & letter_mask) >> 1); + input1 = input1 & ~((input1 & letter_mask) >> 1); + + length &= 0x1f; + const uint8_t *zero_mask = (const uint8_t *)&zero_masks[32 - (length & 0x1f)]; + uint64_t zero_mask0, zero_mask1; + + // sanitize input + memcpy(&zero_mask0, zero_mask, 8); + memcpy(&zero_mask1, zero_mask + 8, 8); + + input0 &= zero_mask0; + input1 &= zero_mask1; + + const uint8_t index = hash(input0); + *code = (uint16_t)types_and_classes[index].mnemonic->value; + *mnemonic = types_and_classes[index].mnemonic; + + uint64_t name0, name1; + memcpy(&name0, (*mnemonic)->key.data, 8); + memcpy(&name1, (*mnemonic)->key.data + 8, 8); + + if (likely(((input0 ^ name0) | (input1 ^ name1)) == 0) && *code) + return types_and_classes[index].code; + else if ((input0 & TYPE_MASK) == TYPE) + return scan_generic_type(data, length, code, mnemonic); + return 0; +} + +#undef TYPE +#undef TYPE_MASK +#undef CLASS +#undef CLASS_MASK + +#endif // TYPE_H diff --git a/src/generic/types.h b/src/generic/types.h new file mode 100644 index 0000000..9c7dccd --- /dev/null +++ b/src/generic/types.h @@ -0,0 +1,3151 @@ +/* + * types.h + * + * Copyright (c) 2023, NLnet Labs. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ +#ifndef TYPES_H +#define TYPES_H + +nonnull_all +static really_inline int32_t scan_type_or_class( + const char *data, + size_t length, + uint16_t *code, + const mnemonic_t **mnemonic); + +nonnull_all +static really_inline int32_t parse_type( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, + const token_t *token); + +nonnull_all +static really_inline int32_t parse_name( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, + const token_t *token); + +#define FIELDS(fields) \ + { (sizeof(fields)/sizeof(fields[0])), fields } + +#define FIELD(name) \ + { { { name, sizeof(name) - 1 } } } + +#define CLASS(name, code) \ + { { { name, sizeof(name) - 1 }, code } } + +#define UNKNOWN_CLASS(code) \ + { { { "", 0 }, code } } + +#define TYPE(name, code, _class, fields, check, parse) \ + { { { name, sizeof(name) - 1 }, code }, _class, false, false, fields, check, parse } + +#define UNKNOWN_TYPE(code) \ + { { { "", 0 }, code }, 0, false, false, { 0, NULL }, check_generic_rr, parse_unknown_rdata } + +#if _WIN32 +// FIXME: actually, for all the check functions, an int32_t will more than suffice +#include +typedef SSIZE_T ssize_t; +#endif + +nonnull((1,2,3,4)) +static really_inline ssize_t check_bytes( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + const uint8_t *data, + const size_t length, + const size_t size) +{ + (void)data; + if (length < size) + SYNTAX_ERROR(parser, "Missing %s in %s", NAME(field), NAME(type)); + return (ssize_t)size; +} + +#define check_int8(...) check_bytes(__VA_ARGS__, sizeof(uint8_t)) + +#define check_int16(...) check_bytes(__VA_ARGS__, sizeof(uint16_t)) + +#define check_int32(...) check_bytes(__VA_ARGS__, sizeof(uint32_t)) + +#define check_ip4(...) check_bytes(__VA_ARGS__, 4) + +#define check_ip6(...) check_bytes(__VA_ARGS__, 16) + +#define check_ilnp64(...) check_bytes(__VA_ARGS__, sizeof(uint64_t)) + +nonnull((1,2,3,4)) +static really_inline ssize_t check_ttl( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + const uint8_t *data, + const size_t length) +{ + uint32_t number; + + if (length < sizeof(number)) + SYNTAX_ERROR(parser, "Missing %s in %s", NAME(field), NAME(type)); + + memcpy(&number, data, sizeof(number)); + number = be32toh(number); + + if (number > INT32_MAX) + SEMANTIC_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + + return 4; +} + +zone_nonnull((1,2,3,4)) +static really_inline ssize_t check_type( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + const uint8_t *data, + const size_t length) +{ + uint16_t number; + + if (length < sizeof(number)) + SYNTAX_ERROR(parser, "Missing %s in %s", NAME(field), NAME(type)); + + memcpy(&number, data, sizeof(number)); + + if (!number) + SEMANTIC_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + + return 2; +} + +zone_nonnull((1,2,3,4)) +static really_inline ssize_t check_name( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + const uint8_t *data, + const size_t length) +{ + size_t label = 0, count = 0; + while (count < length) { + label = data[count]; + count += 1 + label; + if (!label) + break; + } + + if (!count || count > length) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + + return (ssize_t)count; +} + +zone_nonnull((1,2,3,4)) +static really_inline ssize_t check_string( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + const uint8_t *data, + const size_t length) +{ + size_t count; + + if (!length || (count = 1 + (size_t)data[0]) > length) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + + return (ssize_t)count; +} + +zone_nonnull((1,2,3,4)) +static really_inline ssize_t check_nsec( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + const uint8_t *data, + const size_t length) +{ + size_t count = 0; + size_t last_window = 0; + + while ((count + 2) < length) { + const size_t window = (size_t)data[0]; + const size_t blocks = 1 + (size_t)data[1]; + if (window < last_window || !window != !last_window) + SYNTAX_ERROR(parser, "Invalid %s in %s, windows are out-of-order", + NAME(field), NAME(type)); + if (blocks > 32) + SYNTAX_ERROR(parser, "Invalid %s in %s, blocks are out-of-bounds", + NAME(field), NAME(type)); + count += 2 + blocks; + last_window = window; + } + + if (count != length) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + + return (ssize_t)count; +} + +zone_nonnull((1)) +static really_inline int32_t check(size_t *length, ssize_t count) +{ + if (count < 0) + return (int32_t)count; + *length += (size_t)count; + return 0; +} + +nonnull_all +static really_inline int32_t accept_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + (void)type; + + assert(rdata->octets <= rdata->limit); + assert(rdata->octets >= parser->rdata->octets); + size_t length = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + + assert(length <= UINT16_MAX); + assert(parser->owner->length <= UINT8_MAX); + int32_t code = parser->options.accept.callback( + parser, + &(zone_name_t){ (uint8_t)parser->owner->length, parser->owner->octets }, + parser->file->last_type, + parser->file->last_class, + parser->file->last_ttl, + (uint16_t)length, + parser->rdata->octets, + parser->user_data); + + //assert((size_t)code < parser->buffers.size); + //if (result < 0) + // return result; + //parser->rdata = &parser->buffers.rdata.blocks[result]; + return code; +} + +nonnull_all +static int32_t check_a_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + assert(rdata->octets >= parser->rdata->octets); + if ((uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets == 4) + return accept_rr(parser, type, rdata); + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); +} + +nonnull_all +static int32_t parse_a_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_ip4(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_delimiter(parser, type, token)) < 0) + return code; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_ns_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_name(parser, type, &f[0], o, n))) < 0) + return r; + + if (c != n) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_ns_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous_or_quoted(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_name(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_delimiter(parser, type, token)) < 0) + return code; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_soa_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_name(parser, type, &f[0], o, n))) || + (r = check(&c, check_name(parser, type, &f[1], o+c, n-c))) || + (r = check(&c, check_int32(parser, type, &f[2], o+c, n-c))) || + (r = check(&c, check_ttl(parser, type, &f[3], o+c, n-c))) || + (r = check(&c, check_ttl(parser, type, &f[4], o+c, n-c))) || + (r = check(&c, check_ttl(parser, type, &f[5], o+c, n-c))) || + (r = check(&c, check_ttl(parser, type, &f[6], o+c, n-c)))) + return r; + + if (c != n) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_soa_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous_or_quoted(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_name(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_contiguous_or_quoted(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_name(parser, type, &fields[1], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[2], token)) < 0) + return code; + if ((code = parse_int32(parser, type, &fields[2], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[3], token)) < 0) + return code; + if ((code = parse_ttl(parser, type, &fields[3], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[4], token)) < 0) + return code; + if ((code = parse_ttl(parser, type, &fields[4], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[5], token)) < 0) + return code; + if ((code = parse_ttl(parser, type, &fields[5], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[6], token)) < 0) + return code; + if ((code = parse_ttl(parser, type, &fields[6], rdata, token)) < 0) + return code; + if ((code = take_delimiter(parser, type, token)) < 0) + return code; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_wks_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_ip4(parser, type, &f[0], o, n))) || + (r = check(&c, check_int8(parser, type, &f[0], o+c, n-c)))) + return r; + + // any bit may, or may not, be set. confirm the bitmap does not exceed the + // maximum number of ports + if (n > 8192 + 5) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_wks_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_ip4(parser, type, &fields[0], rdata, token) < 0)) + return code; + + if ((code = take_contiguous(parser, type, &fields[1], token)) < 0) + return code; + + int32_t protocol = scan_protocol(token->data, token->length); + if (protocol == -1) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[1]), NAME(type)); + + *rdata->octets++ = (uint8_t)protocol; + uint8_t *bitmap = rdata->octets; + int32_t highest_port = -1; + + take(parser, token); + while (is_contiguous(token)) { + int32_t port = scan_service(token->data, token->length, protocol); + if (port == -1) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&type->rdata.fields[2]), NAME(type)); + + if (port > highest_port) { + // ensure newly used octets are zeroed out before use + size_t offset = highest_port < 0 ? 0 : (size_t)highest_port / 8 + 1; + size_t length = (size_t)port / 8 + 1; + memset(bitmap + offset, 0, length - offset); + highest_port = port; + } + + // bits are counted from left to right, so bit 0 is the left most bit + bitmap[port / 8] |= (1 << (7 - port % 8)); + take(parser, token); + } + + rdata->octets += (size_t)highest_port / 8 + 1; + + if (have_delimiter(parser, type, token) < 0) + return token->code; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_hinfo_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_string(parser, type, &f[0], o, n))) || + (r = check(&c, check_string(parser, type, &f[1], o+c, n-c)))) + return r; + + if (c != n) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_hinfo_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous_or_quoted(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_string(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_quoted_or_contiguous(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_string(parser, type, &fields[1], rdata, token)) < 0) + return code; + if ((code = take_delimiter(parser, type, token)) < 0) + return code; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_minfo_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_name(parser, type, &f[0], o, n))) || + (r = check(&c, check_name(parser, type, &f[1], o+c, n-c)))) + return r; + + if (c != n) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_minfo_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous_or_quoted(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_name(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_contiguous_or_quoted(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_name(parser, type, &fields[1], rdata, token)) < 0) + return code; + if ((code = take_delimiter(parser, type, token)) < 0) + return code; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_mx_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) || + (r = check(&c, check_name(parser, type, &f[1], o+c, n-c)))) + return r; + + if (c != n) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_mx_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_int16(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_name(parser, type, &fields[1], rdata, token)) < 0) + return code; + if ((code = take_delimiter(parser, type, token)) < 0) + return code; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_txt_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_string(parser, type, &f[0], o, n)))) + return r; + + while (c < n) + if ((r = check(&c, check_string(parser, type, &f[0], o+c, n-c)))) + return r; + + if (c != n) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_txt_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + while (is_contiguous_or_quoted(token)) { + if ((code = parse_string(parser, type, &fields[0], rdata, token)) < 0) + return code; + take(parser, token); + } + + if ((code = have_delimiter(parser, type, token)) < 0) + return code; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_x25_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_string(parser, type, &f[0], o, n)))) + return r; + + if (c != n) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_x25_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous_or_quoted(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_string(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_delimiter(parser, type, token)) < 0) + return code; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_isdn_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_string(parser, type, &f[0], o, n)))) + return r; + // subaddress is optional + if (c < n && (r = check(&c, check_string(parser, type, &f[1], o+c, n-c)))) + return r; + + if (c != n) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_isdn_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous_or_quoted(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_string(parser, type, &fields[0], rdata, token)) < 0) + return code; + + // subaddress is optional + take(parser, token); + if (is_contiguous_or_quoted(token)) { + if ((code = parse_string(parser, type, &fields[1], rdata, token)) < 0) + return code; + take(parser, token); + } + + if ((code = have_delimiter(parser, type, token)) < 0) + return code; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_rt_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) || + (r = check(&c, check_name(parser, type, &f[1], o+c, n-c)))) + return r; + + if (c != n) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_rt_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_int16(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_contiguous_or_quoted(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_name(parser, type, &fields[1], rdata, token)) < 0) + return code; + if ((code = take_delimiter(parser, type, token)) < 0) + return code; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_nsap_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + if (rdata->octets == parser->rdata->octets) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_nsap_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_nsap(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_delimiter(parser, type, token)) < 0) + return code; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_nsap_ptr_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + { + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_name(parser, type, &f[0], o, n)))) + return r; + + if (c != n) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + } + + { + // RFC1706 section 6 + // A domain name is generated from an NSAP by reversing the hex nibbles of + // the NSAP, treating each nibble as a separate subdomain, and appending + // the top-level subdomain name "NSAP.INT" to it. For example, the domain + // name used in the reverse lookup for the NSAP + // + // 47.0005.80.005a00.0000.0001.e133.ffffff000162.00 + // + // would appear as + // + // 0.0.2.6.1.0.0.0.f.f.f.f.f.f.3.3.1.e.1.0.0.0.0.0.0.0.0.0.a.5.0.0. + // 0.8.5.0.0.0.7.4.NSAP.INT. + size_t i = 0; + const size_t n = parser->file->owner.length; + const uint8_t *o = parser->file->owner.octets; + for (; i < n; i += 2) + if (o[i] != 1 || (base16_table_dec_32bit_d1[o[i+1]] > 0xff)) + break; + + const uint8_t nsap_int[] = { 4, 'n', 's', 'a', 'p', 3, 'i', 'n', 't', 0 }; + if (strncasecmp((const char *)o + i, (const char *)nsap_int, 9) != 0 || !i || i + 10 != n) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + } + + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_nsap_ptr_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous_or_quoted(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_name(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_delimiter(parser, type, token)) < 0) + return code; + + // RFC1706 section 6 states each nibble is treated as a separate subdomain + return check_nsap_ptr_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_key_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ +#if 0 + int32_t r; + size_t c = 0; + const size_t n = parser->rdata->length; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + // + // FIXME: implement (RFC2065) + // + // FIXME: verify the flag, algorithm and protocol combination is valid + // FIXME: verify the key is valid for type(3)+algorithm(1) + // + // The combination is of course subject to secondary checks! + // +#endif + (void)type; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_key_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_int16(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_int8(parser, type, &fields[1], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[2], token)) < 0) + return code; + if ((code = parse_int8(parser, type, &fields[2], rdata, token)) < 0) + return code; + take(parser, token); + if ((code = parse_base64_sequence(parser, type, &fields[3], rdata, token)) < 0) + return code; + + return check_key_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_px_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) || + (r = check(&c, check_name(parser, type, &f[1], o+c, n-c))) || + (r = check(&c, check_name(parser, type, &f[2], o+c, n-c)))) + return r; + + if (c != n) + SYNTAX_ERROR(parser, "Invalid %s record", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_px_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_int16(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_contiguous_or_quoted(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_name(parser, type, &fields[1], rdata, token)) < 0) + return code; + if ((code = take_contiguous_or_quoted(parser, type, &fields[2], token)) < 0) + return code; + if ((code = parse_name(parser, type, &fields[2], rdata, token)) < 0) + return code; + if ((code = take_delimiter(parser, type, token)) < 0) + return code; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_gpos_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_string(parser, type, &f[0], o, n))) || + (r = check(&c, check_string(parser, type, &f[1], o+c, n-c))) || + (r = check(&c, check_string(parser, type, &f[2], o+c, n-c)))) + return r; + + if (c != n) + SYNTAX_ERROR(parser, "Invalid %s record", NAME(type)); + return accept_rr(parser, type, rdata); +} + +static int32_t parse_gpos_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_latitude(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_longitude(parser, type, &fields[1], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[2], token)) < 0) + return code; + if ((code = parse_altitude(parser, type, &fields[2], rdata, token)) < 0) + return code; + if ((code = take_delimiter(parser, type, token)) < 0) + return code; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_aaaa_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_ip6(parser, type, &f[0], o, n)))) + return r; + + if (c != n) + SYNTAX_ERROR(parser, "Invalid %s record", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_aaaa_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_ip6(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_delimiter(parser, type, token)) < 0) + return code; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_loc_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + if ((uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets != 16) + SYNTAX_ERROR(parser, "Invalid %s record", NAME(type)); + return accept_rr(parser, type, rdata); + + // FIXME: check validity of latitude, longitude and latitude? +} + +nonnull_all +static int32_t parse_loc_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + uint32_t degrees, minutes, seconds; + uint32_t latitude, longitude, altitude; + const rdata_info_t *fields = type->rdata.fields; + static const uint8_t defaults[4] = { 0x00, 0x12, 0x16, 0x13 }; + + // RFC1876 section 3: + // If omitted, minutes and seconds default to zero, size defaults to 1m, + // horizontal precision defaults to 10000m, and vertical precision defaults + // to 10m. + memcpy(rdata->octets, &defaults, sizeof(defaults)); + + // latitude + if ((code = have_contiguous(parser, type, &fields[4], token)) < 0) + return code; + if (scan_degrees(token->data, token->length, °rees) == -1) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[4]), NAME(type)); + if ((code = take_contiguous(parser, type, &fields[4], token)) < 0) + return code; + if (scan_minutes(token->data, token->length, &minutes) == -1) + goto north_south; // minutes default to zero + degrees += minutes; + if ((code = take_contiguous(parser, type, &fields[4], token)) < 0) + return code; + if (scan_seconds(token->data, token->length, &seconds) == -1) + goto north_south; // seconds default to zero + degrees += seconds; + + if ((code = take_contiguous(parser, type, &fields[4], token)) < 0) + return code; +north_south: + if (token->data[0] == 'N') + latitude = htobe32((1u<<31) + degrees); + else if (token->data[1] == 'S') + latitude = htobe32((1u<<31) - degrees); + else + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[4]), NAME(type)); + + memcpy(&rdata->octets[4], &latitude, sizeof(latitude)); + + // longitude + if ((code = take_contiguous(parser, type, &fields[5], token)) < 0) + return code; + if (scan_degrees(token->data, token->length, °rees) == -1) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[5]), NAME(type)); + if ((code = take_contiguous(parser, type, &fields[5], token)) < 0) + return code; + if (scan_minutes(token->data, token->length, &minutes) == -1) + goto east_west; // minutes default to zero + degrees += minutes; + if ((code = take_contiguous(parser, type, &fields[5], token)) < 0) + return code; + if (scan_seconds(token->data, token->length, &seconds) == -1) + goto east_west; // seconds default to zero + degrees += seconds; + + if ((code = take_contiguous(parser, type, &fields[5], token)) < 0) + return code; +east_west: + if (token->data[0] == 'E') + longitude = htobe32((1u<<31) + degrees); + else if (token->data[0] == 'W') + longitude = htobe32((1u<<31) - degrees); + else + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[5]), NAME(type)); + + memcpy(&rdata->octets[8], &longitude, sizeof(longitude)); + + // altitude + if ((code = take_contiguous(parser, type, &fields[6], token)) < 0) + return code; + if (scan_altitude(token->data, token->length, &altitude) == -1) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[6]), NAME(type)); + + altitude = htobe32(altitude); + memcpy(&rdata->octets[12], &altitude, sizeof(altitude)); + + // size + take(parser, token); + if (!is_contiguous(token)) + goto skip_optional; + if (scan_precision(token->data, token->length, &rdata->octets[1])) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[1]), NAME(type)); + + // horizontal precision + take(parser, token); + if (!is_contiguous(token)) + goto skip_optional; + if (scan_precision(token->data, token->length, &rdata->octets[2])) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[2]), NAME(type)); + + // vertical precision + take(parser, token); + if (!is_contiguous(token)) + goto skip_optional; + if (scan_precision(token->data, token->length, &rdata->octets[3])) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[3]), NAME(type)); + + take(parser, token); +skip_optional: + if ((code = have_delimiter(parser, type, token)) < 0) + return code; + + rdata->octets += 16; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_nxt_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_name(parser, type, &f[3], o+c, n-c)))) + return r; + + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_nxt_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous_or_quoted(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_name(parser, type, &fields[0], rdata, token)) < 0) + return code; + take(parser, token); + if ((code = parse_nxt(parser, type, &fields[1], rdata, token)) < 0) + return code; + + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_srv_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) || + (r = check(&c, check_int16(parser, type, &f[1], o+c, n-c))) || + (r = check(&c, check_int16(parser, type, &f[2], o+c, n-c))) || + (r = check(&c, check_name(parser, type, &f[3], o+c, n-c)))) + return r; + + if (c != n) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_srv_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_int16(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_int16(parser, type, &fields[1], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[2], token)) < 0) + return code; + if ((code = parse_int16(parser, type, &fields[2], rdata, token)) < 0) + return code; + if ((code = take_contiguous_or_quoted(parser, type, &fields[3], token)) < 0) + return code; + if ((code = parse_name(parser, type, &fields[3], rdata, token)) < 0) + return code; + if ((code = take_delimiter(parser, type, token)) < 0) + return code; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_naptr_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + // FIXME: implement actual checks + (void)type; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_naptr_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_int16(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_int16(parser, type, &fields[1], rdata, token)) < 0) + return code; + if ((code = take_quoted_or_contiguous(parser, type, &fields[2], token)) < 0) + return code; + if ((code = parse_string(parser, type, &fields[2], rdata, token)) < 0) + return code; + if ((code = take_quoted_or_contiguous(parser, type, &fields[3], token)) < 0) + return code; + if ((code = parse_string(parser, type, &fields[3], rdata, token)) < 0) + return code; + if ((code = take_quoted_or_contiguous(parser, type, &fields[4], token)) < 0) + return code; + if ((code = parse_string(parser, type, &fields[4], rdata, token)) < 0) + return code; + if ((code = take_contiguous_or_quoted(parser, type, &fields[5], token)) < 0) + return code; + if ((code = parse_name(parser, type, &fields[5], rdata, token)) < 0) + return code; + if ((code = take_delimiter(parser, type, token)) < 0) + return code; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_cert_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + // FIXME: implement actual checks + (void)type; + + assert(rdata->octets >= parser->rdata->octets); + if ((uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets < 6) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_cert_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_certificate_type(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_int16(parser, type, &fields[1], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[2], token)) < 0) + return code; + if ((code = parse_algorithm_type(parser, type, &fields[2], rdata, token)) < 0) + return code; + take(parser, token); + if ((code = parse_base64_sequence(parser, type, &fields[3], rdata, token)) < 0) + return code; + + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_apl_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + // FIXME: check correctness of fields and total length + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_apl_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + // RDATA section for APL consists of zero or more fields + while (is_contiguous(token)) { + int32_t length; + const size_t size = (uintptr_t)rdata->limit - (uintptr_t)rdata->octets; + if ((length = scan_apl(token->data, token->length, rdata->octets, size)) < 0) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[0]), NAME(type)); + assert(length == 8 /* ipv4 */ || length == 20 /* ipv6 */); + rdata->octets += (size_t)length; + take(parser, token); + } + + if ((code = have_delimiter(parser, type, token)) < 0) + return code; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_ds_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) || + (r = check(&c, check_int8(parser, type, &f[1], o+c, n-c))) || + (r = check(&c, check_int8(parser, type, &f[2], o+c, n-c)))) + return r; + + // FIXME: can implement checking for digest length based on algorithm here. + // e.g. SHA-1 digest is 20 bytes, see RFC3658 section 2.4 + + if (c >= n) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_ds_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_int16(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_algorithm_type(parser, type, &fields[1], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[2], token)) < 0) + return code; + if ((code = parse_int8(parser, type, &fields[2], rdata, token)) < 0) + return code; + take(parser, token); + if ((code = parse_base16_sequence(parser, type, &fields[3], rdata, token)) < 0) + return code; + + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_sshfp_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_int8(parser, type, &f[0], o, n))) || + (r = check(&c, check_int8(parser, type, &f[1], o, n)))) + return r; + + // https://www.iana.org/assignments/dns-sshfp-rr-parameters + + if (c >= n) + SYNTAX_ERROR(parser, "Missing %s in %s", NAME((&f[0])), NAME(type)); + else if (o[1] == 1 && (n - c) != 20) + SEMANTIC_ERROR(parser, "Wrong fingerprint size for type %s in %s", + "SHA1", NAME(type)); + else if (o[1] == 2 && (n - c) != 32) + SEMANTIC_ERROR(parser, "Wrong fingerprint size for type %s in %s", + "SHA256", NAME(type)); + + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_sshfp_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_int8(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_int8(parser, type, &fields[1], rdata, token)) < 0) + return code; + take(parser, token); + if ((code = parse_base16_sequence(parser, type, &fields[2], rdata, token)) < 0) + return code; + + return check_sshfp_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_ipseckey_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata); + +nonnull_all +static int32_t parse_ipseckey_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token); + +diagnostic_push() +gcc_diagnostic_ignored(missing-field-initializers) +clang_diagnostic_ignored(missing-field-initializers) + +static const rdata_info_t ipseckey_ipv4_rdata_fields[] = { + FIELD("precedence"), + FIELD("gateway type"), + FIELD("algorithm"), + FIELD("gateway"), + FIELD("public key") +}; + +static const type_info_t ipseckey_ipv4[] = { + TYPE("IPSECKEY", ZONE_IPSECKEY, ZONE_IN, FIELDS(ipseckey_ipv4_rdata_fields), + check_ipseckey_rr, parse_ipseckey_rdata), +}; + +static const rdata_info_t ipseckey_ipv6_rdata_fields[] = { + FIELD("precedence"), + FIELD("gateway type"), + FIELD("algorithm"), + FIELD("gateway"), + FIELD("public key") +}; + +static const type_info_t ipseckey_ipv6[] = { + TYPE("IPSECKEY", ZONE_IPSECKEY, ZONE_IN, FIELDS(ipseckey_ipv6_rdata_fields), + check_ipseckey_rr, parse_ipseckey_rdata), +}; + + +diagnostic_pop() + +nonnull_all +static int32_t check_ipseckey_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const type_info_t *t = type; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_int8(parser, type, &f[0], o, n))) || + (r = check(&c, check_int8(parser, type, &f[1], o+c, n-c))) || + (r = check(&c, check_int8(parser, type, &f[2], o+c, n-c)))) + return r; + + switch (parser->rdata->octets[1]) { + case 1: /* IPv4 address */ + t = (const type_info_t *)ipseckey_ipv4; + f = ipseckey_ipv4_rdata_fields; + if ((r = check(&c, check_ip4(parser, t, &f[3], o+c, n-c))) < 0) + return r; + break; + case 2: /* IPv6 address */ + t = (const type_info_t *)ipseckey_ipv6; + f = ipseckey_ipv6_rdata_fields; + if ((r = check(&c, check_ip6(parser, t, &f[3], o+c, n-c))) < 0) + return r; + break; + case 0: /* no gateway */ + case 3: /* domain name */ + if ((r = check(&c, check_name(parser, t, &f[3], o+c, n-c))) < 0) + return r; + break; + default: + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + } + + switch (parser->rdata->octets[2]) { + case 0: + if (c < n) + SYNTAX_ERROR(parser, "Trailing data in %s", NAME(t)); + break; + default: + if (c >= n) + SYNTAX_ERROR(parser, "Missing %s in %s", NAME(&f[4]), NAME(t)); + break; + } + + return accept_rr(parser, t, rdata); +} + +nonnull_all +static int32_t parse_ipseckey_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + uint8_t *octets = rdata->octets; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_int8(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_int8(parser, type, &fields[1], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[2], token)) < 0) + return code; + if ((code = parse_int8(parser, type, &fields[2], rdata, token)) < 0) + return code; + + switch (octets[1]) { + case 1: /* IPv4 address */ + type = (const type_info_t *)ipseckey_ipv4; + fields = type->rdata.fields; + if ((code = take_contiguous(parser, type, &fields[3], token)) < 0) + return code; + if ((code = parse_ip4(parser, type, &fields[3], rdata, token)) < 0) + return code; + break; + case 2: /* IPv6 address */ + type = (const type_info_t *)ipseckey_ipv6; + fields = type->rdata.fields; + if ((code = take_contiguous(parser, type, &fields[3], token)) < 0) + return code; + if ((code = parse_ip6(parser, type, &fields[3], rdata, token)) < 0) + return code; + break; + case 0: /* no gateway */ + case 3: /* domain name */ + if ((code = take_contiguous_or_quoted(parser, type, &fields[3], token)) < 0) + return code; + if ((code = parse_name(parser, type, &fields[3], rdata, token)) < 0) + return code; + break; + default: + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[3]), NAME(type)); + } + + take(parser, token); + switch (octets[2]) { + case 0: + if ((code = have_delimiter(parser, type, token)) < 0) + return code; + break; + default: + if ((code = parse_base64_sequence(parser, type, &fields[4], rdata, token)) < 0) + return code; + break; + } + + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_rrsig_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_type(parser, type, &f[0], o, n))) || + (r = check(&c, check_int8(parser, type, &f[1], o+c, n-c))) || + (r = check(&c, check_int8(parser, type, &f[2], o+c, n-c))) || + (r = check(&c, check_ttl(parser, type, &f[3], o+c, n-c))) || + (r = check(&c, check_int32(parser, type, &f[4], o+c, n-c))) || + (r = check(&c, check_int32(parser, type, &f[5], o+c, n-c))) || + (r = check(&c, check_int16(parser, type, &f[6], o+c, n-c))) || + (r = check(&c, check_name(parser, type, &f[7], o+c, n-c)))) + return r; + + if (c != n) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_rrsig_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_type(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_algorithm_type(parser, type, &fields[1], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[2], token)) < 0) + return code; + if ((code = parse_int8(parser, type, &fields[2], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[3], token)) < 0) + return code; + if ((code = parse_ttl(parser, type, &fields[3], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[4], token)) < 0) + return code; + if ((code = parse_time(parser, type, &fields[4], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[5], token)) < 0) + return code; + if ((code = parse_time(parser, type, &fields[5], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[6], token)) < 0) + return code; + if ((code = parse_int16(parser, type, &fields[6], rdata, token)) < 0) + return code; + if ((code = take_contiguous_or_quoted(parser, type, &fields[7], token)) < 0) + return code; + if ((code = parse_name(parser, type, &fields[7], rdata, token)) < 0) + return code; + take(parser, token); + if ((code = parse_base64_sequence(parser, type, &fields[8], rdata, token)) < 0) + return code; + + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_nsec_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_name(parser, type, &f[0], o, n))) || + (r = check(&c, check_nsec(parser, type, &f[1], o+c, n-c)))) + return r; + + if (c != n) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_nsec_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous_or_quoted(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_name(parser, type, &fields[0], rdata, token)) < 0) + return code; + take(parser, token); + if ((code = parse_nsec(parser, type, &fields[1], rdata, token)) < 0) + return code; + + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_dnskey_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) || + (r = check(&c, check_int8(parser, type, &f[1], o+c, n-c))) || + (r = check(&c, check_int8(parser, type, &f[2], o+c, n-c)))) + return r; + + if (c >= n) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_dnskey_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_int16(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_int8(parser, type, &fields[1], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[2], token)) < 0) + return code; + if ((code = parse_algorithm_type(parser, type, &fields[2], rdata, token)) < 0) + return code; + take(parser, token); + if ((code = parse_base64_sequence(parser, type, &fields[3], rdata, token)) < 0) + return code; + + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_dhcid_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + // RFC4701 section 3.1: + // 2-octet identifier type, 1-octet digest type, followed by one or more + // octets representing the actual identifier + if ((uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets < 4) + SEMANTIC_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_dhcid_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = parse_base64_sequence(parser, type, &fields[0], rdata, token)) < 0) + return code; + + return check_dhcid_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_nsec3_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_int8(parser, type, &f[0], o, n))) || + (r = check(&c, check_int8(parser, type, &f[1], o+c, n-c))) || + (r = check(&c, check_int16(parser, type, &f[2], o+c, n-c))) || + (r = check(&c, check_string(parser, type, &f[3], o+c, n-c))) || + (r = check(&c, check_string(parser, type, &f[4], o+c, n-c))) || + (r = check(&c, check_nsec(parser, type, &f[5], o+c, n-c)))) + return r; + + if (c != n) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_nsec3_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_int8(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_int8(parser, type, &fields[1], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[2], token)) < 0) + return code; + if ((code = parse_int16(parser, type, &fields[2], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[3], token)) < 0) + return code; + if ((code = parse_salt(parser, type, &fields[3], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[4], token)) < 0) + return code; + if ((code = parse_base32(parser, type, &fields[4], rdata, token)) < 0) + return code; + take(parser, token); + if ((code = parse_nsec(parser, type, &fields[5], rdata, token)) < 0) + return code; + + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_nsec3param_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_int8(parser, type, &f[0], o, n))) || + (r = check(&c, check_int8(parser, type, &f[1], o+c, n-c))) || + (r = check(&c, check_int16(parser, type, &f[2], o+c, n-c))) || + (r = check(&c, check_string(parser, type, &f[3], o+c, n-c)))) + return r; + + if (c != n) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_nsec3param_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_int8(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_int8(parser, type, &fields[1], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[2], token)) < 0) + return code; + if ((code = parse_int16(parser, type, &fields[2], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[3], token)) < 0) + return code; + if ((code = parse_salt(parser, type, &fields[3], rdata, token)) < 0) + return code; + if ((code = take_delimiter(parser, type, token)) < 0) + return code; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_tlsa_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_int8(parser, type, &f[0], o, n))) || + (r = check(&c, check_int8(parser, type, &f[1], o+c, n-c))) || + (r = check(&c, check_int8(parser, type, &f[2], o+c, n-c)))) + return r; + + if (c >= n) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_tlsa_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_int8(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_int8(parser, type, &fields[1], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[2], token)) < 0) + return code; + if ((code = parse_int8(parser, type, &fields[2], rdata, token)) < 0) + return code; + take(parser, token); + if ((code = parse_base16_sequence(parser, type, &fields[3], rdata, token)) < 0) + return code; + + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_hip_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + // FIXME: verify field lengths etc + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_hip_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + uint8_t *octets = rdata->octets; + + // reserve octet for HIT length + rdata->octets += 1; + + // PK algorithm + if ((code = have_contiguous(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_int8(parser, type, &fields[1], rdata, token)) < 0) + return code; + + // reserve octets for PK length + rdata->octets += 2; + + // HIT + if ((code = take_contiguous(parser, type, &fields[3], token)) < 0) + return code; + if ((code = parse_base16(parser, type, &fields[3], rdata, token)) < 0) + return code; + + if ((rdata->octets - octets) > 255 + 4) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[3]), NAME(type)); + uint8_t hit_length = (uint8_t)((rdata->octets - octets) - 4); + octets[0] = hit_length; + + // Public Key + if ((code = take_contiguous(parser, type, &fields[4], token)) < 0) + return code; + if ((code = parse_base64(parser, type, &fields[4], rdata, token)) < 0) + return code; + + uint16_t pk_length = htobe16((uint16_t)(((rdata->octets - octets) - hit_length) - 4)); + memcpy(&octets[2], &pk_length, sizeof(pk_length)); + + take(parser, token); + while (is_contiguous_or_quoted(token)) { + if ((code = parse_name(parser, type, &fields[5], rdata, token)) < 0) + return code; + take(parser, token); + } + + if ((code = have_delimiter(parser, type, token)) < 0) + return code; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_openpgpkey_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + // FIXME: as the RDATA contains a digest, it is likely we can make this + // check stricter, at least, for known algorithms + if ((uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets < 4) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_openpgpkey_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = parse_base64_sequence(parser, type, &fields[0], rdata, token)) < 0) + return code; + + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_csync_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_int32(parser, type, &f[0], o, n))) || + (r = check(&c, check_int16(parser, type, &f[1], o+c, n-c))) || + (r = check(&c, check_nsec(parser, type, &f[2], o+c, n-c)))) + return r; + + if (c != n) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_csync_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_int32(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_int16(parser, type, &fields[1], rdata, token)) < 0) + return code; + take(parser, token); + if ((code = parse_nsec(parser, type, &fields[2], rdata, token)) < 0) + return code; + + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_zonemd_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + // FIXME: RDATA contains digests, do extra checks? + assert(rdata->octets >= parser->rdata->octets); + if ((uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets < 6) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_zonemd_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_int32(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_int8(parser, type, &fields[1], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[2], token)) < 0) + return code; + if ((code = parse_int8(parser, type, &fields[2], rdata, token)) < 0) + return code; + take(parser, token); + if ((code = parse_base16_sequence(parser, type, &fields[3], rdata, token)) < 0) + return code; + + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_svcb_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + // FIXME: implement checking parameters etc + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_svcb_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_int16(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_contiguous_or_quoted(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_name(parser, type, &fields[1], rdata, token)) < 0) + return code; + take(parser, token); + if ((code = parse_svc_params(parser, type, &fields[2], rdata, token)) < 0) + return code; + + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_https_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_https_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_int16(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_contiguous_or_quoted(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_name(parser, type, &fields[1], rdata, token)) < 0) + return code; + take(parser, token); + if ((code = parse_svc_params(parser, type, &fields[2], rdata, token)) < 0) + return code; + + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_nid_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) || + (r = check(&c, check_ilnp64(parser, type, &f[1], o+c, n-c)))) + return r; + if (c != n) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_nid_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_int16(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_ilnp64(parser, type, &fields[1], rdata, token)) < 0) + return code; + if ((code = take_delimiter(parser, type, token)) < 0) + return code; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_l32_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) || + (r = check(&c, check_ip4(parser, type, &f[1], o+c, n-c)))) + return r; + + if (c != n) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_l32_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_int16(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_ip4(parser, type, &fields[1], rdata, token)) < 0) + return code; + if ((code = take_delimiter(parser, type, token)) < 0) + return code; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_l64_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) || + (r = check(&c, check_ilnp64(parser, type, &f[1], o+c, n-c)))) + return r; + if (c != n) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_l64_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_int16(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_ilnp64(parser, type, &fields[1], rdata, token)) < 0) + return code; + if ((code = take_delimiter(parser, type, token)) < 0) + return code; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_eui48_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + if ((uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets != 6) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_eui48_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_eui48(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_delimiter(parser, type, token)) < 0) + return code; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_eui64_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + if ((uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets != 8) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_eui64_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_eui64(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_delimiter(parser, type, token)) < 0) + return code; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_uri_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) || + (r = check(&c, check_int16(parser, type, &f[1], o+c, n-c)))) + return r; + if (c >= n) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_uri_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_int16(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_int16(parser, type, &fields[1], rdata, token)) < 0) + return code; + if ((code = take_quoted(parser, type, &fields[2], token)) < 0) + return code; + if ((code = parse_text(parser, type, &fields[2], rdata, token)) < 0) + return code; + if ((code = take_delimiter(parser, type, token)) < 0) + return code; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_caa_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + int32_t r; + size_t c = 0; + const size_t n = (uintptr_t)rdata->octets - (uintptr_t)parser->rdata->octets; + const uint8_t *o = parser->rdata->octets; + const rdata_info_t *f = type->rdata.fields; + + if ((r = check(&c, check_int8(parser, type, &f[0], o, n))) || + (r = check(&c, check_int8(parser, type, &f[1], o+c, n-c)))) + return r; + if (c >= n) + SYNTAX_ERROR(parser, "Invalid %s", NAME(type)); + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_caa_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + const rdata_info_t *fields = type->rdata.fields; + + if ((code = have_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if ((code = parse_int8(parser, type, &fields[0], rdata, token)) < 0) + return code; + if ((code = take_contiguous(parser, type, &fields[1], token)) < 0) + return code; + if ((code = parse_caa_tag(parser, type, &fields[1], rdata, token)) < 0) + return code; + if ((code = take_quoted_or_contiguous(parser, type, &fields[2], token)) < 0) + return code; + if ((code = parse_text(parser, type, &fields[2], rdata, token)) < 0) + return code; + if ((code = take_delimiter(parser, type, token)) < 0) + return code; + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t check_generic_rr( + parser_t *parser, const type_info_t *type, const rdata_t *rdata) +{ + return accept_rr(parser, type, rdata); +} + +nonnull_all +static int32_t parse_generic_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + int32_t code; + uint16_t rdlength; + static const rdata_info_t fields[] = { + FIELD("rdlength"), + FIELD("rdata") + }; + + // discard '\#' + if ((code = take_contiguous(parser, type, &fields[0], token)) < 0) + return code; + if (!scan_int16(token->data, token->length, &rdlength)) + SYNTAX_ERROR(parser, "Invalid RDLENGTH in %s", NAME(type)); + + take(parser, token); + if (is_contiguous(token)) { + struct base16_state state = { 0 }; + + do { + size_t length = token->length + 1 / 2; + if (length > (uintptr_t)rdata->limit - (uintptr_t)rdata->octets) + SYNTAX_ERROR(parser, "Invalid RDATA in %s", NAME(type)); + if (!base16_stream_decode(&state, token->data, token->length, rdata->octets, &length)) + SYNTAX_ERROR(parser, "Invalid RDATA in %s", NAME(type)); + rdata->octets += length; + take(parser, token); + } while (is_contiguous(token)); + + if (state.bytes) + *rdata->octets++ = state.carry; + } + + if ((code = have_delimiter(parser, type, token)) < 0) + return code; + if (rdata->octets - parser->rdata->octets != rdlength) + SYNTAX_ERROR(parser, "Invalid RDATA in %s", NAME(type)); + return type->check(parser, type, rdata); +} + +nonnull_all +static int32_t parse_unknown_rdata( + parser_t *parser, const type_info_t *type, rdata_t *rdata, token_t *token) +{ + (void)type; + (void)rdata; + (void)token; + SYNTAX_ERROR(parser, "Unknown record type"); +} + +diagnostic_push() +gcc_diagnostic_ignored(missing-field-initializers) +clang_diagnostic_ignored(missing-field-initializers) + +static const class_info_t classes[] = { + UNKNOWN_CLASS(0), + CLASS("IN", 1), + CLASS("CS", 2), + CLASS("CH", 3), + CLASS("HS", 4) +}; + +static const rdata_info_t a_rdata_fields[] = { + FIELD("address") +}; + +static const rdata_info_t ns_rdata_fields[] = { + FIELD("host") +}; + +static const rdata_info_t md_rdata_fields[] = { + FIELD("madname") +}; + +static const rdata_info_t mf_rdata_fields[] = { + FIELD("madname") +}; + +static const rdata_info_t cname_rdata_fields[] = { + FIELD("host") +}; + +static const rdata_info_t soa_rdata_fields[] = { + FIELD("primary"), + FIELD("mailbox"), + FIELD("serial"), + FIELD("refresh"), + FIELD("retry"), + FIELD("expire"), + FIELD("minimum") +}; + +static const rdata_info_t mb_rdata_fields[] = { + FIELD("madname") +}; + +static const rdata_info_t mg_rdata_fields[] = { + FIELD("mgmname") +}; + +static const rdata_info_t mr_rdata_fields[] = { + FIELD("newname") +}; + +static const rdata_info_t ptr_rdata_fields[] = { + FIELD("ptrdname") +}; + +static const rdata_info_t hinfo_rdata_fields[] = { + FIELD("cpu"), + FIELD("os") +}; + +static const rdata_info_t minfo_rdata_fields[] = { + FIELD("rmailbx"), + FIELD("emailbx") +}; + +static const rdata_info_t wks_rdata_fields[] = { + FIELD("address"), + FIELD("protocol"), + FIELD("bitmap") +}; + +static const rdata_info_t mx_rdata_fields[] = { + FIELD("priority"), + FIELD("hostname") +}; + +static const rdata_info_t txt_rdata_fields[] = { + FIELD("text") +}; + +static const rdata_info_t rp_rdata_fields[] = { + FIELD("mailbox"), + FIELD("text") +}; + +static const rdata_info_t afsdb_rdata_fields[] = { + FIELD("subtype"), + FIELD("hostname") +}; + +static const rdata_info_t x25_rdata_fields[] = { + FIELD("address") +}; + +static const rdata_info_t isdn_rdata_fields[] = { + FIELD("address"), + FIELD("subaddress") +}; + +static const rdata_info_t rt_rdata_fields[] = { + FIELD("preference"), + FIELD("hostname") +}; + +static const rdata_info_t nsap_rdata_fields[] = { + FIELD("address") +}; + +static const rdata_info_t nsap_ptr_rdata_fields[] = { + FIELD("hostname") +}; + +static const rdata_info_t key_rdata_fields[] = { + FIELD("flags"), + FIELD("protocol"), + FIELD("algorithm"), + FIELD("publickey") +}; + +static const rdata_info_t px_rdata_fields[] = { + FIELD("preference"), + FIELD("map822"), + FIELD("mapx400") +}; + +static const rdata_info_t gpos_rdata_fields[] = { + FIELD("latitude"), + FIELD("longitude"), + FIELD("altitude") +}; + +static const rdata_info_t aaaa_rdata_fields[] = { + FIELD("address") +}; + +static const rdata_info_t loc_rdata_fields[] = { + FIELD("version"), + FIELD("size"), + FIELD("horizontal precision"), + FIELD("vertical precision"), + FIELD("latitude"), + FIELD("longitude"), + FIELD("altitude") +}; + +static const rdata_info_t nxt_rdata_fields[] = { + FIELD("next domain name"), + FIELD("type bit map") +}; + +static const rdata_info_t srv_rdata_fields[] = { + FIELD("priority"), + FIELD("weight"), + FIELD("port"), + FIELD("target") +}; + +static const rdata_info_t naptr_rdata_fields[] = { + FIELD("order"), + FIELD("preference"), + FIELD("flags"), + FIELD("services"), + FIELD("regex"), + FIELD("replacement"), +}; + +static const rdata_info_t kx_rdata_fields[] = { + FIELD("preference"), + FIELD("exchanger") +}; + +static const rdata_info_t sig_rdata_fields[] = { + FIELD("sigtype"), + FIELD("algorithm"), + FIELD("labels"), + FIELD("origttl"), + FIELD("expire"), + FIELD("inception"), + FIELD("keytag"), + FIELD("signer"), + FIELD("signature") +}; + +static const rdata_info_t cert_rdata_fields[] = { + FIELD("type"), + FIELD("key tag"), + FIELD("algorithm"), + FIELD("certificate") +}; + +static const rdata_info_t dname_rdata_fields[] = { + FIELD("source") +}; + +static const rdata_info_t apl_rdata_fields[] = { + FIELD("prefix") +}; + +static const rdata_info_t ds_rdata_fields[] = { + FIELD("keytag"), + FIELD("algorithm"), + FIELD("digtype"), + FIELD("digest") +}; + +static const rdata_info_t sshfp_rdata_fields[] = { + FIELD("algorithm"), + FIELD("ftype"), + FIELD("fingerprint") +}; + +// IPSECKEY is different because the rdata depends on the algorithm +static const rdata_info_t ipseckey_rdata_fields[] = { + FIELD("precedence"), + FIELD("gateway type"), + FIELD("algorithm"), + FIELD("gateway"), + FIELD("public key") +}; + +static const rdata_info_t rrsig_rdata_fields[] = { + FIELD("rrtype"), + FIELD("algorithm"), + FIELD("labels"), + FIELD("origttl"), + FIELD("expire"), + FIELD("inception"), + FIELD("keytag"), + FIELD("signer"), + FIELD("signature") +}; + +static const rdata_info_t nsec_rdata_fields[] = { + FIELD("next"), + FIELD("types") +}; + +static const rdata_info_t dnskey_rdata_fields[] = { + FIELD("flags"), + FIELD("protocol"), + FIELD("algorithm"), + FIELD("publickey") +}; + +static const rdata_info_t dhcid_rdata_fields[] = { + FIELD("dhcpinfo") +}; + +static const rdata_info_t nsec3_rdata_fields[] = { + FIELD("algorithm"), + FIELD("flags"), + FIELD("iterations"), + FIELD("salt"), + FIELD("next"), + FIELD("types") +}; + +static const rdata_info_t nsec3param_rdata_fields[] = { + FIELD("algorithm"), + FIELD("flags"), + FIELD("iterations"), + FIELD("salt") +}; + +static const rdata_info_t tlsa_rdata_fields[] = { + FIELD("usage"), + FIELD("selector"), + FIELD("matching type"), + FIELD("certificate association data") +}; + +static const rdata_info_t smimea_rdata_fields[] = { + FIELD("usage"), + FIELD("selector"), + FIELD("matching type"), + FIELD("certificate association data") +}; + +static const rdata_info_t cds_rdata_fields[] = { + FIELD("keytag"), + FIELD("algorithm"), + FIELD("digtype"), + FIELD("digest") +}; + +static const rdata_info_t cdnskey_rdata_fields[] = { + FIELD("flags"), + FIELD("protocol"), + FIELD("algorithm"), + FIELD("publickey") +}; + +static const rdata_info_t hip_rdata_fields[] = { + FIELD("HIT length"), + FIELD("PK algorithm"), + FIELD("PK length"), + FIELD("HIT"), + FIELD("Public Key"), + FIELD("Rendezvous Servers") +}; + +static const rdata_info_t openpgpkey_rdata_fields[] = { + FIELD("key") +}; + +static const rdata_info_t csync_rdata_fields[] = { + FIELD("serial"), + FIELD("flags"), + FIELD("types") +}; + +static const rdata_info_t zonemd_rdata_fields[] = { + FIELD("serial"), + FIELD("scheme"), + FIELD("algorithm"), + FIELD("digest"), +}; + +static const rdata_info_t svcb_rdata_fields[] = { + FIELD("priority"), + FIELD("target"), + FIELD("params") +}; + +static const rdata_info_t https_rdata_fields[] = { + FIELD("priority"), + FIELD("target"), + FIELD("params") +}; + +static const rdata_info_t spf_rdata_fields[] = { + FIELD("text") +}; + +static const rdata_info_t nid_rdata_fields[] = { + FIELD("preference"), + FIELD("nodeid") +}; + +// RFC6742 specifies the syntax for the locator is compatible with the syntax +// for IPv4 addresses, but then proceeds to provide an example with leading +// zeroes. The example is corrected in the errata. +static const rdata_info_t l32_rdata_fields[] = { + FIELD("preference"), + FIELD("locator") +}; + +static const rdata_info_t l64_rdata_fields[] = { + FIELD("preference"), + FIELD("locator") +}; + +static const rdata_info_t lp_rdata_fields[] = { + FIELD("preference"), + FIELD("pointer") +}; + +static const rdata_info_t eui48_rdata_fields[] = { + FIELD("address") +}; + +static const rdata_info_t eui64_rdata_fields[] = { + FIELD("address") +}; + +static const rdata_info_t uri_rdata_fields[] = { + FIELD("priority"), + FIELD("weight"), + FIELD("target") +}; + +static const rdata_info_t caa_rdata_fields[] = { + FIELD("flags"), + FIELD("tag"), + FIELD("value") +}; + +// https://www.iana.org/assignments/dns-parameters/AVC/avc-completed-template +static const rdata_info_t avc_rdata_fields[] = { + FIELD("text") +}; + +static const rdata_info_t dlv_rdata_fields[] = { + FIELD("key"), + FIELD("algorithm"), + FIELD("type"), + FIELD("digest") +}; + +static const type_info_t types[] = { + UNKNOWN_TYPE(0), + + TYPE("A", ZONE_A, ZONE_ANY, FIELDS(a_rdata_fields), + check_a_rr, parse_a_rdata), + TYPE("NS", ZONE_NS, ZONE_ANY, FIELDS(ns_rdata_fields), + check_ns_rr, parse_ns_rdata), + TYPE("MD", ZONE_MD, ZONE_ANY, FIELDS(md_rdata_fields), // obsolete + check_ns_rr, parse_ns_rdata), + TYPE("MF", ZONE_MF, ZONE_ANY, FIELDS(mf_rdata_fields), // obsolete + check_ns_rr, parse_ns_rdata), + TYPE("CNAME", ZONE_CNAME, ZONE_ANY, FIELDS(cname_rdata_fields), + check_ns_rr, parse_ns_rdata), + TYPE("SOA", ZONE_SOA, ZONE_ANY, FIELDS(soa_rdata_fields), + check_soa_rr, parse_soa_rdata), + TYPE("MB", ZONE_MB, ZONE_ANY, FIELDS(mb_rdata_fields), // experimental + check_ns_rr, parse_ns_rdata), + TYPE("MG", ZONE_MG, ZONE_ANY, FIELDS(mg_rdata_fields), // experimental + check_ns_rr, parse_ns_rdata), + TYPE("MR", ZONE_MR, ZONE_ANY, FIELDS(mr_rdata_fields), // experimental + check_ns_rr, parse_ns_rdata), + + UNKNOWN_TYPE(10), + + TYPE("WKS", ZONE_WKS, ZONE_IN, FIELDS(wks_rdata_fields), + check_wks_rr, parse_wks_rdata), + TYPE("PTR", ZONE_PTR, ZONE_ANY, FIELDS(ptr_rdata_fields), + check_ns_rr, parse_ns_rdata), + TYPE("HINFO", ZONE_HINFO, ZONE_ANY, FIELDS(hinfo_rdata_fields), + check_hinfo_rr, parse_hinfo_rdata), + TYPE("MINFO", ZONE_MINFO, ZONE_ANY, FIELDS(minfo_rdata_fields), + check_minfo_rr, parse_minfo_rdata), + TYPE("MX", ZONE_MX, ZONE_ANY, FIELDS(mx_rdata_fields), + check_mx_rr, parse_mx_rdata), + TYPE("TXT", ZONE_TXT, ZONE_ANY, FIELDS(txt_rdata_fields), + check_txt_rr, parse_txt_rdata), + TYPE("RP", ZONE_RP, ZONE_ANY, FIELDS(rp_rdata_fields), + check_minfo_rr, parse_minfo_rdata), + TYPE("AFSDB", ZONE_AFSDB, ZONE_ANY, FIELDS(afsdb_rdata_fields), + check_mx_rr, parse_mx_rdata), + TYPE("X25", ZONE_X25, ZONE_ANY, FIELDS(x25_rdata_fields), + check_x25_rr, parse_x25_rdata), + TYPE("ISDN", ZONE_ISDN, ZONE_ANY, FIELDS(isdn_rdata_fields), + check_isdn_rr, parse_isdn_rdata), + TYPE("RT", ZONE_RT, ZONE_ANY, FIELDS(rt_rdata_fields), + check_rt_rr, parse_rt_rdata), + TYPE("NSAP", ZONE_NSAP, ZONE_IN, FIELDS(nsap_rdata_fields), + check_nsap_rr, parse_nsap_rdata), + TYPE("NSAP-PTR", ZONE_NSAP_PTR, ZONE_IN, FIELDS(nsap_ptr_rdata_fields), + check_nsap_ptr_rr, parse_nsap_ptr_rdata), + TYPE("SIG", ZONE_SIG, ZONE_ANY, FIELDS(sig_rdata_fields), + check_rrsig_rr, parse_rrsig_rdata), + TYPE("KEY", ZONE_KEY, ZONE_ANY, FIELDS(key_rdata_fields), + check_key_rr, parse_key_rdata), + TYPE("PX", ZONE_PX, ZONE_IN, FIELDS(px_rdata_fields), + check_px_rr, parse_px_rdata), + TYPE("GPOS", ZONE_GPOS, ZONE_ANY, FIELDS(gpos_rdata_fields), + check_gpos_rr, parse_gpos_rdata), + TYPE("AAAA", ZONE_AAAA, ZONE_IN, FIELDS(aaaa_rdata_fields), + check_aaaa_rr, parse_aaaa_rdata), + TYPE("LOC", ZONE_LOC, ZONE_ANY, FIELDS(loc_rdata_fields), + check_loc_rr, parse_loc_rdata), + TYPE("NXT", ZONE_NXT, ZONE_ANY, FIELDS(nxt_rdata_fields), // obsolete + check_nxt_rr, parse_nxt_rdata), + + UNKNOWN_TYPE(31), + UNKNOWN_TYPE(32), + + TYPE("SRV", ZONE_SRV, ZONE_IN, FIELDS(srv_rdata_fields), + check_srv_rr, parse_srv_rdata), + + UNKNOWN_TYPE(34), + + TYPE("NAPTR", ZONE_NAPTR, ZONE_IN, FIELDS(naptr_rdata_fields), + check_naptr_rr, parse_naptr_rdata), + TYPE("KX", ZONE_KX, ZONE_IN, FIELDS(kx_rdata_fields), + check_mx_rr, parse_mx_rdata), + TYPE("CERT", ZONE_CERT, ZONE_ANY, FIELDS(cert_rdata_fields), + check_cert_rr, parse_cert_rdata), + + UNKNOWN_TYPE(38), + + TYPE("DNAME", ZONE_DNAME, ZONE_ANY, FIELDS(dname_rdata_fields), + check_ns_rr, parse_ns_rdata), + + UNKNOWN_TYPE(40), + UNKNOWN_TYPE(41), + + TYPE("APL", ZONE_APL, ZONE_IN, FIELDS(apl_rdata_fields), + check_apl_rr, parse_apl_rdata), + TYPE("DS", ZONE_DS, ZONE_ANY, FIELDS(ds_rdata_fields), + check_ds_rr, parse_ds_rdata), + TYPE("SSHFP", ZONE_SSHFP, ZONE_ANY, FIELDS(sshfp_rdata_fields), + check_sshfp_rr, parse_sshfp_rdata), + TYPE("IPSECKEY", ZONE_IPSECKEY, ZONE_IN, FIELDS(ipseckey_rdata_fields), + check_ipseckey_rr, parse_ipseckey_rdata), + TYPE("RRSIG", ZONE_RRSIG, ZONE_ANY, FIELDS(rrsig_rdata_fields), + check_rrsig_rr, parse_rrsig_rdata), + TYPE("NSEC", ZONE_NSEC, ZONE_ANY, FIELDS(nsec_rdata_fields), + check_nsec_rr, parse_nsec_rdata), + TYPE("DNSKEY", ZONE_DNSKEY, ZONE_ANY, FIELDS(dnskey_rdata_fields), + check_dnskey_rr, parse_dnskey_rdata), + TYPE("DHCID", ZONE_DHCID, ZONE_IN, FIELDS(dhcid_rdata_fields), + check_dhcid_rr, parse_dhcid_rdata), + TYPE("NSEC3", ZONE_NSEC3, ZONE_ANY, FIELDS(nsec3_rdata_fields), + check_nsec3_rr, parse_nsec3_rdata), + TYPE("NSEC3PARAM", ZONE_NSEC3PARAM, ZONE_ANY, FIELDS(nsec3param_rdata_fields), + check_nsec3param_rr, parse_nsec3param_rdata), + TYPE("TLSA", ZONE_TLSA, ZONE_ANY, FIELDS(tlsa_rdata_fields), + check_tlsa_rr, parse_tlsa_rdata), + TYPE("SMIMEA", ZONE_SMIMEA, ZONE_ANY, FIELDS(smimea_rdata_fields), + check_tlsa_rr, parse_tlsa_rdata), + + UNKNOWN_TYPE(54), + + TYPE("HIP", ZONE_HIP, ZONE_ANY, FIELDS(hip_rdata_fields), + check_hip_rr, parse_hip_rdata), + + UNKNOWN_TYPE(56), + UNKNOWN_TYPE(57), + UNKNOWN_TYPE(58), + + TYPE("CDS", ZONE_CDS, ZONE_ANY, FIELDS(cds_rdata_fields), + check_ds_rr, parse_ds_rdata), + TYPE("CDNSKEY", ZONE_CDNSKEY, ZONE_ANY, FIELDS(cdnskey_rdata_fields), + check_dnskey_rr, parse_dnskey_rdata), + TYPE("OPENPGPKEY", ZONE_OPENPGPKEY, ZONE_ANY, FIELDS(openpgpkey_rdata_fields), + check_openpgpkey_rr, parse_openpgpkey_rdata), + TYPE("CSYNC", ZONE_CSYNC, ZONE_ANY, FIELDS(csync_rdata_fields), + check_csync_rr, parse_csync_rdata), + TYPE("ZONEMD", ZONE_ZONEMD, ZONE_ANY, FIELDS(zonemd_rdata_fields), + check_zonemd_rr, parse_zonemd_rdata), + TYPE("SVCB", ZONE_SVCB, ZONE_IN, FIELDS(svcb_rdata_fields), + check_svcb_rr, parse_svcb_rdata), + TYPE("HTTPS", ZONE_HTTPS, ZONE_IN, FIELDS(https_rdata_fields), + check_https_rr, parse_https_rdata), + + UNKNOWN_TYPE(66), + UNKNOWN_TYPE(67), + UNKNOWN_TYPE(68), + UNKNOWN_TYPE(69), + UNKNOWN_TYPE(70), + UNKNOWN_TYPE(71), + UNKNOWN_TYPE(72), + UNKNOWN_TYPE(73), + UNKNOWN_TYPE(74), + UNKNOWN_TYPE(75), + UNKNOWN_TYPE(76), + UNKNOWN_TYPE(77), + UNKNOWN_TYPE(78), + UNKNOWN_TYPE(79), + UNKNOWN_TYPE(80), + UNKNOWN_TYPE(81), + UNKNOWN_TYPE(82), + UNKNOWN_TYPE(83), + UNKNOWN_TYPE(84), + UNKNOWN_TYPE(85), + UNKNOWN_TYPE(86), + UNKNOWN_TYPE(87), + UNKNOWN_TYPE(88), + UNKNOWN_TYPE(89), + UNKNOWN_TYPE(90), + UNKNOWN_TYPE(91), + UNKNOWN_TYPE(92), + UNKNOWN_TYPE(93), + UNKNOWN_TYPE(94), + UNKNOWN_TYPE(95), + UNKNOWN_TYPE(96), + UNKNOWN_TYPE(97), + UNKNOWN_TYPE(98), + + TYPE("SPF", ZONE_SPF, ZONE_ANY, FIELDS(spf_rdata_fields), // obsolete + check_txt_rr, parse_txt_rdata), + + UNKNOWN_TYPE(100), + UNKNOWN_TYPE(101), + UNKNOWN_TYPE(102), + UNKNOWN_TYPE(103), + + TYPE("NID", ZONE_NID, ZONE_ANY, FIELDS(nid_rdata_fields), + check_nid_rr, parse_nid_rdata), + TYPE("L32", ZONE_L32, ZONE_ANY, FIELDS(l32_rdata_fields), + check_l32_rr, parse_l32_rdata), + TYPE("L64", ZONE_L64, ZONE_ANY, FIELDS(l64_rdata_fields), + check_l64_rr, parse_l64_rdata), + TYPE("LP", ZONE_LP, ZONE_ANY, FIELDS(lp_rdata_fields), + check_mx_rr, parse_mx_rdata), + TYPE("EUI48", ZONE_EUI48, ZONE_ANY, FIELDS(eui48_rdata_fields), + check_eui48_rr, parse_eui48_rdata), + TYPE("EUI64", ZONE_EUI64, ZONE_ANY, FIELDS(eui64_rdata_fields), + check_eui64_rr, parse_eui64_rdata), + + UNKNOWN_TYPE(110), + UNKNOWN_TYPE(111), + UNKNOWN_TYPE(112), + UNKNOWN_TYPE(113), + UNKNOWN_TYPE(114), + UNKNOWN_TYPE(115), + UNKNOWN_TYPE(116), + UNKNOWN_TYPE(117), + UNKNOWN_TYPE(118), + UNKNOWN_TYPE(119), + UNKNOWN_TYPE(120), + UNKNOWN_TYPE(121), + UNKNOWN_TYPE(122), + UNKNOWN_TYPE(123), + UNKNOWN_TYPE(124), + UNKNOWN_TYPE(125), + UNKNOWN_TYPE(126), + UNKNOWN_TYPE(127), + UNKNOWN_TYPE(128), + UNKNOWN_TYPE(129), + UNKNOWN_TYPE(130), + UNKNOWN_TYPE(131), + UNKNOWN_TYPE(132), + UNKNOWN_TYPE(133), + UNKNOWN_TYPE(134), + UNKNOWN_TYPE(135), + UNKNOWN_TYPE(136), + UNKNOWN_TYPE(137), + UNKNOWN_TYPE(138), + UNKNOWN_TYPE(139), + UNKNOWN_TYPE(140), + UNKNOWN_TYPE(141), + UNKNOWN_TYPE(142), + UNKNOWN_TYPE(143), + UNKNOWN_TYPE(144), + UNKNOWN_TYPE(145), + UNKNOWN_TYPE(146), + UNKNOWN_TYPE(147), + UNKNOWN_TYPE(148), + UNKNOWN_TYPE(149), + UNKNOWN_TYPE(150), + UNKNOWN_TYPE(151), + UNKNOWN_TYPE(152), + UNKNOWN_TYPE(153), + UNKNOWN_TYPE(154), + UNKNOWN_TYPE(155), + UNKNOWN_TYPE(156), + UNKNOWN_TYPE(157), + UNKNOWN_TYPE(158), + UNKNOWN_TYPE(159), + UNKNOWN_TYPE(160), + UNKNOWN_TYPE(161), + UNKNOWN_TYPE(162), + UNKNOWN_TYPE(163), + UNKNOWN_TYPE(164), + UNKNOWN_TYPE(165), + UNKNOWN_TYPE(166), + UNKNOWN_TYPE(167), + UNKNOWN_TYPE(168), + UNKNOWN_TYPE(169), + UNKNOWN_TYPE(170), + UNKNOWN_TYPE(171), + UNKNOWN_TYPE(172), + UNKNOWN_TYPE(173), + UNKNOWN_TYPE(174), + UNKNOWN_TYPE(175), + UNKNOWN_TYPE(176), + UNKNOWN_TYPE(177), + UNKNOWN_TYPE(178), + UNKNOWN_TYPE(179), + UNKNOWN_TYPE(180), + UNKNOWN_TYPE(181), + UNKNOWN_TYPE(182), + UNKNOWN_TYPE(183), + UNKNOWN_TYPE(184), + UNKNOWN_TYPE(185), + UNKNOWN_TYPE(186), + UNKNOWN_TYPE(187), + UNKNOWN_TYPE(188), + UNKNOWN_TYPE(189), + UNKNOWN_TYPE(190), + UNKNOWN_TYPE(191), + UNKNOWN_TYPE(192), + UNKNOWN_TYPE(193), + UNKNOWN_TYPE(194), + UNKNOWN_TYPE(195), + UNKNOWN_TYPE(196), + UNKNOWN_TYPE(197), + UNKNOWN_TYPE(198), + UNKNOWN_TYPE(199), + UNKNOWN_TYPE(200), + UNKNOWN_TYPE(201), + UNKNOWN_TYPE(202), + UNKNOWN_TYPE(203), + UNKNOWN_TYPE(204), + UNKNOWN_TYPE(205), + UNKNOWN_TYPE(206), + UNKNOWN_TYPE(207), + UNKNOWN_TYPE(208), + UNKNOWN_TYPE(209), + UNKNOWN_TYPE(210), + UNKNOWN_TYPE(211), + UNKNOWN_TYPE(212), + UNKNOWN_TYPE(213), + UNKNOWN_TYPE(214), + UNKNOWN_TYPE(215), + UNKNOWN_TYPE(216), + UNKNOWN_TYPE(217), + UNKNOWN_TYPE(218), + UNKNOWN_TYPE(219), + UNKNOWN_TYPE(220), + UNKNOWN_TYPE(221), + UNKNOWN_TYPE(222), + UNKNOWN_TYPE(223), + UNKNOWN_TYPE(224), + UNKNOWN_TYPE(225), + UNKNOWN_TYPE(226), + UNKNOWN_TYPE(227), + UNKNOWN_TYPE(228), + UNKNOWN_TYPE(229), + UNKNOWN_TYPE(230), + UNKNOWN_TYPE(231), + UNKNOWN_TYPE(232), + UNKNOWN_TYPE(233), + UNKNOWN_TYPE(234), + UNKNOWN_TYPE(235), + UNKNOWN_TYPE(236), + UNKNOWN_TYPE(237), + UNKNOWN_TYPE(238), + UNKNOWN_TYPE(239), + UNKNOWN_TYPE(240), + UNKNOWN_TYPE(241), + UNKNOWN_TYPE(242), + UNKNOWN_TYPE(243), + UNKNOWN_TYPE(244), + UNKNOWN_TYPE(245), + UNKNOWN_TYPE(246), + UNKNOWN_TYPE(247), + UNKNOWN_TYPE(248), + UNKNOWN_TYPE(249), + UNKNOWN_TYPE(250), + UNKNOWN_TYPE(251), + UNKNOWN_TYPE(252), + UNKNOWN_TYPE(253), + UNKNOWN_TYPE(254), + UNKNOWN_TYPE(255), + + TYPE("URI", ZONE_URI, ZONE_ANY, FIELDS(uri_rdata_fields), + check_uri_rr, parse_uri_rdata), + TYPE("CAA", ZONE_CAA, ZONE_ANY, FIELDS(caa_rdata_fields), + check_caa_rr, parse_caa_rdata), + TYPE("AVC", ZONE_AVC, ZONE_ANY, FIELDS(avc_rdata_fields), + check_txt_rr, parse_txt_rdata), + TYPE("DLV", ZONE_DLV, ZONE_ANY, FIELDS(dlv_rdata_fields), // obsolete + check_ds_rr, parse_ds_rdata) +}; + +#undef UNKNOWN_CLASS +#undef CLASS +#undef UNKNOWN_TYPE +#undef TYPE + +diagnostic_pop() + +#endif // TYPES_H diff --git a/src/generic/wks.h b/src/generic/wks.h index 9306dfd..6615f44 100644 --- a/src/generic/wks.h +++ b/src/generic/wks.h @@ -49,8 +49,8 @@ #define PREFIX_IMAPS (0x7370616d69llu) #define PREFIX_POP3S (0x7333706f70llu) -zone_nonnull((1)) -static zone_really_inline int32_t scan_protocol( +nonnull((1)) +static really_inline int32_t scan_protocol( const char *name, size_t length) { // RFC1035 section 3.4.2: @@ -117,8 +117,8 @@ static zone_really_inline int32_t scan_protocol( return number; } -zone_nonnull((1)) -static zone_really_inline int32_t scan_service( +nonnull((1)) +static really_inline int32_t scan_service( const char *name, size_t length, int32_t protocol) { uint8_t digit = (uint8_t)*name - '0'; diff --git a/src/haswell/base32.h b/src/haswell/base32.h index d44bc0a..7073bb0 100644 --- a/src/haswell/base32.h +++ b/src/haswell/base32.h @@ -1,5 +1,5 @@ /* - * base32.h + * base32.h -- Fast Base32 decoder * * Copyright (c) 2023, Daniel Lemire and @aqrit. * @@ -76,22 +76,23 @@ static size_t base32hex_avx(uint8_t *dst, const uint8_t *src) { return (size_t)(src - srcinit); } -zone_nonnull_all -static zone_really_inline int32_t parse_base32( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, +nonnull_all +static really_inline int32_t parse_base32( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, const token_t *token) { - int32_t r; + size_t length = (token->length * 5) / 8; + if ((uintptr_t)rdata->limit - (uintptr_t)rdata->octets < length) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); - if ((r = have_contiguous(parser, type, field, token)) < 0) - return r; - size_t decoded = base32hex_avx(parser->rdata->octets+parser->rdata->length, (const uint8_t*)token->data); - if (is_contiguous((uint8_t)token->data[decoded])) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - if (decoded) - parser->rdata->length += (decoded * 5) / 8; - return ZONE_STRING; + size_t decoded = base32hex_avx(rdata->octets, (const uint8_t*)token->data); + if (decoded != token->length) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + rdata->octets += length; + return 0; } + #endif // BASE32_H diff --git a/src/haswell/bench.c b/src/haswell/bench.c index c53a13a..a12c9ff 100644 --- a/src/haswell/bench.c +++ b/src/haswell/bench.c @@ -7,11 +7,11 @@ * */ #include "zone.h" +#include "attributes.h" #include "diagnostic.h" -#include "log.h" #include "haswell/simd.h" #include "haswell/bits.h" -#include "lexer.h" +#include "generic/parser.h" #include "generic/scanner.h" diagnostic_push() @@ -22,10 +22,10 @@ int32_t zone_bench_haswell_lex(zone_parser_t *parser, size_t *tokens) token_t token; (*tokens) = 0; - lex(parser, &token); + take(parser, &token); while (token.code > 0) { (*tokens)++; - lex(parser, &token); + take(parser, &token); } return token.code ? -1 : 0; diff --git a/src/haswell/parser.c b/src/haswell/parser.c index 06c7e67..c5fcbb3 100644 --- a/src/haswell/parser.c +++ b/src/haswell/parser.c @@ -3,17 +3,16 @@ * * Copyright (c) 2022, NLnet Labs. All rights reserved. * - * See LICENSE for the license. + * SPDX-License-Identifier: BSD-3-Clause. * */ #include "zone.h" +#include "attributes.h" #include "diagnostic.h" -#include "log.h" #include "haswell/simd.h" -#include "fallback/endian.h" +#include "generic/endian.h" #include "haswell/bits.h" -#include "lexer.h" -#include "table.h" +#include "generic/parser.h" #include "generic/scanner.h" #include "generic/number.h" #include "generic/ttl.h" @@ -22,28 +21,30 @@ #include "generic/ip6.h" #include "generic/text.h" #include "generic/name.h" -#include "fallback/base16.h" +#include "generic/base16.h" #include "haswell/base32.h" -#include "fallback/base64.h" +#include "generic/base64.h" #include "generic/nsec.h" #include "generic/nxt.h" #include "generic/caa.h" #include "generic/ilnp64.h" -#include "fallback/eui.h" -#include "fallback/nsap.h" +#include "generic/eui.h" +#include "generic/nsap.h" #include "generic/wks.h" #include "generic/loc.h" #include "generic/gpos.h" #include "generic/apl.h" #include "generic/svcb.h" -#include "types.h" +#include "generic/cert.h" +#include "generic/dnssec.h" +#include "generic/types.h" #include "westmere/type.h" -#include "parser.h" +#include "generic/format.h" diagnostic_push() clang_diagnostic_ignored(missing-prototypes) -int32_t zone_haswell_parse(zone_parser_t *parser) +int32_t zone_haswell_parse(parser_t *parser) { return parse(parser); } diff --git a/src/haswell/simd.h b/src/haswell/simd.h index c0afa5b..7f614ce 100644 --- a/src/haswell/simd.h +++ b/src/haswell/simd.h @@ -33,28 +33,28 @@ typedef simd_8x_t simd_8x32_t; typedef struct { __m256i chunks[2]; } simd_8x64_t; -zone_nonnull_all -static zone_really_inline void simd_loadu_8x(simd_8x_t *simd, const void *address) +nonnull_all +static really_inline void simd_loadu_8x(simd_8x_t *simd, const void *address) { simd->chunks[0] = _mm256_loadu_si256((const __m256i *)(address)); } -zone_nonnull_all -static zone_really_inline void simd_storeu_8x(void *address, simd_8x_t *simd) +nonnull_all +static really_inline void simd_storeu_8x(void *address, simd_8x_t *simd) { _mm256_storeu_si256((__m256i *)address, simd->chunks[0]); } -zone_nonnull_all -static zone_really_inline uint64_t simd_find_8x(const simd_8x_t *simd, char key) +nonnull_all +static really_inline uint64_t simd_find_8x(const simd_8x_t *simd, char key) { const __m256i k = _mm256_set1_epi8(key); const __m256i r = _mm256_cmpeq_epi8(simd->chunks[0], k); return (uint32_t)_mm256_movemask_epi8(r); } -zone_nonnull_all -static zone_really_inline uint64_t simd_find_any_8x( +nonnull_all +static really_inline uint64_t simd_find_any_8x( const simd_8x_t *simd, const simd_table_t table) { const __m256i t = _mm256_loadu_si256((const __m256i *)table); @@ -63,14 +63,14 @@ static zone_really_inline uint64_t simd_find_any_8x( return (uint32_t)_mm256_movemask_epi8(r); } -zone_nonnull_all -static zone_really_inline void simd_loadu_8x16(simd_8x16_t *simd, const uint8_t *address) +nonnull_all +static really_inline void simd_loadu_8x16(simd_8x16_t *simd, const uint8_t *address) { simd->chunks[0] = _mm_loadu_si128((const __m128i *)address); } -zone_nonnull_all -static zone_really_inline uint64_t simd_find_8x16(const simd_8x16_t *simd, char key) +nonnull_all +static really_inline uint64_t simd_find_8x16(const simd_8x16_t *simd, char key) { const __m128i k = _mm_set1_epi8(key); const __m128i r = _mm_cmpeq_epi8(simd->chunks[0], k); @@ -82,15 +82,15 @@ static zone_really_inline uint64_t simd_find_8x16(const simd_8x16_t *simd, char #define simd_storeu_8x32(address, simd) simd_storeu_8x(address, simd) #define simd_find_8x32(simd, key) simd_find_8x(simd, key) -zone_nonnull_all -static zone_really_inline void simd_loadu_8x64(simd_8x64_t *simd, const uint8_t *address) +nonnull_all +static really_inline void simd_loadu_8x64(simd_8x64_t *simd, const uint8_t *address) { simd->chunks[0] = _mm256_loadu_si256((const __m256i *)(address)); simd->chunks[1] = _mm256_loadu_si256((const __m256i *)(address+32)); } -zone_nonnull_all -static zone_really_inline uint64_t simd_find_8x64(const simd_8x64_t *simd, char key) +nonnull_all +static really_inline uint64_t simd_find_8x64(const simd_8x64_t *simd, char key) { const __m256i k = _mm256_set1_epi8(key); @@ -103,8 +103,8 @@ static zone_really_inline uint64_t simd_find_8x64(const simd_8x64_t *simd, char return m0 | (m1 << 32); } -zone_nonnull_all -static zone_really_inline uint64_t simd_find_any_8x64( +nonnull_all +static really_inline uint64_t simd_find_any_8x64( const simd_8x64_t *simd, const simd_table_t table) { const __m256i t = _mm256_loadu_si256((const __m256i *)table); diff --git a/src/lexer.h b/src/lexer.h deleted file mode 100644 index eac3a1b..0000000 --- a/src/lexer.h +++ /dev/null @@ -1,296 +0,0 @@ -/* - * lexer.h -- some useful comment - * - * Copyright (c) 2023, NLnet Labs. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - * - */ -#ifndef LEXER_H -#define LEXER_H - -#include - -extern int32_t zone_open_file( - zone_parser_t *, const zone_string_t *, zone_file_t **); - -extern void zone_close_file( - zone_parser_t *, zone_file_t *); - -typedef struct token token_t; -struct token { - int32_t code; - const char *data; - size_t length; -}; - -// sorted so that errors, end of file and line feeds are less than contiguous -#define END_OF_FILE (0) -#define CONTIGUOUS (1<<0) -#define QUOTED (1<<1) -#define LINE_FEED (1<<2) -#define LEFT_PAREN (1<<4) -#define RIGHT_PAREN (1<<5) -#define BLANK (1<<6) -#define COMMENT (1<<7) - -static const uint8_t contiguous[256] = { - // 0x00 = "\0" - 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x00 - 0x07 - // 0x09 = "\t", 0x0a = "\n", 0x0d = "\r" - 0x01, 0x40, 0x04, 0x01, 0x01, 0x40, 0x01, 0x01, // 0x08 - 0x0f - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x10 - 0x17 - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x18 - 0x1f - // 0x20 = " ", 0x22 = "\"" - 0x40, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x20 - 0x27 - // 0x28 = "(", 0x29 = ")" - 0x10, 0x20, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x28 - 0x2f - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x30 - 0x37 - // 0x3b = ";" - 0x01, 0x01, 0x01, 0x80, 0x01, 0x01, 0x01, 0x01, // 0x38 - 0x3f - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x40 - 0x47 - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x48 - 0x4f - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x50 - 0x57 - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x58 - 0x5f - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x60 - 0x67 - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x68 - 0x6f - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x70 - 0x77 - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x78 - 0x7f - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x80 - 0x87 - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x88 - 0x8f - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x90 - 0x97 - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x98 - 0x9f - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xa0 - 0xa7 - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xa8 - 0xaf - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xb0 - 0xb7 - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xb8 - 0xbf - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xc0 - 0xc7 - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xc8 - 0xcf - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xd0 - 0xd7 - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xd8 - 0xdf - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xe0 - 0xe7 - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xe8 - 0xef - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0xf8 - 0xf7 - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 // 0xf8 - 0xff -}; - -static const uint8_t quoted[256] = { - // 0x00 = "\0" - 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0x00 - 0x07 - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0x08 - 0x0f - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0x10 - 0x17 - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0x18 - 0x1f - // 0x22 = "\"" - 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, // 0x20 - 0x27 - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0x28 - 0x2f - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0x30 - 0x37 - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0x30 - 0x3f - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0x40 - 0x47 - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0x40 - 0x4f - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0x50 - 0x57 - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0x50 - 0x5f - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0x60 - 0x67 - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0x60 - 0x6f - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0x70 - 0x77 - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0x70 - 0x7f - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0x80 - 0x87 - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0x80 - 0x8f - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0x90 - 0x97 - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0x90 - 0x9f - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xa0 - 0xa7 - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xa0 - 0xaf - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xb0 - 0xb7 - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xb0 - 0xbf - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xc0 - 0xc7 - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xc0 - 0xcf - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xd0 - 0xd7 - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xd0 - 0xdf - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xe0 - 0xe7 - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xe0 - 0xef - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xf0 - 0xf7 - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 // 0xf0 - 0xff -}; - -// special constant to mark line feeds with additional line count. i.e. CRLF -// within text. line feeds have no special meaning other than terminating the -// record and require no further processing -static const char line_feed[ZONE_BLOCK_SIZE] = { '\n', '\0' }; - -// special constant used as data on errors -static const char dummy_data[ZONE_BLOCK_SIZE] = { '\0' }; - -zone_nonnull_all -static zone_never_inline void step(zone_parser_t *parser, token_t *token); - -zone_nonnull_all -zone_warn_unused_result -static zone_really_inline int32_t have_contiguous( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token) -{ - if (zone_likely(token->code == CONTIGUOUS)) - return token->code; - else if (token->code < 0) - return token->code; - else if (token->code == QUOTED) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - assert(token->code == END_OF_FILE || token->code == LINE_FEED); - SYNTAX_ERROR(parser, "Missing %s in %s", NAME(field), TNAME(type)); -} - -zone_nonnull_all -zone_warn_unused_result -static zone_really_inline int32_t have_quoted( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token) -{ - if (zone_likely(token->code == QUOTED)) - return token->code; - else if (token->code < 0) - return token->code; - else if (token->code == CONTIGUOUS) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - assert(token->code == END_OF_FILE || token->code == LINE_FEED); - SYNTAX_ERROR(parser, "Missing %s in %s", NAME(field), TNAME(type)); -} - -zone_nonnull_all -zone_warn_unused_result -static zone_really_inline int32_t have_string( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token) -{ - if (zone_likely(token->code & (CONTIGUOUS | QUOTED))) - return token->code; - else if (token->code < 0) - return token->code; - assert(token->code == END_OF_FILE || token->code == LINE_FEED); - SYNTAX_ERROR(parser, "Missing %s in %s", NAME(field), TNAME(type)); -} - -zone_nonnull_all -zone_warn_unused_result -static zone_really_inline int32_t have_delimiter( - zone_parser_t *parser, - const zone_type_info_t *type, - const token_t *token) -{ - if (zone_likely(!(token->code & (CONTIGUOUS | QUOTED)))) - return token->code; - else if (token->code < 0) - return token->code; - assert(token->code == CONTIGUOUS || token->code == QUOTED); - SYNTAX_ERROR(parser, "Trailing data in %s", TNAME(type)); -} - -static zone_really_inline bool is_quoted(uint8_t octet) -{ - return quoted[octet] == QUOTED; -} - -static zone_really_inline bool is_contiguous(uint8_t octet) -{ - return contiguous[octet] == CONTIGUOUS; -} - -static zone_really_inline bool is_blank(uint8_t octet) -{ - return contiguous[octet] == BLANK; -} - -zone_nonnull_all -static zone_really_inline int32_t refill(zone_parser_t *parser) -{ - if (parser->file->buffer.length == parser->file->buffer.size) { - size_t size = parser->file->buffer.size + ZONE_WINDOW_SIZE; - char *data = parser->file->buffer.data; - if (!(data = realloc(data, size + 1))) - OUT_OF_MEMORY(parser, "Cannot increase buffer size to %zu", size); - parser->file->buffer.size = size; - parser->file->buffer.data = data; - } - - size_t count = fread(parser->file->buffer.data + parser->file->buffer.length, - sizeof(parser->file->buffer.data[0]), - parser->file->buffer.size - parser->file->buffer.length, - parser->file->handle); - - if (count == 0 && ferror(parser->file->handle)) - SYNTAX_ERROR(parser, "actually a read error"); - - // always null-terminate so terminating token can point to something - parser->file->buffer.length += (size_t)count; - parser->file->buffer.data[parser->file->buffer.length] = '\0'; - parser->file->end_of_file = feof(parser->file->handle) != 0; - return 0; -} - -#define DEFER_ERROR(parser, token, error) \ - do { \ - token->code = error; \ - token->data = dummy_data; \ - token->length = 0; \ - return; \ - } while (0) - -#define DEFER_SYNTAX_ERROR(parser, token, ...) \ - do { \ - ZONE_LOG(parser, ZONE_ERROR, __VA_ARGS__); \ - token->code = ZONE_SYNTAX_ERROR; \ - token->data = dummy_data; \ - token->length = 0; \ - return; \ - } while (0) - -zone_nonnull_all -static zone_really_inline void lex(zone_parser_t *parser, token_t *token) -{ - for (;;) { - token->data = *parser->file->fields.head++; - token->code = (int32_t)contiguous[ (uint8_t)*token->data ]; - if (zone_likely(token->code == CONTIGUOUS)) { - const char *delimiter = *parser->file->delimiters.head++; - assert(delimiter > token->data); - token->length = (size_t)(delimiter - token->data); - return; - } else if (token->code == LINE_FEED) { - if (zone_unlikely(token->data == line_feed)) - parser->file->span += *parser->file->lines.head++; - parser->file->span++; - if (parser->file->grouped) - continue; - parser->file->line += parser->file->span; - parser->file->span = 0; - parser->file->start_of_line = !is_blank((uint8_t)*(token->data+1)); - token->length = 1; - return; - } else if (token->code == QUOTED) { - token->data++; - const char *delimiter = *parser->file->delimiters.head++; - assert(delimiter >= token->data); // allow empty strings (e.g. "") - token->length = (size_t)(delimiter - token->data); - return; - } else if (token->code == END_OF_FILE) { - break; - } else if (token->code == LEFT_PAREN) { - if (parser->file->grouped) - DEFER_SYNTAX_ERROR(parser, token, "Nested opening brace"); - parser->file->grouped = true; - } else { - assert(token->code == RIGHT_PAREN); - if (!parser->file->grouped) - DEFER_SYNTAX_ERROR(parser, token, "Missing opening brace"); - parser->file->grouped = false; - } - } - - step(parser, token); -} - -#endif // LEXER_H diff --git a/src/log.c b/src/log.c deleted file mode 100644 index 0d4d655..0000000 --- a/src/log.c +++ /dev/null @@ -1,76 +0,0 @@ -/* - * error.c -- some useful comment - * - * Copyright (c) 2023, NLnet Labs. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - * - */ -#include -#include -#include -#include - -#include "zone.h" -#include "log.h" - -zone_nonnull((1,2,4,7)) -static void print_message( - zone_parser_t *parser, - const char *file, - size_t line, - const char *function, - uint32_t category, - const char *message, - void *user_data) -{ - FILE *output = category == ZONE_INFO ? stdout : stderr; - const char *format = "%s:%zu: %s\n"; - (void)file; - (void)line; - (void)function; - (void)user_data; - fprintf(output, format, parser->file->name, parser->file->line, message); -} - -static void log_message( - zone_parser_t *parser, - const char *file, - size_t line, - const char *function, - uint32_t category, - const char *format, - va_list arguments) -{ - char message[2048]; - int length; - zone_log_t log = print_message; - - length = vsnprintf(message, sizeof(message), format, arguments); - assert(length >= 0); - if ((size_t)length >= sizeof(message)) - memcpy(message+(sizeof(message) - 4), "...", 3); - if (parser->options.log.write) - log = parser->options.log.write; - - log(parser, file, line, function, category, message, parser->user_data); -} - -void zone_log( - zone_parser_t *parser, - const char *file, - size_t line, - const char *function, - uint32_t category, - const char *format, - ...) -{ - va_list ap; - - if (!(parser->options.log.categories & category)) - return; - - va_start(ap, format); - log_message(parser, file, line, function, category, format, ap); - va_end(ap); -} diff --git a/src/log.h b/src/log.h deleted file mode 100644 index e624182..0000000 --- a/src/log.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * log.h -- some useful comment - * - * Copyright (c) 2022-2023, NLnet Labs. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - * - */ -#ifndef LOG_H -#define LOG_H - -#include -#include -#include - -#include "zone.h" - -#define NAME(info) ((info)->name.data) -#define TNAME(info) ((info)->name.key.data) - -#define RAISE(parser, code, ...) \ - do { \ - ZONE_LOG(parser, ZONE_ERROR, __VA_ARGS__); \ - return code; \ - } while (0) - -#define SYNTAX_ERROR(parser, ...) \ - RAISE(parser, ZONE_SYNTAX_ERROR, __VA_ARGS__) - -// semantic errors in the zone format are special as a secondary may choose -// to report, but otherwise ignore them. e.g. a TTL with the MSB set. cases -// where the data can be presented in wire format but is otherwise considered -// invalid. e.g. a TTL is limited to 32-bits, values that require more bits -// are invalid without exception, but secondaries may choose to accept values -// with the MSB set in order to update the zone -#define SEMANTIC_ERROR(parser, ...) \ - do { \ - ZONE_LOG(parser, ZONE_ERROR, __VA_ARGS__); \ - if (!parser->options.secondary) \ - return ZONE_SEMANTIC_ERROR; \ - } while (0) - -#define NOT_IMPLEMENTED(parser, ...) \ - RAISE(parser, ZONE_NOT_IMPLEMENTED, __VA_ARGS__) - -#define OUT_OF_MEMORY(parser, ...) \ - RAISE(parser, ZONE_OUT_OF_MEMORY, __VA_ARGS__) - -#define NOT_PERMITTED(parser, ...) \ - RAISE(parser, ZONE_NOT_PERMITTED, __VA_ARGS__) - -#endif // LOG_H diff --git a/src/parser.h b/src/parser.h deleted file mode 100644 index a77f9f5..0000000 --- a/src/parser.h +++ /dev/null @@ -1,321 +0,0 @@ -/* - * parser.h -- recursive descent parser for (DNS) zone data - * - * Copyright (c) 2022, NLnet Labs. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - * - */ -#ifndef PARSER_H -#define PARSER_H - -zone_nonnull_all -static zone_really_inline int32_t parse_owner( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token) -{ - int32_t r; - size_t n = 0; - uint8_t *o = parser->file->owner.octets; - - if (zone_likely(token->code == CONTIGUOUS)) { - // a freestanding "@" denotes the origin - if (token->data[0] == '@' && !is_contiguous((uint8_t)token->data[1])) - goto relative; - r = scan_name(parser, token, o, &n); - if (r == 0) - return (void)(parser->owner->length = n), ZONE_NAME; - if (r > 0) - goto relative; - } else if (token->code == QUOTED) { - if (token->length == 0) - goto invalid; - r = scan_name(parser, token, o, &n); - if (r == 0) - return (void)(parser->owner->length = n), ZONE_NAME; - if (r > 0) - goto relative; - } else { - return have_string(parser, type, field, token); - } - -invalid: - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - -relative: - if (n > 255 - parser->file->origin.length) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - memcpy(o+n, parser->file->origin.octets, parser->file->origin.length); - parser->owner->length = n + parser->file->origin.length; - return ZONE_NAME; -} - -zone_nonnull_all -static zone_really_inline int32_t parse_rr( - zone_parser_t *parser, token_t *token) -{ - static const zone_type_info_t unknown = - { { { "record", 6 }, 0 }, 0, { 0, NULL } }; - static const zone_field_info_t owner = - { { 5, "owner" }, ZONE_NAME, 0, { 0 } }; - static const zone_field_info_t ttl = - { { 3, "ttl" }, ZONE_INT32, 0, { 0 } }; - static const zone_field_info_t type = - { { 4, "type" }, ZONE_INT16, 0, { 0 } }; - static const zone_string_t backslash_hash = { 2, "\\#" }; - - int32_t r; - const type_descriptor_t *descriptor; - uint16_t code; - uint32_t epoch; - const zone_symbol_t *symbol; - - if (parser->file->start_of_line) { - parse_owner(parser, &unknown, &owner, token); - lex(parser, token); - } - - if ((uint8_t)token->data[0] - '0' <= 9) { - if ((r = scan_ttl(parser, &unknown, &ttl, token, &epoch)) < 0) - return r; - goto class_or_type; - } else { - r = scan_type_or_class(parser, &unknown, &type, token, &code, &symbol); - if (zone_likely(r == ZONE_TYPE)) { - parser->file->last_type = code; - goto rdata; - } else if (r == ZONE_CLASS) { - parser->file->last_class = code; - goto ttl_or_type; - } else { - assert(r < 0); - return r; - } - } - -ttl_or_type: - lex(parser, token); - if ((uint8_t)token->data[0] - '0' <= 9) { - if ((r = scan_ttl(parser, &unknown, &ttl, token, &epoch)) < 0) - return r; - goto type; - } else { - if ((r = scan_type(parser, &unknown, &type, token, &code, &symbol)) < 0) - return r; - parser->file->last_type = code; - goto rdata; - } - -class_or_type: - lex(parser, token); - r = scan_type_or_class(parser, &unknown, &type, token, &code, &symbol); - if (zone_likely(r == ZONE_TYPE)) { - parser->file->last_type = code; - goto rdata; - } else if (r == ZONE_CLASS) { - parser->file->last_class = code; - goto type; - } else { - assert(r < 0); - return r; - } - -type: - lex(parser, token); - if ((r = scan_type(parser, &unknown, &type, token, &code, &symbol)) < 0) - return r; - parser->file->last_type = code; - -rdata: - descriptor = (type_descriptor_t *)symbol; - - parser->rdata->length = 0; - - // RFC3597 - // parse generic rdata if rdata starts with "\\#" - lex(parser, token); - if (zone_likely(token->data[0] != '\\')) - return descriptor->parse(parser, &descriptor->info, token); - else if (token->code == CONTIGUOUS && compare(token, &backslash_hash) == 0) - return parse_generic_rdata(parser, &descriptor->info, token); - else - return descriptor->parse(parser, &descriptor->info, token); -} - -// RFC1035 section 5.1 -// $INCLUDE [] [] -zone_nonnull_all -static zone_really_inline int32_t parse_dollar_include( - zone_parser_t *parser, token_t *token) -{ - static const zone_field_info_t fields[] = { - { { 9, "file-name" }, ZONE_STRING, 0, { 0 } }, - { { 11, "domain-name" }, ZONE_NAME, 0, { 0 } } - }; - static const zone_type_info_t type = - { { { "$INCLUDE", 8 }, 0 }, 0, { 1, fields } }; - - int32_t r; - zone_file_t *includer, *file; - zone_name_buffer_t name; - const zone_name_buffer_t *origin = &parser->file->origin; - const uint8_t *delimiters; - - if (parser->options.no_includes) - NOT_PERMITTED(parser, "$INCLUDE directive is disabled"); - lex(parser, token); - if (token->code == CONTIGUOUS) - delimiters = contiguous; - else if (token->code == QUOTED) - delimiters = quoted; - else - return have_string(parser, &type, &fields[0], token); - - // FIXME: a more elegant solution probably exists - const char *p = token->data; - for (; delimiters[(uint8_t)*p] == token->code; p++) ; - const size_t n = (size_t)(p - token->data); - - if ((r = zone_open_file(parser, &(zone_string_t){ n, token->data }, &file)) < 0) - return r; - - // $INCLUDE directive may specify an origin - lex(parser, token); - if (token->code == CONTIGUOUS) { - r = scan_name(parser, token, name.octets, &name.length); - if (r != 0) - goto invalid_name; - origin = &name; - lex(parser, token); - } else if (token->code == QUOTED) { - if (token->length == 0) - goto invalid_name; - r = scan_name(parser, token, name.octets, &name.length); - if (r != 0) - goto invalid_name; - origin = &name; - lex(parser, token); - } - - // store the current owner to restore later if necessary - includer = parser->file; - includer->owner = *parser->owner; - file->includer = includer; - file->owner = *origin; - file->origin = *origin; - file->last_type = 0; - file->last_class = includer->last_class; - file->last_ttl = includer->last_ttl; - file->line = 1; - - if ((r = have_delimiter(parser, &type, token)) < 0) - return r; - - // check for recursive includes - do { - if (strcmp(includer->path, file->path) != 0) - continue; - zone_close_file(parser, file); - SYNTAX_ERROR(parser, "Circular include in $INCLUDE directive"); - } while ((includer = includer->includer)); - - parser->file = file; - return 0; -invalid_name: - zone_close_file(parser, file); - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[1]), TNAME(&type)); -} - -// RFC1035 section 5.1 -// $ORIGIN [] -zone_nonnull((1,2)) -static inline int32_t parse_dollar_origin( - zone_parser_t *parser, token_t *token) -{ - static const zone_field_info_t field = - { { 4, "name" }, ZONE_NAME, 0, { 0 } }; - static const zone_type_info_t type = - { { { "$ORIGIN", 7 }, 0 }, 0, { 1, &field } }; - - int32_t r; - - lex(parser, token); - if (zone_likely(token->code == CONTIGUOUS)) - r = scan_name(parser, token, - parser->file->origin.octets, - &parser->file->origin.length); - else if (token->code == QUOTED) - r = scan_name(parser, token, - parser->file->origin.octets, - &parser->file->origin.length); - else - return have_string(parser, &type, &field, token); - - if (r != 0) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&field), TNAME(&type)); - - lex(parser, token); - return have_delimiter(parser, &type, token); -} - -// RFC2308 section 4 -// $TTL [] -zone_nonnull((1,2)) -static inline int32_t parse_dollar_ttl( - zone_parser_t *parser, token_t *token) -{ - static const zone_field_info_t field = - { { 3, "ttl" }, ZONE_INT32, 0, { 0 } }; - static const zone_type_info_t type = - { { { "$TTL", 4 }, 0 }, 0, { 1, &field } }; - - int32_t r; - - lex(parser, token); - if ((r = scan_ttl(parser, &type, &field, token, - &parser->file->last_ttl)) < 0) - return r; - lex(parser, token); - if ((r = have_delimiter(parser, &type, token)) < 0) - return r; - - parser->file->default_ttl = parser->file->last_ttl; - return 0; -} - -static inline int32_t parse(zone_parser_t *parser) -{ - static const zone_string_t ttl = { 4, "$TTL" }; - static const zone_string_t origin = { 7, "$ORIGIN" }; - static const zone_string_t include = { 8, "$INCLUDE" }; - - int32_t r = 0; - token_t token; - - while (r >= 0) { - lex(parser, &token); - if (zone_likely(token.code == CONTIGUOUS)) { - if (!parser->file->start_of_line || token.data[0] != '$') - r = parse_rr(parser, &token); - else if (compare(&token, &ttl) == 0) - r = parse_dollar_ttl(parser, &token); - else if (compare(&token, &origin) == 0) - r = parse_dollar_origin(parser, &token); - else if (compare(&token, &include) == 0) - r = parse_dollar_include(parser, &token); - else - r = parse_rr(parser, &token); - } else if (token.code == QUOTED) { - r = parse_rr(parser, &token); - } else if (token.code == END_OF_FILE) { - if (parser->file->end_of_file == ZONE_NO_MORE_DATA) - break; - } - } - - return r; -} - -#endif // PARSER_H diff --git a/src/table.h b/src/table.h deleted file mode 100644 index 7d6e2a1..0000000 --- a/src/table.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef TABLE_H -#define TABLE_H - -#include - -#if _WIN32 -#define strncasecmp(s1, s2, n) _strnicmp(s1, s2, n) -#else -#include -#endif - -static int compare(const void *p1, const void *p2) -{ - int r; - const token_t *t = p1; - const zone_string_t *s = p2; - assert(s->length <= ZONE_BLOCK_SIZE); - if ((r = strncasecmp(t->data, s->data, s->length)) != 0) - return r; - // make sure symbol is followed by non-contiguous to avoid matching wrong - // symbol based on prefix. e.g. NSEC3 vs. NSEC3PARAM - return contiguous[ (uint8_t)t->data[s->length] ] == CONTIGUOUS; -} - -static int compare_symbol(const void *p1, const void *p2) -{ - int r; - const token_t *t = p1; - const zone_symbol_t *s = p2; - assert(s->key.length <= ZONE_BLOCK_SIZE); - if ((r = strncasecmp(t->data, s->key.data, s->key.length)) != 0) - return r; - // make sure symbol is followed by non-contiguous to avoid matching wrong - // symbol based on prefix. e.g. NSEC3 vs. NSEC3PARAM - return contiguous[ (uint8_t)t->data[s->key.length] ] == CONTIGUOUS; -} - -static const zone_symbol_t *lookup_symbol( - const zone_table_t *table, const token_t *token) -{ - return bsearch(token, table->symbols, table->length, sizeof(zone_symbol_t), compare_symbol); -} - -#endif // TABLE_H diff --git a/src/types.h b/src/types.h deleted file mode 100644 index 2709cfe..0000000 --- a/src/types.h +++ /dev/null @@ -1,3087 +0,0 @@ -/* - * types.h -- some useful comment - * - * Copyright (c) 2023, NLnet Labs. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - * - */ -#ifndef TYPES_H -#define TYPES_H - -zone_nonnull_all -static zone_really_inline int32_t scan_type_or_class( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token, - uint16_t *code, - const zone_symbol_t **symbol); - -zone_nonnull_all -static zone_really_inline int32_t scan_type( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token, - uint16_t *code, - const zone_symbol_t **symbol); - -zone_nonnull_all -static zone_really_inline int32_t parse_type( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token); - - -#define SYMBOLS(symbols) \ - { (sizeof(symbols)/sizeof(symbols[0])), symbols } - -#define SYMBOL(name, value) \ - { { name, sizeof(name) - 1 }, value } - -#define FIELDS(fields) \ - { (sizeof(fields)/sizeof(fields[0])), fields } - -#define FIELD(name, type, /* qualifiers, symbols */ ...) \ - { { sizeof(name) - 1, name }, type, __VA_ARGS__ } - -#define CLASS(name, code) \ - { { { name, sizeof(name) - 1 }, code } } - -#define UNKNOWN_CLASS(code) \ - { { { "", 0 }, code } } - -#define TYPE(name, code, options, fields, check, parse) \ - { { { { name, sizeof(name) - 1 }, code }, options, fields }, check, parse } - -#define UNKNOWN_TYPE(code) \ - { { { { "", 0 }, code }, 0, { 0, NULL } }, \ - check_generic_rr, parse_unknown_rdata } - -// class descriptor exists to parse classes and types in a uniform way -typedef struct class_descriptor class_descriptor_t; -struct class_descriptor { - zone_symbol_t name; -}; - -typedef struct type_descriptor type_descriptor_t; -struct type_descriptor { - zone_type_info_t info; - int32_t (*check)(zone_parser_t *, const zone_type_info_t *); - int32_t (*parse)(zone_parser_t *, const zone_type_info_t *, token_t *); -}; - -#if _WIN32 -#include -typedef SSIZE_T ssize_t; -#define strncasecmp(s1, s2, n) _strnicmp(s1, s2, n) -#else -#include -#endif - -zone_nonnull((1,2,3,4)) -static zone_really_inline ssize_t check_bytes( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const uint8_t *data, - const size_t length, - const size_t size) -{ - (void)data; - if (length < size) - SYNTAX_ERROR(parser, "Missing %s in %s", NAME(field), TNAME(type)); - return (ssize_t)size; -} - -#define check_int8(...) check_bytes(__VA_ARGS__, sizeof(uint8_t)) - -#define check_int16(...) check_bytes(__VA_ARGS__, sizeof(uint16_t)) - -#define check_int32(...) check_bytes(__VA_ARGS__, sizeof(uint32_t)) - -#define check_ip4(...) check_bytes(__VA_ARGS__, 4) - -#define check_ip6(...) check_bytes(__VA_ARGS__, 16) - -#define check_ilnp64(...) check_bytes(__VA_ARGS__, sizeof(uint64_t)) - -zone_nonnull((1,2,3,4)) -static zone_really_inline ssize_t check_ttl( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const uint8_t *data, - const size_t length) -{ - uint32_t number; - - if (length < sizeof(number)) - SYNTAX_ERROR(parser, "Missing %s in %s", NAME(field), TNAME(type)); - - memcpy(&number, data, sizeof(number)); - number = be32toh(number); - - if (number > INT32_MAX) - SEMANTIC_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - - return 4; -} - -zone_nonnull((1,2,3,4)) -static zone_really_inline ssize_t check_type( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const uint8_t *data, - const size_t length) -{ - uint16_t number; - - if (length < sizeof(number)) - SYNTAX_ERROR(parser, "Missing %s in %s", NAME(field), TNAME(type)); - - memcpy(&number, data, sizeof(number)); - - if (!number) - SEMANTIC_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - - return 2; -} - -zone_nonnull((1,2,3,4)) -static zone_really_inline ssize_t check_name( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const uint8_t *data, - const size_t length) -{ - size_t label = 0, count = 0; - while (count < length) { - label = data[count]; - count += 1 + label; - if (!label) - break; - } - - if (!count || count > length) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - - return (ssize_t)count; -} - -zone_nonnull((1,2,3,4)) -static zone_really_inline ssize_t check_string( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const uint8_t *data, - const size_t length) -{ - size_t count; - - if (!length || (count = 1 + (size_t)data[0]) > length) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - - return (ssize_t)count; -} - -zone_nonnull((1,2,3,4)) -static zone_really_inline ssize_t check_nsec( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const uint8_t *data, - const size_t length) -{ - size_t count = 0; - size_t last_window = 0; - - while ((count + 2) < length) { - const size_t window = (size_t)data[0]; - const size_t blocks = 1 + (size_t)data[1]; - if (window < last_window || !window != !last_window) - SYNTAX_ERROR(parser, "Invalid %s in %s, windows are out-of-order", - NAME(field), TNAME(type)); - if (blocks > 32) - SYNTAX_ERROR(parser, "Invalid %s in %s, blocks are out-of-bounds", - NAME(field), TNAME(type)); - count += 2 + blocks; - last_window = window; - } - - if (count != length) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - - return (ssize_t)count; -} - -zone_nonnull((1)) -static zone_really_inline int32_t check(size_t *length, ssize_t count) -{ - if (count < 0) - return (int32_t)count; - *length += (size_t)count; - return 0; -} - -zone_nonnull_all -static zone_really_inline int32_t accept_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t result; - - assert(parser->owner->length <= UINT8_MAX); - assert(parser->rdata->length <= UINT16_MAX); - result = parser->options.accept.add( - parser, - type, - &(zone_name_t){ (uint8_t)parser->owner->length, parser->owner->octets }, - parser->file->last_type, - parser->file->last_class, - parser->file->last_ttl, - (uint16_t)parser->rdata->length, - parser->rdata->octets, - parser->user_data); - - assert((size_t)result < parser->buffers.size); - if (result < 0) - return result; - parser->rdata = &parser->buffers.rdata.blocks[result]; - return 0; -} - -zone_nonnull_all -static int32_t check_a_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_ip4(parser, type, &f[0], o, n)))) - return r; - - if (c != n) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_a_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_ip4(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = have_delimiter(parser, type, token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_ns_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_name(parser, type, &f[0], o, n))) < 0) - return r; - - if (c != n) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_ns_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_name(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = have_delimiter(parser, type, token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_soa_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_name(parser, type, &f[0], o, n))) || - (r = check(&c, check_name(parser, type, &f[1], o+c, n-c))) || - (r = check(&c, check_int32(parser, type, &f[2], o+c, n-c))) || - (r = check(&c, check_ttl(parser, type, &f[3], o+c, n-c))) || - (r = check(&c, check_ttl(parser, type, &f[4], o+c, n-c))) || - (r = check(&c, check_ttl(parser, type, &f[5], o+c, n-c))) || - (r = check(&c, check_ttl(parser, type, &f[6], o+c, n-c)))) - return r; - - if (c != n) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_soa_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_name(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_name(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_int32(parser, type, &type->rdata.fields[2], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_ttl(parser, type, &type->rdata.fields[3], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_ttl(parser, type, &type->rdata.fields[4], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_ttl(parser, type, &type->rdata.fields[5], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_ttl(parser, type, &type->rdata.fields[6], token)) < 0) - return r; - lex(parser, token); - if ((r = have_delimiter(parser, type, token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_wks_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_ip4(parser, type, &f[0], o, n))) || - (r = check(&c, check_int8(parser, type, &f[0], o+c, n-c)))) - return r; - - // any bit may, or may not, be set. confirm the bitmap does not exceed the - // maximum number of ports - if (n > 8192 + 5) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_wks_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t code; - if ((code = parse_ip4(parser, type, &type->rdata.fields[0], token) < 0)) - return code; - - lex(parser, token); - int32_t protocol = scan_protocol(token->data, token->length); - if (protocol == -1) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&type->rdata.fields[1]), TNAME(type)); - - parser->rdata->octets[parser->rdata->length++] = (uint8_t)protocol; - uint8_t *bitmap = parser->rdata->octets + parser->rdata->length; - int32_t highest_port = -1; - - lex(parser, token); - while (token->code == CONTIGUOUS) { - int32_t port = scan_service(token->data, token->length, protocol); - if (port == -1) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&type->rdata.fields[2]), TNAME(type)); - - if (port > highest_port) { - // ensure newly used octets are zeroed out before use - size_t offset = highest_port < 0 ? 0 : (size_t)highest_port / 8 + 1; - size_t length = (size_t)port / 8 + 1; - memset(bitmap + offset, 0, length - offset); - highest_port = port; - } - - // bits are counted from left to right, so bit 0 is the left most bit - bitmap[port / 8] |= (1 << (7 - port % 8)); - lex(parser, token); - } - - parser->rdata->length += (size_t)highest_port / 8 + 1; - - if ((code = have_delimiter(parser, type, token)) < 0) - return code; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_hinfo_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_string(parser, type, &f[0], o, n))) || - (r = check(&c, check_string(parser, type, &f[1], o+c, n-c)))) - return r; - - if (c != n) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_hinfo_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_string(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_string(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - lex(parser, token); - if ((r = have_delimiter(parser, type, token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_minfo_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_name(parser, type, &f[0], o, n))) || - (r = check(&c, check_name(parser, type, &f[1], o+c, n-c)))) - return r; - - if (c != n) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_minfo_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_name(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_name(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - lex(parser, token); - if ((r = have_delimiter(parser, type, token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_mx_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) || - (r = check(&c, check_name(parser, type, &f[1], o+c, n-c)))) - return r; - - if (c != n) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_mx_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_int16(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_name(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - lex(parser, token); - if ((r = have_delimiter(parser, type, token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_txt_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_string(parser, type, &f[0], o, n)))) - return r; - - while (c < n) - if ((r = check(&c, check_string(parser, type, &f[0], o+c, n-c)))) - return r; - - if (c != n) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_txt_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - do { - if ((r = parse_string(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - } while (token->code & (CONTIGUOUS | QUOTED)); - - if ((r = have_delimiter(parser, type, token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_x25_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_string(parser, type, &f[0], o, n)))) - return r; - - if (c != n) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_x25_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_string(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = have_delimiter(parser, type, token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_isdn_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_string(parser, type, &f[0], o, n)))) - return r; - // subaddress is optional - if (c < n && (r = check(&c, check_string(parser, type, &f[1], o+c, n-c)))) - return r; - - if (c != n) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_isdn_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_string(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - - // subaddress is optional - if (token->code & (CONTIGUOUS | QUOTED)) { - if ((r = parse_string(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - lex(parser, token); - } - - if ((r = have_delimiter(parser, type, token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_rt_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) || - (r = check(&c, check_name(parser, type, &f[1], o+c, n-c)))) - return r; - - if (c != n) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_rt_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_int16(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_name(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - lex(parser, token); - if ((r = have_delimiter(parser, type, token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_nsap_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - if (parser->rdata->length == 0) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_nsap_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_nsap(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = have_delimiter(parser, type, token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_nsap_ptr_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - { - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_name(parser, type, &f[0], o, n)))) - return r; - - if (c != parser->rdata->length) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - } - - { - // RFC1706 section 6 - // A domain name is generated from an NSAP by reversing the hex nibbles of - // the NSAP, treating each nibble as a separate subdomain, and appending - // the top-level subdomain name "NSAP.INT" to it. For example, the domain - // name used in the reverse lookup for the NSAP - // - // 47.0005.80.005a00.0000.0001.e133.ffffff000162.00 - // - // would appear as - // - // 0.0.2.6.1.0.0.0.f.f.f.f.f.f.3.3.1.e.1.0.0.0.0.0.0.0.0.0.a.5.0.0. - // 0.8.5.0.0.0.7.4.NSAP.INT. - size_t i = 0; - const size_t n = parser->file->owner.length; - const uint8_t *o = parser->file->owner.octets; - for (; i < n; i += 2) - if (o[i] != 1 || (b16rmap[o[i+1]] & 0x80)) - break; - - const uint8_t nsap_int[] = { 4, 'n', 's', 'a', 'p', 3, 'i', 'n', 't', 0 }; - if (strncasecmp((const char *)o + i, (const char *)nsap_int, 9) != 0 || !i || i + 10 != n) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - } - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_nsap_ptr_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_name(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = have_delimiter(parser, type, token)) < 0) - return r; - - // RFC1706 section 6 states each nibble is treated as a separate subdomain - return check_nsap_ptr_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_key_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ -#if 0 - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - // - // FIXME: implement (RFC2065) - // - // FIXME: verify the flag, algorithm and protocol combination is valid - // FIXME: verify the key is valid for type(3)+algorithm(1) - // - // The combination is of course subject to secondary checks! - // -#endif - (void)type; - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_key_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_int16(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_int8(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_int8(parser, type, &type->rdata.fields[2], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_base64_sequence(parser, type, &type->rdata.fields[3], token)) < 0) - return r; - - return check_key_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_px_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) || - (r = check(&c, check_name(parser, type, &f[1], o+c, n-c))) || - (r = check(&c, check_name(parser, type, &f[2], o+c, n-c)))) - return r; - - if (c != n) - SYNTAX_ERROR(parser, "Invalid %s record", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_px_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_int16(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_name(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_name(parser, type, &type->rdata.fields[2], token)) < 0) - return r; - lex(parser, token); - if ((r = have_delimiter(parser, type, token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_gpos_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_string(parser, type, &f[0], o, n))) || - (r = check(&c, check_string(parser, type, &f[1], o+c, n-c))) || - (r = check(&c, check_string(parser, type, &f[2], o+c, n-c)))) - return r; - - if (c != n) - SYNTAX_ERROR(parser, "Invalid %s record", TNAME(type)); - return accept_rr(parser, type); -} - -static int32_t parse_gpos_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_latitude(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_longitude(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_altitude(parser, type, &type->rdata.fields[2], token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_aaaa_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_ip6(parser, type, &f[0], o, n)))) - return r; - - if (c != n) - SYNTAX_ERROR(parser, "Invalid %s record", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_aaaa_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_ip6(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = have_delimiter(parser, type, token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_loc_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - if (parser->rdata->length != 16) - SYNTAX_ERROR(parser, "Invalid %s record", TNAME(type)); - return accept_rr(parser, type); - - // FIXME: check validity of latitude, longitude and latitude? -} - -zone_nonnull_all -static int32_t parse_loc_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t result; - uint32_t degrees, minutes, seconds; - uint32_t latitude, longitude, altitude; - const zone_field_info_t *fields = type->rdata.fields; - static const uint32_t defaults = 0x13161200; - - // RFC1876 section 3: - // If omitted, minutes and seconds default to zero, size defaults to 1m, - // horizontal precision defaults to 10000m, and vertical precision defaults - // to 10m. - memcpy(parser->rdata->octets, &defaults, sizeof(defaults)); - parser->rdata->length = 16; - - // latitude - if ((result = have_contiguous(parser, type, &fields[4], token)) < 0) - return result; - if (scan_degrees(token->data, token->length, °rees) == -1) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[4]), TNAME(type)); - lex(parser, token); - if (scan_minutes(token->data, token->length, &minutes) == -1) - goto north_south; // minutes default to zero - degrees += minutes; - lex(parser, token); - if (scan_seconds(token->data, token->length, &seconds) == -1) - goto north_south; // seconds default to zero - degrees += seconds; - - lex(parser, token); - if ((result = have_contiguous(parser, type, &fields[4], token)) < 0) - return result; -north_south: - if (token->data[0] == 'N') - latitude = htobe32((1u<<31) + degrees); - else if (token->data[1] == 'S') - latitude = htobe32((1u<<31) - degrees); - else - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[4]), TNAME(type)); - - memcpy(&parser->rdata->octets[4], &latitude, sizeof(latitude)); - - // longitude - lex(parser, token); - if ((result = have_contiguous(parser, type, &fields[5], token)) < 0) - return result; - if (scan_degrees(token->data, token->length, °rees) == -1) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[5]), TNAME(type)); - lex(parser, token); - if (scan_minutes(token->data, token->length, &minutes) == -1) - goto east_west; // minutes default to zero - degrees += minutes; - lex(parser, token); - if (scan_seconds(token->data, token->length, &seconds) == -1) - goto east_west; // seconds default to zero - degrees += seconds; - - lex(parser, token); - if ((result = have_contiguous(parser, type, &fields[5], token)) < 0) - return result; -east_west: - if (token->data[0] == 'E') - longitude = htobe32((1u<<31) + degrees); - else if (token->data[0] == 'W') - longitude = htobe32((1u<<31) - degrees); - else - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[5]), TNAME(type)); - - memcpy(&parser->rdata->octets[8], &longitude, sizeof(longitude)); - - // altitude - lex(parser, token); - if ((result = have_contiguous(parser, type, &fields[6], token)) < 0) - return result; - if (scan_altitude(token->data, token->length, &altitude) == -1) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[6]), TNAME(type)); - - altitude = htobe32(altitude); - memcpy(&parser->rdata->octets[12], &altitude, sizeof(altitude)); - - // size - lex(parser, token); - if (token->code != CONTIGUOUS) - goto skip_optional; - if (scan_precision(token->data, token->length, &parser->rdata->octets[1])) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[1]), TNAME(type)); - - // horizontal precision - lex(parser, token); - if (token->code != CONTIGUOUS) - goto skip_optional; - if (scan_precision(token->data, token->length, &parser->rdata->octets[2])) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[2]), TNAME(type)); - - // vertical precision - lex(parser, token); - if (token->code != CONTIGUOUS) - goto skip_optional; - if (scan_precision(token->data, token->length, &parser->rdata->octets[3])) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[3]), TNAME(type)); - - lex(parser, token); -skip_optional: - if ((result = have_delimiter(parser, type, token)) < 0) - return result; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_nxt_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_name(parser, type, &f[3], o+c, n-c)))) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_nxt_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_name(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_nxt(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_srv_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) || - (r = check(&c, check_int16(parser, type, &f[1], o+c, n-c))) || - (r = check(&c, check_int16(parser, type, &f[2], o+c, n-c))) || - (r = check(&c, check_name(parser, type, &f[3], o+c, n-c)))) - return r; - - if (c != n) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_srv_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_int16(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_int16(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_int16(parser, type, &type->rdata.fields[2], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_name(parser, type, &type->rdata.fields[3], token)) < 0) - return r; - lex(parser, token); - if ((r = have_delimiter(parser, type, token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_naptr_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - // FIXME: implement actual checks - (void)type; - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_naptr_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_int16(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_int16(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_string(parser, type, &type->rdata.fields[2], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_string(parser, type, &type->rdata.fields[3], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_string(parser, type, &type->rdata.fields[4], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_name(parser, type, &type->rdata.fields[5], token)) < 0) - return r; - lex(parser, token); - if ((r = have_delimiter(parser, type, token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_cert_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - // FIXME: implement actual checks - (void)type; - - if (parser->rdata->length < 6) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_cert_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_symbol16(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_int16(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_symbol8(parser, type, &type->rdata.fields[2], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_base64_sequence(parser, type, &type->rdata.fields[3], token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_apl_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - // FIXME: check correctness of fields and total length - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_apl_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t code; - uint8_t *octets = parser->rdata->octets; - size_t size = ZONE_RDATA_SIZE; - - // RDATA section for APL consists of zero or more items - while (token->code == CONTIGUOUS) { - int32_t length; - if ((length = scan_apl(token->data, token->length, octets, size)) < 0) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&type->rdata.fields[0]), TNAME(type)); - assert(length == 8 /* ipv4 */ || length == 20 /* ipv6 */); - size -= (size_t)length; - octets += (size_t)length; - lex(parser, token); - } - - parser->rdata->length = ZONE_RDATA_SIZE - size; - - if ((code = have_delimiter(parser, type, token)) < 0) - return code; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_ds_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) || - (r = check(&c, check_int8(parser, type, &f[1], o+c, n-c))) || - (r = check(&c, check_int8(parser, type, &f[2], o+c, n-c)))) - return r; - - // FIXME: can implement checking for digest length based on algorithm here. - // e.g. SHA-1 digest is 20 bytes, see RFC3658 section 2.4 - - if (c >= n) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_ds_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_int16(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_symbol8(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_symbol8(parser, type, &type->rdata.fields[2], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_base16_sequence(parser, type, &type->rdata.fields[3], token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_sshfp_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_int8(parser, type, &f[0], o, n))) || - (r = check(&c, check_int8(parser, type, &f[1], o, n)))) - return r; - - // https://www.iana.org/assignments/dns-sshfp-rr-parameters - - if (c >= n) - SYNTAX_ERROR(parser, "Missing %s in %s", NAME((&f[0])), TNAME(type)); - else if (o[1] == 1 && (n - c) != 20) - SEMANTIC_ERROR(parser, "Wrong fingerprint size for type %s in %s", - "SHA1", TNAME(type)); - else if (o[1] == 2 && (n - c) != 32) - SEMANTIC_ERROR(parser, "Wrong fingerprint size for type %s in %s", - "SHA256", TNAME(type)); - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_sshfp_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_int8(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_int8(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_base16_sequence(parser, type, &type->rdata.fields[2], token)) < 0) - return r; - - return check_sshfp_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_ipseckey_rr( - zone_parser_t *parser, const zone_type_info_t *type); - -zone_nonnull_all -static int32_t parse_ipseckey_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token); - -diagnostic_push() -gcc_diagnostic_ignored(missing-field-initializers) -clang_diagnostic_ignored(missing-field-initializers) - -static const zone_field_info_t ipseckey_ipv4_rdata_fields[] = { - FIELD("precedence", ZONE_INT8, 0), - FIELD("gateway type", ZONE_INT8, 0), - FIELD("algorithm", ZONE_INT8, 0), - FIELD("gateway", ZONE_IP4, 0), - FIELD("public key", ZONE_BLOB, ZONE_BASE64) -}; - -static const type_descriptor_t ipseckey_ipv4[] = { - TYPE("IPSECKEY", ZONE_IPSECKEY, ZONE_IN, FIELDS(ipseckey_ipv4_rdata_fields), - check_ipseckey_rr, parse_ipseckey_rdata), -}; - -static const zone_field_info_t ipseckey_ipv6_rdata_fields[] = { - FIELD("precedence", ZONE_INT8, 0), - FIELD("gateway type", ZONE_INT8, 0), - FIELD("algorithm", ZONE_INT8, 0), - FIELD("gateway", ZONE_IP6, 0), - FIELD("public key", ZONE_BLOB, ZONE_BASE64) -}; - -static const type_descriptor_t ipseckey_ipv6[] = { - TYPE("IPSECKEY", ZONE_IPSECKEY, ZONE_IN, FIELDS(ipseckey_ipv6_rdata_fields), - check_ipseckey_rr, parse_ipseckey_rdata), -}; - - -diagnostic_pop() - -zone_nonnull_all -static int32_t check_ipseckey_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_type_info_t *t = type; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_int8(parser, type, &f[0], o, n))) || - (r = check(&c, check_int8(parser, type, &f[1], o+c, n-c))) || - (r = check(&c, check_int8(parser, type, &f[2], o+c, n-c)))) - return r; - - switch (parser->rdata->octets[1]) { - case 1: /* IPv4 address */ - t = (const zone_type_info_t *)ipseckey_ipv4; - f = ipseckey_ipv4_rdata_fields; - if ((r = check(&c, check_ip4(parser, t, &f[3], o+c, n-c))) < 0) - return r; - break; - case 2: /* IPv6 address */ - t = (const zone_type_info_t *)ipseckey_ipv6; - f = ipseckey_ipv6_rdata_fields; - if ((r = check(&c, check_ip6(parser, t, &f[3], o+c, n-c))) < 0) - return r; - break; - case 0: /* no gateway */ - case 3: /* domain name */ - if ((r = check(&c, check_name(parser, t, &f[3], o+c, n-c))) < 0) - return r; - break; - default: - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - } - - switch (parser->rdata->octets[2]) { - case 0: - if (c < n) - SYNTAX_ERROR(parser, "Trailing data in %s", TNAME(t)); - break; - default: - if (c >= n) - SYNTAX_ERROR(parser, "Missing %s in %s", NAME(&f[4]), TNAME(t)); - break; - } - - return accept_rr(parser, t); -} - -zone_nonnull_all -static int32_t parse_ipseckey_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - const zone_type_info_t *t; - - if ((r = parse_int8(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_int8(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_int8(parser, type, &type->rdata.fields[2], token)) < 0) - return r; - lex(parser, token); - - switch (parser->rdata->octets[1]) { - case 1: /* IPv4 address */ - t = (const zone_type_info_t *)ipseckey_ipv4; - if ((r = parse_ip4(parser, t, &t->rdata.fields[3], token)) < 0) - return r; - break; - case 2: /* IPv6 address */ - t = (const zone_type_info_t *)ipseckey_ipv6; - if ((r = parse_ip6(parser, t, &t->rdata.fields[3], token)) < 0) - return r; - break; - case 0: /* no gateway */ - case 3: /* domain name */ - t = type; - if ((r = parse_name(parser, t, &t->rdata.fields[3], token)) < 0) - return r; - break; - default: - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&type->rdata.fields[3]), TNAME(type)); - } - - lex(parser, token); - switch (parser->rdata->octets[2]) { - case 0: - if ((r = have_delimiter(parser, t, token)) < 0) - return r; - break; - default: - if ((r = parse_base64_sequence(parser, t, &t->rdata.fields[4], token)) < 0) - return r; - break; - } - - return accept_rr(parser, (const zone_type_info_t *)t); -} - -zone_nonnull_all -static int32_t check_rrsig_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_type(parser, type, &f[0], o, n))) || - (r = check(&c, check_int8(parser, type, &f[1], o+c, n-c))) || - (r = check(&c, check_int8(parser, type, &f[2], o+c, n-c))) || - (r = check(&c, check_ttl(parser, type, &f[3], o+c, n-c))) || - (r = check(&c, check_int32(parser, type, &f[4], o+c, n-c))) || - (r = check(&c, check_int32(parser, type, &f[5], o+c, n-c))) || - (r = check(&c, check_int16(parser, type, &f[6], o+c, n-c))) || - (r = check(&c, check_name(parser, type, &f[7], o+c, n-c)))) - return r; - - if (c != n) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_rrsig_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_type(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_symbol8(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_int8(parser, type, &type->rdata.fields[2], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_ttl(parser, type, &type->rdata.fields[3], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_time(parser, type, &type->rdata.fields[4], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_time(parser, type, &type->rdata.fields[5], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_int16(parser, type, &type->rdata.fields[6], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_name(parser, type, &type->rdata.fields[7], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_base64_sequence(parser, type, &type->rdata.fields[8], token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_nsec_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_name(parser, type, &f[0], o, n))) || - (r = check(&c, check_nsec(parser, type, &f[1], o+c, n-c)))) - return r; - - if (c != n) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_nsec_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_name(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_nsec(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_dnskey_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) || - (r = check(&c, check_int8(parser, type, &f[1], o+c, n-c))) || - (r = check(&c, check_int8(parser, type, &f[2], o+c, n-c)))) - return r; - - if (c >= n) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_dnskey_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_int16(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_int8(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_symbol8(parser, type, &type->rdata.fields[2], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_base64_sequence(parser, type, &type->rdata.fields[3], token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_dhcid_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - // RFC4701 section 3.1: - // 2-octet identifier type, 1-octet digest type, followed by one or more - // octets representing the actual identifier - if (parser->rdata->length < 4) - SEMANTIC_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_dhcid_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_base64_sequence(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - - return check_dhcid_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_nsec3_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_int8(parser, type, &f[0], o, n))) || - (r = check(&c, check_int8(parser, type, &f[1], o+c, n-c))) || - (r = check(&c, check_int16(parser, type, &f[2], o+c, n-c))) || - (r = check(&c, check_string(parser, type, &f[3], o+c, n-c))) || - (r = check(&c, check_string(parser, type, &f[4], o+c, n-c))) || - (r = check(&c, check_nsec(parser, type, &f[5], o+c, n-c)))) - return r; - - if (c != n) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_nsec3_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_symbol8(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_symbol8(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_int16(parser, type, &type->rdata.fields[2], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_salt(parser, type, &type->rdata.fields[3], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_base32(parser, type, &type->rdata.fields[4], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_nsec(parser, type, &type->rdata.fields[5], token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_nsec3param_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_int8(parser, type, &f[0], o, n))) || - (r = check(&c, check_int8(parser, type, &f[1], o+c, n-c))) || - (r = check(&c, check_int16(parser, type, &f[2], o+c, n-c))) || - (r = check(&c, check_string(parser, type, &f[3], o+c, n-c)))) - return r; - - if (c != n) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_nsec3param_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_symbol8(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_symbol8(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_int16(parser, type, &type->rdata.fields[2], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_salt(parser, type, &type->rdata.fields[3], token)) < 0) - return r; - lex(parser, token); - if ((r = have_delimiter(parser, type, token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_tlsa_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_int8(parser, type, &f[0], o, n))) || - (r = check(&c, check_int8(parser, type, &f[1], o+c, n-c))) || - (r = check(&c, check_int8(parser, type, &f[2], o+c, n-c)))) - return r; - - if (c >= n) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_tlsa_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_int8(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_int8(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_int8(parser, type, &type->rdata.fields[2], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_base16_sequence(parser, type, &type->rdata.fields[3], token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_hip_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - // FIXME: verify field lengths etc - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_hip_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t result; - - // reserve octet for HIT length - parser->rdata->length = 1; - - // PK algorithm - if ((result = parse_int8(parser, type, &type->rdata.fields[1], token)) < 0) - return result; - - // reserve octets for PK length - parser->rdata->length += 2; - - // HIT - lex(parser, token); - if ((result = parse_base16(parser, type, &type->rdata.fields[3], token)) < 0) - return result; - - if (parser->rdata->length > 255 + 4) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&type->rdata.fields[3]), TNAME(type)); - uint8_t hit_length = (uint8_t)(parser->rdata->length - 4); - parser->rdata->octets[0] = hit_length; - - // Public Key - lex(parser, token); - if ((result = parse_base64(parser, type, &type->rdata.fields[4], token)) < 0) - return result; - - if (parser->rdata->length > 65535u + 4u + hit_length) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&type->rdata.fields[4]), TNAME(type)); - uint16_t pk_length = htobe16((uint16_t)((parser->rdata->length - hit_length) - 4)); - memcpy(&parser->rdata->octets[2], &pk_length, sizeof(pk_length)); - - lex(parser, token); - while (token->code & (CONTIGUOUS | QUOTED)) { - if ((result = parse_name(parser, type, &type->rdata.fields[5], token)) < 0) - return result; - lex(parser, token); - } - - if ((result = have_delimiter(parser, type, token)) < 0) - return result; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_openpgpkey_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - // FIXME: as the RDATA contains a digest, it is likely we can make this - // check stricter, at least, for known algorithms - if (parser->rdata->length < 4) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_openpgpkey_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_base64_sequence(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_csync_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_int32(parser, type, &f[0], o, n))) || - (r = check(&c, check_int16(parser, type, &f[1], o+c, n-c))) || - (r = check(&c, check_nsec(parser, type, &f[2], o+c, n-c)))) - return r; - - if (c != n) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_csync_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_int32(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - - lex(parser, token); - if ((r = parse_int16(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - - lex(parser, token); - if ((r = parse_nsec(parser, type, &type->rdata.fields[2], token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_zonemd_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - // FIXME: RDATA contains digests, do extra checks? - if (parser->rdata->length < 6) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_zonemd_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_int32(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_int8(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_int8(parser, type, &type->rdata.fields[2], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_base16_sequence(parser, type, &type->rdata.fields[3], token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_svcb_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - // FIXME: implement checking parameters etc - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_svcb_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_int16(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - // - // See 2.5. Special Handling of "." in TargetName - // if "." is used as the name it does not mean , it means something - // else for svcb!!!! - // - if ((r = parse_name(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_svc_params(parser, type, &type->rdata.fields[2], token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_https_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_https_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_int16(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_name(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_svc_params(parser, type, &type->rdata.fields[2], token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_nid_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) || - (r = check(&c, check_ilnp64(parser, type, &f[1], o+c, n-c)))) - return r; - if (c != n) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_nid_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_int16(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_ilnp64(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - lex(parser, token); - if ((r = have_delimiter(parser, type, token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_l32_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) || - (r = check(&c, check_ip4(parser, type, &f[1], o+c, n-c)))) - return r; - - if (c != n) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_l32_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_int16(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_ip4(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - lex(parser, token); - if ((r = have_delimiter(parser, type, token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_l64_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) || - (r = check(&c, check_ilnp64(parser, type, &f[1], o+c, n-c)))) - return r; - if (c != n) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_l64_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_int16(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_ilnp64(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - lex(parser, token); - if ((r = have_delimiter(parser, type, token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_eui48_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - if (parser->rdata->length != 6) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_eui48_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_eui48(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = have_delimiter(parser, type, token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_eui64_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - if (parser->rdata->length != 8) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_eui64_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_eui64(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = have_delimiter(parser, type, token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_uri_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_int16(parser, type, &f[0], o, n))) || - (r = check(&c, check_int16(parser, type, &f[1], o+c, n-c)))) - return r; - if (c >= n) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_uri_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_int16(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_int16(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_quoted_text(parser, type, &type->rdata.fields[2], token)) < 0) - return r; - lex(parser, token); - if ((r = have_delimiter(parser, type, token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_caa_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - int32_t r; - size_t c = 0; - const size_t n = parser->rdata->length; - const uint8_t *o = parser->rdata->octets; - const zone_field_info_t *f = type->rdata.fields; - - if ((r = check(&c, check_int8(parser, type, &f[0], o, n))) || - (r = check(&c, check_int8(parser, type, &f[1], o+c, n-c)))) - return r; - if (c >= n) - SYNTAX_ERROR(parser, "Invalid %s", TNAME(type)); - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_caa_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - int32_t r; - - if ((r = parse_int8(parser, type, &type->rdata.fields[0], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_caa_tag(parser, type, &type->rdata.fields[1], token)) < 0) - return r; - lex(parser, token); - if ((r = parse_text(parser, type, &type->rdata.fields[2], token)) < 0) - return r; - lex(parser, token); - if ((r = have_delimiter(parser, type, token)) < 0) - return r; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t check_generic_rr( - zone_parser_t *parser, const zone_type_info_t *type) -{ - (void)type; - - return accept_rr(parser, type); -} - -zone_nonnull_all -static int32_t parse_generic_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - static const zone_field_info_t fields[2] = { - { { 12, "RDATA length" }, ZONE_INT16, 0, { 0, NULL } }, - { { 5, "RDATA" }, ZONE_BLOB, 0, { 0, NULL } } - }; - - int32_t r; - - lex(parser, token); // discard "\\#" - if ((r = have_contiguous(parser, type, &fields[0], token)) < 0) - return r; - - size_t n = 0; - const char *p = token->data; - for (;; p++) { - const size_t d = (uint8_t)*p - '0'; - if (d > 9) - break; - n = n * 10 +d; - } - - if (n > UINT16_MAX || p - token->data > 5 || is_contiguous((uint8_t)*p)) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[0]), TNAME(type)); - - lex(parser, token); - if (n) - r = parse_base16_sequence(parser, type, &fields[1], token); - else - r = have_delimiter(parser, type, token); - if (r < 0) - return r; - if (parser->rdata->length != n) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(&fields[0]), TNAME(type)); - return ((const type_descriptor_t *)type)->check(parser, type); -} - -zone_nonnull_all -static int32_t parse_unknown_rdata( - zone_parser_t *parser, const zone_type_info_t *type, token_t *token) -{ - (void)type; - (void)token; - SYNTAX_ERROR(parser, "Unknown record type"); -} - -diagnostic_push() -gcc_diagnostic_ignored(missing-field-initializers) -clang_diagnostic_ignored(missing-field-initializers) - -static const class_descriptor_t classes[] = { - UNKNOWN_CLASS(0), - CLASS("IN", 1), - CLASS("CS", 2), - CLASS("CH", 3), - CLASS("HS", 4) -}; - -static const zone_field_info_t a_rdata_fields[] = { - FIELD("address", ZONE_IP4, 0) -}; - -static const zone_field_info_t ns_rdata_fields[] = { - FIELD("host", ZONE_NAME, ZONE_COMPRESSED) -}; - -static const zone_field_info_t md_rdata_fields[] = { - FIELD("madname", ZONE_NAME, ZONE_COMPRESSED) -}; - -static const zone_field_info_t mf_rdata_fields[] = { - FIELD("madname", ZONE_NAME, ZONE_COMPRESSED) -}; - -static const zone_field_info_t cname_rdata_fields[] = { - FIELD("host", ZONE_NAME, ZONE_COMPRESSED) -}; - -static const zone_field_info_t soa_rdata_fields[] = { - FIELD("primary", ZONE_NAME, ZONE_COMPRESSED), - FIELD("mailbox", ZONE_NAME, ZONE_MAILBOX), - FIELD("serial", ZONE_INT32, 0), - FIELD("refresh", ZONE_INT32, ZONE_TTL), - FIELD("retry", ZONE_INT32, ZONE_TTL), - FIELD("expire", ZONE_INT32, ZONE_TTL), - FIELD("minimum", ZONE_INT32, ZONE_TTL) -}; - -static const zone_field_info_t mb_rdata_fields[] = { - FIELD("madname", ZONE_NAME, ZONE_COMPRESSED) -}; - -static const zone_field_info_t mg_rdata_fields[] = { - FIELD("mgmname", ZONE_NAME, ZONE_MAILBOX) -}; - -static const zone_field_info_t mr_rdata_fields[] = { - FIELD("newname", ZONE_NAME, ZONE_MAILBOX) -}; - -static const zone_field_info_t ptr_rdata_fields[] = { - FIELD("ptrdname", ZONE_NAME, ZONE_COMPRESSED) -}; - -static const zone_field_info_t hinfo_rdata_fields[] = { - FIELD("cpu", ZONE_STRING, 0), - FIELD("os", ZONE_STRING, 0) -}; - -static const zone_field_info_t minfo_rdata_fields[] = { - FIELD("rmailbx", ZONE_NAME, ZONE_MAILBOX), - FIELD("emailbx", ZONE_NAME, ZONE_MAILBOX) -}; - -static const zone_field_info_t wks_rdata_fields[] = { - FIELD("address", ZONE_IP4, 0), - FIELD("protocol", ZONE_INT8, 0), - FIELD("bitmap", ZONE_SERVICE_BITMAP, 0) -}; - -static const zone_field_info_t mx_rdata_fields[] = { - FIELD("priority", ZONE_INT16, 0), - FIELD("hostname", ZONE_NAME, ZONE_COMPRESSED) -}; - -static const zone_field_info_t txt_rdata_fields[] = { - FIELD("text", ZONE_STRING, ZONE_SEQUENCE) -}; - -static const zone_field_info_t rp_rdata_fields[] = { - FIELD("mailbox", ZONE_NAME, ZONE_MAILBOX), - FIELD("text", ZONE_NAME, 0) -}; - -static const zone_field_info_t afsdb_rdata_fields[] = { - FIELD("subtype", ZONE_INT16, 0), - FIELD("hostname", ZONE_NAME, 0) -}; - -static const zone_field_info_t x25_rdata_fields[] = { - FIELD("address", ZONE_STRING, 0) -}; - -static const zone_field_info_t isdn_rdata_fields[] = { - FIELD("address", ZONE_STRING, 0), - FIELD("subaddress", ZONE_STRING, 0) -}; - -static const zone_field_info_t rt_rdata_fields[] = { - FIELD("preference", ZONE_INT16, 0), - FIELD("hostname", ZONE_NAME, 0) -}; - -static const zone_field_info_t nsap_rdata_fields[] = { - FIELD("address", ZONE_BLOB, ZONE_NSAP) -}; - -static const zone_field_info_t nsap_ptr_rdata_fields[] = { - FIELD("hostname", ZONE_NAME, 0) -}; - -static const zone_field_info_t key_rdata_fields[] = { - FIELD("flags", ZONE_INT16, 0), - FIELD("protocol", ZONE_INT8, 0), - FIELD("algorithm", ZONE_INT8, 0), - FIELD("publickey", ZONE_BLOB, ZONE_BASE64) -}; - -static const zone_field_info_t px_rdata_fields[] = { - FIELD("preference", ZONE_INT16, 0), - FIELD("map822", ZONE_NAME, 0), - FIELD("mapx400", ZONE_NAME, 0) -}; - -static const zone_field_info_t gpos_rdata_fields[] = { - FIELD("latitude", ZONE_STRING, 0), - FIELD("longitude", ZONE_STRING, 0), - FIELD("altitude", ZONE_STRING, 0) -}; - -static const zone_field_info_t aaaa_rdata_fields[] = { - FIELD("address", ZONE_IP6, 0) -}; - -static const zone_field_info_t loc_rdata_fields[] = { - FIELD("version", ZONE_INT8, 0), - FIELD("size", ZONE_INT8, 0), - FIELD("horizontal precision", ZONE_INT8, 0), - FIELD("vertical precision", ZONE_INT8, 0), - FIELD("latitude", ZONE_INT32, 0), - FIELD("longitude", ZONE_INT32, 0), - FIELD("altitude", ZONE_INT32, 0) -}; - -static const zone_field_info_t nxt_rdata_fields[] = { - FIELD("next domain name", ZONE_NAME, 0), - FIELD("type bit map", 0, 0) -}; - -static const zone_field_info_t srv_rdata_fields[] = { - FIELD("priority", ZONE_INT16, 0), - FIELD("weight", ZONE_INT16, 0), - FIELD("port", ZONE_INT16, 0), - FIELD("target", ZONE_NAME, 0) -}; - -static const zone_field_info_t naptr_rdata_fields[] = { - FIELD("order", ZONE_INT16, 0), - FIELD("preference", ZONE_INT16, 0), - FIELD("flags", ZONE_STRING, 0), - FIELD("services", ZONE_STRING, 0), - FIELD("regex", ZONE_STRING, 0), - FIELD("replacement", ZONE_NAME, 0), -}; - -static const zone_field_info_t kx_rdata_fields[] = { - FIELD("preference", ZONE_INT16, 0), - FIELD("exchanger", ZONE_NAME, 0) -}; - -// https://www.iana.org/assignments/cert-rr-types/cert-rr-types.xhtml -static const zone_symbol_t cert_type_symbols[] = { - SYMBOL("ACPKIX", 7), - SYMBOL("IACPKIX", 8), - SYMBOL("IPGP", 6), - SYMBOL("IPKIX", 4), - SYMBOL("ISPKI", 5), - SYMBOL("OID", 254), - SYMBOL("PGP", 3), - SYMBOL("PKIX", 1), - SYMBOL("SPKI", 2), - SYMBOL("URI", 253), -}; - -// https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml -static const zone_symbol_t dnssec_algorithm_symbols[] = { - // >> we should remove this from the table and use specialized functions - // instead. much easier! - SYMBOL("DH", 2), - SYMBOL("DSA", 3), - SYMBOL("DSA-NSEC-SHA1", 6), - SYMBOL("ECC", 4), - SYMBOL("ECC-GOST", 12), - SYMBOL("ECDSAP256SHA256", 13), - SYMBOL("ECDSAP384SHA384", 14), - SYMBOL("INDIRECT", 252), - SYMBOL("PRIVATEDNS", 253), - SYMBOL("PRIVATEOID", 254), - SYMBOL("RSAMD5", 1), - SYMBOL("RSASHA1", 5), - SYMBOL("RSASHA1-NSEC3-SHA1", 7), - SYMBOL("RSASHA256", 8), - SYMBOL("RSASHA512", 10) -}; - -static const zone_field_info_t sig_rdata_fields[] = { - FIELD("sigtype", ZONE_INT16, ZONE_TYPE), - FIELD("algorithm", ZONE_INT8, 0, SYMBOLS(dnssec_algorithm_symbols)), - FIELD("labels", ZONE_INT8, 0), - FIELD("origttl", ZONE_INT32, ZONE_TTL), - FIELD("expire", ZONE_INT32, ZONE_TIME), - FIELD("inception", ZONE_INT32, ZONE_TIME), - FIELD("keytag", ZONE_INT16, 0), - FIELD("signer", ZONE_NAME, 0), - FIELD("signature", ZONE_BLOB, ZONE_BASE64) -}; - -static const zone_field_info_t cert_rdata_fields[] = { - FIELD("type", ZONE_INT16, 0, SYMBOLS(cert_type_symbols)), - FIELD("key tag", ZONE_INT16, 0), - FIELD("algorithm", ZONE_INT8, 0, SYMBOLS(dnssec_algorithm_symbols)), - FIELD("certificate", ZONE_BLOB, ZONE_BASE64) -}; - -static const zone_field_info_t dname_rdata_fields[] = { - FIELD("source", ZONE_NAME, 0) -}; - -static const zone_field_info_t apl_rdata_fields[] = { - FIELD("prefix", ZONE_INT16, 0) -}; - -static const zone_symbol_t ds_digest_type_symbols[] = { - SYMBOL("GOST", 3), - SYMBOL("SHA-1", 1), - SYMBOL("SHA-256", 2), - SYMBOL("SHA-384", 4) -}; - -static const zone_field_info_t ds_rdata_fields[] = { - FIELD("keytag", ZONE_INT16, 0), - FIELD("algorithm", ZONE_INT8, 0, SYMBOLS(dnssec_algorithm_symbols)), - FIELD("digtype", ZONE_INT8, 0, SYMBOLS(ds_digest_type_symbols)), - FIELD("digest", ZONE_BLOB, ZONE_BASE16) -}; - -static const zone_field_info_t sshfp_rdata_fields[] = { - FIELD("algorithm", ZONE_INT8, 0), - FIELD("ftype", ZONE_INT8, 0), - FIELD("fingerprint", ZONE_BLOB, ZONE_BASE16) -}; - -// FIXME: IPSECKEY is a little different because the rdata depends on the algorithm! -static const zone_field_info_t ipseckey_rdata_fields[] = { - FIELD("precedence", ZONE_INT8, 0), - FIELD("gateway type", ZONE_INT8, 0), - FIELD("algorithm", ZONE_INT8, 0), - FIELD("gateway", ZONE_NAME, 0), - FIELD("public key", ZONE_BLOB, ZONE_BASE64) -}; - -static const zone_field_info_t rrsig_rdata_fields[] = { - FIELD("rrtype", ZONE_INT16, ZONE_TYPE), - FIELD("algorithm", ZONE_INT8, 0, SYMBOLS(dnssec_algorithm_symbols)), - FIELD("labels", ZONE_INT8, 0), - FIELD("origttl", ZONE_INT32, ZONE_TTL), - FIELD("expire", ZONE_INT32, ZONE_TIME), - FIELD("inception", ZONE_INT32, ZONE_TIME), - FIELD("keytag", ZONE_INT16, 0), - FIELD("signer", ZONE_NAME, 0), - FIELD("signature", ZONE_BLOB, ZONE_BASE64) -}; - -static const zone_field_info_t nsec_rdata_fields[] = { - FIELD("next", ZONE_NAME, 0), - FIELD("types", ZONE_NSEC, 0) -}; - -static const zone_field_info_t dnskey_rdata_fields[] = { - FIELD("flags", ZONE_INT16, 0), - FIELD("protocol", ZONE_INT8, 0), - FIELD("algorithm", ZONE_INT8, 0, SYMBOLS(dnssec_algorithm_symbols)), - FIELD("publickey", ZONE_BLOB, ZONE_BASE64) -}; - -static const zone_field_info_t dhcid_rdata_fields[] = { - FIELD("dhcpinfo", ZONE_BLOB, ZONE_BASE64) -}; - -static const zone_symbol_t nsec3_algorithm_symbols[] = { - SYMBOL("SHA-1", 1) -}; - -static const zone_symbol_t nsec3_flags_symbols[] = { - SYMBOL("OPTOUT", 1) -}; - -static const zone_field_info_t nsec3_rdata_fields[] = { - FIELD("algorithm", ZONE_INT8, 0), - FIELD("flags", ZONE_INT8, 0), - FIELD("iterations", ZONE_INT16, 0), - FIELD("salt", ZONE_STRING | ZONE_BASE16), - FIELD("next", ZONE_STRING | ZONE_BASE32), - FIELD("types", ZONE_NSEC, 0) -}; - -static const zone_field_info_t nsec3param_rdata_fields[] = { - FIELD("algorithm", ZONE_INT8, 0, SYMBOLS(nsec3_algorithm_symbols)), - FIELD("flags", ZONE_INT8, 0, SYMBOLS(nsec3_flags_symbols)), - FIELD("iterations", ZONE_INT16, 0), - FIELD("salt", ZONE_STRING, ZONE_BASE16) -}; - -static const zone_field_info_t tlsa_rdata_fields[] = { - FIELD("usage", ZONE_INT8, 0), - FIELD("selector", ZONE_INT8, 0), - FIELD("matching type", ZONE_INT8, 0), - FIELD("certificate association data", ZONE_BLOB, ZONE_BASE16) -}; - -static const zone_field_info_t smimea_rdata_fields[] = { - FIELD("usage", ZONE_INT8, 0), - FIELD("selector", ZONE_INT8, 0), - FIELD("matching type", ZONE_INT8, 0), - FIELD("certificate association data", ZONE_BLOB, ZONE_BASE16) -}; - -static const zone_field_info_t cds_rdata_fields[] = { - FIELD("keytag", ZONE_INT16, 0), - FIELD("algorithm", ZONE_INT8, 0, SYMBOLS(dnssec_algorithm_symbols)), - FIELD("digtype", ZONE_INT8, 0, SYMBOLS(ds_digest_type_symbols)), - FIELD("digest", ZONE_BLOB, ZONE_BASE16) -}; - -static const zone_field_info_t cdnskey_rdata_fields[] = { - FIELD("flags", ZONE_INT16, 0), - FIELD("protocol", ZONE_INT8, 0), - FIELD("algorithm", ZONE_INT8, 0, SYMBOLS(dnssec_algorithm_symbols)), - FIELD("publickey", ZONE_BLOB, ZONE_BASE64) -}; - -static const zone_field_info_t hip_rdata_fields[] = { - FIELD("HIT length", ZONE_INT8, 0), - FIELD("PK algorithm", ZONE_INT8, 0), - FIELD("PK length", ZONE_INT16, 0), - FIELD("HIT", ZONE_STRING, 0), // FIXME: not really a string - FIELD("Public Key", ZONE_STRING, 0), // FIXME: not really a string - FIELD("Rendezvous Servers", ZONE_NAME, ZONE_SEQUENCE) -}; - -static const zone_field_info_t openpgpkey_rdata_fields[] = { - FIELD("key", ZONE_BLOB, ZONE_BASE64) -}; - -static const zone_field_info_t csync_rdata_fields[] = { - FIELD("serial", ZONE_INT32, 0), - FIELD("flags", ZONE_INT16, 0), - FIELD("types", ZONE_NSEC, 0) -}; - -static const zone_field_info_t zonemd_rdata_fields[] = { - FIELD("serial", ZONE_INT32, 0), - FIELD("scheme", ZONE_INT8, 0), - FIELD("algorithm", ZONE_INT8, 0), - FIELD("digest", ZONE_BLOB, ZONE_BASE16), -}; - -static const zone_field_info_t svcb_rdata_fields[] = { - FIELD("priority", ZONE_INT16, 0), - FIELD("target", ZONE_NAME, 0), - FIELD("params", 0, 0) -}; - -static const zone_field_info_t https_rdata_fields[] = { - FIELD("priority", ZONE_INT16, 0), - FIELD("target", ZONE_NAME, 0), - FIELD("params", 0, 0) -}; - -static const zone_field_info_t spf_rdata_fields[] = { - FIELD("text", ZONE_STRING, ZONE_SEQUENCE) -}; - -static const zone_field_info_t nid_rdata_fields[] = { - FIELD("preference", ZONE_INT16, 0), - FIELD("nodeid", ZONE_ILNP64, 0) -}; - -// RFC6742 specifies the syntax for the locator is compatible with the syntax -// for IPv4 addresses, but then proceeds to provide an example with leading -// zeroes. The example is corrected in the errata. -static const zone_field_info_t l32_rdata_fields[] = { - FIELD("preference", ZONE_INT16, 0), - FIELD("locator", ZONE_IP4, 0) -}; - -static const zone_field_info_t l64_rdata_fields[] = { - FIELD("preference", ZONE_INT16, 0), - FIELD("locator", ZONE_ILNP64, 0) -}; - -static const zone_field_info_t lp_rdata_fields[] = { - FIELD("preference", ZONE_INT16, 0), - FIELD("pointer", ZONE_NAME, 0) -}; - -static const zone_field_info_t eui48_rdata_fields[] = { - FIELD("address", ZONE_EUI48, 0) -}; - -static const zone_field_info_t eui64_rdata_fields[] = { - FIELD("address", ZONE_EUI64, 0) -}; - -static const zone_field_info_t uri_rdata_fields[] = { - FIELD("priority", ZONE_INT16, 0), - FIELD("weight", ZONE_INT16, 0), - FIELD("target", ZONE_BLOB, 0) -}; - -static const zone_field_info_t caa_rdata_fields[] = { - FIELD("flags", ZONE_INT8, 0), - FIELD("tag", ZONE_STRING, ZONE_CAA_TAG), - FIELD("value", ZONE_BLOB, 0) -}; - -// https://www.iana.org/assignments/dns-parameters/AVC/avc-completed-template -static const zone_field_info_t avc_rdata_fields[] = { - FIELD("text", ZONE_STRING, ZONE_SEQUENCE) -}; - -static const zone_field_info_t dlv_rdata_fields[] = { - FIELD("key", ZONE_INT16, 0), - FIELD("algorithm", ZONE_INT8, 0, SYMBOLS(dnssec_algorithm_symbols)), - FIELD("type", ZONE_INT8, 0), - FIELD("digest", ZONE_BLOB, ZONE_BASE16) -}; - -static const type_descriptor_t types[] = { - UNKNOWN_TYPE(0), - - TYPE("A", ZONE_A, ZONE_ANY, FIELDS(a_rdata_fields), - check_a_rr, parse_a_rdata), - TYPE("NS", ZONE_NS, ZONE_ANY, FIELDS(ns_rdata_fields), - check_ns_rr, parse_ns_rdata), - TYPE("MD", ZONE_MD, ZONE_ANY | ZONE_OBSOLETE, FIELDS(md_rdata_fields), - check_ns_rr, parse_ns_rdata), - TYPE("MF", ZONE_MF, ZONE_ANY | ZONE_OBSOLETE, FIELDS(mf_rdata_fields), - check_ns_rr, parse_ns_rdata), - TYPE("CNAME", ZONE_CNAME, ZONE_ANY, FIELDS(cname_rdata_fields), - check_ns_rr, parse_ns_rdata), - TYPE("SOA", ZONE_SOA, ZONE_ANY, FIELDS(soa_rdata_fields), - check_soa_rr, parse_soa_rdata), - TYPE("MB", ZONE_MB, ZONE_ANY | ZONE_EXPERIMENTAL, FIELDS(mb_rdata_fields), - check_ns_rr, parse_ns_rdata), - TYPE("MG", ZONE_MG, ZONE_ANY | ZONE_EXPERIMENTAL, FIELDS(mg_rdata_fields), - check_ns_rr, parse_ns_rdata), - TYPE("MR", ZONE_MR, ZONE_ANY | ZONE_EXPERIMENTAL, FIELDS(mr_rdata_fields), - check_ns_rr, parse_ns_rdata), - - UNKNOWN_TYPE(10), - - TYPE("WKS", ZONE_WKS, ZONE_IN, FIELDS(wks_rdata_fields), - check_wks_rr, parse_wks_rdata), - TYPE("PTR", ZONE_PTR, ZONE_ANY, FIELDS(ptr_rdata_fields), - check_ns_rr, parse_ns_rdata), - TYPE("HINFO", ZONE_HINFO, ZONE_ANY, FIELDS(hinfo_rdata_fields), - check_hinfo_rr, parse_hinfo_rdata), - TYPE("MINFO", ZONE_MINFO, ZONE_ANY, FIELDS(minfo_rdata_fields), - check_minfo_rr, parse_minfo_rdata), - TYPE("MX", ZONE_MX, ZONE_ANY, FIELDS(mx_rdata_fields), - check_mx_rr, parse_mx_rdata), - TYPE("TXT", ZONE_TXT, ZONE_ANY, FIELDS(txt_rdata_fields), - check_txt_rr, parse_txt_rdata), - TYPE("RP", ZONE_RP, ZONE_ANY, FIELDS(rp_rdata_fields), - check_minfo_rr, parse_minfo_rdata), - TYPE("AFSDB", ZONE_AFSDB, ZONE_ANY, FIELDS(afsdb_rdata_fields), - check_mx_rr, parse_mx_rdata), - TYPE("X25", ZONE_X25, ZONE_ANY, FIELDS(x25_rdata_fields), - check_x25_rr, parse_x25_rdata), - TYPE("ISDN", ZONE_ISDN, ZONE_ANY, FIELDS(isdn_rdata_fields), - check_isdn_rr, parse_isdn_rdata), - TYPE("RT", ZONE_RT, ZONE_ANY, FIELDS(rt_rdata_fields), - check_rt_rr, parse_rt_rdata), - TYPE("NSAP", ZONE_NSAP, ZONE_IN, FIELDS(nsap_rdata_fields), - check_nsap_rr, parse_nsap_rdata), - TYPE("NSAP-PTR", ZONE_NSAP_PTR, ZONE_IN, FIELDS(nsap_ptr_rdata_fields), - check_nsap_ptr_rr, parse_nsap_ptr_rdata), - TYPE("SIG", ZONE_SIG, ZONE_ANY, FIELDS(sig_rdata_fields), - check_rrsig_rr, parse_rrsig_rdata), - TYPE("KEY", ZONE_KEY, ZONE_ANY, FIELDS(key_rdata_fields), - check_key_rr, parse_key_rdata), - TYPE("PX", ZONE_PX, ZONE_IN, FIELDS(px_rdata_fields), - check_px_rr, parse_px_rdata), - TYPE("GPOS", ZONE_GPOS, ZONE_ANY, FIELDS(gpos_rdata_fields), - check_gpos_rr, parse_gpos_rdata), - TYPE("AAAA", ZONE_AAAA, ZONE_IN, FIELDS(aaaa_rdata_fields), - check_aaaa_rr, parse_aaaa_rdata), - TYPE("LOC", ZONE_LOC, ZONE_ANY, FIELDS(loc_rdata_fields), - check_loc_rr, parse_loc_rdata), - TYPE("NXT", ZONE_NXT, ZONE_ANY | ZONE_OBSOLETE, FIELDS(nxt_rdata_fields), - check_nxt_rr, parse_nxt_rdata), - - UNKNOWN_TYPE(31), - UNKNOWN_TYPE(32), - - TYPE("SRV", ZONE_SRV, ZONE_IN, FIELDS(srv_rdata_fields), - check_srv_rr, parse_srv_rdata), - - UNKNOWN_TYPE(34), - - TYPE("NAPTR", ZONE_NAPTR, ZONE_IN, FIELDS(naptr_rdata_fields), - check_naptr_rr, parse_naptr_rdata), - TYPE("KX", ZONE_KX, ZONE_IN, FIELDS(kx_rdata_fields), - check_mx_rr, parse_mx_rdata), - TYPE("CERT", ZONE_CERT, ZONE_ANY, FIELDS(cert_rdata_fields), - check_cert_rr, parse_cert_rdata), - - UNKNOWN_TYPE(38), - - TYPE("DNAME", ZONE_DNAME, ZONE_ANY, FIELDS(dname_rdata_fields), - check_ns_rr, parse_ns_rdata), - - UNKNOWN_TYPE(40), - UNKNOWN_TYPE(41), - - TYPE("APL", ZONE_APL, ZONE_IN, FIELDS(apl_rdata_fields), - check_apl_rr, parse_apl_rdata), - TYPE("DS", ZONE_DS, ZONE_ANY, FIELDS(ds_rdata_fields), - check_ds_rr, parse_ds_rdata), - TYPE("SSHFP", ZONE_SSHFP, ZONE_ANY, FIELDS(sshfp_rdata_fields), - check_sshfp_rr, parse_sshfp_rdata), - TYPE("IPSECKEY", ZONE_IPSECKEY, ZONE_IN, FIELDS(ipseckey_rdata_fields), - check_ipseckey_rr, parse_ipseckey_rdata), - TYPE("RRSIG", ZONE_RRSIG, ZONE_ANY, FIELDS(rrsig_rdata_fields), - check_rrsig_rr, parse_rrsig_rdata), - TYPE("NSEC", ZONE_NSEC, ZONE_ANY, FIELDS(nsec_rdata_fields), - check_nsec_rr, parse_nsec_rdata), - TYPE("DNSKEY", ZONE_DNSKEY, ZONE_ANY, FIELDS(dnskey_rdata_fields), - check_dnskey_rr, parse_dnskey_rdata), - TYPE("DHCID", ZONE_DHCID, ZONE_IN, FIELDS(dhcid_rdata_fields), - check_dhcid_rr, parse_dhcid_rdata), - TYPE("NSEC3", ZONE_NSEC3, ZONE_ANY, FIELDS(nsec3_rdata_fields), - check_nsec3_rr, parse_nsec3_rdata), - TYPE("NSEC3PARAM", ZONE_NSEC3PARAM, ZONE_ANY, FIELDS(nsec3param_rdata_fields), - check_nsec3param_rr, parse_nsec3param_rdata), - TYPE("TLSA", ZONE_TLSA, ZONE_ANY, FIELDS(tlsa_rdata_fields), - check_tlsa_rr, parse_tlsa_rdata), - TYPE("SMIMEA", ZONE_SMIMEA, ZONE_ANY, FIELDS(smimea_rdata_fields), - check_tlsa_rr, parse_tlsa_rdata), - - UNKNOWN_TYPE(54), - - TYPE("HIP", ZONE_HIP, ZONE_ANY, FIELDS(hip_rdata_fields), - check_hip_rr, parse_hip_rdata), - - UNKNOWN_TYPE(56), - UNKNOWN_TYPE(57), - UNKNOWN_TYPE(58), - - TYPE("CDS", ZONE_CDS, ZONE_ANY, FIELDS(cds_rdata_fields), - check_ds_rr, parse_ds_rdata), - TYPE("CDNSKEY", ZONE_CDNSKEY, ZONE_ANY, FIELDS(cdnskey_rdata_fields), - check_dnskey_rr, parse_dnskey_rdata), - TYPE("OPENPGPKEY", ZONE_OPENPGPKEY, ZONE_ANY, FIELDS(openpgpkey_rdata_fields), - check_openpgpkey_rr, parse_openpgpkey_rdata), - TYPE("CSYNC", ZONE_CSYNC, ZONE_ANY, FIELDS(csync_rdata_fields), - check_csync_rr, parse_csync_rdata), - TYPE("ZONEMD", ZONE_ZONEMD, ZONE_ANY, FIELDS(zonemd_rdata_fields), - check_zonemd_rr, parse_zonemd_rdata), - TYPE("SVCB", ZONE_SVCB, ZONE_IN, FIELDS(svcb_rdata_fields), - check_svcb_rr, parse_svcb_rdata), - TYPE("HTTPS", ZONE_HTTPS, ZONE_IN, FIELDS(https_rdata_fields), - check_https_rr, parse_https_rdata), - - UNKNOWN_TYPE(66), - UNKNOWN_TYPE(67), - UNKNOWN_TYPE(68), - UNKNOWN_TYPE(69), - UNKNOWN_TYPE(70), - UNKNOWN_TYPE(71), - UNKNOWN_TYPE(72), - UNKNOWN_TYPE(73), - UNKNOWN_TYPE(74), - UNKNOWN_TYPE(75), - UNKNOWN_TYPE(76), - UNKNOWN_TYPE(77), - UNKNOWN_TYPE(78), - UNKNOWN_TYPE(79), - UNKNOWN_TYPE(80), - UNKNOWN_TYPE(81), - UNKNOWN_TYPE(82), - UNKNOWN_TYPE(83), - UNKNOWN_TYPE(84), - UNKNOWN_TYPE(85), - UNKNOWN_TYPE(86), - UNKNOWN_TYPE(87), - UNKNOWN_TYPE(88), - UNKNOWN_TYPE(89), - UNKNOWN_TYPE(90), - UNKNOWN_TYPE(91), - UNKNOWN_TYPE(92), - UNKNOWN_TYPE(93), - UNKNOWN_TYPE(94), - UNKNOWN_TYPE(95), - UNKNOWN_TYPE(96), - UNKNOWN_TYPE(97), - UNKNOWN_TYPE(98), - - TYPE("SPF", ZONE_SPF, ZONE_ANY | ZONE_OBSOLETE, FIELDS(spf_rdata_fields), - check_txt_rr, parse_txt_rdata), - - UNKNOWN_TYPE(100), - UNKNOWN_TYPE(101), - UNKNOWN_TYPE(102), - UNKNOWN_TYPE(103), - - TYPE("NID", ZONE_NID, ZONE_ANY, FIELDS(nid_rdata_fields), - check_nid_rr, parse_nid_rdata), - TYPE("L32", ZONE_L32, ZONE_ANY, FIELDS(l32_rdata_fields), - check_l32_rr, parse_l32_rdata), - TYPE("L64", ZONE_L64, ZONE_ANY, FIELDS(l64_rdata_fields), - check_l64_rr, parse_l64_rdata), - TYPE("LP", ZONE_LP, ZONE_ANY, FIELDS(lp_rdata_fields), - check_mx_rr, parse_mx_rdata), - TYPE("EUI48", ZONE_EUI48, ZONE_ANY, FIELDS(eui48_rdata_fields), - check_eui48_rr, parse_eui48_rdata), - TYPE("EUI64", ZONE_EUI64, ZONE_ANY, FIELDS(eui64_rdata_fields), - check_eui64_rr, parse_eui64_rdata), - - UNKNOWN_TYPE(110), - UNKNOWN_TYPE(111), - UNKNOWN_TYPE(112), - UNKNOWN_TYPE(113), - UNKNOWN_TYPE(114), - UNKNOWN_TYPE(115), - UNKNOWN_TYPE(116), - UNKNOWN_TYPE(117), - UNKNOWN_TYPE(118), - UNKNOWN_TYPE(119), - UNKNOWN_TYPE(120), - UNKNOWN_TYPE(121), - UNKNOWN_TYPE(122), - UNKNOWN_TYPE(123), - UNKNOWN_TYPE(124), - UNKNOWN_TYPE(125), - UNKNOWN_TYPE(126), - UNKNOWN_TYPE(127), - UNKNOWN_TYPE(128), - UNKNOWN_TYPE(129), - UNKNOWN_TYPE(130), - UNKNOWN_TYPE(131), - UNKNOWN_TYPE(132), - UNKNOWN_TYPE(133), - UNKNOWN_TYPE(134), - UNKNOWN_TYPE(135), - UNKNOWN_TYPE(136), - UNKNOWN_TYPE(137), - UNKNOWN_TYPE(138), - UNKNOWN_TYPE(139), - UNKNOWN_TYPE(140), - UNKNOWN_TYPE(141), - UNKNOWN_TYPE(142), - UNKNOWN_TYPE(143), - UNKNOWN_TYPE(144), - UNKNOWN_TYPE(145), - UNKNOWN_TYPE(146), - UNKNOWN_TYPE(147), - UNKNOWN_TYPE(148), - UNKNOWN_TYPE(149), - UNKNOWN_TYPE(150), - UNKNOWN_TYPE(151), - UNKNOWN_TYPE(152), - UNKNOWN_TYPE(153), - UNKNOWN_TYPE(154), - UNKNOWN_TYPE(155), - UNKNOWN_TYPE(156), - UNKNOWN_TYPE(157), - UNKNOWN_TYPE(158), - UNKNOWN_TYPE(159), - UNKNOWN_TYPE(160), - UNKNOWN_TYPE(161), - UNKNOWN_TYPE(162), - UNKNOWN_TYPE(163), - UNKNOWN_TYPE(164), - UNKNOWN_TYPE(165), - UNKNOWN_TYPE(166), - UNKNOWN_TYPE(167), - UNKNOWN_TYPE(168), - UNKNOWN_TYPE(169), - UNKNOWN_TYPE(170), - UNKNOWN_TYPE(171), - UNKNOWN_TYPE(172), - UNKNOWN_TYPE(173), - UNKNOWN_TYPE(174), - UNKNOWN_TYPE(175), - UNKNOWN_TYPE(176), - UNKNOWN_TYPE(177), - UNKNOWN_TYPE(178), - UNKNOWN_TYPE(179), - UNKNOWN_TYPE(180), - UNKNOWN_TYPE(181), - UNKNOWN_TYPE(182), - UNKNOWN_TYPE(183), - UNKNOWN_TYPE(184), - UNKNOWN_TYPE(185), - UNKNOWN_TYPE(186), - UNKNOWN_TYPE(187), - UNKNOWN_TYPE(188), - UNKNOWN_TYPE(189), - UNKNOWN_TYPE(190), - UNKNOWN_TYPE(191), - UNKNOWN_TYPE(192), - UNKNOWN_TYPE(193), - UNKNOWN_TYPE(194), - UNKNOWN_TYPE(195), - UNKNOWN_TYPE(196), - UNKNOWN_TYPE(197), - UNKNOWN_TYPE(198), - UNKNOWN_TYPE(199), - UNKNOWN_TYPE(200), - UNKNOWN_TYPE(201), - UNKNOWN_TYPE(202), - UNKNOWN_TYPE(203), - UNKNOWN_TYPE(204), - UNKNOWN_TYPE(205), - UNKNOWN_TYPE(206), - UNKNOWN_TYPE(207), - UNKNOWN_TYPE(208), - UNKNOWN_TYPE(209), - UNKNOWN_TYPE(210), - UNKNOWN_TYPE(211), - UNKNOWN_TYPE(212), - UNKNOWN_TYPE(213), - UNKNOWN_TYPE(214), - UNKNOWN_TYPE(215), - UNKNOWN_TYPE(216), - UNKNOWN_TYPE(217), - UNKNOWN_TYPE(218), - UNKNOWN_TYPE(219), - UNKNOWN_TYPE(220), - UNKNOWN_TYPE(221), - UNKNOWN_TYPE(222), - UNKNOWN_TYPE(223), - UNKNOWN_TYPE(224), - UNKNOWN_TYPE(225), - UNKNOWN_TYPE(226), - UNKNOWN_TYPE(227), - UNKNOWN_TYPE(228), - UNKNOWN_TYPE(229), - UNKNOWN_TYPE(230), - UNKNOWN_TYPE(231), - UNKNOWN_TYPE(232), - UNKNOWN_TYPE(233), - UNKNOWN_TYPE(234), - UNKNOWN_TYPE(235), - UNKNOWN_TYPE(236), - UNKNOWN_TYPE(237), - UNKNOWN_TYPE(238), - UNKNOWN_TYPE(239), - UNKNOWN_TYPE(240), - UNKNOWN_TYPE(241), - UNKNOWN_TYPE(242), - UNKNOWN_TYPE(243), - UNKNOWN_TYPE(244), - UNKNOWN_TYPE(245), - UNKNOWN_TYPE(246), - UNKNOWN_TYPE(247), - UNKNOWN_TYPE(248), - UNKNOWN_TYPE(249), - UNKNOWN_TYPE(250), - UNKNOWN_TYPE(251), - UNKNOWN_TYPE(252), - UNKNOWN_TYPE(253), - UNKNOWN_TYPE(254), - UNKNOWN_TYPE(255), - - TYPE("URI", ZONE_URI, ZONE_ANY, FIELDS(uri_rdata_fields), - check_uri_rr, parse_uri_rdata), - TYPE("CAA", ZONE_CAA, ZONE_ANY, FIELDS(caa_rdata_fields), - check_caa_rr, parse_caa_rdata), - TYPE("AVC", ZONE_AVC, ZONE_ANY, FIELDS(avc_rdata_fields), - check_txt_rr, parse_txt_rdata), - TYPE("DLV", ZONE_DLV, ZONE_ANY | ZONE_OBSOLETE, FIELDS(dlv_rdata_fields), - check_ds_rr, parse_ds_rdata) -}; - -#undef UNKNOWN_CLASS -#undef CLASS -#undef UNKNOWN_TYPE -#undef TYPE - -diagnostic_pop() - -#endif // TYPES_H diff --git a/src/westmere/base32.h b/src/westmere/base32.h index b6b8994..e705bd1 100644 --- a/src/westmere/base32.h +++ b/src/westmere/base32.h @@ -1,5 +1,5 @@ /* - * base32.h + * base32.h -- Fast Base32 decoder * * Copyright (c) 2023, Daniel Lemire and @aqrit. * @@ -65,22 +65,23 @@ static size_t base32hex_sse(uint8_t *dst, const uint8_t *src) { return (size_t)(src - srcinit); } -zone_nonnull_all -static zone_really_inline int32_t parse_base32( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, +nonnull_all +static really_inline int32_t parse_base32( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, const token_t *token) { - int32_t r; + size_t length = (token->length * 5) / 8; + if ((uintptr_t)rdata->limit - (uintptr_t)rdata->octets < length) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); - if ((r = have_contiguous(parser, type, field, token)) < 0) - return r; - size_t decoded = base32hex_sse(parser->rdata->octets+parser->rdata->length, (const uint8_t*)token->data); - if (is_contiguous((uint8_t)token->data[decoded])) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - if (decoded) - parser->rdata->length += (decoded * 5) / 8; - return ZONE_STRING; + size_t decoded = base32hex_sse(rdata->octets, (const uint8_t*)token->data); + if (decoded != token->length) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + rdata->octets += length; + return 0; } + #endif // BASE32_H diff --git a/src/westmere/bench.c b/src/westmere/bench.c index fe71166..61f1576 100644 --- a/src/westmere/bench.c +++ b/src/westmere/bench.c @@ -7,11 +7,11 @@ * */ #include "zone.h" +#include "attributes.h" #include "diagnostic.h" -#include "log.h" #include "westmere/simd.h" #include "westmere/bits.h" -#include "lexer.h" +#include "generic/parser.h" #include "generic/scanner.h" diagnostic_push() @@ -22,10 +22,10 @@ int32_t zone_bench_westmere_lex(zone_parser_t *parser, size_t *tokens) token_t token; (*tokens) = 0; - lex(parser, &token); + take(parser, &token); while (token.code > 0) { (*tokens)++; - lex(parser, &token); + take(parser, &token); } return token.code ? -1 : 0; diff --git a/src/westmere/ip4.h b/src/westmere/ip4.h index dbc9678..9310a48 100644 --- a/src/westmere/ip4.h +++ b/src/westmere/ip4.h @@ -180,8 +180,8 @@ static inline int sse_inet_aton(const char* ipv4_string, uint8_t* destination, s return (int)(length + check_mask - pattern_ptr[6]); } -zone_nonnull_all -static zone_really_inline int32_t scan_ip4( +nonnull_all +static really_inline int32_t scan_ip4( const char *text, uint8_t *wire, size_t *length) { size_t len; @@ -191,26 +191,21 @@ static zone_really_inline int32_t scan_ip4( return 4; } -zone_nonnull_all -static zone_really_inline int32_t parse_ip4( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, +nonnull_all +static really_inline int32_t parse_ip4( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, const token_t *token) { - int32_t r; - size_t n; - uint8_t *o = &parser->rdata->octets[parser->rdata->length]; - - if ((r = have_contiguous(parser, type, field, token)) < 0) - return r; + size_t length; // Note that this assumes that reading up to token->data + 16 is safe (i.e., we do not cross a page). - if (sse_inet_aton(token->data, o, &n) != 1 || - is_contiguous((uint8_t)token->data[n])) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - parser->rdata->length += 4; - return ZONE_IP4; + if (sse_inet_aton(token->data, rdata->octets, &length) != 1 || length != token->length) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); + rdata->octets += 4; + return 0; } #endif // IP4_H diff --git a/src/westmere/parser.c b/src/westmere/parser.c index b5f246d..5a9763c 100644 --- a/src/westmere/parser.c +++ b/src/westmere/parser.c @@ -3,17 +3,16 @@ * * Copyright (c) 2022, NLnet Labs. All rights reserved. * - * See LICENSE for the license. + * SPDX-License-Identifier: BSD-3-Clause. * */ #include "zone.h" +#include "attributes.h" #include "diagnostic.h" -#include "log.h" #include "westmere/simd.h" -#include "fallback/endian.h" +#include "generic/endian.h" #include "westmere/bits.h" -#include "lexer.h" -#include "table.h" +#include "generic/parser.h" #include "generic/scanner.h" #include "generic/number.h" #include "generic/ttl.h" @@ -22,28 +21,30 @@ #include "generic/ip6.h" #include "generic/text.h" #include "generic/name.h" -#include "fallback/base16.h" +#include "generic/base16.h" #include "westmere/base32.h" -#include "fallback/base64.h" +#include "generic/base64.h" #include "generic/nsec.h" #include "generic/nxt.h" #include "generic/caa.h" #include "generic/ilnp64.h" -#include "fallback/eui.h" -#include "fallback/nsap.h" +#include "generic/eui.h" +#include "generic/nsap.h" #include "generic/wks.h" #include "generic/loc.h" #include "generic/gpos.h" #include "generic/apl.h" #include "generic/svcb.h" -#include "types.h" +#include "generic/cert.h" +#include "generic/dnssec.h" +#include "generic/types.h" #include "westmere/type.h" -#include "parser.h" +#include "generic/format.h" diagnostic_push() clang_diagnostic_ignored(missing-prototypes) -int32_t zone_westmere_parse(zone_parser_t *parser) +int32_t zone_westmere_parse(parser_t *parser) { return parse(parser); } diff --git a/src/westmere/simd.h b/src/westmere/simd.h index 730721e..2efc306 100644 --- a/src/westmere/simd.h +++ b/src/westmere/simd.h @@ -31,28 +31,28 @@ typedef struct { __m128i chunks[2]; } simd_8x32_t; typedef struct { __m128i chunks[4]; } simd_8x64_t; -zone_nonnull_all -static zone_really_inline void simd_loadu_8x(simd_8x_t *simd, const uint8_t *address) +nonnull_all +static really_inline void simd_loadu_8x(simd_8x_t *simd, const uint8_t *address) { simd->chunks[0] = _mm_loadu_si128((const __m128i *)address); } -zone_nonnull_all -static zone_really_inline void simd_storeu_8x(uint8_t *address, const simd_8x_t *simd) +nonnull_all +static really_inline void simd_storeu_8x(uint8_t *address, const simd_8x_t *simd) { _mm_storeu_si128((__m128i *)address, simd->chunks[0]); } -zone_nonnull_all -static zone_really_inline uint64_t simd_find_8x(const simd_8x_t *simd, char key) +nonnull_all +static really_inline uint64_t simd_find_8x(const simd_8x_t *simd, char key) { const __m128i k = _mm_set1_epi8(key); const __m128i r = _mm_cmpeq_epi8(simd->chunks[0], k); return (uint16_t)_mm_movemask_epi8(r); } -zone_nonnull_all -static zone_really_inline uint64_t simd_find_any_8x( +nonnull_all +static really_inline uint64_t simd_find_any_8x( const simd_8x_t *simd, const simd_table_t table) { const __m128i t = _mm_loadu_si128((const __m128i *)table); @@ -64,22 +64,22 @@ static zone_really_inline uint64_t simd_find_any_8x( #define simd_loadu_8x16(simd, address) simd_loadu_8x(simd, address) #define simd_find_8x16(simd, key) simd_find_8x(simd, key) -zone_nonnull_all -static zone_really_inline void simd_loadu_8x32(simd_8x32_t *simd, const char *address) +nonnull_all +static really_inline void simd_loadu_8x32(simd_8x32_t *simd, const char *address) { simd->chunks[0] = _mm_loadu_si128((const __m128i *)(address)); simd->chunks[1] = _mm_loadu_si128((const __m128i *)(address+16)); } -zone_nonnull_all -static zone_really_inline void simd_storeu_8x32(uint8_t *address, const simd_8x32_t *simd) +nonnull_all +static really_inline void simd_storeu_8x32(uint8_t *address, const simd_8x32_t *simd) { _mm_storeu_si128((__m128i *)(address), simd->chunks[0]); _mm_storeu_si128((__m128i *)(address+16), simd->chunks[1]); } -zone_nonnull_all -static zone_really_inline uint64_t simd_find_8x32(const simd_8x32_t *simd, char key) +nonnull_all +static really_inline uint64_t simd_find_8x32(const simd_8x32_t *simd, char key) { const __m128i k = _mm_set1_epi8(key); const __m128i r0 = _mm_cmpeq_epi8(simd->chunks[0], k); @@ -89,8 +89,8 @@ static zone_really_inline uint64_t simd_find_8x32(const simd_8x32_t *simd, char return m0 | (m1 << 16); } -zone_nonnull_all -static zone_really_inline void simd_loadu_8x64(simd_8x64_t *simd, const uint8_t *address) +nonnull_all +static really_inline void simd_loadu_8x64(simd_8x64_t *simd, const uint8_t *address) { simd->chunks[0] = _mm_loadu_si128((const __m128i *)(address)); simd->chunks[1] = _mm_loadu_si128((const __m128i *)(address+16)); @@ -98,8 +98,8 @@ static zone_really_inline void simd_loadu_8x64(simd_8x64_t *simd, const uint8_t simd->chunks[3] = _mm_loadu_si128((const __m128i *)(address+48)); } -zone_nonnull_all -static zone_really_inline uint64_t simd_find_8x64(const simd_8x64_t *simd, char key) +nonnull_all +static really_inline uint64_t simd_find_8x64(const simd_8x64_t *simd, char key) { const __m128i k = _mm_set1_epi8(key); @@ -116,8 +116,8 @@ static zone_really_inline uint64_t simd_find_8x64(const simd_8x64_t *simd, char return m0 | (m1 << 16) | (m2 << 32) | (m3 << 48); } -zone_nonnull_all -static zone_really_inline uint64_t simd_find_any_8x64( +nonnull_all +static really_inline uint64_t simd_find_any_8x64( const simd_8x64_t *simd, const simd_table_t table) { const __m128i t = _mm_loadu_si128((const __m128i *)table); diff --git a/src/westmere/time.h b/src/westmere/time.h index e7874cb..e16eb03 100644 --- a/src/westmere/time.h +++ b/src/westmere/time.h @@ -115,31 +115,25 @@ static bool sse_parse_time(const char *date_string, uint32_t *time_in_second) { return time_in_second64 == (uint32_t)time_in_second64; } -zone_nonnull_all -static zone_really_inline int32_t parse_time( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - token_t *token) +nonnull_all +static really_inline int32_t parse_time( + parser_t *parser, + const type_info_t *type, + const rdata_info_t *field, + rdata_t *rdata, + const token_t *token) { - int32_t r; + uint32_t time; - if ((r = have_contiguous(parser, type, field, token)) < 0) - return r; - - const char *p = token->data; - uint32_t sse_result; - if (!sse_parse_time(p, &sse_result)) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); // FIXME: once support for specifying as an unsigned number of seconds is // implemented, update this check too - if (contiguous[(uint8_t)token->data[14]] == CONTIGUOUS) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); + if (token->length != 14 || !sse_parse_time(token->data, &time)) + SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type)); - uint32_t time = htobe32(sse_result); - memcpy(&parser->rdata->octets[parser->rdata->length], &time, sizeof(time)); - parser->rdata->length += sizeof(time); - return ZONE_INT32; + time = htobe32(time); + memcpy(rdata->octets, &time, sizeof(time)); + rdata->octets += sizeof(time); + return 0; } #endif // TIME_H diff --git a/src/westmere/type.h b/src/westmere/type.h index 1f34c3c..793dd82 100644 --- a/src/westmere/type.h +++ b/src/westmere/type.h @@ -9,267 +9,176 @@ #ifndef TYPE_H #define TYPE_H -#define T(code) { &(types[code].info.name), ZONE_TYPE } -#define C(code) { &(classes[code].name), ZONE_CLASS } +#define V(code) { &(types[0].name), 0 } +#define T(code) { &(types[code].name), 1 } +#define C(code) { &(classes[code].name), 2 } // map hash to type or class descriptor (generated using hash.c) static const struct { - const zone_symbol_t *symbol; - int32_t type; + const mnemonic_t *mnemonic; + int32_t code; } types_and_classes[256] = { - T(0), T(0), T(0), T(0), T(0), T(44), T(0), T(3), - T(0), T(0), T(0), T(0), T(11), T(0), T(42), T(0), - T(0), T(0), T(0), T(0), T(0), T(62), T(0), T(0), - T(0), T(99), T(25), T(0), T(53), T(0), T(0), T(0), - T(0), T(0), T(0), T(0), T(50), T(0), T(0), T(0), - T(0), T(39), T(0), T(21), T(0), T(5), T(0), T(0), - T(0), T(0), T(0), T(0), T(0), T(1), T(0), T(0), - C(1), T(0), T(105), T(49), T(0), T(59), T(0), T(29), - T(0), T(20), T(0), T(6), T(0), T(0), T(0), C(3), - T(0), T(63), T(0), T(0), T(0), C(2), T(43), T(37), - T(0), C(4), T(0), T(0), T(45), T(104), T(2), T(0), - T(23), T(55), T(0), T(24), T(0), T(0), T(0), T(0), - T(0), T(0), T(0), T(7), T(0), T(0), T(0), T(12), - T(0), T(0), T(60), T(0), T(0), T(36), T(10), T(15), - T(0), T(26), T(0), T(0), T(19), T(0), T(0), T(0), - T(0), T(0), T(0), T(65), T(0), T(8), T(0), T(108), - T(0), T(38), T(0), T(9), T(0), T(0), T(0), T(0), - T(0), T(0), T(0), T(0), T(46), T(0), T(0), T(0), - T(0), T(0), T(0), T(0), T(0), T(0), T(27), T(48), - T(0), T(0), T(0), T(0), T(0), T(0), T(0), T(0), - T(0), T(0), T(0), T(0), T(0), T(0), T(0), T(0), - T(0), T(0), T(28), T(4), T(51), T(0), T(0), T(30), - T(0), T(106), T(0), T(0), T(16), T(64), T(0), T(0), - T(0), T(0), T(257), T(0), T(0), T(0), T(0), T(0), - T(256), T(0), T(0), T(0), T(0), T(22), T(0), T(0), - T(0), T(33), T(0), T(61), T(0), T(52), T(0), T(0), - T(259), T(0), T(0), T(0), T(14), T(0), T(0), T(0), - T(13), T(0), T(0), T(0), T(0), T(0), T(107), T(0), - T(0), T(18), T(0), T(17), T(0), T(0), T(35), T(0), - T(0), T(0), T(0), T(0), T(0), T(0), T(0), T(0), - T(0), T(0), T(0), T(0), T(258), T(0), T(0), T(109), - T(0), T(0), T(0), T(0), T(0), T(0), T(47), T(0) + V(0), V(0), V(0), V(0), V(0), T(44), V(0), T(3), + V(0), V(0), V(0), V(0), T(11), V(0), T(42), V(0), + V(0), V(0), V(0), V(0), V(0), T(62), V(0), V(0), + V(0), T(99), T(25), V(0), T(53), V(0), V(0), V(0), + V(0), V(0), V(0), V(0), T(50), V(0), V(0), V(0), + V(0), T(39), V(0), T(21), V(0), T(5), V(0), V(0), + V(0), V(0), V(0), V(0), V(0), T(1), V(0), V(0), + C(1), V(0), T(105), T(49), V(0), T(59), V(0), T(29), + V(0), T(20), V(0), T(6), V(0), V(0), V(0), C(3), + V(0), T(63), V(0), V(0), V(0), C(2), T(43), T(37), + V(0), C(4), V(0), V(0), T(45), T(104), T(2), V(0), + T(23), T(55), V(0), T(24), V(0), V(0), V(0), V(0), + V(0), V(0), V(0), T(7), V(0), V(0), V(0), T(12), + V(0), V(0), T(60), V(0), V(0), T(36), T(10), T(15), + V(0), T(26), V(0), V(0), T(19), V(0), V(0), V(0), + V(0), V(0), V(0), T(65), V(0), T(8), V(0), T(108), + V(0), T(38), V(0), T(9), V(0), V(0), V(0), V(0), + V(0), V(0), V(0), V(0), T(46), V(0), V(0), V(0), + V(0), V(0), V(0), V(0), V(0), V(0), T(27), T(48), + V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0), + V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0), + V(0), V(0), T(28), T(4), T(51), V(0), V(0), T(30), + V(0), T(106), V(0), V(0), T(16), T(64), V(0), V(0), + V(0), V(0), T(257), V(0), V(0), V(0), V(0), V(0), + T(256), V(0), V(0), V(0), V(0), T(22), V(0), V(0), + V(0), T(33), V(0), T(61), V(0), T(52), V(0), V(0), + T(259), V(0), V(0), V(0), T(14), V(0), V(0), V(0), + T(13), V(0), V(0), V(0), V(0), V(0), T(107), V(0), + V(0), T(18), V(0), T(17), V(0), V(0), T(35), V(0), + V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0), + V(0), V(0), V(0), V(0), T(258), V(0), V(0), T(109), + V(0), V(0), V(0), V(0), V(0), V(0), T(47), V(0) }; +#undef V #undef T #undef C -static int8_t zero_masks[32] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, +nonnull_all +static really_inline int32_t scan_generic_type( + const char *data, size_t length, uint16_t *code, const mnemonic_t **mnemonic) +{ + if (scan_int16(data + 4, length - 4, code) == 0) + return -1; + if (*code <= 258) + *mnemonic = &types[*code].name; + else if (*code == 32769) + *mnemonic = &types[259].name; + else + *mnemonic = &types[0].name; + return 1; +} + +nonnull_all +static really_inline int32_t scan_generic_class( + const char *data, size_t length, uint16_t *code, const mnemonic_t **mnemonic) +{ + if (scan_int16(data + 5, length - 5, code) == 0) + return -1; + if (*code < 4) + *mnemonic = &classes[*code].name; + else + *mnemonic = &classes[0].name; + return 2; +} + +#define TYPE (0x45505954llu) +#define TYPE_MASK (0xffffffffllu) +#define CLASS (0x5353414c43llu) +#define CLASS_MASK (0xffffffffffllu) + +static const int8_t zero_masks[48] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1 + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }; -static zone_really_inline uint8_t hash(uint64_t prefix) +static really_inline uint8_t hash(uint64_t prefix) { uint32_t value = (uint32_t)((prefix >> 32) ^ prefix); // magic value is generated using hash.c, rerun when adding types return (uint8_t)((value * 3523264710ull) >> 32); } -zone_nonnull_all -static zone_really_inline int32_t find_type_or_class( - zone_parser_t *parser, - const token_t *token, - uint16_t *code, - const zone_symbol_t **symbol) +nonnull_all +static really_inline int32_t scan_type_or_class( + const char *data, size_t length, uint16_t *code, const mnemonic_t **mnemonic) { - (void)parser; + __m128i input = _mm_loadu_si128((const __m128i *)data); - __m128i input = _mm_loadu_si128((const __m128i *)token->data); + const __m128i letter_mask = + _mm_srli_epi32(_mm_and_si128(input, _mm_set1_epi8(0x40)), 1); - // RRTYPEs consist of [0-9a-zA-Z-] (unofficially, no other values are in use) - // 0x2d : hyphen : 0b0010_1101 - // 0x30 - 0x39 : 0 - 9 : 0b0011_0000 - 0b0011_1001 - // 0x41 - 0x4f : A - O : 0b0100_0001 - 0b0100_1111 - // 0x50 - 0x5a : P - Z : 0b0101_0000 - 0b0101_1010 - // 0x61 - 0x6f : a - o : 0b0110_0001 - 0b0110_1111 - // 0x70 - 0x7a : p - z : 0b0111_0000 - 0b0111_1010 - // - // delimiters for strings consisting of a contiguous set of characters - // 0x00 : end-of-file : 0b0000_0000 - // 0x20 : space : 0b0010_0000 - // 0x22 : quote : 0b0010_0010 - // 0x28 : left parenthesis : 0b0010_1000 - // 0x29 : right parenthesis : 0b0010_1001 - // 0x09 : tab : 0b0000_1001 - // 0x0a : line feed : 0b0000_1010 - // 0x3b : semicolon : 0b0011_1011 - // 0x0d : carriage return : 0b0000_1101 - // - // deltas do not catch ('.' (0x2e) or '/' (0x2f)), but neither is a delimiter - //const __m128i deltas = _mm_setr_epi8( - // -16, -32, -45, 70, -65, 37, -97, 5, 0, 0, 0, 0, 0, 0, 0, 0); - const __m128i nibbles = _mm_and_si128(_mm_srli_epi32(input, 4), _mm_set1_epi8(0x0f)); - //const __m128i check = _mm_add_epi8(_mm_shuffle_epi8(deltas, nibbles), input); + // convert to upper case + input = _mm_andnot_si128(letter_mask, input); - //int mask = (uint16_t)_mm_movemask_epi8(check); - //uint16_t length = (uint16_t)__builtin_ctz((unsigned int)mask); - - const __m128i upper = _mm_setr_epi8( - -1, -1, -1, -1, -1, -1, -33, -33, -1, -1, -1, -1, -1, -1, -1, -1); - - __m128i zero_mask; - //if (token->length > 16) - // zero_mask = _mm_loadu_si128((const __m128i *)zero_masks); - //else - // FIXME: fix! length may read out of bounds! - zero_mask = _mm_loadu_si128((const __m128i *)(zero_masks + 16 - token->length)); - input = _mm_and_si128(input, _mm_shuffle_epi8(upper, nibbles)); - input = _mm_andnot_si128(zero_mask, input); + // sanitize input + const __m128i zero_mask = + _mm_loadu_si128((const __m128i *)&zero_masks[32 - (length & 0x1f)]); + input = _mm_and_si128(input, zero_mask); // input is now sanitized and upper case - // FIXME: there is no reason this cannot be used for fallback - // https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/pull/85 - const uint8_t index = hash((uint64_t)_mm_cvtsi128_si64(input)); - *symbol = types_and_classes[index].symbol; + const uint64_t prefix = (uint64_t)_mm_cvtsi128_si64(input); + const uint8_t index = hash(prefix); + *code = (uint16_t)types_and_classes[index].mnemonic->value; + *mnemonic = types_and_classes[index].mnemonic; - const __m128i compar = _mm_loadu_si128((const __m128i *)(*symbol)->key.data); + const __m128i compar = _mm_loadu_si128((const __m128i *)(*mnemonic)->key.data); const __m128i xorthem = _mm_xor_si128(compar, input); - *code = (uint16_t)(*symbol)->value; - - //const uint8_t delimiter = (uint8_t)token->data[token->length]; - if (_mm_test_all_zeros(xorthem, xorthem))// & (contiguous[delimiter] != CONTIGUOUS)) - return types_and_classes[index].type; + if (likely(_mm_test_all_zeros(xorthem, xorthem))) + return types_and_classes[index].code; + else if ((prefix & TYPE_MASK) == TYPE) + return scan_generic_type(data, length, code, mnemonic); + else if ((prefix & CLASS_MASK) == CLASS) + return scan_generic_class(data, length, code, mnemonic); return 0; } -zone_nonnull_all -static zone_really_inline int32_t scan_generic_type( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token, - uint16_t *code, - const zone_symbol_t **symbol) -{ - const char *ps = token->data + 4, *p = ps; - uint64_t n = 0; - for (;; p++) { - const uint64_t d = (uint8_t)*p - '0'; - if (d > 9) - break; - n = n * 10 + d; - } - - if (!n || n > 65535 || p - ps > 5 || is_contiguous((uint8_t)*p)) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - - *code = (uint16_t)n; - if (*code <= 258) - *symbol = &types[*code].info.name; - else if (*code == ZONE_DLV) - *symbol = &types[259].info.name; - else - *symbol = &types[0].info.name; - return ZONE_TYPE; -} - -zone_nonnull_all -static zone_really_inline int32_t scan_generic_class( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token, - uint16_t *code, - const zone_symbol_t **symbol) +nonnull_all +static really_inline int32_t scan_type( + const char *data, size_t length, uint16_t *code, const mnemonic_t **mnemonic) { - const char *ps = token->data + 5, *p = ps; - uint64_t n = 0; - for (;; p++) { - const uint64_t d = (uint8_t)*p - '0'; - if (d > 9) - break; - n = n * 10 + d; - } - - if (!n || n > 65535 || p - ps >= 5 || is_contiguous((uint8_t)*p)) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); + __m128i input = _mm_loadu_si128((const __m128i *)data); - *code = (uint16_t)n; - if (*code <= 4) - *symbol = &classes[*code].name; - else - *symbol = &classes[0].name; - return ZONE_CLASS; -} - -#define TYPE (0x45505954llu) -#define CLASS (0x5353414c43llu) + const __m128i letter_mask = + _mm_srli_epi32(_mm_and_si128(input, _mm_set1_epi8(0x40)), 1); -zone_nonnull_all -static zone_really_inline int32_t scan_type_or_class( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token, - uint16_t *code, - const zone_symbol_t **symbol) -{ - int32_t r; + // convert to upper case + input = _mm_andnot_si128(letter_mask, input); - if ((r = have_contiguous(parser, type, field, token)) < 0) - return r; - if ((r = find_type_or_class(parser, token, code, symbol))) - return r; + // sanitize input + const __m128i zero_mask = + _mm_loadu_si128((const __m128i *)&zero_masks[32 - (length & 0x1f)]); + input = _mm_and_si128(input, zero_mask); - uint64_t k; - memcpy(&k, token->data, 8); - if ((k & 0xdfdfdfdfllu) == TYPE) - return scan_generic_type(parser, type, field, token, code, symbol); - else if ((k & 0xdfdfdfdfdfllu) == CLASS) - return scan_generic_class(parser, type, field, token, code, symbol); - - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); -} - -zone_nonnull_all -static zone_really_inline int32_t scan_type( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token, - uint16_t *code, - const zone_symbol_t **symbol) -{ - int32_t r; + // input is now sanitized and upper case - if ((r = have_contiguous(parser, type, field, token)) < 0) - return r; - if ((r = find_type_or_class(parser, token, code, symbol)) == ZONE_TYPE) - return r; + const uint64_t prefix = (uint64_t)_mm_cvtsi128_si64(input); + const uint8_t index = hash(prefix); + *code = (uint16_t)types_and_classes[index].mnemonic->value; + *mnemonic = types_and_classes[index].mnemonic; - uint64_t k; - memcpy(&k, token->data, 8); - if ((k & 0xdfdfdfdfllu) == TYPE) - return scan_generic_type(parser, type, field, token, code, symbol); + const __m128i compar = _mm_loadu_si128((const __m128i *)(*mnemonic)->key.data); + const __m128i xorthem = _mm_xor_si128(compar, input); - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); + // FIXME: make sure length matches too! + if (likely(_mm_test_all_zeros(xorthem, xorthem))) + return types_and_classes[index].code == 1; + else if ((prefix & TYPE_MASK) == TYPE) + return scan_generic_type(data, length, code, mnemonic); + return 0; } #undef TYPE +#undef TYPE_MASK #undef CLASS - -zone_nonnull_all -static zone_really_inline int32_t parse_type( - zone_parser_t *parser, - const zone_type_info_t *type, - const zone_field_info_t *field, - const token_t *token) -{ - int32_t r; - uint16_t c; - const zone_symbol_t *s; - - if ((r = scan_type(parser, type, field, token, &c, &s)) != ZONE_TYPE) - SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), TNAME(type)); - c = htobe16(c); - memcpy(&parser->rdata->octets[parser->rdata->length], &c, sizeof(c)); - parser->rdata->length += sizeof(c); - return ZONE_TYPE; -} +#undef CLASS_MASK #endif // TYPE_H diff --git a/src/zone.c b/src/zone.c index d556d21..0f0f89f 100644 --- a/src/zone.c +++ b/src/zone.c @@ -15,10 +15,14 @@ #include #include #if _WIN32 -# include +# include #endif #include "zone.h" + +typedef zone_parser_t parser_t; // convenience + +#include "attributes.h" #include "diagnostic.h" #include "isadetection.h" @@ -41,13 +45,14 @@ static const char not_a_file[] = ""; static int32_t check_options(const zone_options_t *options) { - if (!options->accept.add) + if (!options->accept.callback) return ZONE_BAD_PARAMETER; if (!options->origin) return ZONE_BAD_PARAMETER; if (!options->default_ttl || options->default_ttl > INT32_MAX) return ZONE_BAD_PARAMETER; + // makes no sense?!?! switch (options->default_class) { case ZONE_IN: case ZONE_CS: @@ -61,7 +66,7 @@ static int32_t check_options(const zone_options_t *options) return 0; } -// support escaped characters here too! +// FIXME: replace with parser from fallback static int parse_origin(const char *origin, uint8_t str[255], size_t *len) { size_t lab = 0, oct = 1; @@ -99,23 +104,23 @@ static int parse_origin(const char *origin, uint8_t str[255], size_t *len) #include "isadetection.h" #if HAVE_HASWELL -extern int32_t zone_haswell_parse(zone_parser_t *, void *); +extern int32_t zone_haswell_parse(parser_t *, void *); #endif #if HAVE_WESTMERE -extern int32_t zone_westmere_parse(zone_parser_t *, void *); +extern int32_t zone_westmere_parse(parser_t *, void *); #endif -extern int32_t zone_fallback_parse(zone_parser_t *, void *); +extern int32_t zone_fallback_parse(parser_t *, void *); -typedef struct target target_t; -struct target { +typedef struct kernel kernel_t; +struct kernel { const char *name; uint32_t instruction_set; - int32_t (*parse)(zone_parser_t *, void *); + int32_t (*parse)(parser_t *, void *); }; -static const target_t targets[] = { +static const kernel_t kernels[] = { #if HAVE_HASWELL { "haswell", AVX2, &zone_haswell_parse }, #endif @@ -128,68 +133,68 @@ static const target_t targets[] = { diagnostic_push() msvc_diagnostic_ignored(4996) -static inline const target_t * -select_target(void) +static inline const kernel_t * +select_kernel(void) { const char *preferred; const uint32_t supported = detect_supported_architectures(); - const size_t length = sizeof(targets)/sizeof(targets[0]); + const size_t length = sizeof(kernels)/sizeof(kernels[0]); size_t count = 0; - if ((preferred = getenv("ZONE_TARGET"))) { + if ((preferred = getenv("ZONE_KERNEL"))) { for (; count < length; count++) - if (strcasecmp(preferred, targets[count].name) == 0) + if (strcasecmp(preferred, kernels[count].name) == 0) break; if (count == length) count = 0; } for (; count < length; count++) - if (!targets[count].instruction_set || (targets[count].instruction_set & supported)) - return &targets[count]; + if (!kernels[count].instruction_set || (kernels[count].instruction_set & supported)) + return &kernels[count]; - return &targets[length - 1]; + return &kernels[length - 1]; } diagnostic_pop() -static int32_t parse(zone_parser_t *parser, void *user_data) +static int32_t parse(parser_t *parser, void *user_data) { - const target_t *target; + const kernel_t *kernel; - target = select_target(); - assert(target); + kernel = select_kernel(); + assert(kernel); parser->user_data = user_data; - return target->parse(parser, user_data); + return kernel->parse(parser, user_data); } diagnostic_push() msvc_diagnostic_ignored(4996) -zone_nonnull_all +nonnull_all static int32_t open_file( - zone_parser_t *parser, zone_file_t *file, const zone_string_t *path) + parser_t *parser, zone_file_t *file, const char *path, size_t length) { (void)parser; - if (!(file->name = strndup(path->data, path->length))) + if (!(file->name = strndup(path, length))) return ZONE_OUT_OF_MEMORY; #if _WIN32 char buf[1]; - DWORD length, size = GetFullPathName(file->name, sizeof(buf), buf, NULL); + DWORD size = GetFullPathName(file->name, sizeof(buf), buf, NULL); if (!size) - return ZONE_IO_ERROR; + return ZONE_READ_ERROR; if (!(file->path = malloc(size))) return ZONE_OUT_OF_MEMORY; if (!(length = GetFullPathName(file->name, size, file->path, NULL))) - return ZONE_IO_ERROR; + return ZONE_READ_ERROR; if (length != size - 1) - return ZONE_IO_ERROR; + return ZONE_READ_ERROR; #else char buf[PATH_MAX]; if (!realpath(file->name, buf)) - return ZONE_IO_ERROR; + return ZONE_READ_ERROR; if (!(file->path = strdup(buf))) return ZONE_OUT_OF_MEMORY; #endif @@ -199,7 +204,7 @@ static int32_t open_file( case ENOMEM: return ZONE_OUT_OF_MEMORY; default: - return ZONE_IO_ERROR; + return ZONE_READ_ERROR; } if (!(file->buffer.data = malloc(ZONE_WINDOW_SIZE + 1))) @@ -223,9 +228,9 @@ static int32_t open_file( diagnostic_pop() -static void set_defaults(zone_parser_t *parser) +static void set_defaults(parser_t *parser) { - if (!parser->options.log.write && !parser->options.log.categories) + if (!parser->options.log.callback && !parser->options.log.categories) parser->options.log.categories = (uint32_t)-1; parser->owner = &parser->file->owner; parser->rdata = &parser->buffers.rdata.blocks[0]; @@ -234,9 +239,9 @@ static void set_defaults(zone_parser_t *parser) diagnostic_push() clang_diagnostic_ignored(missing-prototypes) -zone_nonnull_all +nonnull_all void zone_close_file( - zone_parser_t *parser, zone_file_t *file) + parser_t *parser, zone_file_t *file) { assert((file->name == not_a_file) == !file->handle); assert((file->path == not_a_file) == !file->handle); @@ -259,9 +264,9 @@ void zone_close_file( free(file); } -zone_nonnull_all +nonnull_all int32_t zone_open_file( - zone_parser_t *parser, const zone_string_t *path, zone_file_t **fileptr) + parser_t *parser, const char *path, size_t length, zone_file_t **fileptr) { zone_file_t *file; int32_t result; @@ -269,7 +274,7 @@ int32_t zone_open_file( if (!(file = malloc(sizeof(*file)))) return ZONE_OUT_OF_MEMORY; memset(file, 0, sizeof(*file));// - sizeof(file->fields.tape)); - if ((result = open_file(parser, file, path)) < 0) + if ((result = open_file(parser, file, path, length)) < 0) goto err_open; *fileptr = file; @@ -279,7 +284,7 @@ int32_t zone_open_file( return result; } -void zone_close(zone_parser_t *parser) +void zone_close(parser_t *parser) { if (!parser) return; @@ -308,7 +313,7 @@ int32_t zone_open( parser->options = *options; parser->user_data = user_data; file = parser->file = &parser->first; - if ((result = open_file(parser, file, &(zone_string_t){ strlen(path), path })) < 0) + if ((result = open_file(parser, file, path, strlen(path))) < 0) goto error; if (parse_origin(options->origin, file->origin.octets, &file->origin.length) < 0) { result = ZONE_BAD_PARAMETER; @@ -350,7 +355,7 @@ int32_t zone_parse( } int32_t zone_parse_string( - zone_parser_t *parser, + parser_t *parser, const zone_options_t *options, zone_buffers_t *buffers, const char *string, @@ -402,3 +407,57 @@ int32_t zone_parse_string( zone_close(parser); return result; } + +zone_nonnull((1,3)) +static void print_message( + zone_parser_t *parser, + uint32_t category, + const char *message, + void *user_data) +{ + FILE *output = category == ZONE_INFO ? stdout : stderr; + const char *format = "%s:%zu: %s\n"; + (void)user_data; + fprintf(output, format, parser->file->name, parser->file->line, message); +} + +diagnostic_push() +clang_diagnostic_ignored(missing-prototypes) + +void zone_vlog( + zone_parser_t *parser, + uint32_t category, + const char *format, + va_list arguments) +{ + char message[2048]; + int length; + zone_log_t callback = print_message; + + length = vsnprintf(message, sizeof(message), format, arguments); + assert(length >= 0); + if ((size_t)length >= sizeof(message)) + memcpy(message+(sizeof(message) - 4), "...", 3); + if (parser->options.log.callback) + callback = parser->options.log.callback; + + callback(parser, category, message, parser->user_data); +} + +diagnostic_pop() + +void zone_log( + zone_parser_t *parser, + uint32_t category, + const char *format, + ...) +{ + va_list arguments; + + if (!(parser->options.log.categories & category)) + return; + + va_start(arguments, format); + zone_vlog(parser, category, format, arguments); + va_end(arguments); +} diff --git a/tests/base32.c b/tests/base32.c index 010e90a..f7c00f9 100644 --- a/tests/base32.c +++ b/tests/base32.c @@ -12,10 +12,10 @@ #include #include "zone.h" +#include "generic/endian.h" static int32_t add_rr( zone_parser_t *parser, - const zone_type_info_t *info, const zone_name_t *owner, uint16_t type, uint16_t class, @@ -25,7 +25,6 @@ static int32_t add_rr( void *user_data) { (void)parser; - (void)info; (void)owner; (void)type; (void)class; @@ -76,7 +75,9 @@ void base32_syntax(void **state) (void)snprintf(rr, sizeof(rr), rrfmt, tests[i].base32); - options.accept.add = add_rr; + fprintf(stderr, "INPUT: '%s'\n", rr); + + options.accept.callback = add_rr; options.origin = "example.com."; options.default_ttl = 3600; options.default_class = ZONE_IN; diff --git a/tests/include.c b/tests/include.c index 7543634..6e59562 100644 --- a/tests/include.c +++ b/tests/include.c @@ -129,7 +129,6 @@ diagnostic_pop() static int32_t add_rr( zone_parser_t *parser, - const zone_type_info_t *info, const zone_name_t *owner, uint16_t type, uint16_t class, @@ -139,7 +138,6 @@ static int32_t add_rr( void *user_data) { (void)parser; - (void)info; (void)type; (void)owner; (void)class; @@ -165,7 +163,7 @@ void include_from_string(void **state) zone_options_t options = { 0 }; int32_t result; - options.accept.add = &add_rr; + options.accept.callback = &add_rr; options.origin = "example.com."; options.default_ttl = 3600; options.default_class = ZONE_IN; diff --git a/tests/ip4.c b/tests/ip4.c index 553447b..5a94805 100644 --- a/tests/ip4.c +++ b/tests/ip4.c @@ -15,7 +15,6 @@ static int32_t add_rr( zone_parser_t *parser, - const zone_type_info_t *info, const zone_name_t *owner, uint16_t type, uint16_t class, @@ -25,7 +24,6 @@ static int32_t add_rr( void *user_data) { (void)parser; - (void)info; (void)owner; (void)type; (void)class; @@ -90,7 +88,7 @@ void ipv4_syntax(void **state) (void)snprintf(rr, sizeof(rr), " A %s", tests[i].address); - options.accept.add = add_rr; + options.accept.callback = add_rr; options.origin = "example.com."; options.default_ttl = 3600; options.default_class = ZONE_IN; diff --git a/tests/svcb.c b/tests/svcb.c index d4d3f21..b838127 100644 --- a/tests/svcb.c +++ b/tests/svcb.c @@ -261,7 +261,6 @@ static const test_t tests[] = { static int32_t add_rr( zone_parser_t *parser, - const zone_type_info_t *info, const zone_name_t *owner, uint16_t type, uint16_t class, @@ -272,7 +271,6 @@ static int32_t add_rr( { const test_t *test = user_data; (void)parser; - (void)info; (void)owner; (void)class; (void)ttl; @@ -298,7 +296,7 @@ void rfc9460_test_vectors(void **state) zone_options_t options = { 0 }; int32_t result; - options.accept.add = add_rr; + options.accept.callback = add_rr; options.origin = "example.com."; options.default_ttl = 3600; options.default_class = ZONE_IN; diff --git a/tests/time.c b/tests/time.c index 2d197c2..948836e 100644 --- a/tests/time.c +++ b/tests/time.c @@ -12,11 +12,10 @@ #include #include "zone.h" -#include "fallback/endian.h" +#include "generic/endian.h" static int32_t add_rr( zone_parser_t *parser, - const zone_type_info_t *info, const zone_name_t *owner, uint16_t type, uint16_t class, @@ -26,7 +25,6 @@ static int32_t add_rr( void *user_data) { (void)parser; - (void)info; (void)owner; (void)type; (void)class; @@ -90,7 +88,7 @@ void time_stamp_syntax(void **state) char *rr = malloc((size_t)size + 1); (void)snprintf(rr, size, FORMAT, tests[i].timestamp); - options.accept.add = add_rr; + options.accept.callback = add_rr; options.origin = "example.com."; options.default_ttl = 3600; options.default_class = ZONE_IN; diff --git a/tests/types.c b/tests/types.c index eed08e6..5eb8e40 100644 --- a/tests/types.c +++ b/tests/types.c @@ -959,7 +959,6 @@ static const test_t tests[] = { static int32_t add_rr( zone_parser_t *parser, - const zone_type_info_t *info, const zone_name_t *owner, uint16_t type, uint16_t class, @@ -970,7 +969,6 @@ static int32_t add_rr( { const test_t *test = user_data; (void)parser; - (void)info; (void)owner; (void)class; (void)ttl; @@ -996,7 +994,7 @@ void supported_types(void **state) zone_options_t options = { 0 }; int32_t result; - options.accept.add = add_rr; + options.accept.callback = add_rr; options.origin = "example.com."; options.default_ttl = 3600; options.default_class = ZONE_IN;