Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

net.resolver

import net

net exposes this as net.resolver, so import net is enough and the names are called as net.resolver.*. import net.resolver reaches the same definitions directly.

A DNS resolver, written in Zuri from the wire format up.

net.resolve() answers the one question the operating system will answer for you: what addresses does this name have. Everything else a name can carry, from the mail servers of a domain to the text record a service asks you to publish, needs a resolver that speaks DNS itself. This is that resolver.

import net.resolver

for record in resolver.mx('example.com') {
  echo '${record.preference} ${record.exchange}'
}

The module-level functions go through one shared Resolver built from this machine’s own configuration, which is what most programs want. Build your own when you need to ask a particular server, or ask it over TLS:

import net.resolver { Resolver }

var dns = Resolver({
  servers: ['1.1.1.1'],
  tls: true,
  server_name: 'cloudflare-dns.com',
})

echo dns.txt('example.com')

What it does

Queries go out over UDP and fall back to TCP when the answer does not fit, which is the behaviour every resolver is required to have and the reason a large TXT or DNSKEY record arrives intact. EDNS(0) is advertised so that fallback is rare. Each query carries a random identifier from a fresh ephemeral port, answers that do not match the question asked are discarded, and a server that does not answer in time gives way to the next one.

Answers are cached for as long as their own time-to-live says, and a name that does not exist is cached too, for as long as the zone’s own authority record allows. Caching is per resolver and can be turned off.

What it does not do

It does not validate DNSSEC. Signatures are carried through when a server sends them, and the dnssec option asks for them, but nothing here checks one. A program that needs a validated answer wants a validating server and a trusted path to it, which is what tls gives.

Constants

TYPES

net.resolver.TYPES: dict = {...}

The record types this module knows how to read, by name. A type it has no name for is still parsed, reported by its number, and its data handed over as raw bytes.

CLASSES

net.resolver.CLASSES: dict = {...}

The classes a question can be asked in. IN, the internet class, is the only one in general use and is the default everywhere here.

RCODES

net.resolver.RCODES: dict = {...}

What a server said about the question, by name. NOERROR means the query succeeded, which is not the same as it having found anything.

Functions

parse_message()

net.resolver.parse_message(data) -> Response

Turns the bytes of a message into a Response.

Parameters

  • data (bytes)

Returns Response

Raises MalformedResponse if the bytes are not a well-formed message

build_query()

net.resolver.build_query(id: number, name: string, type: number, klass: number, options: dict) -> bytes

Builds the bytes of a query.

Parameters

  • id (number) — The identifier to match the answer against.
  • name (string) — The name to ask about.
  • type (number)
  • klass (number)
  • options (dict) — recursion, edns, udp_size and dnssec.

Returns bytes

exchange()

net.resolver.exchange(server: string, query, options: dict) -> bytes

Asks one server one question and returns what it said.

Used by Resolver, and on its own when you want to talk to a particular server without any of the retrying, caching or search list a resolver puts around it.

Parameters

  • server (string) — An address, optionally with a port.
  • query (bytes) — The message to send.
  • options (dict) — timeout, port, tcp, tls, tls_config and server_name.

Returns bytes — the answer, unparsed

Raises Error on a network failure or a timeout

system_servers()

net.resolver.system_servers() -> list

The nameservers this machine is configured to use, in the order the system lists them.

Read from /etc/resolv.conf on unix and from the network stack on Windows. An address that is only meaningful together with an interface, which is how a link-local IPv6 server is configured, is left out, because nothing here can carry the interface along with it.

import net.resolver

echo resolver.system_servers()
# [127.0.0.53]

Returns list — of string, possibly empty

net.resolver.system_search() -> list

The domain suffixes to try against a name that has no dots in it.

Usually empty away from a managed network. Resolver uses this as the default for its search option.

Returns list — of string, possibly empty

reverse_name()

net.resolver.reverse_name(address: string) -> string

