std.dns
import std.dns
dns.resolve("ecko.sh") # A/AAAA addresses, as a list
dns.lookup("ecko.sh", "MX") # one record type
dns.reverse("93.184.216.34") # PTR: one name, as a string
Needs net. Record types for lookup: A, AAAA, CNAME, MX, TXT.
Names under the reserved .test domain and localhost are answered from a fixed built-in table, never the network - dns.lookup("ecko.test", "MX") gives the same answer on every machine - so examples and tests stay offline.
resolve or lookup
resolve(host) answers with the host's addresses, IPv4 before IPv6, each sorted. That is what you want to answer "what would this machine connect to?"
lookup(host, type) queries DNS for a specific record type, which is what you want to inspect a domain's actual records - MX before sending mail, TXT for a verification token. Both use the system's resolver configuration.
MX records
for mx in dns.lookup("example.com", "MX") {
print("{mx.priority} {mx.host}")
}
Sorted by priority, lowest first - which is delivery order. Pair with smtp.
Errors
A name that does not resolve raises { kind: "net", host } rather than returning an empty list, so a typo does not read as "no records". Catch it where the name came from outside:
try { addrs = dns.resolve(domain) } catch (e) { reject("unknown host") }
DNS is not validation
Two habits worth having:
- Resolving a domain does not mean an address is deliverable. A valid MX record says nothing about whether the mailbox exists. Only sending finds that out.
- DNS answers change. They are cached, they vary by resolver, and they are attacker-influenceable on a hostile network. Do not use a DNS result as an authorization decision.
Not for connecting
You do not need to resolve before connecting - std.net and std.http take a hostname and resolve it themselves. Resolving first and connecting to the address means you lose TLS hostname verification unless you are careful, so pass the name.