// Matrix Construct // // Copyright (C) Matrix Construct Developers, Authors & Contributors // Copyright (C) 2016-2018 Jason Volk // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice is present in all copies. The // full license for this software is available in the LICENSE file. #pragma once #define HAVE_IRCD_RFC1035_H /// RFC 1035 "Domain Names" (Nov. 1987) /// namespace ircd::rfc1035 { IRCD_EXCEPTION(ircd::error, error) struct header; struct question; struct answer; struct record; enum class op :uint8_t; // Section 2.3.4 Size Limits #ifndef NAME_MAX constexpr size_t NAME_MAX {255}; #endif constexpr size_t LABEL_MAX {63}; constexpr size_t TTL_MAX {std::numeric_limits::max()}; constexpr size_t LABEL_BUFSIZE {LABEL_MAX + 1}; constexpr size_t NAME_BUFSIZE {NAME_MAX + 1}; extern const std::array rcode; extern const std::unordered_map qtype; extern const std::map rqtype; bool valid_label(std::nothrow_t, const string_view &); bool valid_name(std::nothrow_t, const string_view &); void valid_label(const string_view &); void valid_name(const string_view &); const_buffer make_name(const mutable_buffer &out, const string_view &fqdn); size_t parse_name(const mutable_buffer &out, const const_buffer &in); mutable_buffer make_query(const mutable_buffer &, const header &, const vector_view &); mutable_buffer make_query(const mutable_buffer &, const uint16_t &id, const vector_view &); mutable_buffer make_query(const mutable_buffer &, const uint16_t &id, const question &); } /// Direct representation of the DNS header. This is laid out for /// little-endian platforms only. The uint16_t's are big endian when this is /// punned on the wire data. The debug() function makes it into a pretty /// string but makes no endian adjustments. /// struct ircd::rfc1035::header { uint16_t id; ///< query identification number uint8_t rd:1; ///< 0 = recursion not desired; 1 = desired uint8_t tc:1; ///< 0 = not-truncated; 1 = 512 bytes of reply only uint8_t aa:1; ///< 0 = non-authoritative; 1 = authoritative uint8_t opcode:4; ///< purpose of message uint8_t qr:1; ///< 0 = query; 1 = respnse uint8_t rcode:4; ///< response code uint8_t cd:1; ///< checking disabled by resolver uint8_t ad:1; ///< authentic data from named uint8_t unused:1; ///< unused bits (MBZ as of 4.9.3a3) uint8_t ra:1; ///< 1 = recursion available uint16_t qdcount; ///< number of question entries uint16_t ancount; ///< number of answer entries uint16_t nscount; ///< number of authority entries uint16_t arcount; ///< number of resource entries std::string debug() const; } __attribute__((packed)); static_assert ( sizeof(ircd::rfc1035::header) == 12, "The RFC1035 header is not the right size in this environment" ); /// DNS operation code /// enum class ircd::rfc1035::op :uint8_t { QUERY = 0, ///< Query [RFC 1035] IQUERY = 1, ///< Inverse Query [RFC 1035, RFC 3425] STATUS = 2, ///< Server status request [RFC 1035] NOTIFY = 4, ///< Notify [RFC 1996] UPDATE = 5, ///< Update [RFC 2136] }; /// Helper class to construct or parse a question. An object is constructed /// with a fully qualified domain string and the query type (qtype). /// /// Note that each part of the fqdn cannot be longer than 63 characters. The /// supplied buffer must be large enough to hold the output, which is about /// the length of the fqdn + 6 bytes. The qtype can be specified as a string /// i.e "A" or "PTR" and it will be translated into the protocol number for /// you in the constructor. All integers are dealt with in host byte order. /// struct ircd::rfc1035::question { uint16_t qtype {0}; uint16_t qclass {0x01}; string_view name; char namebuf[NAME_BUFSIZE]; /// Composes the question into buffer, returns used portion mutable_buffer print(const mutable_buffer &) const; const_buffer parse(const const_buffer &); /// Supply fully qualified domain name and numerical query type question(const string_view &fqdn, const uint16_t &qtype); /// Supply fully qualified domain name and name of query type i.e "A" question(const string_view &fqdn, const string_view &qtype) :question{fqdn, rfc1035::qtype.at(qtype)} {} question() = default; }; /// Helper class to parse an answer. When the DNS header is received we get /// an answer count. For each answer in the answer section parse() is called /// which stocks this object and then returns a buffer tight to that specific /// answer section. The `rdata` is the actual record content which the user /// can then treat later with rfc1035::record. All integers are dealt with in /// host byte order. /// struct ircd::rfc1035::answer { uint16_t qtype {0}; uint16_t qclass {0}; uint32_t ttl {0}; uint16_t rdlength {0}; const_buffer rdata; string_view name; char namebuf[NAME_BUFSIZE]; const_buffer parse(const const_buffer &); answer() = default; }; /// Resource record abstract base. The purpose of this abstraction is to allow /// records of any variety to all be dealt with via a `rfc1035::record *` ptr /// and then be downcasted to the specific derived type elaborated below. Use /// the as() template to downcast. /// /// Generally this object is not instantiated directly; each record type will /// construct this instead. Nevertheless, the full raw data and type number /// for the record is available in here. All constructors (for both this /// abstraction and for the derivations) take an already-parsed rfc1035::answer /// as their argument; the qtype and ttl information from the answer header is /// included while the legacy qclass is omitted. /// struct ircd::rfc1035::record { struct A; struct AAAA; struct CNAME; struct SRV; uint16_t type {0}; time_t ttl {0}; const_buffer rdata; template const T &as() const; record(const answer &); record(const uint16_t &type); record() = default; virtual ~record() noexcept; }; namespace ircd::rfc1035 { bool operator==(const record::A &, const record::A &); bool operator!=(const record::A &, const record::A &); bool operator==(const record::AAAA &, const record::AAAA &); bool operator!=(const record::AAAA &, const record::AAAA &); bool operator==(const record::CNAME &, const record::CNAME &); bool operator!=(const record::CNAME &, const record::CNAME &); bool operator==(const record::SRV &, const record::SRV &); bool operator!=(const record::SRV &, const record::SRV &); } /// Downcast an abstract record reference to the specific record structure. template const T & ircd::rfc1035::record::as() const { return dynamic_cast(*this); } // // Types of records // /// IPv4 address record. /// The integer is laid out in host byte order. struct ircd::rfc1035::record::A :record { uint32_t ip4 {0}; void append(json::stack::object &) const; A(const answer &); A(); }; /// IPv6 address record. /// The integer is laid out in host byte order. struct ircd::rfc1035::record::AAAA :record { uint128_t ip6 {0}; void append(json::stack::object &) const; AAAA(const answer &); AAAA(); }; /// Canonical name aliasing record struct ircd::rfc1035::record::CNAME :record { string_view name; char namebuf[NAME_BUFSIZE]; void append(json::stack::object &) const; CNAME(const answer &); CNAME(); }; /// Service record. /// The integers are laid out in host byte order. struct ircd::rfc1035::record::SRV :record { uint16_t priority {0}; uint16_t weight {0}; uint16_t port {0}; string_view tgt; char tgtbuf[NAME_BUFSIZE]; void append(json::stack::object &) const; SRV(const answer &); SRV(); };