The name an address’s records are published under.

IPv4 addresses live under in-addr.arpa with their octets reversed, and IPv6 addresses under ip6.arpa with one label per nibble.

import net.resolver

echo resolver.reverse_name('192.0.2.7')
echo resolver.reverse_name('2001:db8::1')
7.2.0.192.in-addr.arpa
1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa

Parameters

  • address (string) — An IPv4 or IPv6 address.

Returns string

Raises Error if address is not an address.

shared()

net.resolver.shared() -> Resolver

The resolver the module-level functions use: one shared Resolver built from this machine’s own configuration.

Reach for it when you want the shared cache but need a method the module does not re-export.

Returns Resolver

use()

net.resolver.use(instance: instance) -> Resolver

Replaces the shared resolver, so that every module-level function goes through the one given instead.

import net.resolver { Resolver }
import net.resolver

resolver.use(Resolver({ servers: ['9.9.9.9'] }))

Parameters

  • instance (Resolver)

Returns Resolver — the one now in use.

query()

net.resolver.query(name: string, type, klass) -> Response

Asks the shared resolver about one name and hands back the whole answer.

Parameters

  • name (string)
  • type (string|number)
  • klass (?string|number)

Returns Response

resolve()

net.resolver.resolve(name: string, type, klass) -> list

The records of one type published for a name, through the shared resolver.

Parameters

  • name (string)
  • type (string|number)
  • klass (?string|number)

Returns list — of Record

addresses()

net.resolver.addresses(name: string) -> list

Every address a name has, IPv4 first.

Parameters

  • name (string)

Returns list — of string

a()

net.resolver.a(name: string) -> list

The IPv4 addresses of a name.

Parameters

  • name (string)

Returns list — of string

aaaa()

net.resolver.aaaa(name: string) -> list

The IPv6 addresses of a name.

Parameters

  • name (string)

Returns list — of string

cname()

net.resolver.cname(name: string) -> string|nil

The name this one is an alias for, or nil.

Parameters

  • name (string)

Returns string|nil

mx()

net.resolver.mx(name: string) -> list

The mail servers for a domain, lowest preference first.

Parameters

  • name (string)

Returns list — of MailExchange

ns()

net.resolver.ns(name: string) -> list

The nameservers a domain is delegated to.

Parameters

  • name (string)

Returns list — of string

txt()

net.resolver.txt(name: string) -> list

The text records of a name.

Parameters

  • name (string)

Returns list — of string

soa()

net.resolver.soa(name: string) -> StartOfAuthority|nil

The authority record at the top of a name’s zone, or nil.

Parameters

  • name (string)

Returns StartOfAuthority|nil

srv()

net.resolver.srv(name: string) -> list

The hosts running a service, by priority and then by weight.

Parameters

  • name (string)

Returns list — of Service

ptr()

net.resolver.ptr(address: string) -> list

The names an address points back at.

Parameters

  • address (string)

Returns list — of string

caa()

net.resolver.caa(name: string) -> list

Which certificate authorities a domain allows to issue for it.

Parameters

  • name (string)

Returns list — of Certification

flush()

net.resolver.flush() -> Resolver

Empties the shared resolver’s cache.

Returns Resolver — the shared resolver.

Classes

ResolverError

class net.resolver.ResolverError < Error

The root of every error this module raises.

NameError

class net.resolver.NameError < ResolverError

The name does not exist. This is the NXDOMAIN answer, and it is an answer rather than a failure: an authoritative server has said that nothing is published under this name at all.

ServerError

class net.resolver.ServerError < ResolverError

The server answered, and the answer was a refusal or a failure of its own. rcode carries the code it sent and rcode_name the name of that code.

Constructor

net.resolver.ServerError(message: string, rcode: number)

Parameters

  • message (string)
  • rcode (number) — The code the server replied with.

ResolverTimeout

class net.resolver.ResolverTimeout < ResolverError

