/* Copyright (C) 2020 CZ.NIC, z.s.p.o. 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 . */ /*! * \file * * \brief Structure for holding DNS packet data and metadata. * * \addtogroup pkt * @{ */ #pragma once #include #include #include "libknot/consts.h" #include "libknot/dname.h" #include "libknot/mm_ctx.h" #include "libknot/rrset.h" #include "libknot/rrtype/opt.h" #include "libknot/packet/wire.h" #include "libknot/packet/compr.h" #include "libknot/wire.h" /* Number of packet sections (ANSWER, AUTHORITY, ADDITIONAL). */ #define KNOT_PKT_SECTIONS 3 /*! * \brief Packet flags. */ enum { KNOT_PF_NULL = 0 << 0, /*!< No flags. */ KNOT_PF_FREE = 1 << 1, /*!< Free with packet. */ KNOT_PF_NOTRUNC = 1 << 2, /*!< Don't truncate. */ KNOT_PF_CHECKDUP = 1 << 3, /*!< Check for duplicates. */ KNOT_PF_KEEPWIRE = 1 << 4, /*!< Keep wireformat untouched when parsing. */ KNOT_PF_NOCANON = 1 << 5, /*!< Don't canonicalize rrsets during parsing. */ KNOT_PF_ORIGTTL = 1 << 6, /*!< Write RRSIGs with their original TTL. */ KNOT_PF_SOAMINTTL = 1 << 7, /*!< Write SOA with its minimum-ttl as TTL. */ }; typedef struct knot_pkt knot_pkt_t; /*! * \brief Packet section. * Points to RRSet and RRSet info arrays in the packet. * This structure is required for random access to packet sections. */ typedef struct { knot_pkt_t *pkt; /*!< Owner. */ uint16_t pos; /*!< Position in the rr/rrinfo fields in packet. */ uint16_t count; /*!< Number of RRSets in this section. */ } knot_pktsection_t; /*! * \brief Structure representing a DNS packet. */ struct knot_pkt { uint8_t *wire; /*!< Wire format of the packet. */ size_t size; /*!< Current wire size of the packet. */ size_t max_size; /*!< Maximum allowed size of the packet. */ size_t parsed; /*!< Parsed size. */ uint16_t reserved; /*!< Reserved space. */ uint16_t qname_size; /*!< QNAME size. */ uint16_t rrset_count; /*!< Packet RRSet count. */ uint16_t flags; /*!< Packet flags. */ knot_rrset_t *opt_rr; /*!< OPT RR included in the packet. */ knot_rrset_t *tsig_rr; /*!< TSIG RR stored in the packet. */ /*! EDNS option positions in the wire (if parsed from wire). */ knot_edns_options_t *edns_opts; /*! TSIG RR position in the wire (if parsed from wire). */ struct { uint8_t *pos; size_t len; } tsig_wire; /* Packet sections. */ knot_section_t current; knot_pktsection_t sections[KNOT_PKT_SECTIONS]; /* Packet RRSet (meta)data. */ size_t rrset_allocd; knot_rrinfo_t *rr_info; knot_rrset_t *rr; knot_mm_t mm; /*!< Memory allocation context. */ knot_compr_t compr; /*!< Compression context. */ /*! Lowercased QNAME. MUST BE LAST ITEM! */ knot_dname_storage_t lower_qname; }; /*! * \brief Create new packet over existing memory, or allocate new from memory context. * * \note Packet is allocated from given memory context. * * \param wire If NULL, memory of 'len' size shall be allocated. * Otherwise pointer is used for the wire format of the packet. * \param len Wire format length. * \param mm Memory context (NULL for default). * \return New packet or NULL. */ knot_pkt_t *knot_pkt_new(void *wire, uint16_t len, knot_mm_t *mm); /*! * \brief Copy packet. * * \note Current implementation is not very efficient, as it re-parses the wire. * * \param dst Target packet. * \param src Source packet. * * \return new packet or NULL */ int knot_pkt_copy(knot_pkt_t *dst, const knot_pkt_t *src); /*! * \brief Initialized response from query packet. * * \note Question is not checked, it is expected to be checked already. * * \param pkt Given packet. * \param query Query. * \return KNOT_EOK, KNOT_EINVAL, KNOT_ESPACE */ int knot_pkt_init_response(knot_pkt_t *pkt, const knot_pkt_t *query); /*! \brief Reinitialize packet for another use. */ void knot_pkt_clear(knot_pkt_t *pkt); /*! \brief Begone you foul creature of the underworld. */ void knot_pkt_free(knot_pkt_t *pkt); /*! * \brief Reserve an arbitrary amount of space in the packet. * * \return KNOT_EOK * \return KNOT_ERANGE if size can't be reserved */ int knot_pkt_reserve(knot_pkt_t *pkt, uint16_t size); /*! * \brief Reclaim reserved size. * * \return KNOT_EOK * \return KNOT_ERANGE if size can't be reclaimed */ int knot_pkt_reclaim(knot_pkt_t *pkt, uint16_t size); /* * Packet QUESTION accessors. */ static inline uint16_t knot_pkt_question_size(const knot_pkt_t *pkt) { if (pkt == NULL || pkt->qname_size == 0) { return 0; } return pkt->qname_size + 2 * sizeof(uint16_t); } static inline const knot_dname_t *knot_pkt_qname(const knot_pkt_t *pkt) { if (pkt == NULL || pkt->qname_size == 0) { return NULL; } return pkt->lower_qname; } static inline const knot_dname_t *knot_pkt_wire_qname(const knot_pkt_t *pkt) { if (pkt == NULL || pkt->qname_size == 0) { return NULL; } return pkt->wire + KNOT_WIRE_HEADER_SIZE; } static inline uint16_t knot_pkt_qtype(const knot_pkt_t *pkt) { if (pkt == NULL || pkt->qname_size == 0) { return 0; } unsigned off = KNOT_WIRE_HEADER_SIZE + pkt->qname_size; return knot_wire_read_u16(pkt->wire + off); } static inline uint16_t knot_pkt_qclass(const knot_pkt_t *pkt) { if (pkt == NULL || pkt->qname_size == 0) { return 0; } unsigned off = KNOT_WIRE_HEADER_SIZE + pkt->qname_size + sizeof(uint16_t); return knot_wire_read_u16(pkt->wire + off); } /* * Packet writing API. */ /*! * \brief Begin reading/writing packet section. * * \note You must proceed in the natural order (ANSWER, AUTHORITY, ADDITIONAL). * * \param pkt * \param section_id * \return KNOT_EOK or KNOT_EINVAL */ int knot_pkt_begin(knot_pkt_t *pkt, knot_section_t section_id); /*! * \brief Put QUESTION in the packet. * * \note Since we support QD=1 only, QUESTION is a special type of packet section. * \note Must not be used after putting RRsets into the packet. * * \param pkt * \param qname * \param qclass * \param qtype * \return KNOT_EOK or various errors */ int knot_pkt_put_question(knot_pkt_t *pkt, const knot_dname_t *qname, uint16_t qclass, uint16_t qtype); /*! * \brief Put RRSet into packet. * * \note See compr.h for description on how compression hints work. * \note Available flags: PF_FREE, KNOT_PF_CHECKDUP, KNOT_PF_NOTRUNC * * \param pkt * \param compr_hint Compression hint, see enum knot_compr_hint or absolute * position. * \param rr Given RRSet. * \param rotate Rotate the RRSet order by this count. * \param flags RRSet flags (set PF_FREE if you want RRSet to be freed * with the packet). * * \return KNOT_EOK, KNOT_ESPACE, various errors */ int knot_pkt_put_rotate(knot_pkt_t *pkt, uint16_t compr_hint, const knot_rrset_t *rr, uint16_t rotate, uint16_t flags); /*! \brief Same as knot_pkt_put_rotate but without rrset rotation. */ static inline int knot_pkt_put(knot_pkt_t *pkt, uint16_t compr_hint, const knot_rrset_t *rr, uint16_t flags) { return knot_pkt_put_rotate(pkt, compr_hint, rr, 0, flags); } /*! \brief Get description of the given packet section. */ static inline const knot_pktsection_t *knot_pkt_section(const knot_pkt_t *pkt, knot_section_t section_id) { assert(pkt); return &pkt->sections[section_id]; } /*! \brief Get RRSet from the packet section. */ static inline const knot_rrset_t *knot_pkt_rr(const knot_pktsection_t *section, uint16_t i) { assert(section); return section->pkt->rr + section->pos + i; } /*! \brief Get RRSet offset in the packet wire. */ static inline uint16_t knot_pkt_rr_offset(const knot_pktsection_t *section, uint16_t i) { assert(section); return section->pkt->rr_info[section->pos + i].pos; } /* * Packet parsing API. */ /*! * \brief Parse both packet question and payload. * * Parses both QUESTION and all packet sections, * includes semantic checks over specific RRs (TSIG, OPT). * * \note If KNOT_PF_KEEPWIRE is set, TSIG RR is not stripped from the wire * and is processed as any other RR. * * \param pkt Given packet. * \param flags Parsing flags (allowed KNOT_PF_KEEPWIRE) * * \retval KNOT_EOK if success. * \retval KNOT_ETRAIL if success but with some trailing data. * \retval KNOT_EMALF and other errors. */ int knot_pkt_parse(knot_pkt_t *pkt, unsigned flags); /*! * \brief Parse packet header and a QUESTION section. */ int knot_pkt_parse_question(knot_pkt_t *pkt); /*! * \brief Get packet extended RCODE. * * Extended RCODE is created by considering TSIG RCODE, EDNS RCODE and * DNS Header RCODE. (See RFC 6895, Section 2.3). * * \param pkt Packet to get the response code from. * * \return Whole extended RCODE (0 if pkt == NULL). */ uint16_t knot_pkt_ext_rcode(const knot_pkt_t *pkt); /*! * \brief Get packet extended RCODE name. * * The packet parameter is important as the name depends on TSIG. * * \param pkt Packet to get the response code from. * * \return RCODE name (or empty string if not known). */ const char *knot_pkt_ext_rcode_name(const knot_pkt_t *pkt); /*! * \brief Checks if there is an OPT RR in the packet. */ static inline bool knot_pkt_has_edns(const knot_pkt_t *pkt) { assert(pkt); return pkt->opt_rr != NULL; } /*! * \brief Checks if TSIG is present. */ static inline bool knot_pkt_has_tsig(const knot_pkt_t *pkt) { assert(pkt); return pkt->tsig_rr != NULL; } /*! * \brief Checks if DO bit is set in the packet's OPT RR. */ static inline bool knot_pkt_has_dnssec(const knot_pkt_t *pkt) { assert(pkt); return knot_pkt_has_edns(pkt) && knot_edns_do(pkt->opt_rr); } /*! * \brief Get specific EDNS option from a parsed packet. */ static inline uint8_t *knot_pkt_edns_option(const knot_pkt_t *pkt, uint16_t code) { assert(pkt); if (pkt->edns_opts != NULL && code <= KNOT_EDNS_MAX_OPTION_CODE) { return pkt->edns_opts->ptr[code]; } else { return NULL; } } /*! * \brief Computes a reasonable Padding data length for a given packet and opt RR. * * \param pkt DNS Packet prepared and otherwise ready to go, no OPT yet added. * \param opt_rr OPT RR, not yet including padding. * * \return Required padding length or -1 if padding not required. */ static inline int knot_pkt_default_padding_size(const knot_pkt_t *pkt, const knot_rrset_t *opt_rr) { if (knot_wire_get_qr(pkt->wire)) { return knot_edns_alignment_size(pkt->size + pkt->reserved, knot_rrset_size(opt_rr), KNOT_EDNS_ALIGNMENT_RESPONSE_DEFAULT); } else { return knot_edns_alignment_size(pkt->size + pkt->reserved, knot_rrset_size(opt_rr), KNOT_EDNS_ALIGNMENT_QUERY_DEFAULT); } } static inline size_t knot_pkt_size(const knot_pkt_t *pkt) { assert(pkt); return pkt->size + (knot_pkt_has_tsig(pkt) ? pkt->tsig_wire.len : 0); } /*! @} */