diff --git a/Knot.files b/Knot.files index ead382f77cc630cd51b4a373840cad7c42f3e9f1..fb84e7e102e4687e1f25622306d647a2367fb8cc 100644 --- a/Knot.files +++ b/Knot.files @@ -229,6 +229,8 @@ src/libknot/packet/compr.c src/libknot/packet/compr.h src/libknot/packet/pkt.c src/libknot/packet/pkt.h +src/libknot/packet/rrset-wire.c +src/libknot/packet/rrset-wire.h src/libknot/packet/wire.h src/libknot/processing/process.c src/libknot/processing/process.h diff --git a/src/Makefile.am b/src/Makefile.am index c68efb99c5b7196be3c9fefea5adc95e72e5e748..81bbbe915d1d164a36a6f8fcb4829b88a0177893 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -171,6 +171,8 @@ libknot_la_SOURCES = \ libknot/packet/compr.h \ libknot/packet/pkt.c \ libknot/packet/pkt.h \ + libknot/packet/rrset-wire.c \ + libknot/packet/rrset-wire.h \ libknot/packet/wire.h \ libknot/processing/process.c \ libknot/processing/process.h \ diff --git a/src/libknot/dnssec/rrset-sign.c b/src/libknot/dnssec/rrset-sign.c index 425d61496e143f8fac8ad992bc35864dc8f6a298..c665cc0eaa446de57fecfd4ccaff37ddef4befa3 100644 --- a/src/libknot/dnssec/rrset-sign.c +++ b/src/libknot/dnssec/rrset-sign.c @@ -28,6 +28,7 @@ #include "libknot/packet/wire.h" #include "libknot/rrset.h" #include "libknot/rrtype/rrsig.h" +#include "libknot/packet/rrset-wire.h" #define RRSIG_RDATA_SIGNER_OFFSET 18 diff --git a/src/libknot/dnssec/sig0.c b/src/libknot/dnssec/sig0.c index 5a9342fc0f0dcbbc0488136707f5be2f40313048..f7c36691e13208c998ed2e193399f115b8e26929 100644 --- a/src/libknot/dnssec/sig0.c +++ b/src/libknot/dnssec/sig0.c @@ -22,6 +22,7 @@ #include "libknot/dnssec/sig0.h" #include "libknot/dnssec/sign.h" #include "libknot/packet/wire.h" +#include "libknot/packet/rrset-wire.h" /*! * \brief Lifetime fudge of the SIG(0) packets in seconds. diff --git a/src/libknot/packet/pkt.c b/src/libknot/packet/pkt.c index 7720010b6f3c427832fb360460d53eec1616cdfc..cb04b33c18776a016b1c37d2233611d8ebfaafc0 100644 --- a/src/libknot/packet/pkt.c +++ b/src/libknot/packet/pkt.c @@ -27,6 +27,7 @@ #include "libknot/packet/wire.h" #include "libknot/rrtype/tsig.h" #include "libknot/tsig-op.h" +#include "libknot/packet/rrset-wire.h" /*! \brief Scan packet for RRSet existence. */ static bool pkt_contains(const knot_pkt_t *packet, diff --git a/src/libknot/packet/pkt.h b/src/libknot/packet/pkt.h index 03a24b1b0bee72a20abd5ff5033df49ad9a8b482..42adf6df8e8368e61493fbb2059b112029fc12e1 100644 --- a/src/libknot/packet/pkt.h +++ b/src/libknot/packet/pkt.h @@ -1,7 +1,7 @@ /*! * \file pkt.h * - * \author Lubos Slovak <lubos.slovak@nic.cz> + * \author Marek Vavrusa <marek.vavrusa@nic.cz> * * \brief Structure for holding DNS packet data and metadata. * diff --git a/src/libknot/packet/rrset-wire.c b/src/libknot/packet/rrset-wire.c new file mode 100644 index 0000000000000000000000000000000000000000..8c4622b710e318838b3895b6c4e1cdd9918f1755 --- /dev/null +++ b/src/libknot/packet/rrset-wire.c @@ -0,0 +1,630 @@ +/* Copyright (C) 2014 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 <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#include "libknot/packet/rrset-wire.h" + +#include "libknot/consts.h" +#include "libknot/common.h" +#include "libknot/rrset.h" +#include "libknot/descriptor.h" +#include "libknot/packet/wire.h" +#include "libknot/packet/pkt.h" +#include "libknot/dname.h" + +/*! + * \brief Get maximal size of a domain name in a wire with given capacity. + */ +#define dname_max(wire_capacity) MIN(wire_capacity, KNOT_DNAME_MAXLEN) + +/*! + * \brief Get compression pointer for a given hint. + */ +static uint16_t compr_get_ptr(knot_compr_t *compr, int hint) +{ + if (compr == NULL) { + return 0; + } + + return compr->rrinfo->compress_ptr[hint]; +} + +/*! + * \brief Set compression pointer for a given hint. + */ +static void compr_set_ptr(knot_compr_t *compr, int hint, + const uint8_t *written_at, uint16_t written_size) +{ + if (compr == NULL) { + return; + } + + assert(written_at >= compr->wire); + + uint16_t offset = written_at - compr->wire; + + knot_pkt_compr_hint_set(compr->rrinfo, hint, offset, written_size); +} + +/*! + * \brief Write a fixed block of binary data to wire. + */ +static int write_rdata_fixed(const uint8_t **src, size_t *src_avail, + uint8_t **wire, size_t *capacity, + size_t size) +{ + assert(src && *src); + assert(src_avail); + assert(wire && *wire); + assert(capacity); + + /* Check input/output buffer boundaries */ + + if (size > *src_avail) { + return KNOT_EMALF; + } + + if (size > *capacity) { + return KNOT_ESPACE; + } + + /* Data binary copy */ + + memcpy(*wire, *src, size); + + /* Update buffers */ + + *src += size; + *src_avail -= size; + + *wire += size; + *capacity -= size; + + return KNOT_EOK; +} + +/*! + * \brief Count size of NAPTR RDATA header. + */ +static int naptr_header_size(const uint8_t **src, size_t *src_avail) +{ + assert(src && *src); + assert(src_avail); + + size_t size = 0; + + /* Fixed fields size (order, preference) */ + + size += 2 * sizeof(uint16_t); + + /* Variable fields size (flags, services, regexp) */ + + for (int i = 0; i < 3; i++) { + const uint8_t *len_ptr = *src + size; + if (len_ptr >= *src + *src_avail) { + return KNOT_EMALF; + } + + size += 1 + *len_ptr; + } + + return size; +} + +typedef int (*dname_callback_t)(const uint8_t **, size_t *, uint8_t **, + size_t *, knot_compr_t *, int, int, + knot_rrset_wire_flags_t, const uint8_t *); + +/*! + * \brief Generic function from processing RDATA with de/compression of dnames. + */ +static int traverse_rdata(const rdata_descriptor_t *desc, const uint8_t **src, + size_t *src_avail, uint8_t **wire, size_t *capacity, + knot_compr_t *compr, int compr_hint, + knot_rrset_wire_flags_t flags, const uint8_t *pkt_wire, + dname_callback_t dname_callback) +{ + int ret = KNOT_EOK; + + for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END; i++) { + int type = desc->block_types[i]; + size_t to_copy = 0; + + switch (type) { + case KNOT_RDATA_WF_COMPRESSIBLE_DNAME: + case KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME: + case KNOT_RDATA_WF_FIXED_DNAME: + ret = dname_callback(src, src_avail, wire, capacity, + compr, compr_hint, type, flags, + pkt_wire); + break; + case KNOT_RDATA_WF_NAPTR_HEADER: + ret = naptr_header_size(src, src_avail); + to_copy = ret; + break; + case KNOT_RDATA_WF_REMAINDER: + to_copy = *src_avail; + break; + default: + /* Fixed size block */ + assert(type > 0); + to_copy = type; + } + + if (to_copy > 0) { + ret = write_rdata_fixed(src, src_avail, wire, capacity, + to_copy); + } + + if (ret != KNOT_EOK) { + return ret; + } + } + + return KNOT_EOK; +} + +/*- RRSet to wire ------------------------------------------------------------*/ + +/*! + * \brief Write RR owner to wire. + */ +static int write_owner(const knot_rrset_t *rrset, + uint8_t **wire, size_t *capacity, + knot_compr_t *compr, knot_rrset_wire_flags_t flags) +{ + assert(rrset); + assert(wire && *wire); + assert(capacity); + + uint16_t owner_pointer = compr_get_ptr(compr, COMPR_HINT_OWNER); + + /* Check size */ + + size_t owner_size = 0; + if (owner_pointer > 0) { + owner_size = sizeof(uint16_t); + } else { + owner_size = knot_dname_size(rrset->owner); + } + + if (owner_size > *capacity) { + return KNOT_ESPACE; + } + + /* Write result */ + + if (owner_pointer > 0) { + knot_wire_put_pointer(*wire, owner_pointer); + } else { + int written = knot_compr_put_dname(rrset->owner, *wire, + dname_max(*capacity), compr); + if (written < 0) { + return written; + } + + if (flags & KNOT_RRSET_WIRE_CANONICAL) { + assert(compr == NULL); + knot_dname_to_lower(*wire); + } + + compr_set_ptr(compr, COMPR_HINT_OWNER, *wire, written); + owner_size = written; + } + + /* Update buffer */ + + *wire += owner_size; + *capacity -= owner_size; + + return KNOT_EOK; +} + +/*! + * \brief Write RR type, class, and TTL to wire. + */ +static int write_fixed_header(const knot_rrset_t *rrset, uint16_t rrset_index, + uint8_t **wire, size_t *capacity) +{ + assert(rrset); + assert(rrset_index < rrset->rrs.rr_count); + assert(wire && *wire); + assert(capacity); + + /* Check capacity */ + + size_t size = sizeof(uint16_t) // type + + sizeof(uint16_t) // class + + sizeof(uint32_t); // ttl + + if (size > *capacity) { + return KNOT_ESPACE; + } + + /* Write result */ + + uint32_t ttl = knot_rdata_ttl(knot_rdataset_at(&rrset->rrs, rrset_index)); + uint8_t *write = *wire; + + knot_wire_write_u16(write, rrset->type); + write += sizeof(uint16_t); + knot_wire_write_u16(write, rrset->rclass); + write += sizeof(uint16_t); + knot_wire_write_u32(write, ttl); + write += sizeof(uint32_t); + assert(write == *wire + size); + + /* Update buffer */ + + *wire = write; + *capacity -= size; + + return KNOT_EOK; +} + +/*! + * \brief Compresses and stores one RDATA dname from \a src to \a dst. + */ +static int compress_dname(const uint8_t **src, size_t *src_avail, + uint8_t **dst, size_t *dst_avail, + knot_compr_t *compr, int compr_hint, int type, + knot_rrset_wire_flags_t flags, + const uint8_t *pkt_wire) +{ + assert(src && *src); + assert(src_avail); + assert(dst && *dst); + assert(dst_avail); + UNUSED(pkt_wire); + + /* Different policy for compression and decompression (issue #289). */ + bool compress = (type == KNOT_RDATA_WF_COMPRESSIBLE_DNAME); + + /* Source domain name */ + + const knot_dname_t *dname = *src; + size_t dname_size = knot_dname_size(dname); + + /* Output domain name */ + + int written = knot_compr_put_dname(dname, *dst, dname_max(*dst_avail), + compress ? compr : NULL); + if (written < 0) { + assert(written == KNOT_ESPACE); + return written; + } + + /* Post-processing */ + + if (flags & KNOT_RRSET_WIRE_CANONICAL) { + assert(compr == NULL); + knot_dname_to_lower(*dst); + } + + /* Update compression hints */ + + if (compr_get_ptr(compr, compr_hint) == 0) { + compr_set_ptr(compr, compr_hint, *dst, written); + } + + /* Update buffers */ + + *dst += written; + *dst_avail -= written; + + *src += dname_size; + *src_avail -= dname_size; + + return KNOT_EOK; +} + +/*! + * \brief Write RDLENGTH and RDATA fields of a RR in a wire. + */ +static int write_rdata(const knot_rrset_t *rrset, uint16_t rrset_index, + uint8_t **wire, size_t *capacity, + knot_compr_t *compr, knot_rrset_wire_flags_t flags) +{ + assert(rrset); + assert(rrset_index < rrset->rrs.rr_count); + assert(wire && *wire); + assert(capacity); + + const knot_rdata_t *rdata = knot_rdataset_at(&rrset->rrs, rrset_index); + + /* Reserve space for RDLENGTH */ + + size_t rdlength_size = sizeof(uint16_t); + if (rdlength_size > *capacity) { + return KNOT_ESPACE; + } + + uint8_t *wire_rdlength = *wire; + *wire += rdlength_size; + *capacity -= rdlength_size; + + /* Write RDATA */ + + uint8_t *wire_rdata_begin = *wire; + int compr_hint = COMPR_HINT_RDATA + rrset_index; + + const uint8_t *src = knot_rdata_data(rdata); + size_t src_avail = knot_rdata_rdlen(rdata); + if (src_avail > 0) { + /* Only write non-empty data. */ + const rdata_descriptor_t *desc = + knot_get_rdata_descriptor(rrset->type); + int ret = traverse_rdata(desc, &src, &src_avail, wire, + capacity, compr, compr_hint, flags, + NULL, compress_dname); + if (ret != KNOT_EOK) { + return ret; + } + } + + if (src_avail > 0) { + /* Trailing data in the message. */ + return KNOT_EMALF; + } + + /* Write final RDLENGTH */ + + size_t rdlength = *wire - wire_rdata_begin; + knot_wire_write_u16(wire_rdlength, rdlength); + + return KNOT_EOK; +} + +/*! + * \brief Write one RR from a RR Set to wire. + */ +static int write_rr(const knot_rrset_t *rrset, uint16_t rrset_index, + uint8_t **wire, size_t *capacity, knot_compr_t *compr, + knot_rrset_wire_flags_t flags) +{ + int ret; + + ret = write_owner(rrset, wire, capacity, compr, flags); + if (ret != KNOT_EOK) { + return ret; + } + + ret = write_fixed_header(rrset, rrset_index, wire, capacity); + if (ret != KNOT_EOK) { + return ret; + } + + ret = write_rdata(rrset, rrset_index, wire, capacity, compr, flags); + if (ret != KNOT_EOK) { + return ret; + } + + return KNOT_EOK; +} + +int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, uint16_t max_size, + knot_compr_t *compr, knot_rrset_wire_flags_t flags) +{ + if (!rrset || !wire) { + return KNOT_EINVAL; + } + + if (flags & KNOT_RRSET_WIRE_CANONICAL) { + compr = NULL; + } + + uint8_t *write = wire; + size_t capacity = max_size; + + for (uint16_t i = 0; i < rrset->rrs.rr_count; i++) { + int ret = write_rr(rrset, i, &write, &capacity, compr, flags); + if (ret != KNOT_EOK) { + return ret; + } + } + + size_t written = write - wire; + + return written; +} + +/*- RRSet to wire ------------------------------------------------------------*/ + +static bool allow_zero_rdata(const knot_rrset_t *rr, const rdata_descriptor_t *desc) +{ + return rr->rclass != KNOT_CLASS_IN || // NONE and ANY for DDNS + rr->type == KNOT_RRTYPE_APL || // APLs can have 0 RDLENGTH + desc->type_name == NULL; // Unknown RR types can have 0 RDLENGTH +} + +/*! + * \brief Parses and decompresses one RDATA dname from \a src to \a dst. + */ +static int decompress_dname(const uint8_t **src, size_t *src_avail, + uint8_t **dst, size_t *dst_avail, + knot_compr_t *compr, int compr_hint, int type, + knot_rrset_wire_flags_t flags, + const uint8_t *pkt_wire) +{ + assert(src && *src); + assert(src_avail); + assert(dst && *dst); + assert(dst_avail); + UNUSED(compr); + UNUSED(compr_hint); + + /* Different policy for compression and decompression (issue #289). */ + bool decompress = (type == KNOT_RDATA_WF_COMPRESSIBLE_DNAME + || type == KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME); + + int ret = knot_dname_wire_check(*src, *src + *src_avail, pkt_wire); + if (ret <= 0) { + return KNOT_EMALF; + } + + size_t dname_size = ret; + int written = dname_size; + + if (decompress) { + int ret = knot_dname_unpack(*dst, *src, *dst_avail, pkt_wire); + if (ret <= 0) { + return ret; + } + written = ret; + } else if (dname_size > *dst_avail) { + return KNOT_ESPACE; + } else { + memcpy(*dst, *src, dname_size); + } + + /* Post-processing */ + + if (flags & KNOT_RRSET_WIRE_CANONICAL) { + knot_dname_to_lower(*dst); + } + + /* Update buffers */ + + *dst += written; + *dst_avail -= written; + + *src += dname_size; + *src_avail -= dname_size; + + return KNOT_EOK; +} + +/*! + * \brief Parse RDATA part of one RR from packet wireformat. + */ +static int parse_rdata(const uint8_t *pkt_wire, size_t *pos, size_t pkt_size, + mm_ctx_t *mm, uint32_t ttl, uint16_t rdlength, + knot_rrset_t *rrset) +{ + assert(pkt_wire); + assert(pos); + assert(rrset); + + if (pkt_size - *pos < rdlength) { + return KNOT_EMALF; + } + + const rdata_descriptor_t *desc = knot_get_rdata_descriptor(rrset->type); + + /* Check for obsolete record. */ + if (desc->type_name == NULL) { + desc = knot_get_obsolete_rdata_descriptor(rrset->type); + } + + if (rdlength == 0) { + if (allow_zero_rdata(rrset, desc)) { + return knot_rrset_add_rdata(rrset, NULL, 0, ttl, mm); + } else { + return KNOT_EMALF; + } + } + + size_t dst_avail = rdlength + KNOT_DNAME_MAXLEN; + uint8_t rdata_buffer[dst_avail]; + memset(rdata_buffer, 0, dst_avail); + + const uint8_t *src = pkt_wire + *pos; + size_t src_avail = rdlength; + uint8_t *dst = rdata_buffer; + + int ret = traverse_rdata(desc, &src, &src_avail, &dst, &dst_avail, + NULL, 0, KNOT_RRSET_WIRE_NONE, pkt_wire, + decompress_dname); + if (ret != KNOT_EOK) { + return ret; + } + + assert(src_avail == 0); + *pos += rdlength; + + size_t dst_size = dst - rdata_buffer; + assert(dst_size == rdlength + KNOT_DNAME_MAXLEN - dst_avail); + + return knot_rrset_add_rdata(rrset, rdata_buffer, dst_size, ttl, mm); +} + +/*! + * \brief Parse header of one RR from packet wireformat. + */ +static int parse_header(const uint8_t *pkt_wire, size_t *pos, + size_t pkt_size, mm_ctx_t *mm, knot_rrset_t *rrset, + uint32_t *ttl, uint16_t *rdlen) +{ + assert(pkt_wire); + assert(pos); + assert(rrset); + assert(ttl); + assert(rdlen); + + knot_dname_t *owner = knot_dname_parse(pkt_wire, pos, pkt_size, mm); + if (owner == NULL) { + return KNOT_EMALF; + } + knot_dname_to_lower(owner); + + if (pkt_size - *pos < KNOT_RR_HEADER_SIZE) { + knot_dname_free(&owner, mm); + return KNOT_EMALF; + } + + uint16_t type = knot_wire_read_u16(pkt_wire + *pos); + uint16_t rclass = knot_wire_read_u16(pkt_wire + *pos + sizeof(uint16_t)); + *ttl = knot_wire_read_u32(pkt_wire + *pos + 2 * sizeof(uint16_t)); + *rdlen = knot_wire_read_u16(pkt_wire + *pos + 4 * sizeof(uint16_t)); + + *pos += KNOT_RR_HEADER_SIZE; + + if (pkt_size - *pos < *rdlen) { + knot_dname_free(&owner, mm); + return KNOT_EMALF; + } + + knot_rrset_init(rrset, owner, type, rclass); + + return KNOT_EOK; +} + +int knot_rrset_rr_from_wire(const uint8_t *pkt_wire, size_t *pos, + size_t pkt_size, mm_ctx_t *mm, knot_rrset_t *rrset) +{ + if (pkt_wire == NULL || pos == NULL || rrset == NULL) { + return KNOT_EINVAL; + } + + uint32_t ttl = 0; + uint16_t rdlen = 0; + int ret = parse_header(pkt_wire, pos, pkt_size, mm, rrset, &ttl, &rdlen); + if (ret == KNOT_EOK) { + ret = parse_rdata(pkt_wire, pos, pkt_size, mm, ttl, rdlen, + rrset); + if (ret != KNOT_EOK) { + knot_rrset_clear(rrset, mm); + } + } + + return ret; +} diff --git a/src/libknot/packet/rrset-wire.h b/src/libknot/packet/rrset-wire.h new file mode 100644 index 0000000000000000000000000000000000000000..e06010f50eeb8a7cfe68c1c2eea4f7be4cb8cacb --- /dev/null +++ b/src/libknot/packet/rrset-wire.h @@ -0,0 +1,78 @@ +/*! + * \file rrset.h + * + * \author Lubos Slovak <lubos.slovak@nic.cz> + * \author Jan Vcelak <jan.vcelak@nic.cz> + * + * \brief RRSet from/to wire conversion functions. + * + * \addtogroup libknot + * @{ + */ +/* Copyright (C) 2014 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/>. + */ + +#pragma once + +#include <stdint.h> +#include <stdbool.h> + +#include "libknot/mempattern.h" +#include "libknot/dname.h" +#include "libknot/rrset.h" + +struct knot_compr; + +/*! + * \brief Flags controlling RR set from/to wire conversion. + */ +enum knot_rrset_wire_flags { + KNOT_RRSET_WIRE_NONE = 0, + KNOT_RRSET_WIRE_CANONICAL = 1 << 0, +}; + +typedef enum knot_rrset_wire_flags knot_rrset_wire_flags_t; + +/*! + * \brief Write RR Set content to a wire. + * + * Function accepts \ref KNOT_RRSET_WIRE_CANONICAL flag, which causes the + * output to be written in canonical representation. + * + * \param rrset RRSet to be converted. + * \param wire Output wire buffer. + * \param max_size Capacity of wire buffer. + * \param compr Compression context. + * \param flags Flags controlling the output. + * + * \return Output size, negative number on error (KNOT_E*). + */ +int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, uint16_t max_size, + struct knot_compr *compr, knot_rrset_wire_flags_t flags); + +/*! +* \brief Creates one RR from wire, stores it into \a rrset. +* +* \param pkt_wire Source wire (the whole packet). +* \param pos Position in \a wire where to start parsing. +* \param pkt_size Total size of data in \a wire (size of the packet). +* \param mm Memory context. +* \param rrset Destination RRSet. +* +* \return KNOT_E* +*/ +int knot_rrset_rr_from_wire(const uint8_t *pkt_wire, size_t *pos, + size_t pkt_size, mm_ctx_t *mm, knot_rrset_t *rrset); diff --git a/src/libknot/rrset.c b/src/libknot/rrset.c index fb80d1b852ad7e34daad441cfca99dcf96f3a32e..4b6c1f6a92b86c7e94a403da3eb31c81aa25e29c 100644 --- a/src/libknot/rrset.c +++ b/src/libknot/rrset.c @@ -21,10 +21,11 @@ #include <stdio.h> #include <stdlib.h> +#include "libknot/rrset.h" + #include "libknot/consts.h" #include "libknot/common.h" #include "libknot/mempattern.h" -#include "libknot/rrset.h" #include "libknot/rrset-dump.h" #include "libknot/descriptor.h" #include "common/debug.h" @@ -33,475 +34,6 @@ #include "libknot/packet/pkt.h" #include "libknot/dname.h" -/*! - * \brief Get maximal size of a domain name in a wire with given capacity. - */ -#define dname_max(wire_capacity) MIN(wire_capacity, KNOT_DNAME_MAXLEN) - -/*! - * \brief Get compression pointer for a given hint. - */ -static uint16_t compr_get_ptr(knot_compr_t *compr, int hint) -{ - if (compr == NULL) { - return 0; - } - - return compr->rrinfo->compress_ptr[hint]; -} - -/*! - * \brief Set compression pointer for a given hint. - */ -static void compr_set_ptr(knot_compr_t *compr, int hint, - const uint8_t *written_at, uint16_t written_size) -{ - if (compr == NULL) { - return; - } - - assert(written_at >= compr->wire); - - uint16_t offset = written_at - compr->wire; - - knot_pkt_compr_hint_set(compr->rrinfo, hint, offset, written_size); -} - -/*! - * \brief Write RR owner to wire. - */ -static int write_owner(const knot_rrset_t *rrset, - uint8_t **wire, size_t *capacity, - knot_compr_t *compr, knot_rrset_wire_flags_t flags) -{ - assert(rrset); - assert(wire && *wire); - assert(capacity); - - uint16_t owner_pointer = compr_get_ptr(compr, COMPR_HINT_OWNER); - - /* Check size */ - - size_t owner_size = 0; - if (owner_pointer > 0) { - owner_size = sizeof(uint16_t); - } else { - owner_size = knot_dname_size(rrset->owner); - } - - if (owner_size > *capacity) { - return KNOT_ESPACE; - } - - /* Write result */ - - if (owner_pointer > 0) { - knot_wire_put_pointer(*wire, owner_pointer); - } else { - int written = knot_compr_put_dname(rrset->owner, *wire, - dname_max(*capacity), compr); - if (written < 0) { - return written; - } - - if (flags & KNOT_RRSET_WIRE_CANONICAL) { - assert(compr == NULL); - knot_dname_to_lower(*wire); - } - - compr_set_ptr(compr, COMPR_HINT_OWNER, *wire, written); - owner_size = written; - } - - /* Update buffer */ - - *wire += owner_size; - *capacity -= owner_size; - - return KNOT_EOK; -} - -/*! - * \brief Write RR type, class, and TTL to wire. - */ -static int write_fixed_header(const knot_rrset_t *rrset, uint16_t rrset_index, - uint8_t **wire, size_t *capacity) -{ - assert(rrset); - assert(rrset_index < rrset->rrs.rr_count); - assert(wire && *wire); - assert(capacity); - - /* Check capacity */ - - size_t size = sizeof(uint16_t) // type - + sizeof(uint16_t) // class - + sizeof(uint32_t); // ttl - - if (size > *capacity) { - return KNOT_ESPACE; - } - - /* Write result */ - - uint32_t ttl = knot_rdata_ttl(knot_rdataset_at(&rrset->rrs, rrset_index)); - uint8_t *write = *wire; - - knot_wire_write_u16(write, rrset->type); - write += sizeof(uint16_t); - knot_wire_write_u16(write, rrset->rclass); - write += sizeof(uint16_t); - knot_wire_write_u32(write, ttl); - write += sizeof(uint32_t); - assert(write == *wire + size); - - /* Update buffer */ - - *wire = write; - *capacity -= size; - - return KNOT_EOK; -} - -/*! - * \brief Write a fixed block of binary data to wire. - */ -static int write_rdata_fixed(const uint8_t **src, size_t *src_avail, - uint8_t **wire, size_t *capacity, - size_t size) -{ - assert(src && *src); - assert(src_avail); - assert(wire && *wire); - assert(capacity); - - /* Check input/output buffer boundaries */ - - if (size > *src_avail) { - return KNOT_EMALF; - } - - if (size > *capacity) { - return KNOT_ESPACE; - } - - /* Data binary copy */ - - memcpy(*wire, *src, size); - - /* Update buffers */ - - *src += size; - *src_avail -= size; - - *wire += size; - *capacity -= size; - - return KNOT_EOK; -} - -/*! - * \brief Count size of NAPTR RDATA header. - */ -static int naptr_header_size(const uint8_t **src, size_t *src_avail) -{ - assert(src && *src); - assert(src_avail); - - size_t size = 0; - - /* Fixed fields size (order, preference) */ - - size += 2 * sizeof(uint16_t); - - /* Variable fields size (flags, services, regexp) */ - - for (int i = 0; i < 3; i++) { - const uint8_t *len_ptr = *src + size; - if (len_ptr >= *src + *src_avail) { - return KNOT_EMALF; - } - - size += 1 + *len_ptr; - } - - return size; -} - -/*! - * \brief Compresses and stores one RDATA dname from \a src to \a dst. - */ -static int compress_dname(const uint8_t **src, size_t *src_avail, - uint8_t **dst, size_t *dst_avail, - knot_compr_t *compr, int compr_hint, int type, - knot_rrset_wire_flags_t flags, - const uint8_t *pkt_wire) -{ - assert(src && *src); - assert(src_avail); - assert(dst && *dst); - assert(dst_avail); - UNUSED(pkt_wire); - - bool compress = (type == KNOT_RDATA_WF_COMPRESSIBLE_DNAME); - - /* Source domain name */ - - const knot_dname_t *dname = *src; - size_t dname_size = knot_dname_size(dname); - - /* Output domain name */ - - int written = knot_compr_put_dname(dname, *dst, dname_max(*dst_avail), - compress ? compr : NULL); - if (written < 0) { - assert(written == KNOT_ESPACE); - return written; - } - - /* Post-processing */ - - if (flags & KNOT_RRSET_WIRE_CANONICAL) { - assert(compr == NULL); - knot_dname_to_lower(*dst); - } - - /* Update compression hints */ - - if (compr_get_ptr(compr, compr_hint) == 0) { - compr_set_ptr(compr, compr_hint, *dst, written); - } - - /* Update buffers */ - - *dst += written; - *dst_avail -= written; - - *src += dname_size; - *src_avail -= dname_size; - - return KNOT_EOK; -} - -/*! - * \brief Parses and decompresses one RDATA dname from \a src to \a dst. - */ -static int decompress_dname(const uint8_t **src, size_t *src_avail, - uint8_t **dst, size_t *dst_avail, - knot_compr_t *compr, int compr_hint, int type, - knot_rrset_wire_flags_t flags, - const uint8_t *pkt_wire) -{ - assert(src && *src); - assert(src_avail); - assert(dst && *dst); - assert(dst_avail); - UNUSED(compr); - UNUSED(compr_hint); - - bool decompress = (type == KNOT_RDATA_WF_COMPRESSIBLE_DNAME - || type == KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME); - - int ret = knot_dname_wire_check(*src, *src + *src_avail, pkt_wire); - if (ret <= 0) { - return KNOT_EMALF; - } - - size_t dname_size = ret; - int written = dname_size; - - if (decompress) { - int ret = knot_dname_unpack(*dst, *src, *dst_avail, pkt_wire); - if (ret <= 0) { - return ret; - } - written = ret; - } else if (dname_size > *dst_avail) { - return KNOT_ESPACE; - } else { - memcpy(*dst, *src, dname_size); - } - - /* Post-processing */ - - if (flags & KNOT_RRSET_WIRE_CANONICAL) { - knot_dname_to_lower(*dst); - } - - /* Update buffers */ - - *dst += written; - *dst_avail -= written; - - *src += dname_size; - *src_avail -= dname_size; - - return KNOT_EOK; -} - -typedef int (*dname_callback_t)(const uint8_t **, size_t *, uint8_t **, - size_t *, knot_compr_t *, int, int, - knot_rrset_wire_flags_t, const uint8_t *); - -/*! - * \brief Generic function from processing RDATA with de/compression of dnames. - */ -static int traverse_rdata(const rdata_descriptor_t *desc, const uint8_t **src, - size_t *src_avail, uint8_t **wire, size_t *capacity, - knot_compr_t *compr, int compr_hint, - knot_rrset_wire_flags_t flags, const uint8_t *pkt_wire, - dname_callback_t dname_callback) -{ - int ret = KNOT_EOK; - - for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END; i++) { - int type = desc->block_types[i]; - size_t to_copy = 0; - - switch (type) { - case KNOT_RDATA_WF_COMPRESSIBLE_DNAME: - case KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME: - case KNOT_RDATA_WF_FIXED_DNAME: - ret = dname_callback(src, src_avail, wire, capacity, - compr, compr_hint, type, flags, - pkt_wire); - break; - case KNOT_RDATA_WF_NAPTR_HEADER: - ret = naptr_header_size(src, src_avail); - to_copy = ret; - break; - case KNOT_RDATA_WF_REMAINDER: - to_copy = *src_avail; - break; - default: - /* Fixed size block */ - assert(type > 0); - to_copy = type; - } - - if (to_copy > 0) { - ret = write_rdata_fixed(src, src_avail, wire, capacity, - to_copy); - } - - if (ret != KNOT_EOK) { - return ret; - } - } - - return KNOT_EOK; -} - -/*! - * \brief Write RDLENGTH and RDATA fields of a RR in a wire. - */ -static int write_rdata(const knot_rrset_t *rrset, uint16_t rrset_index, - uint8_t **wire, size_t *capacity, - knot_compr_t *compr, knot_rrset_wire_flags_t flags) -{ - assert(rrset); - assert(rrset_index < rrset->rrs.rr_count); - assert(wire && *wire); - assert(capacity); - - const knot_rdata_t *rdata = knot_rdataset_at(&rrset->rrs, rrset_index); - - /* Reserve space for RDLENGTH */ - - size_t rdlength_size = sizeof(uint16_t); - if (rdlength_size > *capacity) { - return KNOT_ESPACE; - } - - uint8_t *wire_rdlength = *wire; - *wire += rdlength_size; - *capacity -= rdlength_size; - - /* Write RDATA */ - - uint8_t *wire_rdata_begin = *wire; - int compr_hint = COMPR_HINT_RDATA + rrset_index; - - const uint8_t *src = knot_rdata_data(rdata); - size_t src_avail = knot_rdata_rdlen(rdata); - if (src_avail > 0) { - /* Only write non-empty data. */ - const rdata_descriptor_t *desc = - knot_get_rdata_descriptor(rrset->type); - int ret = traverse_rdata(desc, &src, &src_avail, wire, - capacity, compr, compr_hint, flags, - NULL, compress_dname); - if (ret != KNOT_EOK) { - return ret; - } - } - - if (src_avail > 0) { - /* Trailing data in the message. */ - return KNOT_EMALF; - } - - /* Write final RDLENGTH */ - - size_t rdlength = *wire - wire_rdata_begin; - knot_wire_write_u16(wire_rdlength, rdlength); - - return KNOT_EOK; -} - -/*! - * \brief Write one RR from a RR Set to wire. - */ -static int write_rr(const knot_rrset_t *rrset, uint16_t rrset_index, - uint8_t **wire, size_t *capacity, knot_compr_t *compr, - knot_rrset_wire_flags_t flags) -{ - int ret; - - ret = write_owner(rrset, wire, capacity, compr, flags); - if (ret != KNOT_EOK) { - return ret; - } - - ret = write_fixed_header(rrset, rrset_index, wire, capacity); - if (ret != KNOT_EOK) { - return ret; - } - - ret = write_rdata(rrset, rrset_index, wire, capacity, compr, flags); - if (ret != KNOT_EOK) { - return ret; - } - - return KNOT_EOK; -} - -int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, uint16_t max_size, - knot_compr_t *compr, knot_rrset_wire_flags_t flags) -{ - if (!rrset || !wire) { - return KNOT_EINVAL; - } - - if (flags & KNOT_RRSET_WIRE_CANONICAL) { - compr = NULL; - } - - uint8_t *write = wire; - size_t capacity = max_size; - - for (uint16_t i = 0; i < rrset->rrs.rr_count; i++) { - int ret = write_rr(rrset, i, &write, &capacity, compr, flags); - if (ret != KNOT_EOK) { - return ret; - } - } - - size_t written = write - wire; - - return written; -} - knot_rrset_t *knot_rrset_new(const knot_dname_t *owner, uint16_t type, uint16_t rclass, mm_ctx_t *mm) { @@ -578,129 +110,6 @@ void knot_rrset_clear(knot_rrset_t *rrset, mm_ctx_t *mm) } } -static bool allow_zero_rdata(const knot_rrset_t *rr, const rdata_descriptor_t *desc) -{ - return rr->rclass != KNOT_CLASS_IN || // NONE and ANY for DDNS - rr->type == KNOT_RRTYPE_APL || // APLs can have 0 RDLENGTH - desc->type_name == NULL; // Unknown RR types can have 0 RDLENGTH -} - -/*! - * \brief Parse RDATA part of one RR from packet wireformat. - */ -static int parse_rdata(const uint8_t *pkt_wire, size_t *pos, size_t pkt_size, - mm_ctx_t *mm, uint32_t ttl, uint16_t rdlength, - knot_rrset_t *rrset) -{ - assert(pkt_wire); - assert(pos); - assert(rrset); - - if (pkt_size - *pos < rdlength) { - return KNOT_EMALF; - } - - const rdata_descriptor_t *desc = knot_get_rdata_descriptor(rrset->type); - - /* Check for obsolete record. */ - if (desc->type_name == NULL) { - desc = knot_get_obsolete_rdata_descriptor(rrset->type); - } - - if (rdlength == 0) { - if (allow_zero_rdata(rrset, desc)) { - return knot_rrset_add_rdata(rrset, NULL, 0, ttl, mm); - } else { - return KNOT_EMALF; - } - } - - size_t dst_avail = rdlength + KNOT_DNAME_MAXLEN; - uint8_t rdata_buffer[dst_avail]; - memset(rdata_buffer, 0, dst_avail); - - const uint8_t *src = pkt_wire + *pos; - size_t src_avail = rdlength; - uint8_t *dst = rdata_buffer; - - int ret = traverse_rdata(desc, &src, &src_avail, &dst, &dst_avail, - NULL, 0, KNOT_RRSET_WIRE_NONE, pkt_wire, - decompress_dname); - if (ret != KNOT_EOK) { - return ret; - } - - assert(src_avail == 0); - *pos += rdlength; - - size_t dst_size = dst - rdata_buffer; - assert(dst_size == rdlength + KNOT_DNAME_MAXLEN - dst_avail); - - return knot_rrset_add_rdata(rrset, rdata_buffer, dst_size, ttl, mm); -} - -/*! - * \brief Parse header of one RR from packet wireformat. - */ -static int parse_header(const uint8_t *pkt_wire, size_t *pos, - size_t pkt_size, mm_ctx_t *mm, knot_rrset_t *rrset, - uint32_t *ttl, uint16_t *rdlen) -{ - assert(pkt_wire); - assert(pos); - assert(rrset); - assert(ttl); - assert(rdlen); - - knot_dname_t *owner = knot_dname_parse(pkt_wire, pos, pkt_size, mm); - if (owner == NULL) { - return KNOT_EMALF; - } - knot_dname_to_lower(owner); - - if (pkt_size - *pos < KNOT_RR_HEADER_SIZE) { - knot_dname_free(&owner, mm); - return KNOT_EMALF; - } - - uint16_t type = knot_wire_read_u16(pkt_wire + *pos); - uint16_t rclass = knot_wire_read_u16(pkt_wire + *pos + sizeof(uint16_t)); - *ttl = knot_wire_read_u32(pkt_wire + *pos + 2 * sizeof(uint16_t)); - *rdlen = knot_wire_read_u16(pkt_wire + *pos + 4 * sizeof(uint16_t)); - - *pos += KNOT_RR_HEADER_SIZE; - - if (pkt_size - *pos < *rdlen) { - knot_dname_free(&owner, mm); - return KNOT_EMALF; - } - - knot_rrset_init(rrset, owner, type, rclass); - - return KNOT_EOK; -} - -int knot_rrset_rr_from_wire(const uint8_t *pkt_wire, size_t *pos, - size_t pkt_size, mm_ctx_t *mm, knot_rrset_t *rrset) -{ - if (pkt_wire == NULL || pos == NULL || rrset == NULL) { - return KNOT_EINVAL; - } - - uint32_t ttl = 0; - uint16_t rdlen = 0; - int ret = parse_header(pkt_wire, pos, pkt_size, mm, rrset, &ttl, &rdlen); - if (ret == KNOT_EOK) { - ret = parse_rdata(pkt_wire, pos, pkt_size, mm, ttl, rdlen, - rrset); - if (ret != KNOT_EOK) { - knot_rrset_clear(rrset, mm); - } - } - - return ret; -} - int knot_rrset_add_rdata(knot_rrset_t *rrset, const uint8_t *rdata, const uint16_t size, const uint32_t ttl, mm_ctx_t *mm) diff --git a/src/libknot/rrset.h b/src/libknot/rrset.h index d90f6a5f92a09b43c3dc2ea9ea79fe42b1a833cc..8025838ac7353e1ea5d2c88a181fffde11bfd6ad 100644 --- a/src/libknot/rrset.h +++ b/src/libknot/rrset.h @@ -34,8 +34,6 @@ #include "libknot/dname.h" #include "libknot/rdataset.h" -struct knot_compr; - /*! * \brief Structure for representing RRSet. * @@ -123,49 +121,6 @@ void knot_rrset_free(knot_rrset_t **rrset, mm_ctx_t *mm); */ void knot_rrset_clear(knot_rrset_t *rrset, mm_ctx_t *mm); -/* ---------- Wire conversions (legacy, to be done in knot_pkt_t) ----------- */ - -/*! - * \brief Flags controlling RR set from/to wire conversion. - */ -enum knot_rrset_wire_flags { - KNOT_RRSET_WIRE_NONE = 0, - KNOT_RRSET_WIRE_CANONICAL = 1 << 0, -}; - -typedef enum knot_rrset_wire_flags knot_rrset_wire_flags_t; - -/*! - * \brief Write RR Set content to a wire. - * - * Function accepts \ref KNOT_RRSET_WIRE_CANONICAL flag, which causes the - * output to be written in canonical representation. - * - * \param rrset RRSet to be converted. - * \param wire Output wire buffer. - * \param max_size Capacity of wire buffer. - * \param compr Compression context. - * \param flags Flags controlling the output. - * - * \return Output size, negative number on error (KNOT_E*). - */ -int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, uint16_t max_size, - struct knot_compr *compr, knot_rrset_wire_flags_t flags); - -/*! -* \brief Creates one RR from wire, stores it into \a rrset. -* -* \param pkt_wire Source wire (the whole packet). -* \param pos Position in \a wire where to start parsing. -* \param pkt_size Total size of data in \a wire (size of the packet). -* \param mm Memory context. -* \param rrset Destination RRSet. -* -* \return KNOT_E* -*/ -int knot_rrset_rr_from_wire(const uint8_t *pkt_wire, size_t *pos, - size_t pkt_size, mm_ctx_t *mm, knot_rrset_t *rrset); - /* ---------- RR addition. (legacy, functionality in knot_rdataset_t) ------- */ /*! diff --git a/src/libknot/tsig-op.c b/src/libknot/tsig-op.c index 59935a80c3aaed6210fba7f11b36b51bbbd7b9a8..43c6b6c009139dac48714c15aca865ddedbfa589 100644 --- a/src/libknot/tsig-op.c +++ b/src/libknot/tsig-op.c @@ -31,6 +31,7 @@ #include "libknot/packet/wire.h" #include "libknot/consts.h" #include "libknot/dnssec/key.h" +#include "libknot/packet/rrset-wire.h" const int KNOT_TSIG_MAX_DIGEST_SIZE = 64; // size of HMAC-SHA512 digest const uint16_t KNOT_TSIG_FUDGE_DEFAULT = 300; // default Fudge value