No server answered in time. Every configured server was asked, as many times as attempts allows, and none of them replied.

MalformedResponse

class net.resolver.MalformedResponse < ResolverError

Something came back that is not a DNS message, or is one that contradicts itself. A truncated answer, a compression pointer that loops, a record whose length runs past the end of the message.

NoServersError

class net.resolver.NoServersError < ResolverError

The resolver has nowhere to send a query: no servers were given and the system has none configured.

MailExchange

class net.resolver.MailExchange

One mail server for a domain, as an MX record names it.

Mail is delivered to the exchange with the lowest preference that answers, so Resolver.mx() hands them back in that order.

  • printable — has a @to_string(), so echo and print() show something useful

Constructor

net.resolver.MailExchange(preference: number, exchange: string)

Parameters

  • preference (number) — Lower is tried first.
  • exchange (string) — The name of the mail server.

MailExchange.to_string()

net.resolver.MailExchange.to_string()

Service

class net.resolver.Service

Where a service lives, as an SRV record names it: the host and port, with the priority and weight that decide which of several to use.

Clients try the lowest priority first, and choose among equal priorities in proportion to their weight.

  • printable — has a @to_string(), so echo and print() show something useful

Constructor

net.resolver.Service(priority: number, weight: number, port: number, target: string)

Parameters

  • priority (number) — Lower is tried first.
  • weight (number) — The share of traffic among equal priorities.
  • port (number) — The port the service listens on.
  • target (string) — The name of the host running it.

Service.address()

net.resolver.Service.address() -> string

The host:port this record points at, ready to hand to TcpStream.connect().

Returns string

Service.to_string()

net.resolver.Service.to_string()

StartOfAuthority

class net.resolver.StartOfAuthority

The authority record at the top of a zone, which says who publishes it and how long the rest of the world may hold on to what it says.

  • printable — has a @to_string(), so echo and print() show something useful

Constructor

net.resolver.StartOfAuthority(primary, mailbox, serial, refresh, retry, expire, minimum)

Parameters

  • primary (string) — The name of the primary nameserver.
  • mailbox (string) — The address of whoever is responsible, with the first dot standing in for the @.
  • serial (number) — The version of the zone.
  • refresh (number) — Seconds a secondary waits between checks.
  • retry (number) — Seconds a secondary waits after a failed one.
  • expire (number) — Seconds a secondary may serve the zone without reaching the primary.
  • minimum (number) — Seconds a negative answer may be cached.

StartOfAuthority.to_string()

net.resolver.StartOfAuthority.to_string()

Certification

class net.resolver.Certification

A CAA record: which certificate authorities a domain allows to issue for it.

  • printable — has a @to_string(), so echo and print() show something useful

Constructor

net.resolver.Certification(flags: number, tag: string, value: string)

Parameters

  • flags (number) — Bit 7 set means a client must understand the tag or refuse to issue.
  • tag (string) — issue, issuewild or iodef in practice.
  • value (string) — What the tag names, usually an authority’s domain.

Certification.to_string()

net.resolver.Certification.to_string()

Record

class net.resolver.Record

One resource record: a name, what kind of thing it holds, how long it may be cached, and the thing itself.

What data holds depends on the type:

typedata
A, AAAAthe address, as a string
NS, CNAME, PTR, DNAMEthe name, as a string
MXa MailExchange
SRVa Service
SOAa StartOfAuthority
CAAa Certification
TXTa list of strings, one per character-string
anything elsethe record’s data, as bytes
  • printable — has a @to_string(), so echo and print() show something useful

Constructor

net.resolver.Record(name, type, klass, ttl, data)

Parameters

  • name (string) — The name this record is published under.
  • type (number) — The record type, as a number.
  • klass (number) — The record class, as a number.
  • ttl (number) — Seconds this record may be cached for.
  • data (any) — The record’s contents, per the table above.

Record.text()

net.resolver.Record.text() -> string

