# DNS
`dns/*` exposes the system resolver directly: hostname → address
(`getaddrinfo`), address → hostname (`getnameinfo`), plus literal-address
helpers. It is the same resolver `net/connect`, `http/*`, and
`http/ws-*` use internally, so `/etc/hosts`, `/etc/resolv.conf`, nsswitch,
and search domains all apply.
Lookups are **synchronous** — a call blocks the VM thread for the duration
of the lookup, exactly like `net/connect`'s internal resolution. There is
no Future-returning variant yet.
## `dns/*` Stdlib functions
| Function | Returns | Purpose |
|---|---|---|
| `[dns/lookup $host ?$opts]` | vector of address strings | Resolve a hostname |
| `[dns/addr-info $host ?$opts]` | vector of record maps | Full `getaddrinfo` records |
| `[dns/reverse $addr ?$opts]` | hostname string | PTR lookup for an IP literal |
| `[dns/hostname]` | string | Local hostname |
| `[dns/local-addresses ?$opts]` | vector of address strings | Interface addresses |
| `[dns/servers]` | vector of address strings | Nameservers from `/etc/resolv.conf` |
| `[dns/parse $s]` | canonical address string | Normalise an IP literal |
| `[dns/ip? $s]` | bool | IPv4 or IPv6 literal? |
| `[dns/ipv4? $s]` | bool | IPv4 literal? |
| `[dns/ipv6? $s]` | bool | IPv6 literal? |
Errors return `%Err{ :message "..." }` (the `gai_strerror` text for
resolver failures).
## Options
`dns/lookup`, `dns/addr-info`, and `dns/local-addresses` accept an option
map. The vocabulary follows bun's `dns.zig`:
| Key | Values | Default |
|---|---|---|
| `:family` | `:ipv4`, `:ipv6`, `:any` | `:any` |
| `:socktype` | `:stream`, `:dgram`, `:any` | `:stream` |
| `:protocol` | `:tcp`, `:udp`, `:any` | `:any` |
| `:flags` | vector of `:passive`, `:canonname`, `:numerichost`, `:numericserv`, `:v4mapped`, `:all`, `:addrconfig` | none |
| `:port` | 0..65535 | unset |
| `:backend` | `:system` / `:libc` | `:system` |
`:stream` is the default socket type for Node/bun compatibility (it keeps
the resolver from returning duplicate entries). `:backend :c-ares` is
recognised but unsupported — deft has no vendored c-ares, so it returns an
`%Err`. Unknown option keys are ignored.
## Resolving
[dns/lookup "example.com"] # => @{ "93.184.216.34" "2606:2800:220:1:248:1893:25c8:1946" } [dns/lookup "localhost" %{ :family :ipv4 }] # => @{ "127.0.0.1" } # Literal fast path — no resolver traffic: [dns/lookup "127.0.0.1" %{ :flags @{ :numerichost } }] # => @{ "127.0.0.1" }
`dns/addr-info` keeps the metadata `dns/lookup` drops:
[dns/addr-info "example.com" %{ :port 443 }] # => @{ %{ :address "93.184.216.34" :family :ipv4 :socktype :stream # :protocol :tcp :port 443 } ... }
Records carry no TTL — `getaddrinfo` does not expose one.
## Reverse and local names
[dns/reverse "8.8.8.8"] # => "dns.google" [dns/reverse "127.0.0.1" %{ :numeric true }] # => "127.0.0.1" (no PTR) [dns/hostname] # => "plotinus" [dns/local-addresses %{ :family :ipv4 }] # => @{ "127.0.0.1" "192.168.1.20" ... } [dns/servers] # => @{ "127.0.0.53" }
`dns/reverse` requires an IP literal and uses `NI_NAMEREQD`, so an
address without a PTR record is an `%Err` rather than the numeric form.
`dns/servers` is informational: the platform resolver reads
`/etc/resolv.conf` itself, so the list cannot be overridden at runtime.
## Literal helpers
[dns/parse "0:0:0:0:0:0:0:1"] # => "::1" [dns/ip? "::1"] # => true [dns/ipv4? "::1"] # => false [dns/ipv6? "::1"] # => true
`dns/parse` returns the canonical form (longest zero run shortened, no
port); predicates are pure `inet_pton` checks with no network access.
## Quick Reference
| Form | Purpose |
|---|---|
| `[dns/lookup $host ?$opts]` | Hostname → addresses |
| `[dns/addr-info $host ?$opts]` | Hostname → full records |
| `[dns/reverse $addr ?$opts]` | Address → hostname |
| `[dns/hostname]` | Local hostname |
| `[dns/local-addresses ?$opts]` | Interface addresses |
| `[dns/servers]` | Configured nameservers |
| `[dns/parse $s]` | Canonical IP literal |
| `[dns/ip? $s]` / `[dns/ipv4? $s]` / `[dns/ipv6? $s]` | Literal predicates |