Program Listing for File dns_format.h

Return to documentation for file (utils/dns_format.h)

#pragma once

// Including \0 terminators
#define DOMAIN_NAME_MAX_SIZE 256
#define CHARACTER_STRING_MAX_SIZE 1800
#define CAA_TAG_MAX_SIZE 16

#include <stdint.h>

#include <array>
#include <optional>
#include <string_view>

#include "fixed_name.hpp"

enum class DnsQType {
    A = 1,      // Ipv4 address
    NS = 2,     // Nameserver
    CNAME = 5,  // Canonical name
    SOA = 6,    // Start of authority zone
    PTR = 12,   // Domain name pointer
    MX = 15,    // Mail server
    TXT = 16,   // Txt record
    AAAA = 28,  // Ipv6 address
    DNAME = 39, // Delegation name record
    OPT = 41,   // Edns opt record
    CAA = 257,  // Certificate Authority Authorization
};

template <>
struct glz::meta<DnsQType> {
    using enum DnsQType;
    static constexpr auto value =
        enumerate(A, NS, CNAME, SOA, PTR, MX, TXT, AAAA, DNAME, CAA, OPT);
};

inline const char* GetQTypeMessage(const DnsQType q_type) {
    switch (q_type) {
        case DnsQType::A:
            return "A";
        case DnsQType::NS:
            return "NS";
        case DnsQType::CNAME:
            return "CNAME";
        case DnsQType::DNAME:
            return "DNAME";
        case DnsQType::SOA:
            return "SOA";
        case DnsQType::PTR:
            return "PTR";
        case DnsQType::MX:
            return "MX";
        case DnsQType::TXT:
            return "TXT";
        case DnsQType::AAAA:
            return "AAAA";
        case DnsQType::CAA:
            return "CAA";
        case DnsQType::OPT:
            return "OPT";
        default:
            return "unknown";
    }
}

inline std::optional<DnsQType> GetQTypeFromString(std::string_view q_type) {
    if (q_type == "A") {
        return DnsQType::A;
    } else if (q_type == "NS") {
        return DnsQType::NS;
    } else if (q_type == "CNAME") {
        return DnsQType::CNAME;
    } else if (q_type == "DNAME") {
        return DnsQType::DNAME;
    } else if (q_type == "SOA") {
        return DnsQType::SOA;
    } else if (q_type == "PTR") {
        return DnsQType::PTR;
    } else if (q_type == "MX") {
        return DnsQType::MX;
    } else if (q_type == "TXT") {
        return DnsQType::TXT;
    } else if (q_type == "AAAA") {
        return DnsQType::AAAA;
    } else if (q_type == "CAA") {
        return DnsQType::CAA;
    } else if (q_type == "OPT") {
        return DnsQType::OPT;
    }

    return std::nullopt;
}

enum class DnsRCode {
    NOERROR = 0,
    FORMERROR = 1,
    SERVFAIL = 2,
    NXDOMAIN = 3,
    NOTIMP = 4,
    REFUSED = 5,
    YXDOMAIN = 6,
    XYRRSET = 7,
    NXRRSET = 8,
    NOTAUTH = 9,
    NOTZONE = 10,
    DSOTYPENI = 11,
    BADVERS = 16,
    BADKEY = 17,
    BADTIME = 18,
    BADMODE = 19,
    BADNAM = 20,
    BADALG = 21,
    BADTRUNC = 22,
    BADCOOKIE = 23
};

template <>
struct glz::meta<DnsRCode> {
    using enum DnsRCode;
    static constexpr auto value = enumerate(NOERROR, FORMERROR, SERVFAIL, NXDOMAIN, NOTIMP,
        REFUSED, YXDOMAIN, XYRRSET, NXRRSET, NOTAUTH, NOTZONE, DSOTYPENI, BADVERS, BADKEY,
        BADTIME, BADMODE, BADNAM, BADALG, BADTRUNC, BADCOOKIE);
};