A TXT record’s strings joined into one, which is how a record split across several character-strings is meant to be read. Any other type raises.

Returns string

Raises ResolverError if this is not a TXT record.

Record.to_string()

net.resolver.Record.to_string() -> string

The record as a zone file would write it.

Returns string

Question

class net.resolver.Question

The question a message asks, echoed back in the answer so a reply can be matched to what it replies to.

  • printable — has a @to_string(), so echo and print() show something useful

Constructor

net.resolver.Question(name: string, type: number, klass: number)

Parameters

  • name (string)
  • type (number)
  • klass (number)

Question.to_string()

net.resolver.Question.to_string()

Response

class net.resolver.Response

A whole answer: what was asked, what came back, and what the server said about it.

  • printable — has a @to_string(), so echo and print() show something useful

Constructor

net.resolver.Response(id, flags, question, answers, authorities, additionals, extended)

Response.records_of()

net.resolver.Response.records_of(type) -> list

The answers of one type, leaving out the aliases and anything else the server sent along.

Parameters

  • type (string|number)

Returns list — of Record

Response.ok()

net.resolver.Response.ok() -> Response

Raises when the server reported anything other than success. A name that does not exist raises NameError; everything else raises ServerError.

Returns Response — itself, so this can be chained.

Raises NameError|ServerError

Response.to_string()

net.resolver.Response.to_string()

Resolver

class net.resolver.Resolver

Asks questions of the domain name system and reads the answers.

One resolver holds its own configuration and its own cache, so a program can keep several: one for the machine’s own servers, one for a particular provider over TLS, one with caching off for a test.

import net.resolver { Resolver }

var dns = Resolver()

echo dns.addresses('example.com')
echo dns.mx('example.com')
  • printable — has a @to_string(), so echo and print() show something useful

Constructor

net.resolver.Resolver(options: ?dict)

Builds a resolver.

Every option has a default, and the defaults describe this machine, so Resolver() behaves the way the rest of the system does.

optiondefaultwhat it does
serversthe system’s ownthe addresses to ask, in order
port53, or 853 with tlsthe port to ask them on
timeout5000milliseconds one server gets to answer
attempts2times to go round the whole list
tcpfalsesend over TCP rather than UDP
tlsfalsesend over TLS, which implies TCP
server_namenonethe name to verify the TLS certificate against
tls_configa default TlsConfigthe trust settings for TLS
searchthe system’s ownsuffixes to try against a short name
ndots1dots a name needs before it is tried unsuffixed first
recursiontrueask the server to do the work
ednstrueadvertise a larger UDP payload
udp_size1232how much larger
dnssecfalseask for signature records, without validating them
cachetrueremember answers for as long as they say
cache_size512entries to keep before evicting

Parameters

  • options (?dict)

Raises ResolverError if an option is not one of the above.

Resolver.servers()

net.resolver.Resolver.servers() -> list

The servers this resolver asks, in the order it asks them.

Returns list — of string

Raises NoServersError if none were given and the system has none.

Resolver.search()

net.resolver.Resolver.search() -> list

The suffixes this resolver tries against a name too short to be meant on its own.

Returns list — of string, possibly empty

Resolver.flush()

net.resolver.Resolver.flush() -> Resolver

Empties this resolver’s cache.

Returns Resolver — itself.

Resolver.query()

net.resolver.Resolver.query(name: string, type, klass) -> Response

Asks about one name and hands back the whole answer, including the records the server sent along with it.

The search list is applied, so a short name is tried with each configured suffix. The answer is not checked: a name that does not exist comes back as a Response whose rcode says so, which is what ok() is for.

import net.resolver { Resolver }

var response = Resolver().query('example.com', 'MX').ok()

echo response.answers

Parameters

  • name (string)
  • type (string|number) — A name from TYPES or a number.
  • klass (?string|number) — IN unless told otherwise.

Returns Response

