net.resolver
import net
netexposes this asnet.resolver, soimport netis enough and the names are called asnet.resolver.*.import net.resolverreaches 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_sizeanddnssec.
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_configandserver_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
system_search()
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(), soechoandprint()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(), soechoandprint()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(), soechoandprint()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(), soechoandprint()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,issuewildoriodefin 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:
| type | data |
|---|---|
A, AAAA | the address, as a string |
NS, CNAME, PTR, DNAME | the name, as a string |
MX | a MailExchange |
SRV | a Service |
SOA | a StartOfAuthority |
CAA | a Certification |
TXT | a list of strings, one per character-string |
| anything else | the record’s data, as bytes |
- printable — has a
@to_string(), soechoandprint()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(), soechoandprint()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(), soechoandprint()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(), soechoandprint()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.
| option | default | what it does |
|---|---|---|
servers | the system’s own | the addresses to ask, in order |
port | 53, or 853 with tls | the port to ask them on |
timeout | 5000 | milliseconds one server gets to answer |
attempts | 2 | times to go round the whole list |
tcp | false | send over TCP rather than UDP |
tls | false | send over TLS, which implies TCP |
server_name | none | the name to verify the TLS certificate against |
tls_config | a default TlsConfig | the trust settings for TLS |
search | the system’s own | suffixes to try against a short name |
ndots | 1 | dots a name needs before it is tried unsuffixed first |
recursion | true | ask the server to do the work |
edns | true | advertise a larger UDP payload |
udp_size | 1232 | how much larger |
dnssec | false | ask for signature records, without validating them |
cache | true | remember answers for as long as they say |
cache_size | 512 | entries 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 fromTYPESor a number.klass(?string|number) —INunless 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