dns (dns_erlang v5.0.10)

View Source

The dns module is the primary entry point for the functionality in this library. The module exports various types used in type specs, such as message/0, which indicates a #dns_message{} record, query/0 which represents a single #dns_query{} record, questions/0, which represents a list of queries, etc.

It also exports functions for encoding and decoding messages, TSIG supporting functions, and various utility functions for comparing domain names, converting domain names into different cases, converting to and from label lists, etc.

Summary

Functions: parsing

Decode a binary DNS message.

Decode a binary DNS query message with strict header validation.

Encode a message/0 record.

Encode a dns_message record - will truncate the message as needed.

Functions: utilities

Returns a random integer suitable for use as DNS message identifier.

Return current unix time in seconds.

Return the unix time in seconds from a timestamp or universal time.

Types: integer codes

DNS Message class. See RFC 1035: §4.1.2.

Decoding errors.

EDNS option code (e.g. NSID, COOKIE).

EDNS extended rcode (NOERROR or BADVERS).

LLQ (Long-Lived Query) error code.

LLQ (Long-Lived Query) opcode.

DNS Message ID. See RFC 1035: §4.1.1.

DNS opcode. See RFC 1035: §4.1.1.

DNS Return code. See RFC 1035: §4.1.1.

Time to live in seconds. See RFC 1035: §4.1.3.

DNS resource record type (e.g. A, AAAA, NS). See RFC 1035: §3.2.2.

Unsigned 2-bits integer

Unsigned 4-bits integer

Unsigned 8-bits integer

Unsigned 16-bits integer

Unsigned 32-bits integer

Unsigned 48-bits integer

Unsigned 64-bits integer

Unix timestamp in seconds.

Types: options

Options for encode_message/2 (max size, TC mode, TSIG).

Options for adding TSIG when encoding (name, alg, secret, time, fudge, etc.).

SVCB/HTTPS service parameters map. Keys are SVCB parameter keys (e.g. alpn, port).

Options for TSIG verification (time, fudge, etc.).

Types: records

List of OPT pseudo-RRs and/or resource records (additional section).

List of resource records (answer section).

List of resource records (authority section).

Main DNS message structure.

EDNS Cookie option. See RFC 7873.

EDNS Client Subnet option.

EDNS Extended DNS Error option. See RFC 8914.

EDNS LLQ option.

EDNS NSID option.

EDNS Owner option.

EDNS UL (Update Lease) option.

Unknown or unparsed EDNS option.

EDNS OPT pseudo-RR. See RFC 6891.

Single EDNS option (NSID, UL, ECS, LLQ, Owner, Cookie, EDE, or unknown).

Single DNS question (name, class, type). See RFC 1035.

List of query records (question section).

Union of question, answer, authority, or additional section list type.

Resource record. See RFC 1035: §3.2.1.

Resource record data: binary for unknown types or a specific rrdata record.

RRSIG record payload (DNSSEC signature).

Types: strings

Domain name, expressed as a sequence of label/0, as defined in RFC 1035: §3.1.

DNS labels. See RFC 1035: §2.3.1.

A list of dns:label/0

DNS wire message format.

Types: TSIG

DNS algorithm identifier for DNSSEC and TSIG.

TSIG algorithm name (e.g. HMAC-SHA256).

TSIG error code (NOERROR, BADSIG, BADKEY, BADTIME).

TSIG message authentication code (MAC).

Functions: parsing

decode_message(MsgBin)

-spec decode_message(message_bin()) -> {decode_error(), message() | undefined, binary()} | message().

Decode a binary DNS message.

decode_query(MsgBin)

-spec decode_query(message_bin()) -> {decode_error(), message() | undefined, binary()} | message().

Decode a binary DNS query message with strict header validation.

Performs header guard checks before decoding the message body to prevent DoS attacks. For standard queries (opcode 0), validates that:

  • ANCount = 0 (queries should not have answers)
  • NSCount = 0 (queries should not have authority records)
  • QDCount = 1 (standard queries must have exactly one question)

For NOTIFY (opcode 4) and UPDATE (opcode 5), allows decoding to proceed. For other opcodes, falls back to standard decoding.

encode_message(Msg)

-spec encode_message(message()) -> message_bin().

Encode a message/0 record.

encode_message(Msg, Opts)

-spec encode_message(message(), encode_message_opts()) ->
                        message_bin() |
                        {message_bin(), tsig_mac()} |
                        {truncated, message_bin(), message()} |
                        {truncated, message_bin(), tsig_mac(), message()}.

Encode a dns_message record - will truncate the message as needed.

Functions: TSIG