Raises NoServersError if there is nowhere to send the query.

Raises ResolverTimeout if no server answered.

Raises MalformedResponse if what came back is not a valid answer.

Resolver.resolve()

net.resolver.Resolver.resolve(name: string, type, klass) -> list

The records of one type published for a name.

Aliases are followed: asking for the A records of a name that is a CNAME gives the addresses at the end of the chain. A name that exists but publishes nothing of this type gives an empty list, which is a different thing from the name not existing.

Parameters

  • name (string)
  • type (string|number)
  • klass (?string|number)

Returns list — of Record

Raises NameError if the name does not exist.

Raises ServerError if the server reported a failure.

Resolver.addresses()

net.resolver.Resolver.addresses(name: string) -> list

Every address a name has, IPv4 first and then IPv6.

Parameters

  • name (string)

Returns list — of string

Raises NameError if the name does not exist.

Resolver.a()

net.resolver.Resolver.a(name: string) -> list

The IPv4 addresses of a name.

Parameters

  • name (string)

Returns list — of string

Raises NameError if the name does not exist.

Resolver.aaaa()

net.resolver.Resolver.aaaa(name: string) -> list

The IPv6 addresses of a name.

Parameters

  • name (string)

Returns list — of string

Raises NameError if the name does not exist.

Resolver.cname()

net.resolver.Resolver.cname(name: string) -> string|nil

The name this one is an alias for, or nil when it is not one.

Parameters

  • name (string)

Returns string|nil

Raises NameError if the name does not exist.

Resolver.mx()

net.resolver.Resolver.mx(name: string) -> list

The mail servers for a domain, lowest preference first, which is the order they should be tried in.

import net.resolver { Resolver }

for server in Resolver().mx('example.com') {
  echo '${server.preference} ${server.exchange}'
}

Parameters

  • name (string)

Returns list — of MailExchange

Raises NameError if the name does not exist.

Resolver.ns()

net.resolver.Resolver.ns(name: string) -> list

The nameservers a domain is delegated to.

Parameters

  • name (string)

Returns list — of string

Raises NameError if the name does not exist.

Resolver.txt()

net.resolver.Resolver.txt(name: string) -> list

The text records of a name, one string per record.

A record split across several character-strings, which is how anything longer than 255 bytes is published, is joined back together, because the split is a limit of the wire format and not part of what was published.

Parameters

  • name (string)

Returns list — of string

Raises NameError if the name does not exist.

Resolver.soa()

net.resolver.Resolver.soa(name: string) -> StartOfAuthority|nil

The authority record at the top of the zone a name belongs to, or nil when the name is not the top of one.

Parameters

  • name (string)

Returns StartOfAuthority|nil

Raises NameError if the name does not exist.

Resolver.srv()

net.resolver.Resolver.srv(name: string) -> list

The hosts running a service, by priority and then by weight.

Among equal priorities the record with the larger weight should take proportionally more traffic; this returns them heaviest first, and choosing between them is the caller’s to make.

Parameters

  • name (string) — The full service name, such as _imap._tcp.example.com.

Returns list — of Service

Raises NameError if the name does not exist.

Resolver.ptr()

net.resolver.Resolver.ptr(address: string) -> list

The names an address points back at.

Takes the address itself rather than a reversed name, so there is no need to build 4.3.2.1.in-addr.arpa by hand.

import net.resolver { Resolver }

echo Resolver().ptr('8.8.8.8')
# [dns.google]

Parameters

  • address (string) — An IPv4 or IPv6 address.

Returns list — of string

Raises NameError if nothing is published for the address.

Resolver.caa()

net.resolver.Resolver.caa(name: string) -> list

Which certificate authorities a domain allows to issue for it.

Parameters

  • name (string)

Returns list — of Certification

Raises NameError if the name does not exist.

Resolver.to_string()

net.resolver.Resolver.to_string()

2026, Richard Ore and Zuri contributors