Net.DNS
module Net.DNS
Typed DNS lookup with explicit resolver ownership and bounded caching.
Use DNS.addresses for an occasional hostname lookup. Own a Resolver when a service performs repeated lookups, needs cache statistics, or must query a particular nameserver. Resolver ownership makes both caching and cleanup visible instead of hiding process-wide state behind every lookup.
using Net.DNS
let resolver = Resolver.system.try
let name = Name.parse("example.test").try
let addresses = resolver.addresses(name).try
resolver.close
record Name
A validated DNS name with caller-facing and IDNA ASCII spellings.
display keeps the readable form supplied by the caller; ascii is the wire-safe IDNA form used in DNS queries. Keeping both lets an error message say what the user typed without sending noncanonical labels to a resolver.
Fields
displayStringasciiString
type RecordType
Record families supported by Resolver.lookup.
Variants
AAAAACNAMEMXTXTSRVPTR
type DNSSECStatus
DNSSEC state reported by the underlying resolver. Kex does not independently validate DNSSEC and therefore commonly reports Indeterminate.
Variants
SecureInsecureBogusIndeterminate
type DNSRecord
Typed DNS resource records, preserving MX/SRV priorities and TXT chunks.
Variants
AddressRecord(Net.IP.Address)CanonicalName(Name)MailExchange(Integer, Name)TextRecord([String])ServiceRecord(Integer, Integer, Net.Port, Name)PointerRecord(Name)
record LookupResponse
Records and the reported DNSSEC state from one lookup.
Fields
records[DNSRecord]dnssecDNSSECStatus
record CacheOptions
Bounds for a resolver-owned positive and negative cache.
Positive answers honor their DNS TTL up to maximumTtl. Failed lookups are cached for negativeTtl, preventing a missing hostname from hammering the configured nameserver on every request.
Fields
entriesIntegeroptionalmaximumTtlDurationoptionalnegativeTtlDurationoptional
record Nameserver
One DNS server used by a custom resolver.
Fields
addressNet.IP.AddressportNet.Port
record ResolverOptions
Isolated resolver configuration. An empty search list only queries the name as written; search domains are tried in order for single-label names.
Fields
cacheCacheOptionsoptionalnameservers[Nameserver]search[Name]optionalretriesIntegeroptionaltimeoutDurationoptional
record CacheStatistics
Lifetime cache counters. clear empties entries but keeps these counters.
Compare hits with misses when tuning entries or TTL bounds. A rising evictions count means the resolver is seeing more distinct names than its cache can retain.
Fields
entriesIntegerhitsIntegermissesIntegernegativeHitsIntegerevictionsInteger
type Resolver
An opaque, process-safe resolver that owns its cache.
module Net.DNS.Name
Validation and IDNA conversion for DNS names.
function parse
Validates a name and converts Unicode labels to IDNA ASCII.
parse(text) : String -> Result<Name, NetError>
Parameters
textString- a dotted hostname, optionally with a trailing dot
Returns: Result<Name, NetError> — the name, or Parse
Examples
Preparing a user-supplied hostname for lookup
let name = Name.parse("münich.example").try
IO.printLine(name.display)
resolver.addresses(name).try
module Net.DNS.Resolver
Constructors for long-lived, cache-owning resolvers.
function system
Opens a resolver using system configuration and default cache bounds.
Reuse the returned resolver for the lifetime of a service so repeated names benefit from its bounded cache, then close it during shutdown.
system()
Returns: Result<Resolver, NetError> — a resolver handle
Examples
A service-owned resolver
let resolver = Resolver.system.try
let upstream = resolver.addresses(Name.parse("api.example.com").try).try
resolver.close
function custom
Opens an isolated resolver with typed nameservers and query bounds.
This does not inherit the machine's search domains or nameservers. It is useful for tests, service discovery, and applications with their own DNS policy.
custom(options)
Parameters
optionsResolverOptions- nameservers, search domains, retry count,
Returns: Result<Resolver, NetError> — a resolver, or Parse
Examples
Querying a DNS server used by a local integration test
let nameserver = Nameserver {
address: Net.IP.Address.parse("127.0.0.1").try,
port: Net.Port.from(5353).try
}
Resolver.custom(ResolverOptions {
nameservers: [nameserver],
timeout: 100.milliseconds
}).try
make Resolver
addresses
Resolves AAAA and A records, returning IPv6 addresses first.
This is the convenient operation for connecting to a host. Use lookup when record type, TTL-related behavior, or DNSSEC status matters.
addresses(name)
Parameters
nameName- the validated hostname
Returns: Result<[Net.IP.Address], NetError> — addresses or Resolve
Examples
Resolving an application upstream
let host = Name.parse("api.example.com").try
let addresses = resolver.addresses(host).try
lookup
Looks up one supported resource-record family.
lookup(kind, name)
Parameters
kindRecordType- the requested family
nameName- the validated owner name
Returns: Result<LookupResponse, NetError> — typed records or Resolve
Examples
Discovering a domain's mail exchangers
let response = resolver.lookup(MX, Name.parse("example.com").try).try
response.records.each { |record| IO.inspect(record) }
clear
Empties cached entries without resetting lifetime counters.
Existing statistics remain meaningful across a manual refresh, while the next lookup is forced back to DNS.
clear()
Returns: Void —
Examples
Refreshing service discovery after configuration changes
resolver.clear
statistics
Reports current occupancy and lifetime cache counters.
statistics()
Returns: CacheStatistics — current entries and lifetime counters
Examples
Reporting whether the cache is doing useful work
let stats = resolver.statistics
IO.printLine("DNS cache: ${stats.hits} hits, ${stats.misses} misses")
close
Idempotently closes the resolver.
Calls after closing fail with Closed; closing again is harmless.
close()
Returns: Void —
module Net.DNS.DNS
Convenience lookup operations for callers that do not need resolver ownership or cache reuse.
function addresses
Resolves a name once with a short-lived system resolver.
Prefer this for command-line tools and one-off checks. A server that looks up names repeatedly should own a Resolver so it can reuse cached answers.
addresses(name) : Name -> Result<[Net.IP.Address], NetError>
Parameters
nameName- the validated hostname
Returns: Result<[Net.IP.Address], NetError> — addresses or Resolve
Examples
Resolving one host in a short-lived command
let addresses = DNS.addresses(Name.parse("example.com").try).try