add_tsig(Msg, Alg, Name, Secret, ErrCode)

-spec add_tsig(message(), tsig_alg(), dname(), binary(), tsig_error()) -> message().

Equivalent to add_tsig(Msg, Alg, Name, Secret, ErrCode, #{name => Name, alg => Alg}).

add_tsig(Msg, Alg, Name, Secret, ErrCode, Options)

-spec add_tsig(message(), tsig_alg(), dname(), binary(), tsig_error(), encode_tsig_opts()) -> message().

Generates and then appends a TSIG RR to a message.

Supports MD5, SHA1, SHA224, SHA256, SHA384 and SHA512 algorithms.

verify_tsig(MsgBin, Name, Secret)

-spec verify_tsig(message_bin(), dname(), binary()) -> {ok, tsig_mac()} | {error, tsig_error()}.

Equivalent to verify_tsig(MsgBin, Name, Secret, #{}).

verify_tsig(MsgBin, Name, Secret, Options)

-spec verify_tsig(message_bin(), dname(), binary(), tsig_opts()) ->
                     {ok, tsig_mac()} | {error, tsig_error()}.

Verifies a TSIG message signature.

Functions: utilities

random_id()

-spec random_id() -> message_id().

Returns a random integer suitable for use as DNS message identifier.

unix_time()

-spec unix_time() -> unix_time().

Return current unix time in seconds.

unix_time/1

-spec unix_time(erlang:timestamp() | calendar:datetime1970()) -> unix_time().

Return the unix time in seconds from a timestamp or universal time.

Types: integer codes

class()

-type class() :: uint16().

DNS Message class. See RFC 1035: §4.1.2.

decode_error()

-type decode_error() :: formerr | truncated | notimp | trailing_garbage.

Decoding errors.

Can be one of the following:

  • formerr: the message was malformed.
  • truncated: the message was partially decoded, as data was found missing from the message.
  • trailing_garbage: the message was successfully decoded, but there was trailing garbage at the end of the message.
  • notimp: the opcode is not implemented (e.g., IQUERY, STATUS, DSO). The message struct contains minimal fields needed to construct a NOTIMP response.

eoptcode()

-type eoptcode() :: uint16().

EDNS option code (e.g. NSID, COOKIE).

ercode()

-type ercode() :: 0 | 16.

EDNS extended rcode (NOERROR or BADVERS).

llqerrcode()

-type llqerrcode() :: 0..6.

LLQ (Long-Lived Query) error code.

llqopcode()

-type llqopcode() :: 1..3.

LLQ (Long-Lived Query) opcode.

message_id()

-type message_id() :: uint16().

DNS Message ID. See RFC 1035: §4.1.1.

opcode()

-type opcode() :: uint4().

DNS opcode. See RFC 1035: §4.1.1.

rcode()

-type rcode() :: uint4().

DNS Return code. See RFC 1035: §4.1.1.

ttl()

-type ttl() :: 0..1 bsl 31 - 1.

Time to live in seconds. See RFC 1035: §4.1.3.

type()

-type type() :: uint16().

DNS resource record type (e.g. A, AAAA, NS). See RFC 1035: §3.2.2.

uint2()

-type uint2() :: 0..1.

Unsigned 2-bits integer

uint4()

-type uint4() :: 0..15.

Unsigned 4-bits integer

uint8()

-type uint8() :: 0..1 bsl 8 - 1.

Unsigned 8-bits integer

uint16()

-type uint16() :: 0..1 bsl 16 - 1.

Unsigned 16-bits integer

uint32()

-type uint32() :: 0..1 bsl 32 - 1.

Unsigned 32-bits integer

uint48()

-type uint48() :: 0..1 bsl 48 - 1.

Unsigned 48-bits integer

uint64()

-type uint64() :: 0..1 bsl 64 - 1.

Unsigned 64-bits integer

unix_time()

-type unix_time() :: 0..4294967295.

Unix timestamp in seconds.

Types: options

encode_message_opts()

-type encode_message_opts() ::
          #{max_size => 512..65535, tc_mode => default | axfr | llq_event, tsig => encode_tsig_opts()}.

Options for encode_message/2 (max size, TC mode, TSIG).

encode_tsig_opts()

-type encode_tsig_opts() ::
          #{name := dname(),
            alg := tsig_alg(),
            msgid => message_id(),
            secret => binary(),
            errcode => tsig_error(),
            other => binary(),
            time => unix_time(),
            fudge => non_neg_integer(),
            mac => tsig_mac(),
            tail => boolean()}.

Options for adding TSIG when encoding (name, alg, secret, time, fudge, etc.).

svcb_svc_params()

-type svcb_svc_params() ::
          #{0 => [dns:uint16()],
            1 => [binary()],
            2 => none,
            3 => inet:port_number(),
            5 => binary(),
            4 => [inet:ip4_address()],
            6 => [inet:ip6_address()],
            uint16() => none | binary()}.

