/* * Copyright (c) 2024, Ali Mohammad Pur * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include namespace DNS { namespace Messages { struct DomainName; struct ParseContext { CountingStream& stream; NonnullOwnPtr> pointers; }; enum class OpCode : u8; struct Options { // 1 1 1 1 1 1 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ // | ID | // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ // |QR| Opcode |AA|TC|RD|RA| Z |AD|CD| RCODE | constexpr inline static u16 QRMask = 0b1000000000000000; constexpr inline static u16 OpCodeMask = 0b0111100000000000; constexpr inline static u16 AuthoritativeAnswerMask = 0b0000010000000000; constexpr inline static u16 TruncatedMask = 0b0000001000000000; constexpr inline static u16 RecursionDesiredMask = 0b0000000100000000; constexpr inline static u16 RecursionAvailableMask = 0b0000000010000000; constexpr inline static u16 AuthenticatedDataMask = 0b0000000000100000; constexpr inline static u16 CheckingDisabledMask = 0b0000000000010000; constexpr inline static u16 ResponseCodeMask = 0b0000000000001111; enum class ResponseCode : u16 { NoError = 0, FormatError = 1, ServerFailure = 2, NameError = 3, NotImplemented = 4, Refused = 5, }; void set_is_question(bool value) { raw = (raw & ~QRMask) | (value ? QRMask : 0); } void set_is_authoritative_answer(bool value) { raw = (raw & ~AuthoritativeAnswerMask) | (value ? AuthoritativeAnswerMask : 0); } void set_is_truncated(bool value) { raw = (raw & ~TruncatedMask) | (value ? TruncatedMask : 0); } void set_recursion_desired(bool value) { raw = (raw & ~RecursionDesiredMask) | (value ? RecursionDesiredMask : 0); } void set_recursion_available(bool value) { raw = (raw & ~RecursionAvailableMask) | (value ? RecursionAvailableMask : 0); } void set_response_code(ResponseCode code) { raw = (raw & ~ResponseCodeMask) | static_cast(code); } void set_checking_disabled(bool value) { raw = (raw & ~CheckingDisabledMask) | (value ? CheckingDisabledMask : 0); } void set_authenticated_data(bool value) { raw = (raw & ~AuthenticatedDataMask) | (value ? AuthenticatedDataMask : 0); } void set_op_code(OpCode code) { raw = (raw & ~OpCodeMask) | (static_cast(code) << 11); } bool is_question() const { return (raw & QRMask) == 0; } bool is_authoritative_answer() const { return (raw & AuthoritativeAnswerMask) != 0; } bool is_truncated() const { return (raw & TruncatedMask) != 0; } bool recursion_desired() const { return (raw & RecursionDesiredMask) != 0; } bool recursion_available() const { return (raw & RecursionAvailableMask) != 0; } bool checking_disabled() const { return (raw & CheckingDisabledMask) != 0; } bool authenticated_data() const { return (raw & AuthenticatedDataMask) != 0; } ResponseCode response_code() const { return static_cast(raw & ResponseCodeMask); } OpCode op_code() const { return static_cast((raw & OpCodeMask) >> 11); } String to_string() const; NetworkOrdered raw { 0 }; }; StringView to_string(Options::ResponseCode); struct Header { NetworkOrdered id; Options options; NetworkOrdered question_count; NetworkOrdered answer_count; NetworkOrdered authority_count; NetworkOrdered additional_count; }; struct DomainName { Vector labels; static DomainName from_string(StringView); static ErrorOr from_raw(ParseContext&); ErrorOr to_raw(ByteBuffer&) const; String to_string() const; }; // Listing from IANA https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4. enum class ResourceType : u16 { Reserved = 0, // [RFC6895] A = 1, // a host address [RFC1035] NS = 2, // an authoritative name server [RFC1035] MD = 3, // a mail destination (OBSOLETE - use MX) [RFC1035] MF = 4, // a mail forwarder (OBSOLETE - use MX) [RFC1035] CNAME = 5, // the canonical name for an alias [RFC1035] SOA = 6, // marks the start of a zone of authority [RFC1035] MB = 7, // a mailbox domain name (EXPERIMENTAL) [RFC1035] MG = 8, // a mail group member (EXPERIMENTAL) [RFC1035] MR = 9, // a mail rename domain name (EXPERIMENTAL) [RFC1035] NULL_ = 10, // a null RR (EXPERIMENTAL) [RFC1035] WKS = 11, // a well known service description [RFC1035] PTR = 12, // a domain name pointer [RFC1035] HINFO = 13, // host information [RFC1035] MINFO = 14, // mailbox or mail list information [RFC1035] MX = 15, // mail exchange [RFC1035] TXT = 16, // text strings [RFC1035] RP = 17, // for Responsible Person [RFC1183] AFSDB = 18, // for AFS Data Base location [RFC1183][RFC5864] X25 = 19, // for X.25 PSDN address [RFC1183] ISDN = 20, // for ISDN address [RFC1183] RT = 21, // for Route Through [RFC1183] NSAP = 22, // for NSAP address, NSAP style A record (DEPRECATED) [RFC1706][Moving TPC.INT and NSAP.INT infrastructure domains to historic] NSAP_PTR = 23, // for domain name pointer, NSAP style (DEPRECATED) [RFC1706][Moving TPC.INT and NSAP.INT infrastructure domains to historic] SIG = 24, // for security signature [RFC2536][RFC2931][RFC3110][RFC4034] KEY = 25, // for security key [RFC2536][RFC2539][RFC3110][RFC4034] PX = 26, // X.400 mail mapping information [RFC2163] GPOS = 27, // Geographical Position [RFC1712] AAAA = 28, // IP6 Address [RFC3596] LOC = 29, // Location Information [RFC1876] NXT = 30, // Next Domain (OBSOLETE) [RFC2535][RFC3755] EID = 31, // Endpoint Identifier [Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt] NIMLOC = 32, // Nimrod Locator [1][Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt] SRV = 33, // Server Selection [1][RFC2782] ATMA = 34, // ATM Address "[ ATM Forum Technical Committee, "ATM Name System, V2.0", Doc ID: AF-DANS-0152.000, July 2000. Available from and held in escrow by IANA.]" NAPTR = 35, // Naming Authority Pointer [RFC3403] KX = 36, // Key Exchanger [RFC2230] CERT = 37, // CERT [RFC4398] A6 = 38, // A6 (OBSOLETE - use AAAA) [RFC2874][RFC3226][RFC6563] DNAME = 39, // DNAME [RFC6672] SINK = 40, // SINK [Donald_E_Eastlake][draft-eastlake-kitchen-sink] OPT = 41, // OPT [RFC3225][RFC6891] APL = 42, // APL [RFC3123] DS = 43, // Delegation Signer [RFC4034] SSHFP = 44, // SSH Key Fingerprint [RFC4255] IPSECKEY = 45, // IPSECKEY [RFC4025] RRSIG = 46, // RRSIG [RFC4034] NSEC = 47, // NSEC [RFC4034][RFC9077] DNSKEY = 48, // DNSKEY [RFC4034] DHCID = 49, // DHCID [RFC4701] NSEC3 = 50, // NSEC3 [RFC5155][RFC9077] NSEC3PARAM = 51, // NSEC3PARAM [RFC5155] TLSA = 52, // TLSA [RFC6698] SMIMEA = 53, // S/MIME cert association [RFC8162] HIP = 55, // Host Identity Protocol [RFC8005] NINFO = 56, // NINFO [Jim_Reid] RKEY = 57, // RKEY [Jim_Reid] TALINK = 58, // Trust Anchor LINK [Wouter_Wijngaards] CDS = 59, // Child DS [RFC7344] CDNSKEY = 60, // DNSKEY(s) the Child wants reflected in DS [RFC7344] OPENPGPKEY = 61, // OpenPGP Key [RFC7929] CSYNC = 62, // Child-To-Parent Synchronization [RFC7477] ZONEMD = 63, // Message Digest Over Zone Data [RFC8976] SVCB = 64, // General-purpose service binding [RFC9460] HTTPS = 65, // SVCB-compatible type for use with HTTP [RFC9460] SPF = 99, // [RFC7208] UINFO = 100, // [IANA-Reserved] UID = 101, // [IANA-Reserved] GID = 102, // [IANA-Reserved] UNSPEC = 103, // [IANA-Reserved] NID = 104, // [RFC6742] L32 = 105, // [RFC6742] L64 = 106, // [RFC6742] LP = 107, // [RFC6742] EUI48 = 108, // an EUI-48 address [RFC7043] EUI64 = 109, // an EUI-64 address [RFC7043] NXNAME = 128, // NXDOMAIN indicator for Compact Denial of Existence [draft-ietf-dnsop-compact-denial-of-existence-04] TKEY = 249, // Transaction Key [RFC2930] TSIG = 250, // Transaction Signature [RFC8945] IXFR = 251, // incremental transfer [RFC1995] AXFR = 252, // transfer of an entire zone [RFC1035][RFC5936] MAILB = 253, // mailbox-related RRs (MB, MG or MR) [RFC1035] MAILA = 254, // mail agent RRs (OBSOLETE - see MX) [RFC1035] ANY = 255, // A request for some or all records the server has available [RFC1035][RFC6895][RFC8482] URI = 256, // URI [RFC7553] CAA = 257, // Certification Authority Restriction [RFC8659] AVC = 258, // Application Visibility and Control [Wolfgang_Riedel] DOA = 259, // Digital Object Architecture [draft-durand-doa-over-dns] AMTRELAY = 260, // Automatic Multicast Tunneling Relay [RFC8777] RESINFO = 261, // Resolver Information as Key/Value Pairs [RFC9606] WALLET = 262, // Public wallet address [Paul_Hoffman] CLA = 263, // BP Convergence Layer Adapter [draft-johnson-dns-ipn-cla-07] IPN = 264, // BP Node Number [draft-johnson-dns-ipn-cla-07] TA = 32768, // DNSSEC Trust Authorities "[Sam_Weiler][Deploying DNSSEC Without a Signed Root. Technical Report 1999-19, Information Networking Institute, Carnegie Mellon University, April 2004.]" DLV = 32769, // DNSSEC Lookaside Validation (OBSOLETE) [RFC8749][RFC4431] }; StringView to_string(ResourceType); Optional resource_type_from_string(StringView); // Listing from IANA https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-2. enum class Class : u16 { IN = 1, // the Internet [RFC1035] CH = 3, // the CHAOS class [Moon1981] HS = 4, // Hesiod [Dyer1987] }; StringView to_string(Class); // Listing from IANA https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-3. enum class OpCode : u8 { Query = 0, // a standard query (QUERY) IQuery = 1, // an inverse query (IQUERY) Status = 2, // a server status request (STATUS) Notify = 4, // NOTIFY Update = 5, // dynamic update (RFC 2136) DSO = 6, // DNS Stateful Operations (DSO) [RFC8490] Reserved = 7, // [RFC6895] ReservedMask = 15 // [RFC6895] }; StringView to_string(OpCode); namespace TLSA { // Listings from IANA https://www.iana.org/assignments/dane-parameters/dane-parameters.xhtml. enum class CertUsage : u8 { CAConstraint = 0, ServiceCertificateConstraint = 1, TrustAnchorAssertion = 2, DomainIssuedCertificate = 3, Private = 255 }; enum class Selector : u8 { FullCertificate = 0, SubjectPublicKeyInfo = 1, Private = 255 }; enum class MatchingType : u8 { Full = 0, SHA256 = 1, SHA512 = 2, Private = 255 }; } namespace DNSSEC { // Listing from IANA https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml. enum class Algorithm : u8 { RSAMD5 = 1, // RSA/MD5 [RFC4034][RFC3110] DSA = 3, // DSA/SHA-1 [RFC3755][RFC2536] RSASHA1 = 5, // RSA/SHA-1 [RFC3110] RSASHA1NSEC3SHA1 = 7, // [RFC5155] RSASHA256 = 8, // RSA/SHA-256 [RFC5702] RSASHA512 = 10, // RSA/SHA-512 [RFC5702] ECDSAP256SHA256 = 13, // ECDSA Curve P-256 with SHA-256 [RFC6605] ECDSAP384SHA384 = 14, // ECDSA Curve P-384 with SHA-384 [RFC6605] ED25519 = 15, // Ed25519 [RFC8080] Unknown = 255 // Reserved for Private Use }; static inline StringView to_string(Algorithm algorithm) { switch (algorithm) { case Algorithm::RSAMD5: return "RSAMD5"sv; case Algorithm::DSA: return "DSA"sv; case Algorithm::RSASHA1: return "RSASHA1"sv; case Algorithm::RSASHA1NSEC3SHA1: return "RSASHA1NSEC3SHA1"sv; case Algorithm::RSASHA256: return "RSASHA256"sv; case Algorithm::RSASHA512: return "RSASHA512"sv; case Algorithm::ECDSAP256SHA256: return "ECDSAP256SHA256"sv; case Algorithm::ECDSAP384SHA384: return "ECDSAP384SHA384"sv; case Algorithm::ED25519: return "ED25519"sv; case Algorithm::Unknown: return "Unknown"sv; } VERIFY_NOT_REACHED(); } // Listing from IANA https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml. enum class DigestType : u8 { SHA1 = 1, // SHA-1 [RFC3658] SHA256 = 2, // SHA-256 [RFC4509] GOST3411 = 3, // GOST R 34.11-94 [RFC5933] SHA384 = 4, // SHA-384 [RFC6605] SHA512 = 5, // SHA-512 [RFC6605] SHA224 = 6, // SHA-224 [RFC6605] Unknown = 255 // Reserved for Private Use }; static inline StringView to_string(DigestType digest_type) { switch (digest_type) { case DigestType::SHA1: return "SHA1"sv; case DigestType::SHA256: return "SHA256"sv; case DigestType::GOST3411: return "GOST3411"sv; case DigestType::SHA384: return "SHA384"sv; case DigestType::SHA512: return "SHA512"sv; case DigestType::SHA224: return "SHA224"sv; case DigestType::Unknown: return "Unknown"sv; } VERIFY_NOT_REACHED(); } // Listing from IANA https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml. enum class NSEC3HashAlgorithm : u8 { SHA1 = 1, // [RFC5155] SHA256 = 2, // [RFC6605] GOST3411 = 3, // [RFC5933] SHA384 = 4, // [RFC6605] SHA512 = 5, // [RFC6605] SHA224 = 6, // [RFC6605] Unknown = 255 // Reserved for Private Use }; static inline StringView to_string(NSEC3HashAlgorithm hash_algorithm) { switch (hash_algorithm) { case NSEC3HashAlgorithm::SHA1: return "SHA1"sv; case NSEC3HashAlgorithm::SHA256: return "SHA256"sv; case NSEC3HashAlgorithm::GOST3411: return "GOST3411"sv; case NSEC3HashAlgorithm::SHA384: return "SHA384"sv; case NSEC3HashAlgorithm::SHA512: return "SHA512"sv; case NSEC3HashAlgorithm::SHA224: return "SHA224"sv; case NSEC3HashAlgorithm::Unknown: return "Unknown"sv; } VERIFY_NOT_REACHED(); } } struct Question { DomainName name; ResourceType type; Class class_; static ErrorOr from_raw(ParseContext&); ErrorOr to_raw(ByteBuffer&) const; }; namespace Records { struct A { IPv4Address address; static constexpr ResourceType type = ResourceType::A; static ErrorOr from_raw(ParseContext&); ErrorOr to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); } ErrorOr to_string() const { return address.to_string(); } }; struct AAAA { IPv6Address address; static constexpr ResourceType type = ResourceType::AAAA; static ErrorOr from_raw(ParseContext&); ErrorOr to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); } ErrorOr to_string() const { return address.to_string(); } }; struct TXT { ByteString content; static constexpr ResourceType type = ResourceType::TXT; static ErrorOr from_raw(ParseContext&); ErrorOr to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); } ErrorOr to_string() const { return String::formatted("Text: '{}'", StringView { content }); } }; struct CNAME { DomainName names; static constexpr ResourceType type = ResourceType::CNAME; static ErrorOr from_raw(ParseContext&); ErrorOr to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); } ErrorOr to_string() const { return names.to_string(); } }; struct NS { DomainName name; static constexpr ResourceType type = ResourceType::NS; static ErrorOr from_raw(ParseContext&); ErrorOr to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); } ErrorOr to_string() const { return name.to_string(); } }; struct SOA { DomainName mname; DomainName rname; u32 serial; u32 refresh; u32 retry; u32 expire; u32 minimum; static constexpr ResourceType type = ResourceType::SOA; static ErrorOr from_raw(ParseContext&); ErrorOr to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); } ErrorOr to_string() const { return String::formatted("SOA MName: '{}', RName: '{}', Serial: {}, Refresh: {}, Retry: {}, Expire: {}, Minimum: {}", mname.to_string(), rname.to_string(), serial, refresh, retry, expire, minimum); } }; struct MX { u16 preference; DomainName exchange; static constexpr ResourceType type = ResourceType::MX; static ErrorOr from_raw(ParseContext&); ErrorOr to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); } ErrorOr to_string() const { return String::formatted("MX Preference: {}, Exchange: '{}'", preference, exchange.to_string()); } }; struct PTR { DomainName name; static constexpr ResourceType type = ResourceType::PTR; static ErrorOr from_raw(ParseContext&); ErrorOr to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); } ErrorOr to_string() const { return name.to_string(); } }; struct SRV { u16 priority; u16 weight; u16 port; DomainName target; static constexpr ResourceType type = ResourceType::SRV; static ErrorOr from_raw(ParseContext&); ErrorOr to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); } ErrorOr to_string() const { return String::formatted("SRV Priority: {}, Weight: {}, Port: {}, Target: '{}'", priority, weight, port, target.to_string()); } }; struct DNSKEY { u16 flags; u8 protocol; DNSSEC::Algorithm algorithm; ByteBuffer public_key; constexpr static inline u16 FlagSecureEntryPoint = 0b1000000000000000; constexpr static inline u16 FlagZoneKey = 0b0100000000000000; constexpr static inline u16 FlagRevoked = 0b0010000000000000; constexpr bool is_secure_entry_point() const { return flags & FlagSecureEntryPoint; } constexpr bool is_zone_key() const { return flags & FlagZoneKey; } constexpr bool is_revoked() const { return flags & FlagRevoked; } constexpr bool is_key_signing_key() const { return is_secure_entry_point() && is_zone_key() && !is_revoked(); } static constexpr ResourceType type = ResourceType::DNSKEY; static ErrorOr from_raw(ParseContext&); ErrorOr to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); } ErrorOr to_string() const { return String::formatted("DNSKEY Flags: {}{}{}{}({}), Protocol: {}, Algorithm: {}, Public Key: {}", is_secure_entry_point() ? "sep "sv : ""sv, is_zone_key() ? "zone "sv : ""sv, is_revoked() ? "revoked "sv : ""sv, is_key_signing_key() ? "ksk "sv : ""sv, flags, protocol, DNSSEC::to_string(algorithm), TRY(encode_base64(public_key))); } }; struct CDNSKEY : public DNSKEY { template CDNSKEY(Ts&&... args) : DNSKEY(forward(args)...) { } static constexpr ResourceType type = ResourceType::CDNSKEY; static ErrorOr from_raw(ParseContext& raw) { return DNSKEY::from_raw(raw); } }; struct DS { u16 key_tag; DNSSEC::Algorithm algorithm; DNSSEC::DigestType digest_type; ByteBuffer digest; static constexpr ResourceType type = ResourceType::DS; static ErrorOr from_raw(ParseContext&); ErrorOr to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); } ErrorOr to_string() const { return "DS"_string; } }; struct CDS : public DS { template CDS(Ts&&... args) : DS(forward(args)...) { } static constexpr ResourceType type = ResourceType::CDS; static ErrorOr from_raw(ParseContext& raw) { return DS::from_raw(raw); } }; struct SIG { ResourceType type_covered; DNSSEC::Algorithm algorithm; u8 label_count; u32 original_ttl; UnixDateTime expiration; UnixDateTime inception; u16 key_tag; DomainName signers_name; ByteBuffer signature; static constexpr ResourceType type = ResourceType::SIG; static ErrorOr from_raw(ParseContext&); ErrorOr to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); } ErrorOr to_string() const; }; struct RRSIG : public SIG { template RRSIG(Ts&&... args) : SIG(forward(args)...) { } static constexpr ResourceType type = ResourceType::RRSIG; static ErrorOr from_raw(ParseContext& raw) { return SIG::from_raw(raw); } }; struct NSEC { DomainName next_domain_name; Vector types; static constexpr ResourceType type = ResourceType::NSEC; static ErrorOr from_raw(ParseContext&); ErrorOr to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); } ErrorOr to_string() const { return "NSEC"_string; } }; struct NSEC3 { DNSSEC::NSEC3HashAlgorithm hash_algorithm; u8 flags; u16 iterations; ByteBuffer salt; DomainName next_hashed_owner_name; Vector types; static constexpr ResourceType type = ResourceType::NSEC3; static ErrorOr from_raw(ParseContext&); ErrorOr to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); } ErrorOr to_string() const { return "NSEC3"_string; } }; struct NSEC3PARAM { DNSSEC::NSEC3HashAlgorithm hash_algorithm; u8 flags; u16 iterations; ByteBuffer salt; constexpr static inline u8 FlagOptOut = 0b10000000; constexpr bool is_opt_out() const { return flags & FlagOptOut; } static constexpr ResourceType type = ResourceType::NSEC3PARAM; static ErrorOr from_raw(ParseContext&); ErrorOr to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); } ErrorOr to_string() const { return "NSEC3PARAM"_string; } }; struct TLSA { Messages::TLSA::CertUsage cert_usage; Messages::TLSA::Selector selector; Messages::TLSA::MatchingType matching_type; ByteBuffer certificate_association_data; static ErrorOr from_raw(ParseContext&); ErrorOr to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); } ErrorOr to_string() const { return "TLSA"_string; } }; struct HINFO { ByteString cpu; ByteString os; static constexpr ResourceType type = ResourceType::HINFO; static ErrorOr from_raw(ParseContext&); ErrorOr to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); } ErrorOr to_string() const { return String::formatted("HINFO CPU: '{}', OS: '{}'", StringView { cpu }, StringView { os }); } }; struct OPT { struct Option { u16 code; ByteBuffer data; }; // 1 1 1 1 1 1 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ // | UDP Payload Size | // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ // | Extended RCode | VER | ZZ | // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ // |DO| Z | // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ // | OPT-LEN / OPT-DATA... NetworkOrdered udp_payload_size { 0 }; NetworkOrdered extended_rcode_and_flags { 0 }; Vector