summaryrefslogtreecommitdiffstats
path: root/src/libknot/rrtype
diff options
context:
space:
mode:
Diffstat (limited to 'src/libknot/rrtype')
-rw-r--r--src/libknot/rrtype/dnskey.h72
-rw-r--r--src/libknot/rrtype/ds.h64
-rw-r--r--src/libknot/rrtype/naptr.c47
-rw-r--r--src/libknot/rrtype/naptr.h41
-rw-r--r--src/libknot/rrtype/nsec.h50
-rw-r--r--src/libknot/rrtype/nsec3.h98
-rw-r--r--src/libknot/rrtype/nsec3param.h64
-rw-r--r--src/libknot/rrtype/opt.c687
-rw-r--r--src/libknot/rrtype/opt.h587
-rw-r--r--src/libknot/rrtype/rdname.h98
-rw-r--r--src/libknot/rrtype/rrsig.h100
-rw-r--r--src/libknot/rrtype/soa.h94
-rw-r--r--src/libknot/rrtype/svcb.h44
-rw-r--r--src/libknot/rrtype/tsig.c409
-rw-r--r--src/libknot/rrtype/tsig.h122
-rw-r--r--src/libknot/rrtype/zonemd.h71
16 files changed, 2648 insertions, 0 deletions
diff --git a/src/libknot/rrtype/dnskey.h b/src/libknot/rrtype/dnskey.h
new file mode 100644
index 0000000..229b353
--- /dev/null
+++ b/src/libknot/rrtype/dnskey.h
@@ -0,0 +1,72 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup rrtype
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/rdata.h"
+#include "libknot/wire.h"
+
+/*! See https://www.iana.org/assignments/dnskey-flags */
+/*! /brief "Secure entry point" marks KSK and CSK in practice. */
+#define KNOT_DNSKEY_FLAG_SEP 1
+/*! /brief The key is ALLOWED to be used for zone contents signing. */
+#define KNOT_DNSKEY_FLAG_ZONE 256
+/*! /brief The key MUST NOT be used for validation. */
+#define KNOT_DNSKEY_FLAG_REVOKE 128
+
+static inline
+uint16_t knot_dnskey_flags(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u16(rdata->data);
+}
+
+static inline
+uint8_t knot_dnskey_proto(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data + 2);
+}
+
+static inline
+uint8_t knot_dnskey_alg(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data + 3);
+}
+
+static inline
+uint16_t knot_dnskey_key_len(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->len - 4;
+}
+
+static inline
+const uint8_t *knot_dnskey_key(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data + 4;
+}
+
+/*! @} */
diff --git a/src/libknot/rrtype/ds.h b/src/libknot/rrtype/ds.h
new file mode 100644
index 0000000..cb82e08
--- /dev/null
+++ b/src/libknot/rrtype/ds.h
@@ -0,0 +1,64 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup rrtype
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/rdata.h"
+#include "libknot/wire.h"
+
+static inline
+uint16_t knot_ds_key_tag(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u16(rdata->data);
+}
+
+static inline
+uint8_t knot_ds_alg(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data + 2);
+}
+
+static inline
+uint8_t knot_ds_digest_type(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data + 3);
+}
+
+static inline
+uint16_t knot_ds_digest_len(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->len - 4;
+}
+
+static inline
+const uint8_t *knot_ds_digest(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data + 4;
+}
+
+/*! @} */
diff --git a/src/libknot/rrtype/naptr.c b/src/libknot/rrtype/naptr.c
new file mode 100644
index 0000000..9307816
--- /dev/null
+++ b/src/libknot/rrtype/naptr.c
@@ -0,0 +1,47 @@
+/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+
+#include "libknot/attribute.h"
+#include "libknot/rrtype/naptr.h"
+#include "contrib/wire_ctx.h"
+
+_public_
+int knot_naptr_header_size(const uint8_t *naptr, const uint8_t *maxp)
+{
+ if (!naptr || !maxp || naptr >= maxp) {
+ return KNOT_EINVAL;
+ }
+
+ wire_ctx_t wire = wire_ctx_init_const(naptr, maxp - naptr);
+
+ /* Fixed fields size (order, preference) */
+ wire_ctx_skip(&wire, 2 * sizeof(uint16_t));
+
+ /* Variable fields size (flags, services, regexp) */
+ for (int i = 0; i < 3; i++) {
+ uint8_t size = wire_ctx_read_u8(&wire);
+ wire_ctx_skip(&wire, size);
+ }
+
+ if (wire.error != KNOT_EOK) {
+ return KNOT_EMALF;
+ }
+
+ return wire_ctx_offset(&wire);
+}
diff --git a/src/libknot/rrtype/naptr.h b/src/libknot/rrtype/naptr.h
new file mode 100644
index 0000000..ab14a9f
--- /dev/null
+++ b/src/libknot/rrtype/naptr.h
@@ -0,0 +1,41 @@
+/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup rrtype
+ * @{
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+/*!
+ * \brief Counts the size of the NAPTR RDATA before the Replacement domain name.
+ *
+ * See RFC 2915.
+ *
+ * \param naptr Wire format of NAPTR record.
+ * \param maxp Limit of the wire format.
+ *
+ * \retval KNOT_EMALF if the record is malformed.
+ * \retval Size of the RDATA before the Replacement domain name.
+ */
+int knot_naptr_header_size(const uint8_t *naptr, const uint8_t *maxp);
+
+/*! @} */
diff --git a/src/libknot/rrtype/nsec.h b/src/libknot/rrtype/nsec.h
new file mode 100644
index 0000000..be1ce31
--- /dev/null
+++ b/src/libknot/rrtype/nsec.h
@@ -0,0 +1,50 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup rrtype
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/dname.h"
+#include "libknot/rdata.h"
+
+static inline
+const knot_dname_t *knot_nsec_next(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data;
+}
+
+static inline
+uint16_t knot_nsec_bitmap_len(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->len - knot_dname_size(knot_nsec_next(rdata));
+}
+
+static inline
+const uint8_t *knot_nsec_bitmap(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data + knot_dname_size(knot_nsec_next(rdata));
+}
+
+/*! @} */
diff --git a/src/libknot/rrtype/nsec3.h b/src/libknot/rrtype/nsec3.h
new file mode 100644
index 0000000..5bb94bd
--- /dev/null
+++ b/src/libknot/rrtype/nsec3.h
@@ -0,0 +1,98 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup rrtype
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/rdata.h"
+#include "libknot/wire.h"
+
+/*!
+ * \brief NSEC3 rdata constants.
+ */
+#define KNOT_NSEC3_ALGORITHM_SHA1 1
+#define KNOT_NSEC3_FLAG_OPT_OUT 1
+
+static inline
+uint8_t knot_nsec3_alg(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data);
+}
+
+static inline
+uint8_t knot_nsec3_flags(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data + 1);
+}
+
+static inline
+uint16_t knot_nsec3_iters(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u16(rdata->data + 2);
+}
+
+static inline
+uint8_t knot_nsec3_salt_len(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data + 4);
+}
+
+static inline
+const uint8_t *knot_nsec3_salt(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data + 5;
+}
+
+static inline
+uint8_t knot_nsec3_next_len(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data + 5 + knot_nsec3_salt_len(rdata));
+}
+
+static inline
+const uint8_t *knot_nsec3_next(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data + 6 + knot_nsec3_salt_len(rdata);
+}
+
+static inline
+uint16_t knot_nsec3_bitmap_len(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->len - 6 - knot_nsec3_salt_len(rdata) - knot_nsec3_next_len(rdata);
+}
+
+static inline
+const uint8_t *knot_nsec3_bitmap(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data + 6 + knot_nsec3_salt_len(rdata) + knot_nsec3_next_len(rdata);
+}
+
+/*! @} */
diff --git a/src/libknot/rrtype/nsec3param.h b/src/libknot/rrtype/nsec3param.h
new file mode 100644
index 0000000..43bce18
--- /dev/null
+++ b/src/libknot/rrtype/nsec3param.h
@@ -0,0 +1,64 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup rrtype
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/rdata.h"
+#include "libknot/wire.h"
+
+static inline
+uint8_t knot_nsec3param_alg(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data);
+}
+
+static inline
+uint8_t knot_nsec3param_flags(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data + 1);
+}
+
+static inline
+uint16_t knot_nsec3param_iters(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u16(rdata->data + 2);
+}
+
+static inline
+uint8_t knot_nsec3param_salt_len(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data + 4);
+}
+
+static inline
+const uint8_t *knot_nsec3param_salt(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data + 5;
+}
+
+/*! @} */
diff --git a/src/libknot/rrtype/opt.c b/src/libknot/rrtype/opt.c
new file mode 100644
index 0000000..294689c
--- /dev/null
+++ b/src/libknot/rrtype/opt.c
@@ -0,0 +1,687 @@
+/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#include "libknot/attribute.h"
+#include "libknot/consts.h"
+#include "libknot/descriptor.h"
+#include "libknot/lookup.h"
+#include "libknot/packet/pkt.h"
+#include "libknot/rrtype/opt.h"
+#include "contrib/mempattern.h"
+#include "contrib/wire_ctx.h"
+
+/*! \brief Some implementation-related constants. */
+enum {
+ /*! \brief Byte offset of the extended RCODE field in TTL. */
+ EDNS_OFFSET_RCODE = 0,
+ /*! \brief Byte offset of the version field in TTL. */
+ EDNS_OFFSET_VERSION = 1,
+
+ /*! \brief Byte offset of the family field in option data. */
+ EDNS_OFFSET_CLIENT_SUBNET_FAMILY = 0,
+ /*! \brief Byte offset of the source mask field in option data. */
+ EDNS_OFFSET_CLIENT_SUBNET_SRC_MASK = 2,
+ /*! \brief Byte offset of the destination mask field in option data. */
+ EDNS_OFFSET_CLIENT_SUBNET_DST_MASK = 3,
+ /*! \brief Byte offset of the address field in option data. */
+ EDNS_OFFSET_CLIENT_SUBNET_ADDR = 4,
+};
+
+_public_
+int knot_edns_init(knot_rrset_t *opt_rr, uint16_t max_pld,
+ uint8_t ext_rcode, uint8_t ver, knot_mm_t *mm)
+{
+ if (opt_rr == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ /* Initialize RRSet. */
+ knot_dname_t *owner = knot_dname_copy((const uint8_t *)"", mm);
+ if (owner == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ knot_rrset_init(opt_rr, owner, KNOT_RRTYPE_OPT, max_pld, 0);
+
+ /* Create empty RDATA */
+ int ret = knot_rrset_add_rdata(opt_rr, NULL, 0, mm);
+ if (ret == KNOT_EOK) {
+ knot_edns_set_ext_rcode(opt_rr, ext_rcode);
+ knot_edns_set_version(opt_rr, ver);
+ }
+
+ return ret;
+}
+
+_public_
+uint8_t knot_edns_get_ext_rcode(const knot_rrset_t *opt_rr)
+{
+ assert(opt_rr != NULL);
+ uint32_t ttl = 0;
+ wire_ctx_t w = wire_ctx_init((uint8_t *)&ttl, sizeof(ttl));
+ // TTL is stored in machine byte order. Convert it to wire order first.
+ wire_ctx_write_u32(&w, opt_rr->ttl);
+ wire_ctx_set_offset(&w, EDNS_OFFSET_RCODE);
+ return wire_ctx_read_u8(&w);
+}
+
+static void set_value_to_ttl(knot_rrset_t *opt_rr, size_t offset, uint8_t value)
+{
+ uint32_t ttl = 0;
+ wire_ctx_t w = wire_ctx_init((uint8_t *)&ttl, sizeof(ttl));
+ // TTL is stored in machine byte order. Convert it to wire order first.
+ wire_ctx_write_u32(&w, opt_rr->ttl);
+ // Set the Extended RCODE in the converted TTL
+ wire_ctx_set_offset(&w, offset);
+ wire_ctx_write_u8(&w, value);
+ // Convert it back to machine byte order
+ wire_ctx_set_offset(&w, 0);
+ uint32_t ttl_local = wire_ctx_read_u32(&w);
+ // Store the TTL to the RDATA
+ opt_rr->ttl = ttl_local;
+}
+
+_public_
+void knot_edns_set_ext_rcode(knot_rrset_t *opt_rr, uint8_t ext_rcode)
+{
+ assert(opt_rr != NULL);
+ set_value_to_ttl(opt_rr, EDNS_OFFSET_RCODE, ext_rcode);
+}
+
+_public_
+uint8_t knot_edns_get_version(const knot_rrset_t *opt_rr)
+{
+ assert(opt_rr != NULL);
+ uint32_t ttl = 0;
+ wire_ctx_t w = wire_ctx_init((uint8_t *)&ttl, sizeof(ttl));
+ // TTL is stored in machine byte order. Convert it to wire order first.
+ wire_ctx_write_u32(&w, opt_rr->ttl);
+ wire_ctx_set_offset(&w, EDNS_OFFSET_VERSION);
+ return wire_ctx_read_u8(&w);
+}
+
+_public_
+void knot_edns_set_version(knot_rrset_t *opt_rr, uint8_t version)
+{
+ assert(opt_rr != NULL);
+ set_value_to_ttl(opt_rr, EDNS_OFFSET_VERSION, version);
+}
+
+/*!
+ * \brief Add new EDNS option by replacing RDATA of OPT RR.
+ *
+ * \param opt OPT RR structure to add the Option to.
+ * \param code Option code.
+ * \param size Option data length in bytes.
+ * \param mm Memory context.
+ *
+ * \return Pointer to uninitialized option data.
+ */
+static uint8_t *edns_add(knot_rrset_t *opt, uint16_t code, uint16_t size,
+ knot_mm_t *mm)
+{
+ assert(opt->rrs.count == 1);
+
+ // extract old RDATA
+
+ uint8_t *old_data = opt->rrs.rdata->data;
+ uint16_t old_data_len = opt->rrs.rdata->len;
+
+ // construct new RDATA
+
+ uint16_t new_data_len = old_data_len + KNOT_EDNS_OPTION_HDRLEN + size;
+ uint8_t new_data[new_data_len];
+
+ wire_ctx_t wire = wire_ctx_init(new_data, new_data_len);
+ wire_ctx_write(&wire, old_data, old_data_len);
+ wire_ctx_write_u16(&wire, code);
+ wire_ctx_write_u16(&wire, size);
+
+ // prepare EDNS option data
+
+ size_t offset = wire_ctx_offset(&wire);
+ wire_ctx_clear(&wire, size);
+
+ assert(wire_ctx_available(&wire) == 0);
+ assert(wire.error == KNOT_EOK);
+
+ // replace RDATA
+
+ knot_rdataset_clear(&opt->rrs, mm);
+ if (knot_rrset_add_rdata(opt, new_data, new_data_len, mm) != KNOT_EOK) {
+ return NULL;
+ }
+
+ return opt->rrs.rdata->data + offset;
+}
+
+_public_
+int knot_edns_reserve_option(knot_rrset_t *opt_rr, uint16_t code,
+ uint16_t size, uint8_t **wire_ptr, knot_mm_t *mm)
+{
+ if (!opt_rr) {
+ return KNOT_EINVAL;
+ }
+
+ uint8_t *wire = edns_add(opt_rr, code, size, mm);
+ if (!wire) {
+ return KNOT_ENOMEM;
+ }
+
+ if (wire_ptr) {
+ *wire_ptr = wire;
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_edns_add_option(knot_rrset_t *opt_rr, uint16_t code,
+ uint16_t size, const uint8_t *data, knot_mm_t *mm)
+{
+ if (!opt_rr || (size > 0 && !data)) {
+ return KNOT_EINVAL;
+ }
+
+ uint8_t *wire = edns_add(opt_rr, code, size, mm);
+ if (!wire) {
+ return KNOT_ENOMEM;
+ }
+
+ if (size > 0) {
+ memcpy(wire, data, size);
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+uint8_t *knot_edns_get_option(const knot_rrset_t *opt_rr, uint16_t code,
+ const uint8_t *previous)
+{
+ if (opt_rr == NULL) {
+ return NULL;
+ }
+
+ knot_rdata_t *rdata = opt_rr->rrs.rdata;
+ if (rdata == NULL || rdata->len == 0) {
+ return NULL;
+ }
+
+ wire_ctx_t wire = wire_ctx_init_const(rdata->data, rdata->len);
+ if (previous != NULL) {
+ if (previous < wire.wire) {
+ return NULL;
+ }
+ wire_ctx_set_offset(&wire, previous - wire.wire + 2);
+ uint16_t opt_len = wire_ctx_read_u16(&wire);
+ wire_ctx_skip(&wire, opt_len);
+ }
+
+ while (wire_ctx_available(&wire) > 0 && wire.error == KNOT_EOK) {
+ uint8_t *pos = wire.position;
+ uint16_t opt_code = wire_ctx_read_u16(&wire);
+ uint16_t opt_len = wire_ctx_read_u16(&wire);
+ wire_ctx_skip(&wire, opt_len);
+ if (wire.error == KNOT_EOK && opt_code == code) {
+ return pos;
+ }
+ }
+
+ return NULL;
+}
+
+_public_
+int knot_edns_get_options(knot_rrset_t *opt_rr, knot_edns_options_t **out,
+ knot_mm_t *mm)
+{
+ if (opt_rr == NULL || opt_rr->rrs.count > 1 || out == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ knot_rdata_t *rdata = opt_rr->rrs.rdata;
+ if (rdata == NULL || rdata->len == 0) {
+ return KNOT_EOK;
+ }
+
+ knot_edns_options_t *options = mm_calloc(mm, 1, sizeof(*options));
+
+ wire_ctx_t wire = wire_ctx_init_const(rdata->data, rdata->len);
+
+ while (wire_ctx_available(&wire) > 0 && wire.error == KNOT_EOK) {
+ uint8_t *pos = wire.position;
+ uint16_t opt_code = wire_ctx_read_u16(&wire);
+ uint16_t opt_len = wire_ctx_read_u16(&wire);
+ wire_ctx_skip(&wire, opt_len);
+ if (wire.error == KNOT_EOK && opt_code <= KNOT_EDNS_MAX_OPTION_CODE) {
+ options->ptr[opt_code] = pos;
+ }
+ }
+
+ if (wire.error != KNOT_EOK) {
+ mm_free(mm, options);
+ return wire.error;
+ }
+
+ *out = options;
+ return KNOT_EOK;
+}
+
+_public_
+int knot_edns_alignment_size(size_t current_pkt_size,
+ size_t current_opt_size,
+ size_t block_size)
+{
+ if (current_opt_size == 0 || block_size == 0) {
+ return -1;
+ }
+
+ size_t current_size = current_pkt_size + current_opt_size;
+ if (current_size % block_size == 0) {
+ return -1;
+ }
+
+ size_t modulo = (current_size + KNOT_EDNS_OPTION_HDRLEN) % block_size;
+
+ return (modulo == 0) ? 0 : block_size - modulo;
+}
+
+/*!
+ * \brief EDNS Client Subnet family data.
+ */
+typedef struct {
+ int platform; //!< Platform family identifier.
+ uint16_t iana; //!< IANA family identifier.
+ size_t offset; //!< Socket address offset.
+ size_t size; //!< Socket address size.
+} ecs_family_t;
+
+#define ECS_INIT(platform, iana, type, member) \
+ { platform, iana, offsetof(type, member), sizeof(((type *)0)->member) }
+
+/*!
+ * \brief Supported EDNS Client Subnet families.
+ *
+ * https://www.iana.org/assignments/address-family-numbers/address-family-numbers.xml
+ */
+static const ecs_family_t ECS_FAMILIES[] = {
+ ECS_INIT(AF_INET, KNOT_ADDR_FAMILY_IPV4, struct sockaddr_in, sin_addr),
+ ECS_INIT(AF_INET6, KNOT_ADDR_FAMILY_IPV6, struct sockaddr_in6, sin6_addr),
+ { 0 }
+};
+
+/*!
+ * \brief Lookup ECS family by platform identifier.
+ */
+static const ecs_family_t *ecs_family_by_platform(int family)
+{
+ for (const ecs_family_t *f = ECS_FAMILIES; f->size > 0; f++) {
+ if (f->platform == family) {
+ return f;
+ }
+ }
+
+ return NULL;
+}
+
+/*!
+ * \brief Lookup ECS family by IANA identifier.
+ */
+static const ecs_family_t *ecs_family_by_iana(uint16_t family)
+{
+ for (const ecs_family_t *f = ECS_FAMILIES; f->size > 0; f++) {
+ if (f->iana == family) {
+ return f;
+ }
+ }
+
+ return NULL;
+}
+
+/*!
+ * \brief Get ECS address prefix size in bytes.
+ */
+static uint16_t ecs_prefix_size(uint8_t prefix)
+{
+ return (prefix + 7) / 8;
+}
+
+static uint8_t ecs_prefix_lsb_mask(uint8_t prefix)
+{
+ int modulo = prefix % 8;
+ if (modulo == 0) {
+ return 0xff;
+ } else {
+ return 0xff << (8 - modulo);
+ }
+}
+
+/*!
+ * \brief Write raw network address prefix and clear the rest of the buffer.
+ */
+static void ecs_write_address(wire_ctx_t *dst, wire_ctx_t *src, int8_t prefix)
+{
+ size_t count = ecs_prefix_size(prefix);
+ uint8_t lsb_mask = ecs_prefix_lsb_mask(prefix);
+
+ if (count > 0) {
+ wire_ctx_copy(dst, src, count);
+ if (dst->error != KNOT_EOK) {
+ return;
+ }
+ dst->position[-1] &= lsb_mask;
+ }
+
+ size_t blank = wire_ctx_available(dst);
+ wire_ctx_clear(dst, blank);
+}
+
+/*!
+ * \brief Check if ECS parameters are valid.
+ */
+static bool ecs_is_valid(const knot_edns_client_subnet_t *ecs)
+{
+ if (ecs == NULL) {
+ return false;
+ }
+
+ const ecs_family_t *f = ecs_family_by_iana(ecs->family);
+
+ return f != NULL && // known family check
+ (ecs->source_len <= f->size * 8) && // family address maximum check
+ (ecs->scope_len <= f->size * 8); // family address maximum check
+}
+
+_public_
+uint16_t knot_edns_client_subnet_size(const knot_edns_client_subnet_t *ecs)
+{
+ if (!ecs_is_valid(ecs)) {
+ return 0;
+ }
+
+ return sizeof(ecs->family) +
+ sizeof(ecs->source_len) +
+ sizeof(ecs->scope_len) +
+ ecs_prefix_size(ecs->source_len);
+}
+
+_public_
+int knot_edns_client_subnet_write(uint8_t *option, uint16_t option_len,
+ const knot_edns_client_subnet_t *ecs)
+{
+ if (option == NULL || ecs == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (!ecs_is_valid(ecs)) {
+ return KNOT_EINVAL;
+ }
+
+ wire_ctx_t wire = wire_ctx_init(option, option_len);
+ wire_ctx_t addr = wire_ctx_init_const(ecs->address, sizeof(ecs->address));
+
+ wire_ctx_write_u16(&wire, ecs->family);
+ wire_ctx_write_u8(&wire, ecs->source_len);
+ wire_ctx_write_u8(&wire, ecs->scope_len);
+ ecs_write_address(&wire, &addr, ecs->source_len);
+
+ if (wire.error != KNOT_EOK) {
+ return wire.error;
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_edns_client_subnet_parse(knot_edns_client_subnet_t *ecs,
+ const uint8_t *option, uint16_t option_len)
+{
+ if (ecs == NULL || option == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ knot_edns_client_subnet_t result = { 0 };
+
+ wire_ctx_t wire = wire_ctx_init_const(option, option_len);
+ wire_ctx_t addr = wire_ctx_init(result.address, sizeof(result.address));
+
+ result.family = wire_ctx_read_u16(&wire);
+ result.source_len = wire_ctx_read_u8(&wire);
+ result.scope_len = wire_ctx_read_u8(&wire);
+ ecs_write_address(&addr, &wire, result.source_len);
+
+ if (addr.error != KNOT_EOK || wire.error != KNOT_EOK) {
+ return KNOT_EMALF;
+ }
+
+ if (!ecs_is_valid(&result)) {
+ return KNOT_EMALF;
+ }
+
+ *ecs = result;
+ return KNOT_EOK;
+}
+
+_public_
+int knot_edns_client_subnet_set_addr(knot_edns_client_subnet_t *ecs,
+ const struct sockaddr_storage *addr)
+{
+ if (ecs == NULL || addr == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ const ecs_family_t *f = ecs_family_by_platform(addr->ss_family);
+ if (f == NULL) {
+ return KNOT_ENOTSUP;
+ }
+
+ ecs->family = f->iana;
+ ecs->source_len = f->size * 8;
+ ecs->scope_len = 0;
+
+ wire_ctx_t dst = wire_ctx_init(ecs->address, sizeof(ecs->address));
+ wire_ctx_t src = wire_ctx_init_const((uint8_t *)addr + f->offset, f->size);
+ ecs_write_address(&dst, &src, ecs->source_len);
+
+ assert(dst.error == KNOT_EOK);
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_edns_client_subnet_get_addr(struct sockaddr_storage *addr,
+ const knot_edns_client_subnet_t *ecs)
+{
+ if (addr == NULL || ecs == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ const ecs_family_t *f = ecs_family_by_iana(ecs->family);
+ if (f == NULL) {
+ return KNOT_ENOTSUP;
+ }
+
+ addr->ss_family = f->platform;
+
+ wire_ctx_t dst = wire_ctx_init((uint8_t *)addr + f->offset, f->size);
+ wire_ctx_t src = wire_ctx_init_const(ecs->address, sizeof(ecs->address));
+ ecs_write_address(&dst, &src, ecs->source_len);
+
+ assert(dst.error == KNOT_EOK);
+
+ return KNOT_EOK;
+}
+
+_public_
+uint16_t knot_edns_keepalive_size(uint16_t timeout)
+{
+ return (timeout > 0) ? sizeof(uint16_t) : 0;
+}
+
+_public_
+int knot_edns_keepalive_write(uint8_t *option, uint16_t option_len, uint16_t timeout)
+{
+ if (option == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (timeout == 0) {
+ return KNOT_EOK;
+ }
+
+ wire_ctx_t wire = wire_ctx_init(option, option_len);
+ wire_ctx_write_u16(&wire, timeout);
+
+ return wire.error;
+}
+
+_public_
+int knot_edns_keepalive_parse(uint16_t *timeout, const uint8_t *option,
+ uint16_t option_len)
+{
+ if (timeout == NULL || option == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ *timeout = 0;
+
+ if (option_len > 0) {
+ wire_ctx_t wire = wire_ctx_init_const(option, option_len);
+ *timeout = wire_ctx_read_u16(&wire);
+
+ if (wire.error != KNOT_EOK) {
+ return KNOT_EMALF;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+uint16_t knot_edns_chain_size(const knot_dname_t *point)
+{
+ return knot_dname_size(point);
+}
+
+_public_
+int knot_edns_chain_write(uint8_t *option, uint16_t option_len,
+ const knot_dname_t *point)
+{
+ if (option == NULL || point == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ wire_ctx_t wire = wire_ctx_init(option, option_len);
+ wire_ctx_write(&wire, point, knot_dname_size(point));
+
+ return wire.error;
+}
+
+_public_
+int knot_edns_chain_parse(knot_dname_t **point, const uint8_t *option,
+ uint16_t option_len, knot_mm_t *mm)
+{
+ if (point == NULL || option == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = knot_dname_wire_check(option, option + option_len, NULL);
+ if (ret <= 0) {
+ return KNOT_EMALF;
+ }
+
+ *point = knot_dname_copy(option, mm);
+ if (*point == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+uint16_t knot_edns_cookie_size(const knot_edns_cookie_t *cc,
+ const knot_edns_cookie_t *sc)
+{
+ if (cc == NULL || cc->len != KNOT_EDNS_COOKIE_CLNT_SIZE) {
+ return 0;
+ } else if (sc == NULL || sc->len == 0) {
+ return KNOT_EDNS_COOKIE_CLNT_SIZE;
+ } else if (sc->len < KNOT_EDNS_COOKIE_SRVR_MIN_SIZE ||
+ sc->len > KNOT_EDNS_COOKIE_SRVR_MAX_SIZE) {
+ return 0;
+ } else {
+ return cc->len + sc->len;
+ }
+}
+
+_public_
+int knot_edns_cookie_write(uint8_t *option, uint16_t option_len,
+ const knot_edns_cookie_t *cc,
+ const knot_edns_cookie_t *sc)
+{
+ if (option == NULL || cc == NULL || cc->len != KNOT_EDNS_COOKIE_CLNT_SIZE) {
+ return KNOT_EINVAL;
+ }
+
+ wire_ctx_t wire = wire_ctx_init(option, option_len);
+ wire_ctx_write(&wire, cc->data, cc->len);
+
+ if (sc != NULL && sc->len > 0) {
+ if (sc->len < KNOT_EDNS_COOKIE_SRVR_MIN_SIZE ||
+ sc->len > KNOT_EDNS_COOKIE_SRVR_MAX_SIZE) {
+ return KNOT_EINVAL;
+ }
+ wire_ctx_write(&wire, sc->data, sc->len);
+ }
+
+ return wire.error;
+}
+
+_public_
+int knot_edns_cookie_parse(knot_edns_cookie_t *cc, knot_edns_cookie_t *sc,
+ const uint8_t *option, uint16_t option_len)
+{
+ if (cc == NULL || sc == NULL || option == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (option_len != KNOT_EDNS_COOKIE_CLNT_SIZE &&
+ (option_len < KNOT_EDNS_COOKIE_CLNT_SIZE + KNOT_EDNS_COOKIE_SRVR_MIN_SIZE ||
+ option_len > KNOT_EDNS_COOKIE_CLNT_SIZE + KNOT_EDNS_COOKIE_SRVR_MAX_SIZE)) {
+ return KNOT_EMALF;
+ }
+ assert(option_len >= KNOT_EDNS_COOKIE_CLNT_SIZE);
+
+ memcpy(cc->data, option, KNOT_EDNS_COOKIE_CLNT_SIZE);
+ cc->len = KNOT_EDNS_COOKIE_CLNT_SIZE;
+
+ size_t sc_len = option_len - KNOT_EDNS_COOKIE_CLNT_SIZE;
+ if (sc_len == 0) {
+ sc->len = 0;
+ } else {
+ memcpy(sc->data, option + KNOT_EDNS_COOKIE_CLNT_SIZE, sc_len);
+ sc->len = sc_len;
+ }
+
+ return KNOT_EOK;
+}
diff --git a/src/libknot/rrtype/opt.h b/src/libknot/rrtype/opt.h
new file mode 100644
index 0000000..bce87c2
--- /dev/null
+++ b/src/libknot/rrtype/opt.h
@@ -0,0 +1,587 @@
+/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Functions for manipulating the EDNS OPT pseudo-RR.
+ *
+ * \addtogroup rrtype
+ * @{
+ */
+
+#pragma once
+
+#include <assert.h>
+#include <sys/socket.h>
+
+#include "libknot/consts.h"
+#include "libknot/rrset.h"
+#include "libknot/wire.h"
+
+/*! \brief Constants related to EDNS. */
+enum {
+ /*! \brief Supported EDNS version. */
+ KNOT_EDNS_VERSION = 0,
+
+ /*! \brief Bit mask for DO bit. */
+ KNOT_EDNS_DO_MASK = (uint32_t)(1 << 15),
+
+ /*! \brief Minimal UDP payload with EDNS enabled. */
+ KNOT_EDNS_MIN_UDP_PAYLOAD = 512,
+ /*! \brief Minimal payload when using DNSSEC (RFC4035/sec.3). */
+ KNOT_EDNS_MIN_DNSSEC_PAYLOAD = 1220,
+ /*! \brief Maximal UDP payload with EDNS enabled. */
+ KNOT_EDNS_MAX_UDP_PAYLOAD = 4096,
+
+ /*! \brief Minimum size of EDNS OPT RR in wire format. */
+ KNOT_EDNS_MIN_SIZE = 11,
+ /*! \brief Position of the Ext RCODE field in wire format of OPT RR. */
+ KNOT_EDNS_EXT_RCODE_POS = 5,
+ /*! \brief EDNS OPTION header size. */
+ KNOT_EDNS_OPTION_HDRLEN = 4,
+
+ /*! \brief Maximal size of EDNS client subnet address in bytes (IPv6). */
+ KNOT_EDNS_CLIENT_SUBNET_ADDRESS_MAXLEN = 16,
+
+ /*! \brief Default EDNS alignment size for a query. */
+ KNOT_EDNS_ALIGNMENT_QUERY_DEFAULT = 128,
+ /*! \brief Default EDNS alignment size for a response. */
+ KNOT_EDNS_ALIGNMENT_RESPONSE_DEFAULT = 468,
+
+ /*! \brief Current EDNS cookie version. */
+ KNOT_EDNS_COOKIE_VERSION = 1,
+ /*! \brief EDNS client cookie size. */
+ KNOT_EDNS_COOKIE_CLNT_SIZE = 8,
+ /*! \brief EDNS minimum server cookie size. */
+ KNOT_EDNS_COOKIE_SRVR_MIN_SIZE = 8,
+ /*! \brief EDNS maximum server cookie size. */
+ KNOT_EDNS_COOKIE_SRVR_MAX_SIZE = 32,
+
+ /*! \brief NSID option code. */
+ KNOT_EDNS_OPTION_NSID = 3,
+ /*! \brief EDNS Client subnet option code. */
+ KNOT_EDNS_OPTION_CLIENT_SUBNET = 8,
+ /*! \brief EDNS Expire option code. */
+ KNOT_EDNS_OPTION_EXPIRE = 9,
+ /*! \brief EDNS DNS Cookie option code. */
+ KNOT_EDNS_OPTION_COOKIE = 10,
+ /*! \brief EDNS TCP Keepalive option code. */
+ KNOT_EDNS_OPTION_TCP_KEEPALIVE = 11,
+ /*! \brief EDNS Padding option code. */
+ KNOT_EDNS_OPTION_PADDING = 12,
+ /*! \brief EDNS Chain query option code. */
+ KNOT_EDNS_OPTION_CHAIN = 13,
+
+ /*! \brief EDNS Extended error code. */
+ KNOT_EDNS_OPTION_EDE = 15,
+ /*! \brief The minimal length for EDE option including option header. */
+ KNOT_EDNS_EDE_MIN_LENGTH = 6,
+
+ /*! \brief Maximal currently known option code. */
+ KNOT_EDNS_MAX_OPTION_CODE = 17,
+};
+
+/* Helpers for splitting extended RCODE. */
+#define KNOT_EDNS_RCODE_HI(rc) ((rc >> 4) & 0x00ff)
+#define KNOT_EDNS_RCODE_LO(rc) (rc & 0x000f)
+
+/*!
+ * \brief Initialize OPT RR.
+ *
+ * \param opt_rr OPT RR to initialize.
+ * \param max_pld Max UDP payload.
+ * \param ext_rcode Extended RCODE.
+ * \param ver Version.
+ * \param mm Memory context.
+ *
+ * \return KNOT_EOK or an error
+ */
+int knot_edns_init(knot_rrset_t *opt_rr, uint16_t max_pld,
+ uint8_t ext_rcode, uint8_t ver, knot_mm_t *mm);
+
+/*!
+ * \brief Returns size of the OPT RR in wire format.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ *
+ * \param opt_rr OPT RR to count the wire size of.
+ *
+ * \return Size of the OPT RR in bytes.
+ */
+static inline
+size_t knot_edns_wire_size(knot_rrset_t *opt_rr)
+{
+ assert(opt_rr != NULL);
+ return KNOT_EDNS_MIN_SIZE + opt_rr->rrs.rdata->len;
+}
+
+/*!
+ * \brief Returns the Max UDP payload value stored in the OPT RR.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ *
+ * \param opt_rr OPT RR to get the value from.
+ *
+ * \return Max UDP payload in bytes.
+ */
+static inline
+uint16_t knot_edns_get_payload(const knot_rrset_t *opt_rr)
+{
+ assert(opt_rr != NULL);
+ return opt_rr->rclass;
+}
+
+/*!
+ * \brief Sets the Max UDP payload field in the OPT RR.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ *
+ * \param opt_rr OPT RR to set the value to.
+ * \param payload UDP payload in bytes.
+ */
+static inline
+void knot_edns_set_payload(knot_rrset_t *opt_rr, uint16_t payload)
+{
+ assert(opt_rr != NULL);
+ opt_rr->rclass = payload;
+}
+
+/*!
+ * \brief Returns the Extended RCODE stored in the OPT RR.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ * \note There is an assert() for debug checking of the parameter.
+ *
+ * \param opt_rr OPT RR to get the Extended RCODE from.
+ *
+ * \return Extended RCODE.
+ */
+uint8_t knot_edns_get_ext_rcode(const knot_rrset_t *opt_rr);
+
+/*!
+ * \brief Concatenates OPT RR Extended RCODE field and normal RCODE to get the
+ * whole Extended RCODE.
+ *
+ * Extended RCODE is created by using the Extended RCODE field from OPT RR as
+ * higher 8 bits and the RCODE from DNS Header as the lower 4 bits, resulting
+ * in a 12-bit unsigned integer. (See RFC 6891, Section 6.1.3).
+ *
+ * \param ext_rcode Extended RCODE field from OPT RR.
+ * \param rcode RCODE from DNS Header.
+ *
+ * \return 12-bit Extended RCODE.
+ */
+static inline
+uint16_t knot_edns_whole_rcode(uint8_t ext_rcode, uint8_t rcode)
+{
+ uint16_t high = ext_rcode;
+ return (high << 4) | rcode;
+}
+
+/*!
+ * \brief Sets the Extended RCODE field in the OPT RR.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ * \note There is an assert() for debug checking of the parameter.
+ *
+ * \param opt_rr OPT RR to set the Extended RCODE to.
+ * \param ext_rcode Extended RCODE to set.
+ */
+void knot_edns_set_ext_rcode(knot_rrset_t *opt_rr, uint8_t ext_rcode);
+
+/*!
+ * \brief Sets the Extended RCODE field in OPT RR wire.
+ *
+ * \param opt_rr Position of the OPT RR in packet.
+ * \param ext_rcode Higher 8 bits of Extended RCODE.
+ */
+static inline
+void knot_edns_set_ext_rcode_wire(uint8_t *opt_rr, uint8_t ext_rcode)
+{
+ assert(opt_rr != NULL);
+ *(opt_rr + KNOT_EDNS_EXT_RCODE_POS) = ext_rcode;
+}
+
+/*!
+ * \brief Returns the EDNS version stored in the OPT RR.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ * \note There is an assert() for debug checking of the parameter.
+ *
+ * \param opt_rr OPT RR to get the EDNS version from.
+ *
+ * \return EDNS version.
+ */
+uint8_t knot_edns_get_version(const knot_rrset_t *opt_rr);
+
+/*!
+ * \brief Sets the EDNS version field in the OPT RR.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ * \note There is an assert() for debug checking of the parameter.
+ *
+ * \param opt_rr OPT RR to set the EDNS version to.
+ * \param version EDNS version to set.
+ */
+void knot_edns_set_version(knot_rrset_t *opt_rr, uint8_t version);
+
+/*!
+ * \brief Returns the state of the DO bit in the OPT RR flags.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ *
+ * \param opt_rr OPT RR to get the DO bit from.
+ *
+ * \return true if the DO bit is set.
+ * \return false if the DO bit is not set.
+ */
+static inline
+bool knot_edns_do(const knot_rrset_t *opt_rr)
+{
+ assert(opt_rr != NULL);
+ return opt_rr->ttl & KNOT_EDNS_DO_MASK;
+}
+
+/*!
+ * \brief Sets the DO bit in the OPT RR.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ *
+ * \param opt_rr OPT RR to set the DO bit in.
+ */
+static inline
+void knot_edns_set_do(knot_rrset_t *opt_rr)
+{
+ assert(opt_rr != NULL);
+ opt_rr->ttl |= KNOT_EDNS_DO_MASK;
+}
+
+/*!
+ * \brief Add EDNS option into the package with empty (zeroed) content.
+ *
+ * \param[in] opt_rr OPT RR structure to reserve the option in.
+ * \param[in] code Option code.
+ * \param[in] size Desired option size.
+ * \param[out] wire_ptr Pointer to reserved option data (can be NULL).
+ * \param[in] mm Memory context.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_edns_reserve_option(knot_rrset_t *opt_rr, uint16_t code,
+ uint16_t size, uint8_t **wire_ptr, knot_mm_t *mm);
+
+/*!
+ * \brief Adds EDNS Option to the OPT RR.
+ *
+ * \note The function now supports adding empty OPTION (just having its code).
+ *
+ * \param opt_rr OPT RR structure to add the Option to.
+ * \param code Option code.
+ * \param size Option data length in bytes.
+ * \param data Option data.
+ * \param mm Memory context.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_ENOMEM
+ */
+int knot_edns_add_option(knot_rrset_t *opt_rr, uint16_t code,
+ uint16_t size, const uint8_t *data, knot_mm_t *mm);
+
+/*!
+ * \brief Searches the OPT RR for option with the specified code.
+ *
+ * \param opt_rr OPT RR structure to search in.
+ * \param code Option code to search for.
+ * \param previous (Optional) Previously returned option to start searching from,
+ * to be able to return another option with the same code.
+ *
+ * \retval pointer to option if found
+ * \retval NULL otherwise.
+ */
+uint8_t *knot_edns_get_option(const knot_rrset_t *opt_rr, uint16_t code,
+ const uint8_t *previous);
+
+/*!
+ * \brief Pointers to every option in the OPT RR wire.
+ */
+typedef struct {
+ uint8_t *ptr[KNOT_EDNS_MAX_OPTION_CODE + 1];
+} knot_edns_options_t;
+
+/*!
+ * \brief Initializes pointers to options in a given OPT RR.
+ *
+ * \note If the OPT RR has no options, the output is NULL.
+ *
+ * \param opt_rr OPT RR structure to be used.
+ * \param out Structure to be initialized.
+ * \param mm Memory context.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_edns_get_options(knot_rrset_t *opt_rr, knot_edns_options_t **out,
+ knot_mm_t *mm);
+
+/*!
+ * \brief Returns the option code.
+ *
+ * \param opt EDNS option (including code, length and data portion).
+ *
+ * \return EDNS option code
+ */
+static inline uint16_t knot_edns_opt_get_code(const uint8_t *opt)
+{
+ assert(opt != NULL);
+ return knot_wire_read_u16(opt);
+}
+
+/*!
+ * \brief Returns the option data length.
+ *
+ * \param opt EDNS option (including code, length and data portion).
+ *
+ * \return EDNS option length
+ */
+static inline uint16_t knot_edns_opt_get_length(const uint8_t *opt)
+{
+ assert(opt != NULL);
+ return knot_wire_read_u16(opt + sizeof(uint16_t));
+}
+
+/*!
+ * \brief Returns pointer to option data.
+ *
+ * \warning No safety checks are performed on the supplied data.
+ *
+ * \param opt EDNS option (including code, length and data portion).
+ *
+ * \retval pointer to place where ENDS option data would reside
+ */
+static inline uint8_t *knot_edns_opt_get_data(uint8_t *opt)
+{
+ return opt + KNOT_EDNS_OPTION_HDRLEN;
+}
+
+/*!
+ * \brief Computes additional Padding data length for required packet alignment.
+ *
+ * \param current_pkt_size Current packet size.
+ * \param current_opt_size Current OPT rrset size (OPT must be used).
+ * \param block_size Required packet block length (must be non-zero).
+ *
+ * \return Required padding length or -1 if padding not required.
+ */
+int knot_edns_alignment_size(size_t current_pkt_size,
+ size_t current_opt_size,
+ size_t block_size);
+
+/*!
+ * \brief EDNS Client Subnet content.
+ *
+ * \see draft-ietf-dnsop-edns-client-subnet
+ */
+typedef struct {
+ /*! \brief FAMILY */
+ uint16_t family;
+ /*! \brief SOURCE PREFIX-LENGTH */
+ uint8_t source_len;
+ /*! \brief SCOPE PREFIX-LENGTH */
+ uint8_t scope_len;
+ /*! \brief ADDRESS */
+ uint8_t address[KNOT_EDNS_CLIENT_SUBNET_ADDRESS_MAXLEN];
+} knot_edns_client_subnet_t;
+
+/*!
+ * \brief Get the wire size of the EDNS Client Subnet option.
+ *
+ * \param ecs EDNS Client Subnet data.
+ *
+ * \return Size of the EDNS option data.
+ */
+uint16_t knot_edns_client_subnet_size(const knot_edns_client_subnet_t *ecs);
+
+/*!
+ * \brief Write EDNS Client Subnet data from the ECS structure to wire.
+ *
+ * \param option EDNS option data buffer.
+ * \param option_len EDNS option data buffer size.
+ * \param ecs EDNS Client Subnet data.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_edns_client_subnet_write(uint8_t *option, uint16_t option_len,
+ const knot_edns_client_subnet_t *ecs);
+
+/*!
+ * \brief Parse EDNS Client Subnet data from wire to the ECS structure.
+ *
+ * \param[out] ecs EDNS Client Subnet data.
+ * \param[in] option EDNS option data.
+ * \param[in] option_len EDNS option size.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_edns_client_subnet_parse(knot_edns_client_subnet_t *ecs,
+ const uint8_t *option, uint16_t option_len);
+
+/*!
+ * \brief Set address to the ECS structure.
+ *
+ * \note It also resets the lengths.
+ *
+ * \param ecs ECS structure to set address into.
+ * \param addr Address to be set.
+ *
+ * \return Error code. KNOT_EOK if successful.
+ */
+int knot_edns_client_subnet_set_addr(knot_edns_client_subnet_t *ecs,
+ const struct sockaddr_storage *addr);
+
+/*!
+ * \brief Get address from the ECS structure.
+ *
+ * Only the family and raw address is set in the structure. The bits not
+ * covered by the prefix length are cleared.
+ *
+ * \param addr Address to be set.
+ * \param ecs ECS structure to retrieve address from.
+ */
+int knot_edns_client_subnet_get_addr(struct sockaddr_storage *addr,
+ const knot_edns_client_subnet_t *ecs);
+
+/*!
+ * \brief Get size of the EDNS Keepalive option wire size.
+ *
+ * \param[in] timeout EDNS TCP Keepalive timeout.
+ *
+ * \return Size of the EDNS option data.
+ */
+uint16_t knot_edns_keepalive_size(uint16_t timeout);
+
+/*!
+ * \brief Writes EDNS TCP Keepalive wire data.
+ *
+ * \param[out] option EDNS option data buffer.
+ * \param[in] option_len EDNS option data buffer size.
+ * \param[in] timeout EDNS TCP Keepalive timeout.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_edns_keepalive_write(uint8_t *option, uint16_t option_len, uint16_t timeout);
+
+/*!
+ * \brief Parses EDNS TCP Keepalive wire data.
+ *
+ * \param[out] timeout EDNS TCP Keepalive timeout.
+ * \param[in] option EDNS option data.
+ * \param[in] option_len EDNS option size.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_edns_keepalive_parse(uint16_t *timeout, const uint8_t *option,
+ uint16_t option_len);
+
+/*!
+ * \brief Get size of the EDNS Chain option wire size.
+ *
+ * \param[in] point EDNS Chain closest trusted point.
+ *
+ * \return Size of the EDNS option data or 0 if invalid input.
+ */
+uint16_t knot_edns_chain_size(const knot_dname_t *point);
+
+/*!
+ * \brief Writes EDNS Chain wire data.
+ *
+ * \param[out] option EDNS option data buffer.
+ * \param[in] option_len EDNS option data buffer size.
+ * \param[in] point EDNS Chain closest trusted point.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_edns_chain_write(uint8_t *option, uint16_t option_len,
+ const knot_dname_t *point);
+
+/*!
+ * \brief Parses EDNS Chain wire data.
+ *
+ * \param[out] point EDNS Chain closest trusted point.
+ * \param[in] option EDNS option data.
+ * \param[in] option_len EDNS option size.
+ * \param[in] mm Memory context.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_edns_chain_parse(knot_dname_t **point, const uint8_t *option,
+ uint16_t option_len, knot_mm_t *mm);
+
+/*!
+ * \brief DNS Cookie content.
+ */
+typedef struct {
+ uint8_t data[KNOT_EDNS_COOKIE_SRVR_MAX_SIZE]; /*!< Cookie data. */
+ uint16_t len; /*!< Cookie length. */
+} knot_edns_cookie_t;
+
+/*!
+ * \brief Get size of the EDNS Cookie option wire size.
+ *
+ * \param[in] cc Client cookie.
+ * \param[in] sc Server cookie (can be NULL).
+ *
+ * \return Size of the EDNS option data or 0 if invalid input.
+ */
+uint16_t knot_edns_cookie_size(const knot_edns_cookie_t *cc,
+ const knot_edns_cookie_t *sc);
+
+/*!
+ * \brief Writes EDNS cookie wire data.
+ *
+ * \param[out] option EDNS option data buffer.
+ * \param[in] option_len EDNS option data buffer size.
+ * \param[in] cc EDNS client cookie.
+ * \param[in] sc EDNS server cookie (can be NULL).
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_edns_cookie_write(uint8_t *option, uint16_t option_len,
+ const knot_edns_cookie_t *cc,
+ const knot_edns_cookie_t *sc);
+
+/*!
+ * \brief Parses EDNS Cookie wire data.
+ *
+ * \param[out] cc EDNS client cookie.
+ * \param[out] sc EDNS server cookie.
+ * \param[in] option EDNS option data.
+ * \param[in] option_len EDNS option size.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_edns_cookie_parse(knot_edns_cookie_t *cc, knot_edns_cookie_t *sc,
+ const uint8_t *option, uint16_t option_len);
+
+/*! @} */
diff --git a/src/libknot/rrtype/rdname.h b/src/libknot/rrtype/rdname.h
new file mode 100644
index 0000000..d2be870
--- /dev/null
+++ b/src/libknot/rrtype/rdname.h
@@ -0,0 +1,98 @@
+/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup rrtype
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/descriptor.h"
+#include "libknot/dname.h"
+#include "libknot/rdata.h"
+#include "libknot/rrtype/svcb.h"
+
+static inline
+const knot_dname_t *knot_cname_name(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data;
+}
+
+static inline
+const knot_dname_t *knot_dname_target(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data;
+}
+
+static inline
+const knot_dname_t *knot_ns_name(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data;
+}
+
+static inline
+const knot_dname_t *knot_ptr_name(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data;
+}
+
+static inline
+const knot_dname_t *knot_mx_name(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data + 2;
+}
+
+static inline
+const knot_dname_t *knot_srv_name(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data + 6;
+}
+
+static inline
+const knot_dname_t *knot_rdata_name(const knot_rdata_t *rdata, uint16_t type)
+{
+ assert(rdata);
+ switch (type) {
+ case KNOT_RRTYPE_NS:
+ return knot_ns_name(rdata);
+ case KNOT_RRTYPE_PTR:
+ return knot_ptr_name(rdata);
+ case KNOT_RRTYPE_MX:
+ return knot_mx_name(rdata);
+ case KNOT_RRTYPE_SRV:
+ return knot_srv_name(rdata);
+ case KNOT_RRTYPE_CNAME:
+ return knot_cname_name(rdata);
+ case KNOT_RRTYPE_DNAME:
+ return knot_dname_target(rdata);
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ return knot_svcb_target(rdata);
+ }
+
+ return NULL;
+}
+
+/*! @} */
diff --git a/src/libknot/rrtype/rrsig.h b/src/libknot/rrtype/rrsig.h
new file mode 100644
index 0000000..5a3643d
--- /dev/null
+++ b/src/libknot/rrtype/rrsig.h
@@ -0,0 +1,100 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup rrtype
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/dname.h"
+#include "libknot/rdata.h"
+#include "libknot/wire.h"
+
+static inline
+uint16_t knot_rrsig_type_covered(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u16(rdata->data);
+}
+
+static inline
+uint8_t knot_rrsig_alg(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data + 2);
+}
+
+static inline
+uint8_t knot_rrsig_labels(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data + 3);
+}
+
+static inline
+uint32_t knot_rrsig_original_ttl(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u32(rdata->data + 4);
+}
+
+static inline
+uint32_t knot_rrsig_sig_expiration(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u32(rdata->data + 8);
+}
+
+static inline
+uint32_t knot_rrsig_sig_inception(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u32(rdata->data + 12);
+}
+
+static inline
+uint16_t knot_rrsig_key_tag(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u16(rdata->data + 16);
+}
+
+static inline
+const knot_dname_t *knot_rrsig_signer_name(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data + 18;
+}
+
+static inline
+uint16_t knot_rrsig_signature_len(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->len - 18 - knot_dname_size(knot_rrsig_signer_name(rdata));
+}
+
+static inline
+const uint8_t *knot_rrsig_signature(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data + 18 + knot_dname_size(knot_rrsig_signer_name(rdata));
+}
+
+/*! @} */
diff --git a/src/libknot/rrtype/soa.h b/src/libknot/rrtype/soa.h
new file mode 100644
index 0000000..9449f00
--- /dev/null
+++ b/src/libknot/rrtype/soa.h
@@ -0,0 +1,94 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup rrtype
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/dname.h"
+#include "libknot/rdata.h"
+#include "libknot/wire.h"
+
+static inline
+const knot_dname_t *knot_soa_primary(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data;
+}
+
+static inline
+const knot_dname_t *knot_soa_mailbox(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data + knot_dname_size(knot_soa_primary(rdata));
+}
+
+static inline
+size_t knot_soa_names_len(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_dname_size(knot_soa_primary(rdata)) +
+ knot_dname_size(knot_soa_mailbox(rdata));
+}
+
+static inline
+uint32_t knot_soa_serial(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u32(rdata->data + knot_soa_names_len(rdata));
+}
+
+static inline
+void knot_soa_serial_set(knot_rdata_t *rdata, uint32_t serial)
+{
+ assert(rdata);
+ knot_wire_write_u32(rdata->data + knot_soa_names_len(rdata), serial);
+}
+
+static inline
+uint32_t knot_soa_refresh(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u32(rdata->data + knot_soa_names_len(rdata) + 4);
+}
+
+static inline
+uint32_t knot_soa_retry(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u32(rdata->data + knot_soa_names_len(rdata) + 8);
+}
+
+static inline
+uint32_t knot_soa_expire(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u32(rdata->data + knot_soa_names_len(rdata) + 12);
+}
+
+static inline
+uint32_t knot_soa_minimum(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u32(rdata->data + knot_soa_names_len(rdata) + 16);
+}
+
+/*! @} */
diff --git a/src/libknot/rrtype/svcb.h b/src/libknot/rrtype/svcb.h
new file mode 100644
index 0000000..abee597
--- /dev/null
+++ b/src/libknot/rrtype/svcb.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup rrtype
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/dname.h"
+#include "libknot/rdata.h"
+#include "libknot/wire.h"
+
+static inline
+uint32_t knot_svcb_priority(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u16(rdata->data);
+}
+
+static inline
+const knot_dname_t *knot_svcb_target(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data + 2;
+}
+
+/*! @} */
diff --git a/src/libknot/rrtype/tsig.c b/src/libknot/rrtype/tsig.c
new file mode 100644
index 0000000..83f8436
--- /dev/null
+++ b/src/libknot/rrtype/tsig.c
@@ -0,0 +1,409 @@
+/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <time.h>
+
+#include "libdnssec/tsig.h"
+#include "libknot/attribute.h"
+#include "libknot/rrtype/tsig.h"
+#include "libknot/consts.h"
+#include "libknot/dname.h"
+#include "libknot/errcode.h"
+#include "libknot/rrset.h"
+#include "libknot/wire.h"
+#include "contrib/wire_ctx.h"
+
+/*! \brief TSIG field offsets. */
+typedef enum tsig_off_t {
+ TSIG_ALGNAME_O = 0,
+ TSIG_TSIGNED_O,
+ TSIG_FUDGE_O,
+ TSIG_MACLEN_O,
+ TSIG_MAC_O,
+ TSIG_ORIGID_O,
+ TSIG_ERROR_O,
+ TSIG_OLEN_O,
+ TSIG_OTHER_O
+} tsig_off_t;
+
+/* Helpers for RDATA offset calculation. */
+#define TSIG_OFF_MACLEN (4 * sizeof(uint16_t))
+#define TSIG_FIXED_RDLEN (8 * sizeof(uint16_t))
+#define TSIG_OTHER_MAXLEN (3 * sizeof(uint16_t))
+
+/*!
+ * \brief Seek offset of a TSIG RR field.
+ *
+ * \param rr TSIG RR.
+ * \param id Field index.
+ * \param nb Required number of bytes after the offset (for boundaries check).
+ * \return pointer to field on wire or NULL.
+ */
+static uint8_t* rdata_seek(const knot_rrset_t *rr, tsig_off_t id, size_t nb)
+{
+ const knot_rdata_t *rr_data = knot_rdataset_at(&rr->rrs, 0);
+ if (!rr_data || rr_data->len == 0) {
+ return NULL;
+ }
+
+ wire_ctx_t wire = wire_ctx_init_const(rr_data->data, rr_data->len);
+
+ /* TSIG RR names should be already sanitized on parse. */
+ size_t alg_len = knot_dname_size(wire.wire);
+
+ /* Not pretty, but fast. */
+ switch (id) {
+ case TSIG_ALGNAME_O: break;
+ case TSIG_TSIGNED_O:
+ wire_ctx_skip(&wire, alg_len); break;
+ case TSIG_FUDGE_O:
+ wire_ctx_skip(&wire, alg_len + 3 * sizeof(uint16_t));
+ break;
+ case TSIG_MACLEN_O:
+ wire_ctx_skip(&wire, alg_len + 4 * sizeof(uint16_t));
+ break;
+ case TSIG_MAC_O:
+ wire_ctx_skip(&wire, alg_len + 5 * sizeof(uint16_t));
+ break;
+ case TSIG_ORIGID_O:
+ wire_ctx_skip(&wire, alg_len + 4 * sizeof(uint16_t));
+ wire_ctx_skip(&wire, wire_ctx_read_u16(&wire));
+ break;
+ case TSIG_ERROR_O:
+ wire_ctx_skip(&wire, alg_len + 4 * sizeof(uint16_t));
+ wire_ctx_skip(&wire, wire_ctx_read_u16(&wire));
+ wire_ctx_skip(&wire, sizeof(uint16_t));
+ break;
+ case TSIG_OLEN_O:
+ wire_ctx_skip(&wire, alg_len + 4 * sizeof(uint16_t));
+ wire_ctx_skip(&wire, wire_ctx_read_u16(&wire));
+ wire_ctx_skip(&wire, 2 * sizeof(uint16_t));
+ break;
+ case TSIG_OTHER_O:
+ wire_ctx_skip(&wire, alg_len + 4 * sizeof(uint16_t));
+ wire_ctx_skip(&wire, wire_ctx_read_u16(&wire));
+ wire_ctx_skip(&wire, 2 * sizeof(uint16_t));
+ assert(nb == 0);
+ nb = wire_ctx_read_u16(&wire);
+ if (wire_ctx_available(&wire) != nb) {
+ return NULL;
+ }
+ break;
+ }
+
+ if (wire.error != KNOT_EOK) {
+ return NULL;
+ }
+
+ /* Check remaining bytes. */
+
+ if (wire_ctx_available(&wire) < nb){
+ return NULL;
+ }
+
+ return wire.position;
+}
+
+static int rdata_set_tsig_error(knot_rrset_t *tsig, uint16_t tsig_error)
+{
+ uint8_t *rd = rdata_seek(tsig, TSIG_ERROR_O, sizeof(uint16_t));
+ if (!rd) {
+ return KNOT_ERROR;
+ }
+
+ knot_wire_write_u16(rd, tsig_error);
+ return KNOT_EOK;
+}
+
+_public_
+int knot_tsig_create_rdata(knot_rrset_t *rr, const knot_dname_t *alg,
+ uint16_t maclen, uint16_t tsig_err)
+{
+ if (rr == NULL || alg == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ size_t alg_len = knot_dname_size(alg);
+ size_t rdlen = alg_len + TSIG_FIXED_RDLEN + maclen;
+ if (tsig_err == KNOT_RCODE_BADTIME) {
+ rdlen += TSIG_OTHER_MAXLEN;
+ }
+ uint8_t rd[rdlen];
+ memset(rd, 0, rdlen);
+
+ /* Copy alg name. */
+ knot_dname_to_wire(rd, alg, rdlen);
+
+ /* Set MAC variable length in advance. */
+ size_t offset = alg_len + TSIG_OFF_MACLEN;
+ knot_wire_write_u16(rd + offset, maclen);
+
+ int ret = knot_rrset_add_rdata(rr, rd, rdlen, NULL);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /* Set error. */
+ rdata_set_tsig_error(rr, tsig_err);
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_tsig_rdata_set_time_signed(knot_rrset_t *tsig, uint64_t time)
+{
+ uint8_t *rd = rdata_seek(tsig, TSIG_TSIGNED_O, 3*sizeof(uint16_t));
+ if (!rd) {
+ return KNOT_ERROR;
+ }
+
+ knot_wire_write_u48(rd, time);
+ return KNOT_EOK;
+}
+
+_public_
+int knot_tsig_rdata_set_fudge(knot_rrset_t *tsig, uint16_t fudge)
+{
+ uint8_t *rd = rdata_seek(tsig, TSIG_FUDGE_O, sizeof(uint16_t));
+ if (!rd) {
+ return KNOT_ERROR;
+ }
+
+ knot_wire_write_u16(rd, fudge);
+ return KNOT_EOK;
+}
+
+_public_
+int knot_tsig_rdata_set_mac(knot_rrset_t *tsig, uint16_t length, const uint8_t *mac)
+{
+ uint8_t *rd = rdata_seek(tsig, TSIG_MAC_O, length);
+ if (!rd) {
+ return KNOT_ERROR;
+ }
+
+ /*! \note Cannot change length, as rdata is already preallocd. */
+
+ /* Copy the actual MAC. */
+ memcpy(rd, mac, length);
+ return KNOT_EOK;
+}
+
+_public_
+int knot_tsig_rdata_set_orig_id(knot_rrset_t *tsig, uint16_t id)
+{
+ uint8_t *rd = rdata_seek(tsig, TSIG_ORIGID_O, sizeof(uint16_t));
+ if (!rd) {
+ return KNOT_ERROR;
+ }
+
+ /* Write the length - 2. */
+ knot_wire_write_u16(rd, id);
+ return KNOT_EOK;
+}
+
+_public_
+int knot_tsig_rdata_set_other_data(knot_rrset_t *tsig, uint16_t len,
+ const uint8_t *other_data)
+{
+ if (len > TSIG_OTHER_MAXLEN) {
+ return KNOT_EINVAL;
+ }
+
+ uint8_t *rd = rdata_seek(tsig, TSIG_OLEN_O, len + sizeof(uint16_t));
+ if (!rd) {
+ return KNOT_ERROR;
+ }
+
+ /* Write the length. */
+ knot_wire_write_u16(rd, len);
+
+ /* Copy the actual data. */
+ if (len > 0) {
+ memcpy(rd + sizeof(uint16_t), other_data, len);
+ }
+ return KNOT_EOK;
+}
+
+_public_
+const knot_dname_t *knot_tsig_rdata_alg_name(const knot_rrset_t *tsig)
+{
+ return knot_rdataset_at(&tsig->rrs, 0)->data;
+}
+
+_public_
+dnssec_tsig_algorithm_t knot_tsig_rdata_alg(const knot_rrset_t *tsig)
+{
+ /* Get the algorithm name. */
+ const knot_dname_t *alg_name = knot_tsig_rdata_alg_name(tsig);
+ if (!alg_name) {
+ return DNSSEC_TSIG_UNKNOWN;
+ }
+
+ return dnssec_tsig_algorithm_from_dname(alg_name);
+}
+
+_public_
+uint64_t knot_tsig_rdata_time_signed(const knot_rrset_t *tsig)
+{
+ /*! \todo How to return invalid value? */
+ uint8_t *rd = rdata_seek(tsig, TSIG_TSIGNED_O, 3*sizeof(uint16_t));
+ if (!rd) {
+ return 0;
+ }
+ return knot_wire_read_u48(rd);
+}
+
+_public_
+uint16_t knot_tsig_rdata_fudge(const knot_rrset_t *tsig)
+{
+ uint8_t *rd = rdata_seek(tsig, TSIG_FUDGE_O, sizeof(uint16_t));
+ if (!rd) {
+ return 0;
+ }
+ return knot_wire_read_u16(rd);
+}
+
+_public_
+const uint8_t *knot_tsig_rdata_mac(const knot_rrset_t *tsig)
+{
+ uint8_t *rd = rdata_seek(tsig, TSIG_MAC_O, 0);
+ if (!rd) {
+ return NULL;
+ }
+ return rd;
+}
+
+_public_
+size_t knot_tsig_rdata_mac_length(const knot_rrset_t *tsig)
+{
+ uint8_t *rd = rdata_seek(tsig, TSIG_MACLEN_O, sizeof(uint16_t));
+ if (!rd) {
+ return 0;
+ }
+ return knot_wire_read_u16(rd);
+}
+
+_public_
+uint16_t knot_tsig_rdata_orig_id(const knot_rrset_t *tsig)
+{
+ uint8_t *rd = rdata_seek(tsig, TSIG_ORIGID_O, sizeof(uint16_t));
+ if (!rd) {
+ return 0;
+ }
+ return knot_wire_read_u16(rd);
+}
+
+_public_
+uint16_t knot_tsig_rdata_error(const knot_rrset_t *tsig)
+{
+ uint8_t *rd = rdata_seek(tsig, TSIG_ERROR_O, sizeof(uint16_t));
+ if (!rd) {
+ return 0;
+ }
+ return knot_wire_read_u16(rd);
+}
+
+_public_
+const uint8_t *knot_tsig_rdata_other_data(const knot_rrset_t *tsig)
+{
+ uint8_t *rd = rdata_seek(tsig, TSIG_OTHER_O, 0);
+ if (!rd) {
+ return NULL;
+ }
+ return rd;
+}
+
+_public_
+uint16_t knot_tsig_rdata_other_data_length(const knot_rrset_t *tsig)
+{
+ uint8_t *rd = rdata_seek(tsig, TSIG_OLEN_O, sizeof(uint16_t));
+ if (!rd) {
+ return 0;
+ }
+ return knot_wire_read_u16(rd);
+}
+
+_public_
+size_t knot_tsig_rdata_tsig_variables_length(const knot_rrset_t *tsig)
+{
+ if (tsig == NULL) {
+ return 0;
+ }
+ /* Key name, Algorithm name and Other data have variable lengths. */
+ const knot_dname_t *key_name = tsig->owner;
+ if (!key_name) {
+ return 0;
+ }
+
+ const knot_dname_t *alg_name = knot_tsig_rdata_alg_name(tsig);
+ if (!alg_name) {
+ return 0;
+ }
+
+ uint16_t other_data_length = knot_tsig_rdata_other_data_length(tsig);
+
+ return knot_dname_size(key_name) + knot_dname_size(alg_name) +
+ other_data_length + KNOT_TSIG_VARIABLES_LENGTH;
+}
+
+_public_
+size_t knot_tsig_rdata_tsig_timers_length(void)
+{
+ /*! \todo Cleanup */
+ return KNOT_TSIG_TIMERS_LENGTH;
+}
+
+_public_
+size_t knot_tsig_wire_size(const knot_tsig_key_t *key)
+{
+ if (key == NULL || key->name == NULL) {
+ return 0;
+ }
+
+ return knot_dname_size(key->name) + TSIG_FIXED_RDLEN +
+ sizeof(uint16_t) + /* TYPE */
+ sizeof(uint16_t) + /* CLASS */
+ sizeof(uint32_t) + /* TTL */
+ sizeof(uint16_t) + /* RDATA length. */
+ knot_dname_size(dnssec_tsig_algorithm_to_dname(key->algorithm)) +
+ dnssec_tsig_algorithm_size(key->algorithm); /* MAC length. */
+}
+
+_public_
+size_t knot_tsig_wire_maxsize(const knot_tsig_key_t *key)
+{
+ size_t size = knot_tsig_wire_size(key);
+ if (size == 0) {
+ return 0;
+ }
+
+ /* In case of BADTIME other data. */
+ return size + TSIG_OTHER_MAXLEN;
+}
+
+_public_
+bool knot_tsig_rdata_is_ok(const knot_rrset_t *tsig)
+{
+ return (tsig != NULL
+ && knot_rdataset_at(&tsig->rrs, 0) != NULL
+ && rdata_seek(tsig, TSIG_OTHER_O, 0) != NULL
+ && knot_tsig_rdata_alg_name(tsig) != NULL);
+}
diff --git a/src/libknot/rrtype/tsig.h b/src/libknot/rrtype/tsig.h
new file mode 100644
index 0000000..083a063
--- /dev/null
+++ b/src/libknot/rrtype/tsig.h
@@ -0,0 +1,122 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief TSIG manipulation.
+ *
+ * \addtogroup rrtype
+ * @{
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include "libdnssec/binary.h"
+#include "libdnssec/tsig.h"
+#include "libknot/consts.h"
+#include "libknot/rrset.h"
+#include "libknot/tsig.h"
+
+enum tsig_consts {
+ KNOT_TSIG_ITEM_COUNT = 7,
+ KNOT_TSIG_VARIABLES_LENGTH = sizeof(uint16_t) // class
+ + sizeof(uint32_t) // ttl
+ + 6 // time signed
+ + sizeof(uint16_t) // fudge
+ + sizeof(uint16_t) // error
+ + sizeof(uint16_t),// other data length
+ KNOT_TSIG_TIMERS_LENGTH = sizeof(uint16_t) //fudge
+ + 6 // time signed
+};
+
+/*!
+ * \brief Create TSIG RDATA.
+ *
+ * \param rr TSIG RR to contain created data.
+ * \param alg Algorithm name.
+ * \param maclen Algorithm MAC len (may be set to 0 for empty MAC).
+ * \param tsig_err TSIG error code.
+ *
+ * \retval KNOT_EINVAL
+ * \retval KNOT_EOK
+ */
+int knot_tsig_create_rdata(knot_rrset_t *rr, const knot_dname_t *alg,
+ uint16_t maclen, uint16_t tsig_err);
+
+int knot_tsig_rdata_set_time_signed(knot_rrset_t *tsig, uint64_t time);
+
+int knot_tsig_rdata_store_current_time(knot_rrset_t *tsig);
+
+int knot_tsig_rdata_set_fudge(knot_rrset_t *tsig, uint16_t fudge);
+
+int knot_tsig_rdata_set_mac(knot_rrset_t *tsig, uint16_t length, const uint8_t *mac);
+
+int knot_tsig_rdata_set_orig_id(knot_rrset_t *tsig, uint16_t id);
+
+int knot_tsig_rdata_set_other_data(knot_rrset_t *tsig, uint16_t length,
+ const uint8_t *other_data);
+
+const knot_dname_t *knot_tsig_rdata_alg_name(const knot_rrset_t *tsig);
+
+dnssec_tsig_algorithm_t knot_tsig_rdata_alg(const knot_rrset_t *tsig);
+
+uint64_t knot_tsig_rdata_time_signed(const knot_rrset_t *tsig);
+
+uint16_t knot_tsig_rdata_fudge(const knot_rrset_t *tsig);
+
+const uint8_t *knot_tsig_rdata_mac(const knot_rrset_t *tsig);
+
+size_t knot_tsig_rdata_mac_length(const knot_rrset_t *tsig);
+
+uint16_t knot_tsig_rdata_orig_id(const knot_rrset_t *tsig);
+
+uint16_t knot_tsig_rdata_error(const knot_rrset_t *tsig);
+
+const uint8_t *knot_tsig_rdata_other_data(const knot_rrset_t *tsig);
+
+uint16_t knot_tsig_rdata_other_data_length(const knot_rrset_t *tsig);
+
+size_t knot_tsig_rdata_tsig_variables_length(const knot_rrset_t *tsig);
+
+size_t knot_tsig_rdata_tsig_timers_length(void);
+
+/*!
+ * \brief Return standard TSIG RRSET wire size for given algorithm.
+ *
+ * \param key Signing key descriptor.
+ *
+ * \return RRSET wire size.
+ */
+size_t knot_tsig_wire_size(const knot_tsig_key_t *key);
+
+/*!
+ * \brief Return TSIG RRSET maximum wire size for given algorithm.
+ *
+ * This size is reached if error reply with BADTIME.
+ *
+ * \param key Signing key descriptor.
+ *
+ * \return RRSET wire size.
+ */
+size_t knot_tsig_wire_maxsize(const knot_tsig_key_t *key);
+
+/*! \todo Documentation. */
+bool knot_tsig_rdata_is_ok(const knot_rrset_t *tsig);
+
+/*! @} */
diff --git a/src/libknot/rrtype/zonemd.h b/src/libknot/rrtype/zonemd.h
new file mode 100644
index 0000000..a4d734b
--- /dev/null
+++ b/src/libknot/rrtype/zonemd.h
@@ -0,0 +1,71 @@
+/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup rrtype
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/rdata.h"
+#include "libknot/wire.h"
+
+#define KNOT_ZONEMD_SCHEME_SIMPLE 1
+#define KNOT_ZONEMD_ALGORITHM_SHA384 1
+#define KNOT_ZONEMD_ALGORITHM_SHA512 2
+
+static inline
+uint32_t knot_zonemd_soa_serial(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u32(rdata->data);
+}
+
+static inline
+uint8_t knot_zonemd_scheme(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data + 4);
+}
+
+static inline
+uint8_t knot_zonemd_algorithm(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data + 5);
+}
+
+static inline
+size_t knot_zonemd_digest_size(const knot_rdata_t *rdata)
+{
+ switch (knot_zonemd_algorithm(rdata)) {
+ case KNOT_ZONEMD_ALGORITHM_SHA384: return 48;
+ case KNOT_ZONEMD_ALGORITHM_SHA512: return 64;
+ default: return 0;
+ }
+}
+
+static inline
+const uint8_t *knot_zonemd_digest(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data + 6;
+}
+
+/*! @} */