diff --git a/Knot.files b/Knot.files index e15a4336951a4364e20ee231d06d569efabff18b..c6af0132104826a1ab8b16a5b0df7f1eb4898034 100644 --- a/Knot.files +++ b/Knot.files @@ -49,6 +49,12 @@ src/dnslib/descriptor.h src/dnslib/descriptor.c src/dnslib/zone.h src/dnslib/zone.c +src/dnslib/packet.h +src/dnslib/packet.c +src/dnslib/query.h +src/dnslib/query.c +src/dnslib/response2.h +src/dnslib/response2.c src/dnslib/response.h src/dnslib/wire.h src/dnslib/response.c diff --git a/src/dnslib/debug.h b/src/dnslib/debug.h index 5bbf95930bac4b6ca365c26da5c628d5d039e58d..8ddcc7c313bc9880933ee1b1630f80dd06304313 100644 --- a/src/dnslib/debug.h +++ b/src/dnslib/debug.h @@ -78,6 +78,7 @@ void dnslib_zone_dump(dnslib_zone_t *zone, char loaded_zone); //#define DNSLIB_ZONEDB_DEBUG //#define DNSLIB_DNAME_DEBUG //#define DNSLIB_RESPONSE_DEBUG +//#define DNSLIB_PACKET_DEBUG //#define DNSLIB_EDNS_DEBUG //#define DNSLIB_RRSET_DEBUG //#define DNSLIB_NSEC3_DEBUG @@ -124,6 +125,16 @@ void dnslib_zone_dump(dnslib_zone_t *zone, char loaded_zone); #define DEBUG_DNSLIB_RESPONSE(cmds) #endif +#ifdef DNSLIB_PACKET_DEBUG +#define debug_dnslib_packet(msg...) fprintf(stderr, msg) +#define debug_dnslib_packet_hex(data, len) hex_print((data), (len)) +#define DEBUG_DNSLIB_PACKET(cmds) do { cmds } while (0) +#else +#define debug_dnslib_packet(msg...) +#define debug_dnslib_packet_hex(data, len) +#define DEBUG_DNSLIB_PACKET(cmds) +#endif + #ifdef DNSLIB_EDNS_DEBUG #define debug_dnslib_edns(msg...) fprintf(stderr, msg) #else diff --git a/src/dnslib/packet.c b/src/dnslib/packet.c new file mode 100644 index 0000000000000000000000000000000000000000..2628b40bf3539bedf4e579c90e730cb41c26d751 --- /dev/null +++ b/src/dnslib/packet.c @@ -0,0 +1,330 @@ +#include "dnslib/packet.h" + +/*----------------------------------------------------------------------------*/ +/*! + * \brief Default sizes for response structure parts and steps for increasing + * them. + */ +enum { + DEFAULT_ANCOUNT = 6, /*!< Default count of Answer RRSets. */ + DEFAULT_NSCOUNT = 8, /*!< Default count of Authority RRSets. */ + DEFAULT_ARCOUNT = 28, /*!< Default count of Additional RRSets. */ + + DEFAULT_ANCOUNT_QUERY = 1, /*!< Default count of Answer RRSets. */ + DEFAULT_NSCOUNT_QUERY = 0, /*!< Default count of Authority RRSets. */ + DEFAULT_ARCOUNT_QUERY = 1, /*!< Default count of Additional RRSets. */ + /*! + * \brief Default count of all domain names in response. + * + * Used for compression table. + */ + DEFAULT_DOMAINS_IN_RESPONSE = 22, + + /*! \brief Default count of temporary RRSets stored in response. */ + DEFAULT_TMP_RRSETS = 5, + + /*! \brief Default count of temporary RRSets stored in query. */ + DEFAULT_TMP_RRSETS_QUERY = 2, + + STEP_ANCOUNT = 6, /*!< Step for increasing space for Answer RRSets. */ + STEP_NSCOUNT = 8, /*!< Step for increasing space for Authority RRSets.*/ + STEP_ARCOUNT = 8,/*!< Step for increasing space for Additional RRSets.*/ + STEP_DOMAINS = 10, /*!< Step for resizing compression table. */ + STEP_TMP_RRSETS = 5 /*!< Step for increasing temorary RRSets count. */ +}; + +/*----------------------------------------------------------------------------*/ +#define PREALLOC_RRSETS(count) (count * sizeof(dnslib_rrset_t *)) + +/*! \brief Sizes for preallocated space in the response structure. */ +enum { + /*! \brief Size of the response structure itself. */ + PREALLOC_PACKET = sizeof(dnslib_packet_t), + /*! \brief Space for QNAME dname structure. */ + PREALLOC_QNAME_DNAME = sizeof(dnslib_dname_t), + /*! \brief Space for QNAME name (maximum domain name size). */ + PREALLOC_QNAME_NAME = 256, + /*! \brief Space for QNAME labels (maximum label count). */ + PREALLOC_QNAME_LABELS = 127, + /*! \brief Total space for QNAME. */ + PREALLOC_QNAME = PREALLOC_QNAME_DNAME + + PREALLOC_QNAME_NAME + + PREALLOC_QNAME_LABELS, + /*! + * \brief Space for RR owner wire format. + * + * Temporary buffer, used when putting RRSets to the response. + */ + PREALLOC_RR_OWNER = 256, + +// /*! \brief Space for Answer RRSets. */ +// PREALLOC_ANSWER = DEFAULT_ANCOUNT * sizeof(dnslib_dname_t *), +// /*! \brief Space for Authority RRSets. */ +// PREALLOC_AUTHORITY = DEFAULT_NSCOUNT * sizeof(dnslib_dname_t *), +// /*! \brief Space for Additional RRSets. */ +// PREALLOC_ADDITIONAL = DEFAULT_ARCOUNT * sizeof(dnslib_dname_t *), +// /*! \brief Total size for Answer, Authority and Additional RRSets. */ +// PREALLOC_RRSETS = PREALLOC_ANSWER +// + PREALLOC_AUTHORITY +// + PREALLOC_ADDITIONAL, + /*! \brief Space for one part of the compression table (domain names).*/ + PREALLOC_DOMAINS = + DEFAULT_DOMAINS_IN_RESPONSE * sizeof(dnslib_dname_t *), + /*! \brief Space for other part of the compression table (offsets). */ + PREALLOC_OFFSETS = + DEFAULT_DOMAINS_IN_RESPONSE * sizeof(size_t), + PREALLOC_COMPRESSION = PREALLOC_DOMAINS + PREALLOC_OFFSETS, + +// /*! \brief Space for temporary RRSets. */ +// PREALLOC_TMP_RRSETS = +// DEFAULT_TMP_RRSETS * sizeof(dnslib_rrset_t *), + + PREALLOC_QUERY = PREALLOC_PACKET + + PREALLOC_QNAME + + PREALLOC_RRSETS(DEFAULT_ANCOUNT_QUERY) + + PREALLOC_RRSETS(DEFAULT_NSCOUNT_QUERY) + + PREALLOC_RRSETS(DEFAULT_ARCOUNT_QUERY) + + PREALLOC_RRSETS(DEFAULT_TMP_RRSETS_QUERY), + + /*! \brief Total preallocated size for the response. */ + PREALLOC_TOTAL = PREALLOC_PACKET + + PREALLOC_QNAME + + PREALLOC_RR_OWNER + + PREALLOC_RRSETS + + PREALLOC_COMPRESSION + + PREALLOC_RRSETS(DEFAULT_TMP_RRSETS), +}; + +/*----------------------------------------------------------------------------*/ +/* Non-API functions */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* API functions */ +/*----------------------------------------------------------------------------*/ + +dnslib_packet_t *dnslib_packet_new(dnslib_packet_prealloc_type_t prealloc) +{ + /*! \todo Implement! */ + return NULL; +} + +/*----------------------------------------------------------------------------*/ + +int dnslib_packet_parse_from_wire(dnslib_packet_t *packet, uint8_t *wireformat, + size_t size, int question_only) +{ + /*! \todo Implement! */ + return DNSLIB_ERROR; +} + +/*----------------------------------------------------------------------------*/ + +uint8_t dnslib_packet_opcode(const dnslib_packet_t *packet) +{ + return dnslib_wire_flags_get_opcode(packet->header.flags1); +} + +/*----------------------------------------------------------------------------*/ + +const dnslib_dname_t *dnslib_packet_qname(const dnslib_packet_t *packet) +{ + return packet->question.qname; +} + +/*----------------------------------------------------------------------------*/ + +uint16_t dnslib_packet_qtype(const dnslib_packet_t *packet) +{ + return packet->question.qtype; +} + +/*----------------------------------------------------------------------------*/ + +uint16_t dnslib_packet_qclass(const dnslib_packet_t *packet) +{ + return packet->question.qclass; +} + +/*----------------------------------------------------------------------------*/ + +short dnslib_packet_answer_rrset_count(const dnslib_packet_t *packet) +{ + return packet->an_rrsets; +} + +/*----------------------------------------------------------------------------*/ + +short dnslib_packet_authority_rrset_count(const dnslib_packet_t *packet) +{ + return packet->ns_rrsets; +} + +/*----------------------------------------------------------------------------*/ + +short dnslib_packet_additional_rrset_count(const dnslib_packet_t *packet) +{ + return packet->ar_rrsets; +} + +/*----------------------------------------------------------------------------*/ + +const dnslib_rrset_t *dnslib_packet_answer_rrset( + const dnslib_packet_t *packet, short pos) +{ + if (pos > packet->an_rrsets) { + return NULL; + } + + return packet->answer[pos]; +} + +/*----------------------------------------------------------------------------*/ + +const dnslib_rrset_t *dnslib_packet_authority_rrset( + dnslib_packet_t *packet, short pos) +{ + if (pos > packet->ns_rrsets) { + return NULL; + } + + return packet->authority[pos]; +} + +/*----------------------------------------------------------------------------*/ + +const dnslib_rrset_t *dnslib_packet_additional_rrset( + dnslib_packet_t *packet, short pos) +{ + if (pos > packet->ar_rrsets) { + return NULL; + } + + return packet->additional[pos]; +} + +/*----------------------------------------------------------------------------*/ + +void dnslib_packet_free(dnslib_packet_t **packet) +{ + if (packet == NULL || *packet == NULL) { + return; + } + + // free temporary domain names + debug_dnslib_packet("Freeing tmp domains...\n"); + dnslib_packet_free_tmp_rrsets(*packet); + + // check if some additional space was allocated for the packet + debug_dnslib_packet("Freeing additional allocated space...\n"); + dnslib_packet_free_allocated_space(*packet); + + // free the space for wireformat + assert((*packet)->wireformat != NULL); + free((*packet)->wireformat); + + debug_dnslib_packet("Freeing packet structure\n"); + free(*packet); + *packet = NULL; +} + +/*----------------------------------------------------------------------------*/ +#ifdef DNSLIB_PACKET_DEBUG +static void dnslib_packet_dump_rrsets(const dnslib_rrset_t **rrsets, + int count) +{ + for (int i = 0; i < count; ++i) { + debug_dnslib_packet(" RRSet %d:\n", i + 1); + char *name = dnslib_dname_to_str(rrsets[i]->owner); + debug_dnslib_packet(" Owner: %s\n", name); + free(name); + debug_dnslib_packet(" Type: %s\n", + dnslib_rrtype_to_string(rrsets[i]->type)); + debug_dnslib_packet(" Class: %s\n", + dnslib_rrclass_to_string(rrsets[i]->rclass)); + debug_dnslib_packet(" TTL: %d\n", rrsets[i]->ttl); + debug_dnslib_packet(" RDATA: "); + + dnslib_rrtype_descriptor_t *desc = + dnslib_rrtype_descriptor_by_type(rrsets[i]->type); + + const dnslib_rdata_t *rdata = dnslib_rrset_rdata(rrsets[i]); + while (rdata != NULL) { + for (int j = 0; j < rdata->count; ++j) { + switch (desc->wireformat[j]) { + case DNSLIB_RDATA_WF_COMPRESSED_DNAME: + case DNSLIB_RDATA_WF_LITERAL_DNAME: + case DNSLIB_RDATA_WF_UNCOMPRESSED_DNAME: + name = dnslib_dname_to_str( + rdata->items[j].dname); + debug_dnslib_packet("%s \n",name); + free(name); + break; + case DNSLIB_RDATA_WF_BINARYWITHLENGTH: + debug_dnslib_packet_hex( + (char *)rdata->items[j].raw_data, + rdata->items[j].raw_data[0]); + break; + default: + debug_dnslib_packet_hex( + (char *)&rdata->items[j].raw_data[1], + rdata->items[j].raw_data[0]); + break; + } + } + rdata = dnslib_rrset_rdata_next(rrsets[i], rdata); + } + } +} +#endif +/*----------------------------------------------------------------------------*/ + +void dnslib_packet_dump(const dnslib_packet_t *packet) +{ +#ifdef DNSLIB_PACKET_DEBUG + debug_dnslib_packet("DNS packet:\n-----------------------------\n"); + + debug_dnslib_packet("\nHeader:\n"); + debug_dnslib_packet(" ID: %u", packet->header.id); + debug_dnslib_packet(" FLAGS: %s %s %s %s %s %s %s\n", + dnslib_wire_flags_get_qr(packet->header.flags1) ? "qr" : "", + dnslib_wire_flags_get_aa(packet->header.flags1) ? "aa" : "", + dnslib_wire_flags_get_tc(packet->header.flags1) ? "tc" : "", + dnslib_wire_flags_get_rd(packet->header.flags1) ? "rd" : "", + dnslib_wire_flags_get_ra(packet->header.flags2) ? "ra" : "", + dnslib_wire_flags_get_ad(packet->header.flags2) ? "ad" : "", + dnslib_wire_flags_get_cd(packet->header.flags2) ? "cd" : ""); + debug_dnslib_packet(" QDCOUNT: %u\n", packet->header.qdcount); + debug_dnslib_packet(" ANCOUNT: %u\n", packet->header.ancount); + debug_dnslib_packet(" NSCOUNT: %u\n", packet->header.nscount); + debug_dnslib_packet(" ARCOUNT: %u\n", packet->header.arcount); + + debug_dnslib_packet("\nQuestion:\n"); + char *qname = dnslib_dname_to_str(packet->question.qname); + debug_dnslib_packet(" QNAME: %s\n", qname); + free(qname); + debug_dnslib_packet(" QTYPE: %u (%s)\n", packet->question.qtype, + dnslib_rrtype_to_string(packet->question.qtype)); + debug_dnslib_packet(" QCLASS: %u (%s)\n", packet->question.qclass, + dnslib_rrclass_to_string(packet->question.qclass)); + + debug_dnslib_packet("\nAnswer RRSets:\n"); + dnslib_packet_dump_rrsets(packet->answer, packet->an_rrsets); + debug_dnslib_packet("\nAuthority RRSets:\n"); + dnslib_packet_dump_rrsets(packet->authority, packet->ns_rrsets); + debug_dnslib_packet("\nAdditional RRSets:\n"); + dnslib_packet_dump_rrsets(packet->additional, packet->ar_rrsets); + + /*! \todo Dumping of Answer, Authority and Additional sections. */ + + debug_dnslib_packet("\nEDNS:\n"); + debug_dnslib_packet(" Version: %u\n", packet->opt_rr.version); + debug_dnslib_packet(" Payload: %u\n", packet->opt_rr.payload); + debug_dnslib_packet(" Extended RCODE: %u\n", + packet->opt_rr.ext_rcode); + + debug_dnslib_packet("\nPacket size: %d\n", packet->size); + debug_dnslib_packet("\n-----------------------------\n"); +#endif +} + diff --git a/src/dnslib/packet.h b/src/dnslib/packet.h new file mode 100644 index 0000000000000000000000000000000000000000..a927fc67a370cb214a18fff9c63bb1ecbdf38470 --- /dev/null +++ b/src/dnslib/packet.h @@ -0,0 +1,269 @@ +/*! + * \file packet.h + * + * \author Lubos Slovak <lubos.slovak@nic.cz> + * + * \brief Structure for holding DNS packet data and metadata. + * + * \addtogroup dnslib + * @{ + */ + +#ifndef _KNOT_DNSLIB_PACKET_H_ +#define _KNOT_DNSLIB_PACKET_H_ + +#include <stdint.h> +#include <string.h> + +#include "dnslib/dname.h" +#include "dnslib/rrset.h" +#include "dnslib/edns.h" + +/*----------------------------------------------------------------------------*/ +/*! + * \brief Structure for holding information needed for compressing domain names. + * + * It's a simple table of domain names and their offsets in wire format of the + * packet. + * + * \todo Consider using some better lookup structure, such as skip-list. + */ +struct dnslib_compressed_dnames { + const dnslib_dname_t **dnames; /*!< Domain names present in packet. */ + size_t *offsets; /*!< Offsets of domain names in the packet. */ + short count; /*!< Count of items in the previous arrays. */ + short max; /*!< Capacity of the structure (allocated). */ +}; + +typedef struct dnslib_compressed_dnames dnslib_compressed_dnames_t; + +/*----------------------------------------------------------------------------*/ +/*! + * \brief Structure representing the DNS packet header. + */ +struct dnslib_header { + uint16_t id; /*!< ID stored in host byte order. */ + uint8_t flags1; /*!< First octet of header flags. */ + uint8_t flags2; /*!< Second octet of header flags. */ + uint16_t qdcount; /*!< Number of Question RRs, in host byte order. */ + uint16_t ancount; /*!< Number of Answer RRs, in host byte order. */ + uint16_t nscount; /*!< Number of Authority RRs, in host byte order. */ + uint16_t arcount; /*!< Number of Additional RRs, in host byte order. */ +}; + +typedef struct dnslib_header dnslib_header_t; + +/*! + * \brief Structure representing one Question entry in the DNS packet. + */ +struct dnslib_question { + dnslib_dname_t *qname; /*!< Question domain name. */ + uint16_t qtype; /*!< Question TYPE. */ + uint16_t qclass; /*!< Question CLASS. */ +}; + +typedef struct dnslib_question dnslib_question_t; + +enum dnslib_packet_prealloc_type { + DNSLIB_PACKET_PREALLOC_NONE, + DNSLIB_PACKET_PREALLOC_QUERY, + DNSLIB_PACKET_PREALLOC_RESPONSE +}; + +typedef enum dnslib_packet_prealloc_type dnslib_packet_prealloc_type_t; + +/*----------------------------------------------------------------------------*/ +/*! + * \brief Structure representing a DNS packet. + * + * \note QNAME, Answer, Authority and Additonal sections are by default put to + * preallocated space after the structure with default sizes. If the + * space is not enough, more space is allocated dynamically. + */ +struct dnslib_packet { + /*! \brief DNS header. */ + dnslib_header_t header; + + /*! + * \brief Question section. + * + * \note Only one Question is supported! + */ + dnslib_question_t question; + + uint8_t *owner_tmp; /*!< Allocated space for RRSet owner wire format.*/ + + const dnslib_rrset_t **answer; /*!< Answer RRSets. */ + const dnslib_rrset_t **authority; /*!< Authority RRSets. */ + const dnslib_rrset_t **additional; /*!< Additional RRSets. */ + + short an_rrsets; /*!< Count of Answer RRSets in the response. */ + short ns_rrsets; /*!< Count of Authority RRSets in the response. */ + short ar_rrsets; /*!< Count of Additional RRSets in the response. */ + + short max_an_rrsets; /*!< Allocated space for Answer RRsets. */ + short max_ns_rrsets; /*!< Allocated space for Authority RRsets. */ + short max_ar_rrsets; /*!< Allocated space for Additional RRsets. */ + + dnslib_opt_rr_t opt_rr; /*!< OPT RR included in the packet. */ + + uint8_t *wireformat; /*!< Wire format of the packet. */ + size_t size; /*!< Current wire size of the packet. */ + size_t max_size; /*!< Maximum allowed size of the packet. */ + + /*! \brief Information needed for compressing domain names in packet. */ + dnslib_compressed_dnames_t compression; + + /*! \brief RRSets to be destroyed with the packet structure. */ + const dnslib_rrset_t **tmp_rrsets; + short tmp_rrsets_count; /*!< Count of temporary RRSets. */ + short tmp_rrsets_max; /*!< Allocated space for temporary RRSets. */ + + struct dnslib_packet *query; /*!< Associated query. */ +}; + +typedef struct dnslib_packet dnslib_packet_t; + +/*----------------------------------------------------------------------------*/ +/*! + * \brief Creates new empty packet structure. + * + * \param prealloc What space should be preallocated in the structure. + * + * \return New packet structure or NULL if an error occured. + */ +dnslib_packet_t *dnslib_packet_new(dnslib_packet_prealloc_type_t prealloc); + +/*! + * \brief Parses the DNS packet from wire format. + * + * \param packet Packet structure to parse into. + * \param wireformat Wire format of the DNS packet. + * \param size Size of the wire format in bytes. + * \param question_only Set to <> 0 if you do not want to parse the whole + * packet. In such case the parsing will end after the + * Question section. Set to 0 to parse the whole packet. + * + * \retval DNSLIB_EOK + */ +int dnslib_packet_parse_from_wire(dnslib_packet_t *packet, uint8_t *wireformat, + size_t size, int question_only); + +/*! + * \brief Returns the OPCODE of the packet. + * + * \param packet Packet (with parsed query) to get the OPCODE from. + * + * \return OPCODE stored in the packet. + */ +uint8_t dnslib_packet_opcode(const dnslib_packet_t *packet); + +/*! + * \brief Returns the QNAME from the packet. + * + * \param packet Packet (with parsed query) to get the QNAME from. + * + * \return QNAME stored in the packet. + */ +const dnslib_dname_t *dnslib_packet_qname(const dnslib_packet_t *packet); + +/*! + * \brief Returns the QTYPE from the packet. + * + * \param packet Packet (with parsed query) to get the QTYPE from. + * + * \return QTYPE stored in the packet. + */ +uint16_t dnslib_packet_qtype(const dnslib_packet_t *packet); + +/*! + * \brief Returns the QCLASS from the packet. + * + * \param response Packet (with parsed query) to get the QCLASS from. + * + * \return QCLASS stored in the packet. + */ +uint16_t dnslib_packet_qclass(const dnslib_packet_t *packet); + +/*! + * \brief Returns number of RRSets in Answer section of the packet. + * + * \param response Packet to get the Answer RRSet count from. + */ +short dnslib_packet_answer_rrset_count(const dnslib_packet_t *packet); + +/*! + * \brief Returns number of RRSets in Authority section of the packet. + * + * \param response Packet to get the Authority RRSet count from. + */ +short dnslib_packet_authority_rrset_count(const dnslib_packet_t *packet); + +/*! + * \brief Returns number of RRSets in Additional section of the packet. + * + * \param response Packet to get the Additional RRSet count from. + */ +short dnslib_packet_additional_rrset_count(const dnslib_packet_t *packet); + +/*! + * \brief Returns the requested Answer RRset. + * + * \param packet Packet to get the RRSet from. + * \param pos Position of the RRSet in the Answer section (RRSets are stored + * in the order they were added to the response or parsed from the + * query). + * + * \return The RRSet on position \a pos in the Answer section of \a packet + * or NULL if there is no such RRSet. + */ +const dnslib_rrset_t *dnslib_packet_answer_rrset( + const dnslib_packet_t *packet, short pos); + +/*! + * \brief Returns the requested Authority RRset. + * + * \param packet Packet to get the RRSet from. + * \param pos Position of the RRSet in the Authority section (RRSets are stored + * in the order they were added to the response or parsed from the + * query). + * + * \return The RRSet on position \a pos in the Authority section of \a packet + * or NULL if there is no such RRSet. + */ +const dnslib_rrset_t *dnslib_packet_authority_rrset( + dnslib_packet_t *packet, short pos); + +/*! + * \brief Returns the requested Additional RRset. + * + * \param packet Packet to get the RRSet from. + * \param pos Position of the RRSet in the Additional section (RRSets are stored + * in the order they were added to the response or parsed from the + * query). + * + * \return The RRSet on position \a pos in the Additional section of \a packet + * or NULL if there is no such RRSet. + */ +const dnslib_rrset_t *dnslib_packet_additional_rrset( + dnslib_packet_t *packet, short pos); + +/*! + * \brief Properly destroys the packet structure. + * + * \param response Packet to be destroyed. + */ +void dnslib_packet_free(dnslib_packet_t **packet); + +/*! + * \brief Dumps the whole packet in human-readable form. + * + * \note This function is empty unless DNSLIB_PACKET_DEBUG is defined. + * + * \param resp Packet to dump. + */ +void dnslib_packet_dump(const dnslib_packet_t *packet); + +#endif /* _KNOT_DNSLIB_PACKET_H_ */ + +/*! @} */ diff --git a/src/dnslib/query.c b/src/dnslib/query.c new file mode 100644 index 0000000000000000000000000000000000000000..7824f79651fa5cfcef396e9e7e3291c6695be09b --- /dev/null +++ b/src/dnslib/query.c @@ -0,0 +1,15 @@ +#include "dnslib/query.h" + +/*----------------------------------------------------------------------------*/ + +int dnslib_response_dnssec_requested(const dnslib_packet_t *query) +{ + return dnslib_edns_do(&query->opt_rr); +} + +/*----------------------------------------------------------------------------*/ + +int dnslib_response_nsid_requested(const dnslib_packet_t *query) +{ + return dnslib_edns_has_option(&query->opt_rr, EDNS_OPTION_NSID); +} diff --git a/src/dnslib/query.h b/src/dnslib/query.h new file mode 100644 index 0000000000000000000000000000000000000000..f78ed8887ad09fba6d71544fd55768c8445207a9 --- /dev/null +++ b/src/dnslib/query.h @@ -0,0 +1,49 @@ +/*! + * \file query.h + * + * \author Lubos Slovak <lubos.slovak@nic.cz> + * + * \brief API for manipulating queries. + * + * \addtogroup dnslib + * @{ + */ + +#ifndef _KNOT_DNSLIB_QUERY_H_ +#define _KNOT_DNSLIB_QUERY_H_ + +#include <stdint.h> +#include <string.h> + +#include "dnslib/packet.h" +#include "dnslib/dname.h" +#include "dnslib/rrset.h" +#include "dnslib/edns.h" + +/*----------------------------------------------------------------------------*/ +/*! + * \brief Checks if DNSSEC was requested in the query (i.e. the DO bit was set). + * + * \param query Packet where the parsed query is stored. + * + * \retval 0 if the DO bit was not set in the query, or the query is not yet + * parsed. + * \retval > 0 if DO bit was set in the query. + */ +int dnslib_query_dnssec_requested(const dnslib_packet_t *query); + +/*! + * \brief Checks if NSID was requested in the query (i.e. the NSID option was + * present in the query OPT RR). + * + * \param query Packet where the parsed query is stored. + * + * \retval 0 if the NSID option was not present in the query, or the query is + * not yet parsed. + * \retval > 0 if the NSID option was present in the query. + */ +int dnslib_query_nsid_requested(const dnslib_packet_t *query); + +#endif /* _KNOT_DNSLIB_QUERY_H_ */ + +/*! @} */ diff --git a/src/dnslib/response2.c b/src/dnslib/response2.c new file mode 100644 index 0000000000000000000000000000000000000000..a873c08a0dc6362fdec2deb93d4a63cad61415ad --- /dev/null +++ b/src/dnslib/response2.c @@ -0,0 +1,277 @@ +#include "dnslib/response2.h" + +/*----------------------------------------------------------------------------*/ + +int dnslib_packet_response_from_query(dnslib_packet_t *response, + dnslib_packet_t *query) +{ + /*! \todo Implement! */ + return DNSLIB_ERROR; +} + +/*----------------------------------------------------------------------------*/ + +int dnslib_response_add_opt(dnslib_packet_t *resp, + const dnslib_opt_rr_t *opt_rr, + int override_max_size) +{ + if (resp == NULL || opt_rr == NULL) { + return DNSLIB_EBADARG; + } + + // copy the OPT RR + resp->edns_response.version = opt_rr->version; + resp->edns_response.ext_rcode = opt_rr->ext_rcode; + resp->edns_response.payload = opt_rr->payload; + resp->edns_response.size = opt_rr->size; + + // if max size is set, it means there is some reason to be that way, + // so we can't just set it to higher value + + if (override_max_size && resp->max_size > 0 + && resp->max_size < opt_rr->payload) { + return DNSLIB_EPAYLOAD; + } + + // set max size (less is OK) + if (override_max_size) { + resp->max_size = resp->edns_response.payload; + } + + return DNSLIB_EOK; +} + +/*----------------------------------------------------------------------------*/ + +int dnslib_response_set_max_size(dnslib_packet_t *resp, int max_size) +{ + if (resp == NULL || max_size <= 0) { + return DNSLIB_EBADARG; + } + + if (resp->max_size < max_size) { + // reallocate space for the wire format (and copy anything + // that might have been there before + uint8_t *wire_new = (uint8_t *)malloc(max_size); + if (wire_new == NULL) { + return DNSLIB_ENOMEM; + } + + memcpy(wire_new, resp->wireformat, resp->max_size); + resp->wireformat = wire_new; + } + + // set max size + resp->max_size = max_size; + + return DNSLIB_EOK; +} + +/*----------------------------------------------------------------------------*/ + +int dnslib_response_add_rrset_answer(dnslib_packet_t *response, + const dnslib_rrset_t *rrset, int tc, + int check_duplicates, int compr_cs) +{ + if (response == NULL || rrset == NULL) { + return DNSLIB_EBADARG; + } + + debug_dnslib_response("add_rrset_answer()\n"); + assert(response->header.arcount == 0); + assert(response->header.nscount == 0); + + if (response->an_rrsets == response->max_an_rrsets + && dnslib_response_realloc_rrsets(&response->answer, + &response->max_an_rrsets, DEFAULT_ANCOUNT, STEP_ANCOUNT) + != DNSLIB_EOK) { + return DNSLIB_ENOMEM; + } + + if (check_duplicates && dnslib_response_contains(response, rrset)) { + return DNSLIB_EOK; + } + + debug_dnslib_response("Trying to add RRSet to Answer section.\n"); + debug_dnslib_response("RRset: %p\n", rrset); + debug_dnslib_response("Owner: %p\n", rrset->owner); + + int rrs = dnslib_response_try_add_rrset(response->answer, + &response->an_rrsets, response, + response->max_size + - response->size + - response->edns_response.size, + rrset, tc, compr_cs); + + if (rrs >= 0) { + response->header.ancount += rrs; + return DNSLIB_EOK; + } + + return DNSLIB_ESPACE; +} + +/*----------------------------------------------------------------------------*/ + +int dnslib_response_add_rrset_authority(dnslib_packet_t *response, + const dnslib_rrset_t *rrset, int tc, + int check_duplicates, int compr_cs) +{ + if (response == NULL || rrset == NULL) { + return DNSLIB_EBADARG; + } + + assert(response->header.arcount == 0); + + if (response->ns_rrsets == response->max_ns_rrsets + && dnslib_response_realloc_rrsets(&response->authority, + &response->max_ns_rrsets, DEFAULT_NSCOUNT, STEP_NSCOUNT) + != 0) { + return DNSLIB_ENOMEM; + } + + if (check_duplicates && dnslib_response_contains(response, rrset)) { + return DNSLIB_EOK; + } + + debug_dnslib_response("Trying to add RRSet to Authority section.\n"); + + int rrs = dnslib_response_try_add_rrset(response->authority, + &response->ns_rrsets, response, + response->max_size + - response->size + - response->edns_response.size, + rrset, tc, compr_cs); + + if (rrs >= 0) { + response->header.nscount += rrs; + return DNSLIB_EOK; + } + + return DNSLIB_ESPACE; +} + +/*----------------------------------------------------------------------------*/ + +int dnslib_response_add_rrset_additional(dnslib_packet_t *response, + const dnslib_rrset_t *rrset, int tc, + int check_duplicates, int compr_cs) +{ + if (response == NULL || rrset == NULL) { + return DNSLIB_EBADARG; + } + + // if this is the first additional RRSet, add EDNS OPT RR first + if (response->header.arcount == 0 + && response->edns_response.version != EDNS_NOT_SUPPORTED) { + dnslib_response_edns_to_wire(response); + } + + if (response->ar_rrsets == response->max_ar_rrsets + && dnslib_response_realloc_rrsets(&response->additional, + &response->max_ar_rrsets, DEFAULT_ARCOUNT, STEP_ARCOUNT) + != 0) { + return DNSLIB_ENOMEM; + } + + if (check_duplicates && dnslib_response_contains(response, rrset)) { + return DNSLIB_EOK; + } + + debug_dnslib_response("Trying to add RRSet to Additional section.\n"); + + int rrs = dnslib_response_try_add_rrset(response->additional, + &response->ar_rrsets, response, + response->max_size + - response->size, rrset, tc, + compr_cs); + + if (rrs >= 0) { + response->header.arcount += rrs; + return DNSLIB_EOK; + } + + return DNSLIB_ESPACE; +} + +/*----------------------------------------------------------------------------*/ + +void dnslib_response_set_rcode(dnslib_packet_t *response, short rcode) +{ + dnslib_wire_flags_set_rcode(&response->header.flags2, rcode); + dnslib_wire_set_rcode(response->wireformat, rcode); +} + +/*----------------------------------------------------------------------------*/ + +void dnslib_response_set_aa(dnslib_packet_t *response) +{ + dnslib_wire_flags_set_aa(&response->header.flags1); + dnslib_wire_set_aa(response->wireformat); +} + +/*----------------------------------------------------------------------------*/ + +void dnslib_response_set_tc(dnslib_packet_t *response) +{ + dnslib_wire_flags_set_tc(&response->header.flags1); + dnslib_wire_set_tc(response->wireformat); +} + +/*----------------------------------------------------------------------------*/ + +int dnslib_response_add_tmp_rrset(dnslib_packet_t *response, + dnslib_rrset_t *tmp_rrset) +{ + if (response->tmp_rrsets_count == response->tmp_rrsets_max + && dnslib_response_realloc_rrsets(&response->tmp_rrsets, + &response->tmp_rrsets_max, DEFAULT_TMP_RRSETS, + STEP_TMP_RRSETS) != DNSLIB_EOK) { + return DNSLIB_ENOMEM; + } + + response->tmp_rrsets[response->tmp_rrsets_count++] = tmp_rrset; + + return DNSLIB_EOK; +} + +/*----------------------------------------------------------------------------*/ + +int dnslib_response_add_nsid(dnslib_packet_t *response, const uint8_t *data, + uint16_t length) +{ + return dnslib_edns_add_option(&response->edns_response, + EDNS_OPTION_NSID, length, data); +} + +/*----------------------------------------------------------------------------*/ + +int dnslib_response_to_wire(dnslib_packet_t *resp, + uint8_t **resp_wire, size_t *resp_size) +{ + if (resp == NULL || resp_wire == NULL || resp_size == NULL + || *resp_wire != NULL) { + return DNSLIB_EBADARG; + } + + assert(resp->size <= resp->max_size); + + // if there are no additional RRSets, add EDNS OPT RR + if (resp->header.arcount == 0 + && resp->edns_response.version != EDNS_NOT_SUPPORTED) { + dnslib_response_edns_to_wire(resp); + } + + // set ANCOUNT to the packet + dnslib_wire_set_ancount(resp->wireformat, resp->header.ancount); + // set NSCOUNT to the packet + dnslib_wire_set_nscount(resp->wireformat, resp->header.nscount); + // set ARCOUNT to the packet + dnslib_wire_set_arcount(resp->wireformat, resp->header.arcount); + + //assert(response->size == size); + *resp_wire = resp->wireformat; + *resp_size = resp->size; + + return DNSLIB_EOK; +} diff --git a/src/dnslib/response2.h b/src/dnslib/response2.h new file mode 100644 index 0000000000000000000000000000000000000000..ff5686fabd07c874f4d77240ec37eb8a9c75bd83 --- /dev/null +++ b/src/dnslib/response2.h @@ -0,0 +1,219 @@ +/*! + * \file response2.h + * + * \author Lubos Slovak <lubos.slovak@nic.cz> + * + * \brief API for response manipulation. + * + * \addtogroup dnslib + * @{ + */ + +#ifndef _KNOT_DNSLIB_RESPONSE2_H_ +#define _KNOT_DNSLIB_RESPONSE2_H_ + +#include <stdint.h> +#include <string.h> + +#include "dnslib/packet.h" + +#include "dnslib/dname.h" +#include "dnslib/rrset.h" +#include "dnslib/edns.h" + +/*! + * \brief Default maximum DNS response size + * + * This size must be supported by all servers and clients. + */ +static const short DNSLIB_MAX_RESPONSE_SIZE = 512; + +/*----------------------------------------------------------------------------*/ +/*! + * \brief Initializes response from the given query. + * + * Copies the header, Changes QR bit to 1, copies the Question section and + * stores pointer to the query packet structure in the response packet + * structure. + * + * \param response Packet structure representing the response. + * \param query Packet structure representing the query. + * + * \retval DNSLIB_EOK + */ +int dnslib_packet_response_from_query(dnslib_packet_t *response, + dnslib_packet_t *query); + +/*! + * \brief Sets the OPT RR of the response. + * + * This function also allocates space for the wireformat of the response, if + * the payload in the OPT RR is larger than the current maximum size of the + * response and copies the current wireformat over to the new space. + * + * \note The contents of the OPT RR are copied. + * + * \param resp Response to set the OPT RR to. + * \param opt_rr OPT RR to set. + * + * \retval DNSLIB_EOK + * \retval DNSLIB_EBADARG + * \retval DNSLIB_ENOMEM + * + * \todo Needs test. + */ +int dnslib_response_add_opt(dnslib_packet_t *resp, + const dnslib_opt_rr_t *opt_rr, + int override_max_size); + +/*! + * \brief Sets the maximum size of the response and allocates space for wire + * format (if needed). + * + * This function also allocates space for the wireformat of the response, if + * the given max size is larger than the current maximum size of the response + * and copies the current wireformat over to the new space. + * + * \warning Do not call this function if you are not completely sure that the + * current wire format of the response fits into the new space. + * It does not update the current size of the wire format, so the + * produced response may be larger than the given max size. + * + * \param resp Response to set the maximum size of. + * \param max_size Maximum size of the response. + * + * \retval DNSLIB_EOK + * \retval DNSLIB_EBADARG + * \retval DNSLIB_ENOMEM + * + * \todo Needs test. + */ +int dnslib_response_set_max_size(dnslib_packet_t *resp, int max_size); + +/*! + * \brief Adds a RRSet to the Answer section of the response. + * + * \param response Response to add the RRSet into. + * \param rrset RRSet to be added. + * \param tc Set to <> 0 if omitting this RRSet should result in the TC bit set. + * Otherwise set to 0. + * \param check_duplicates Set to <> 0 if the RRSet should not be added to the + * response in case it is already there. + * \param compr_cs Set to <> 0 if dname compression should use case sensitive + * comparation. Set to 0 otherwise. + * + * \retval DNSLIB_EOK if successful, or the RRSet was already in the answer. + * \retval DNSLIB_ENOMEM + * \retval DNSLIB_ESPACE + */ +int dnslib_response_add_rrset_answer(dnslib_packet_t *response, + const dnslib_rrset_t *rrset, int tc, + int check_duplicates, int compr_cs); + +/*! + * \brief Adds a RRSet to the Authority section of the response. + * + * \param response Response to add the RRSet into. + * \param rrset RRSet to be added. + * \param tc Set to <> 0 if omitting this RRSet should result in the TC bit set. + * Otherwise set to 0. + * \param check_duplicates Set to <> 0 if the RRSet should not be added to the + * response in case it is already there. + * \param compr_cs Set to <> 0 if dname compression should use case sensitive + * comparation. Set to 0 otherwise. + * + * \retval DNSLIB_EOK if successful, or the RRSet was already in the answer. + * \retval DNSLIB_ENOMEM + * \retval DNSLIB_ESPACE + */ +int dnslib_response_add_rrset_authority(dnslib_packet_t *response, + const dnslib_rrset_t *rrset, int tc, + int check_duplicates, int compr_cs); + +/*! + * \brief Adds a RRSet to the Additional section of the response. + * + * \param response Response to add the RRSet into. + * \param rrset RRSet to be added. + * \param tc Set to <> 0 if omitting this RRSet should result in the TC bit set. + * Otherwise set to 0. + * \param check_duplicates Set to <> 0 if the RRSet should not be added to the + * response in case it is already there. + * \param compr_cs Set to <> 0 if dname compression should use case sensitive + * comparation. Set to 0 otherwise. + * + * \retval DNSLIB_EOK if successful, or the RRSet was already in the answer. + * \retval DNSLIB_ENOMEM + * \retval DNSLIB_ESPACE + */ +int dnslib_response_add_rrset_additional(dnslib_packet_t *response, + const dnslib_rrset_t *rrset, int tc, + int check_duplicates, int compr_cs); + +/*! + * \brief Sets the RCODE of the response. + * + * \param response Response to set the RCODE in. + * \param rcode RCODE to set. + */ +void dnslib_response_set_rcode(dnslib_packet_t *response, short rcode); + +/*! + * \brief Sets the AA bit of the response to 1. + * + * \param response Response in which the AA bit should be set. + */ +void dnslib_response_set_aa(dnslib_packet_t *response); + +/*! + * \brief Sets the TC bit of the response to 1. + * + * \param response Response in which the TC bit should be set. + */ +void dnslib_response_set_tc(dnslib_packet_t *response); + +/*! + * \brief Adds RRSet to the list of temporary RRSets. + * + * Temporary RRSets are fully freed when the response structure is destroyed. + * + * \param response Response to which the temporary RRSet should be added. + * \param tmp_rrset Temporary RRSet to be stored in the response. + * + * \retval DNSLIB_EOK + * \retval DNSLIB_ENOMEM + */ +int dnslib_response_add_tmp_rrset(dnslib_packet_t *response, + dnslib_rrset_t *tmp_rrset); + +/*! + * \brief Adds NSID option to the response. + * + * \param response Response to add the NSID option into. + * \param data NSID data. + * \param length Size of NSID data in bytes. + * + * \retval DNSLIB_EOK + * \retval DNSLIB_ENOMEM + */ +int dnslib_response_add_nsid(dnslib_packet_t *response, const uint8_t *data, + uint16_t length); + +/*! + * \brief Converts the response to wire format. + * + * \param response Response to be converted to wire format. + * \param resp_wire Here the wire format of the response will be stored. + * Space for the response will be allocated. *resp_wire must + * be set to NULL (to avoid leaks). + * \param resp_size The size of the response in wire format will be stored here. + * + * \retval DNSLIB_EOK + * \retval DNSLIB_EBADARG + */ +int dnslib_response_to_wire(dnslib_packet_t *response, + uint8_t **resp_wire, size_t *resp_size); + +#endif /* _KNOT_DNSLIB_RESPONSE2_H_ */ + +/*! @} */