SVCB/HTTPS service parameters map. Keys are SVCB parameter keys (e.g. alpn, port).

tsig_opts()

-type tsig_opts() ::
          #{time => unix_time(),
            fudge => non_neg_integer(),
            mac => tsig_mac(),
            tail => boolean(),
            atom() => _}.

Options for TSIG verification (time, fudge, etc.).

Types: records

additional()

-type additional() :: [optrr() | rr()].

List of OPT pseudo-RRs and/or resource records (additional section).

answers()

-type answers() :: [rr()].

List of resource records (answer section).

authority()

-type authority() :: [rr()].

List of resource records (authority section).

message()

-type message() ::
          #dns_message{id :: dns:message_id(),
                       qr :: boolean(),
                       oc :: dns:opcode(),
                       aa :: boolean(),
                       tc :: boolean(),
                       rd :: boolean(),
                       ra :: boolean(),
                       ad :: boolean(),
                       cd :: boolean(),
                       rc :: dns:rcode(),
                       qc :: dns:uint16(),
                       anc :: dns:uint16(),
                       auc :: dns:uint16(),
                       adc :: dns:uint16(),
                       questions :: dns:questions(),
                       answers :: dns:answers(),
                       authority :: dns:authority(),
                       additional :: dns:additional()}.

Main DNS message structure.

opt_cookie()

-type opt_cookie() :: #dns_opt_cookie{client :: <<_:64>>, server :: undefined | <<_:64, _:_*8>>}.

EDNS Cookie option. See RFC 7873.

opt_ecs()

-type opt_ecs() ::
          #dns_opt_ecs{family :: dns:uint16(),
                       source_prefix_length :: dns:uint8(),
                       scope_prefix_length :: dns:uint8(),
                       address :: binary()}.

EDNS Client Subnet option.

opt_ede()

-type opt_ede() :: #dns_opt_ede{info_code :: dns:uint16(), extra_text :: binary()}.

EDNS Extended DNS Error option. See RFC 8914.

opt_llq()

-type opt_llq() ::
          #dns_opt_llq{opcode :: dns:uint16(),
                       errorcode :: dns:uint16(),
                       id :: dns:uint64(),
                       leaselife :: dns:uint32()}.

EDNS LLQ option.

opt_nsid()

-type opt_nsid() :: #dns_opt_nsid{data :: binary()}.

EDNS NSID option.

opt_owner()

-type opt_owner() ::
          #dns_opt_owner{seq :: dns:uint8(),
                         primary_mac :: <<_:48>>,
                         wakeup_mac :: <<>> | <<_:48>>,
                         password :: <<>> | <<_:48>>}.

EDNS Owner option.

opt_ul()

-type opt_ul() :: #dns_opt_ul{lease :: dns:uint32()}.

EDNS UL (Update Lease) option.

opt_unknown()

-type opt_unknown() :: #dns_opt_unknown{id :: integer(), bin :: binary()}.

Unknown or unparsed EDNS option.

optrr()

-type optrr() ::
          #dns_optrr{udp_payload_size :: dns:uint16(),
                     ext_rcode :: dns:uint8(),
                     version :: dns:uint8(),
                     dnssec :: boolean(),
                     data :: [dns:optrr_elem()]}.

EDNS OPT pseudo-RR. See RFC 6891.

optrr_elem()

-type optrr_elem() ::
          opt_nsid() |
          opt_ul() |
          opt_unknown() |
          opt_ecs() |
          opt_llq() |
          opt_owner() |
          opt_cookie() |
          opt_ede().

Single EDNS option (NSID, UL, ECS, LLQ, Owner, Cookie, EDE, or unknown).

query()

-type query() :: #dns_query{name :: dns:dname(), class :: dns:class(), type :: dns:type()}.

Single DNS question (name, class, type). See RFC 1035.

questions()

-type questions() :: [query()].

List of query records (question section).

records()

-type records() :: additional() | answers() | authority() | questions().

Union of question, answer, authority, or additional section list type.

rr()

-type rr() ::
          #dns_rr{name :: dns:dname(),
                  type :: dns:type(),
                  class :: dns:class(),
                  ttl :: dns:ttl(),
                  data :: dns:rrdata()}.

Resource record. See RFC 1035: §3.2.1.

rrdata()