inline const char* GetRCodeMessage(const DnsRCode r_code) {
    switch (r_code) {
        case DnsRCode::NOERROR:
            return "NOERROR";
            break;
        case DnsRCode::FORMERROR:
            return "FORMERROR";
            break;
        case DnsRCode::SERVFAIL:
            return "SERVFAIL";
            break;
        case DnsRCode::NXDOMAIN:
            return "NXDOMAIN";
            break;
        case DnsRCode::NOTIMP:
            return "NOTIMP";
            break;
        case DnsRCode::REFUSED:
            return "REFUSED";
            break;
        case DnsRCode::YXDOMAIN:
            return "YXDOMAIN";
            break;
        case DnsRCode::XYRRSET:
            return "XYRRSET";
            break;
        case DnsRCode::NOTAUTH:
            return "NOTAUTH";
            break;
        case DnsRCode::NOTZONE:
            return "NOTZONE";
            break;
        case DnsRCode::DSOTYPENI:
            return "DSOTYPENI";
            break;
        case DnsRCode::BADVERS:
            return "BADVERS";
            break;
        case DnsRCode::BADKEY:
            return "BADKEY";
            break;
        case DnsRCode::BADTIME:
            return "BADTIME";
            break;
        case DnsRCode::BADMODE:
            return "BADMODE";
            break;
        case DnsRCode::BADNAM:
            return "BADNAM";
            break;
        case DnsRCode::BADALG:
            return "BADALG";
            break;
        case DnsRCode::BADTRUNC:
            return "BADTRUNC";
            break;
        case DnsRCode::BADCOOKIE:
            return "BADCOOKIE";
            break;

        default:
            return "unknown";
            break;
    }
}

inline std::optional<DnsRCode> GetRCodeFromMessage(std::string_view error_msg) {
    if (error_msg == "NOERROR")
        return DnsRCode::NOERROR;
    else if (error_msg == "FORMERROR")
        return DnsRCode::FORMERROR;
    else if (error_msg == "SERVFAIL")
        return DnsRCode::SERVFAIL;
    else if (error_msg == "NXDOMAIN")
        return DnsRCode::NXDOMAIN;
    else if (error_msg == "NOTIMP")
        return DnsRCode::NOTIMP;
    else if (error_msg == "REFUSED")
        return DnsRCode::REFUSED;
    else if (error_msg == "YXDOMAIN")
        return DnsRCode::YXDOMAIN;
    else if (error_msg == "XYRRSET")
        return DnsRCode::XYRRSET;
    else if (error_msg == "NOTAUTH")
        return DnsRCode::NOTAUTH;
    else if (error_msg == "NOTZONE")
        return DnsRCode::NOTZONE;
    else if (error_msg == "DSOTYPENI")
        return DnsRCode::DSOTYPENI;
    else if (error_msg == "BADVERS")
        return DnsRCode::BADVERS;
    else if (error_msg == "BADKEY")
        return DnsRCode::BADKEY;
    else if (error_msg == "BADTIME")
        return DnsRCode::BADTIME;
    else if (error_msg == "BADMODE")
        return DnsRCode::BADMODE;
    else if (error_msg == "BADNAM")
        return DnsRCode::BADNAM;
    else if (error_msg == "BADALG")
        return DnsRCode::BADALG;
    else if (error_msg == "BADTRUNC")
        return DnsRCode::BADTRUNC;
    else if (error_msg == "BADCOOKIE")
        return DnsRCode::BADCOOKIE;

    return std::nullopt;
}

struct [[gnu::packed]] RData {
    unsigned short type;
    unsigned short _class;
    unsigned int ttl;
    unsigned short data_len;
};

struct [[gnu::packed]] QuestionInfo {
    unsigned short qtype;
    unsigned short qclass;
};

using DnsName = FixedName<DOMAIN_NAME_MAX_SIZE>;
using TxtString = FixedName<CHARACTER_STRING_MAX_SIZE>;
using CAATag = FixedName<CAA_TAG_MAX_SIZE>;

struct [[gnu::packed]] DnsHeader {
    unsigned short id; // identification number

    unsigned char rd     : 1; // recursion desired
    unsigned char tc     : 1; // truncated message
    unsigned char aa     : 1; // authoritive answer
    unsigned char opcode : 4; // purpose of message
    unsigned char qr     : 1; // query/response flag

    unsigned char rcode : 4; // response code
    unsigned char cd    : 1; // checking disabled
    unsigned char ad    : 1; // authenticated data
    unsigned char z     : 1; // its z! reserved
    unsigned char ra    : 1; // recursion available

    unsigned short q_count;    // number of question entries
    unsigned short ans_count;  // number of answer entries
    unsigned short auth_count; // number of authority entries
    unsigned short add_count;  // number of resource entries
};