diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 00:53:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 00:53:35 +0000 |
commit | 69c6a41ffb878ef98c9378ed4b1634a404cfaa7f (patch) | |
tree | b2a4f704565d62fbb129ab9dc3b35977c50e6e7f /src/libknot/rrtype | |
parent | Initial commit. (diff) | |
download | knot-69c6a41ffb878ef98c9378ed4b1634a404cfaa7f.tar.xz knot-69c6a41ffb878ef98c9378ed4b1634a404cfaa7f.zip |
Adding upstream version 2.7.6.upstream/2.7.6upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/libknot/rrtype')
-rw-r--r-- | src/libknot/rrtype/dnskey.h | 71 | ||||
-rw-r--r-- | src/libknot/rrtype/ds.h | 63 | ||||
-rw-r--r-- | src/libknot/rrtype/naptr.c | 47 | ||||
-rw-r--r-- | src/libknot/rrtype/naptr.h | 40 | ||||
-rw-r--r-- | src/libknot/rrtype/nsec.h | 49 | ||||
-rw-r--r-- | src/libknot/rrtype/nsec3.h | 97 | ||||
-rw-r--r-- | src/libknot/rrtype/nsec3param.h | 63 | ||||
-rw-r--r-- | src/libknot/rrtype/opt.c | 675 | ||||
-rw-r--r-- | src/libknot/rrtype/opt.h | 574 | ||||
-rw-r--r-- | src/libknot/rrtype/rdname.h | 84 | ||||
-rw-r--r-- | src/libknot/rrtype/rrsig.h | 99 | ||||
-rw-r--r-- | src/libknot/rrtype/soa.h | 93 | ||||
-rw-r--r-- | src/libknot/rrtype/tsig.c | 404 | ||||
-rw-r--r-- | src/libknot/rrtype/tsig.h | 121 |
14 files changed, 2480 insertions, 0 deletions
diff --git a/src/libknot/rrtype/dnskey.h b/src/libknot/rrtype/dnskey.h new file mode 100644 index 0000000..ff6f4b9 --- /dev/null +++ b/src/libknot/rrtype/dnskey.h @@ -0,0 +1,71 @@ +/* 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 <http://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..d447929 --- /dev/null +++ b/src/libknot/rrtype/ds.h @@ -0,0 +1,63 @@ +/* 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 <http://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..ec513ca --- /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 <http://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..a067f64 --- /dev/null +++ b/src/libknot/rrtype/naptr.h @@ -0,0 +1,40 @@ +/* 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 <http://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..896ab3a --- /dev/null +++ b/src/libknot/rrtype/nsec.h @@ -0,0 +1,49 @@ +/* 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 <http://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..f69c774 --- /dev/null +++ b/src/libknot/rrtype/nsec3.h @@ -0,0 +1,97 @@ +/* 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 <http://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..239271a --- /dev/null +++ b/src/libknot/rrtype/nsec3param.h @@ -0,0 +1,63 @@ +/* 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 <http://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..3784858 --- /dev/null +++ b/src/libknot/rrtype/opt.c @@ -0,0 +1,675 @@ +/* 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 <http://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/rrtype/opt.h" +#include "libknot/packet/pkt.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; + } + + memcpy(wire, data, size); + + return KNOT_EOK; +} + +_public_ +uint8_t *knot_edns_get_option(const knot_rrset_t *opt_rr, uint16_t code) +{ + 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); + + 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. + * + * http://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_memset(dst, 0, 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..24d80f5 --- /dev/null +++ b/src/libknot/rrtype/opt.h @@ -0,0 +1,574 @@ +/* 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 <http://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_DEFALT = 128, + /*! \brief Default EDNS alignment size for a response. */ + KNOT_EDNS_ALIGNMENT_RESPONSE_DEFAULT = 468, + + /*! \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 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 Maximal currently relevant option code. */ + KNOT_EDNS_MAX_OPTION_CODE = 14, +}; + +/* 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. + * + * \retval pointer to option if found + * \retval NULL otherwise. + */ +uint8_t *knot_edns_get_option(const knot_rrset_t *opt_rr, uint16_t code); + +/*! + * \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..4354a6b --- /dev/null +++ b/src/libknot/rrtype/rdname.h @@ -0,0 +1,84 @@ +/* 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 <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \addtogroup rrtype + * @{ + */ + +#pragma once + +#include "libknot/descriptor.h" +#include "libknot/dname.h" +#include "libknot/rdata.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_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_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); + } + + return NULL; +} + +/*! @} */ diff --git a/src/libknot/rrtype/rrsig.h b/src/libknot/rrtype/rrsig.h new file mode 100644 index 0000000..3a4872f --- /dev/null +++ b/src/libknot/rrtype/rrsig.h @@ -0,0 +1,99 @@ +/* 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 <http://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..a7aebfc --- /dev/null +++ b/src/libknot/rrtype/soa.h @@ -0,0 +1,93 @@ +/* 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 <http://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/tsig.c b/src/libknot/rrtype/tsig.c new file mode 100644 index 0000000..f4b9b71 --- /dev/null +++ b/src/libknot/rrtype/tsig.c @@ -0,0 +1,404 @@ +/* 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 <http://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, 3 * sizeof(uint16_t)); + 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. */ + 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) +{ + /*! \todo Check size, needs to check variable-length fields. */ + 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 + && knot_tsig_rdata_time_signed(tsig) != 0); +} diff --git a/src/libknot/rrtype/tsig.h b/src/libknot/rrtype/tsig.h new file mode 100644 index 0000000..2dd3696 --- /dev/null +++ b/src/libknot/rrtype/tsig.h @@ -0,0 +1,121 @@ +/* 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 <http://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) //fugde + + 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); + +/*! @} */ |