-type rrdata() ::
          binary() |
          #dns_rrdata_a{ip :: inet:ip4_address()} |
          #dns_rrdata_aaaa{ip :: inet:ip6_address()} |
          #dns_rrdata_afsdb{subtype :: dns:uint16(), hostname :: dns:dname()} |
          #dns_rrdata_caa{flags :: dns:uint8(), tag :: binary(), value :: binary()} |
          #dns_rrdata_cdnskey{flags :: dns:uint16(),
                              protocol :: dns:uint8(),
                              alg :: dns:uint8(),
                              public_key :: iodata(),
                              keytag :: integer()} |
          #dns_rrdata_cds{keytag :: dns:uint16(),
                          alg :: dns:uint8(),
                          digest_type :: dns:uint8(),
                          digest :: binary()} |
          #dns_rrdata_cert{type :: dns:uint16(),
                           keytag :: dns:uint16(),
                           alg :: dns:uint8(),
                           cert :: binary()} |
          #dns_rrdata_cname{dname :: dns:dname()} |
          #dns_rrdata_dhcid{data :: binary()} |
          #dns_rrdata_dlv{keytag :: dns:uint16(),
                          alg :: dns:uint8(),
                          digest_type :: dns:uint8(),
                          digest :: binary()} |
          #dns_rrdata_dname{dname :: dns:dname()} |
          #dns_rrdata_eui48{address :: <<_:48>>} |
          #dns_rrdata_eui64{address :: <<_:64>>} |
          #dns_rrdata_dnskey{flags :: dns:uint16(),
                             protocol :: dns:uint8(),
                             alg :: dns:uint8(),
                             public_key :: iodata(),
                             keytag :: integer()} |
          #dns_rrdata_ds{keytag :: dns:uint16(),
                         alg :: dns:uint8(),
                         digest_type :: dns:uint8(),
                         digest :: binary()} |
          #dns_rrdata_hinfo{cpu :: binary(), os :: binary()} |
          #dns_rrdata_ipseckey{precedence :: dns:uint8(),
                               alg :: dns:uint8(),
                               gateway :: dns:dname() | inet:ip_address(),
                               public_key :: binary()} |
          #dns_rrdata_key{type :: dns:uint2(),
                          xt :: 0..1,
                          name_type :: dns:uint2(),
                          sig :: dns:uint4(),
                          protocol :: dns:uint8(),
                          alg :: dns:uint8(),
                          public_key :: binary()} |
          #dns_rrdata_kx{preference :: dns:uint16(), exchange :: dns:dname()} |
          #dns_rrdata_loc{size :: integer(),
                          horiz :: integer(),
                          vert :: integer(),
                          lat :: dns:uint32(),
                          lon :: dns:uint32(),
                          alt :: dns:uint32()} |
          #dns_rrdata_mb{madname :: dns:dname()} |
          #dns_rrdata_mg{madname :: dns:dname()} |
          #dns_rrdata_minfo{rmailbx :: dns:dname(), emailbx :: dns:dname()} |
          #dns_rrdata_mr{newname :: dns:dname()} |
          #dns_rrdata_mx{preference :: dns:uint16(), exchange :: dns:dname()} |
          #dns_rrdata_naptr{order :: dns:uint16(),
                            preference :: dns:uint16(),
                            flags :: binary(),
                            services :: binary(),
                            regexp :: binary(),
                            replacement :: dns:dname()} |
          #dns_rrdata_ns{dname :: dns:dname()} |
          #dns_rrdata_nsec{next_dname :: dns:dname(), types :: [non_neg_integer()]} |
          #dns_rrdata_nsec3{hash_alg :: dns:uint8(),
                            opt_out :: boolean(),
                            iterations :: dns:uint16(),
                            salt :: binary(),
                            hash :: binary(),
                            types :: [non_neg_integer()]} |
          #dns_rrdata_nsec3param{hash_alg :: dns:uint8(),
                                 flags :: dns:uint8(),
                                 iterations :: dns:uint16(),
                                 salt :: binary()} |
          #dns_rrdata_nxt{dname :: dns:dname(), types :: [non_neg_integer()]} |
          #dns_rrdata_csync{soa_serial :: dns:uint32(),
                            flags :: dns:uint16(),
                            types :: [non_neg_integer()]} |
          #dns_rrdata_dsync{rrtype :: dns:uint16(),
                            scheme :: dns:uint8(),
                            port :: dns:uint16(),
                            target :: dns:dname()} |
          #dns_rrdata_openpgpkey{data :: binary()} |
          #dns_rrdata_ptr{dname :: dns:dname()} |
          #dns_rrdata_rp{mbox :: dns:dname(), txt :: dns:dname()} |
          #dns_rrdata_rrsig{type_covered :: dns:uint16(),
                            alg :: 3 | 5 | 6 | 7 | 8 | 10 | 13 | 14 | 15 | 16,
                            labels :: dns:uint8(),
                            original_ttl :: dns:uint32(),
                            expiration :: dns:uint32(),
                            inception :: dns:uint32(),
                            keytag :: dns:uint16(),
                            signers_name :: dns:dname(),
                            signature :: binary()} |
          #dns_rrdata_rt{preference :: dns:uint16(), host :: dns:dname()} |
          #dns_rrdata_soa{mname :: dns:dname(),
                          rname :: dns:dname(),
                          serial :: dns:uint32(),
                          refresh :: dns:uint32(),
                          retry :: dns:uint32(),
                          expire :: dns:uint32(),
                          minimum :: dns:uint32()} |
          #dns_rrdata_spf{spf :: [binary()]} |
          #dns_rrdata_srv{priority :: dns:uint16(),
                          weight :: dns:uint16(),
                          port :: dns:uint16(),
                          target :: dns:dname()} |
          #dns_rrdata_svcb{svc_priority :: dns:uint16(),
                           target_name :: dns:dname(),
                           svc_params :: dns:svcb_svc_params()} |
          #dns_rrdata_https{svc_priority :: dns:uint16(),
                            target_name :: dns:dname(),
                            svc_params :: dns:svcb_svc_params()} |
          #dns_rrdata_sshfp{alg :: dns:uint8(), fp_type :: dns:uint8(), fp :: binary()} |
          #dns_rrdata_tlsa{usage :: dns:uint8(),
                           selector :: dns:uint8(),
                           matching_type :: dns:uint8(),
                           certificate :: binary()} |
          #dns_rrdata_smimea{usage :: dns:uint8(),
                             selector :: dns:uint8(),
                             matching_type :: dns:uint8(),
                             certificate :: binary()} |
          #dns_rrdata_tsig{alg :: dns:tsig_alg(),
                           time :: dns:uint48(),
                           fudge :: dns:uint16(),
                           mac :: binary(),
                           msgid :: dns:uint16(),
                           err :: dns:uint16(),
                           other :: binary()} |
          #dns_rrdata_txt{txt :: [binary()]} |
          #dns_rrdata_uri{priority :: dns:uint16(), weight :: dns:uint16(), target :: binary()} |
          #dns_rrdata_resinfo{data :: [binary()]} |
          #dns_rrdata_wallet{data :: binary()} |
          #dns_rrdata_zonemd{serial :: dns:uint32(),
                             scheme :: dns:uint8(),
                             algorithm :: dns:uint8(),
                             hash :: binary()}.

Resource record data: binary for unknown types or a specific rrdata record.

rrdata_rrsig()

-type rrdata_rrsig() ::
          #dns_rrdata_rrsig{type_covered :: dns:uint16(),
                            alg :: 3 | 5 | 6 | 7 | 8 | 10 | 13 | 14 | 15 | 16,
                            labels :: dns:uint8(),
                            original_ttl :: dns:uint32(),
                            expiration :: dns:uint32(),
                            inception :: dns:uint32(),
                            keytag :: dns:uint16(),
                            signers_name :: dns:dname(),
                            signature :: binary()}.

RRSIG record payload (DNSSEC signature).

Types: strings

dname()

-type dname() :: binary().

Domain name, expressed as a sequence of label/0, as defined in RFC 1035: §3.1.

label()

-type label() :: binary().

DNS labels. See RFC 1035: §2.3.1.

The labels must follow the rules for ARPANET host names. They must start with a letter, end with a letter or digit, and have as interior characters only letters, digits, and hyphen. There are also some restrictions on the length. Labels must be 63 characters or less.

labels()

-type labels() :: [label()].

A list of dns:label/0

message_bin()

-type message_bin() :: <<_:96, _:_*8>>.

DNS wire message format.

The general form is a 96bits header, followed by a variable number of questions, answers, authorities, and additional records.

Types: TSIG

alg()

-type alg() :: 3 | 6 | 5 | 7 | 8 | 10 | 13 | 14 | 15 | 16.

DNS algorithm identifier for DNSSEC and TSIG.

tsig_alg()

-type tsig_alg() :: binary().

TSIG algorithm name (e.g. HMAC-SHA256).

tsig_error()

-type tsig_error() :: 0 | 16..18.

TSIG error code (NOERROR, BADSIG, BADKEY, BADTIME).

tsig_mac()

-type tsig_mac() :: binary().

TSIG message authentication code (MAC).