djbdns-client — configuring the DNS client in djbdns
The DNS client library common to all djbwares utilities is centred upon a dns_resolve() function.
Like other DNS client libraries, it talks to one or more (local) proxy DNS servers, which do most of the actual lookup work.
Several domain names are handled by the library internally, however, without contacting any servers at all.
There are separate DNS server configuration settings for:
DNS-SD lookups under service.arpa., per RFC 9665;
onion. lookups, per RFC 7686;
mDNS lookups under local. and several subdomains of ip-addr.arpa. and ip6.arpa. corresponding to link-local IP addresses, per RFC 8375;
homenet lookups under home.arpa., per RFC 8375; and
regular lookups, for everything else.
There is also a name qualification procedure that some tools apply to some input names before performing the name lookups proper.
If the input string to be passed through name qualification is the human-readable form of an IP address, the DNS client simply synthesizes and returns corresponding A and AAAA resource records (and empty resource record sets for other types) without performing any name qualification or lookups.
This is designed to accommodate a common mis-usage where people supply human-readable form IP addresses where domain names should be supplied.
Renormalization is performed, so leading zeroes are discarded; but numeric values that are not legal for an IP address mean that the string will not take this short circuit synthesis route.
::1 is not taken to be a human-readable form of an IP version 6 address, for example.
It must be spelled out in full as the single-label pseudo domain name 0:0:0:0:0:0:0:1.
In the original Bernstein DNS client library, if the input string was to be passed through name qualification, and was an arbitrary length string of arbitrary valued dot-separated numbers it would be converted, without any name qualification or lookups being performed, into possibly more than 1 IPv4 address, not caring about numeric overflow or values larger than 256, and not caring about partially-complete addresses. This wholly undocumented mechanism would lead to stupid consequences and real-world risks such as:
the domain name 6.2.8.2.999999999999 apparently existing and having an IPv4 address,
the domain name 24.75.345.200 (from a Sandra Bullock movie in the 1990s) apparently existing and having someone's real (and not reserved-use) IPv4 address, and
the domain name 1729.86400.99999.2147483647.100000000.10000000.10000000.10000000 apparently existing and having two IPv4 addresses.
This undocumented behaviour, that for one thing would make the deliberately invalid or malformed IPv4 addresses used in movies and television dramas work and have surprising consequences for unsuspecting people who just happened to be assigned the real IP addresses, is not present in this version of the DNS client library.
When name qualification is used, various queries have directly synthesized answers created by the DNS client library itself, short-circuiting any query to a proxy DNS server.
Per RFC 7686, if there are no configured onion. proxy DNS servers, onion. and all of its subdomains are synthetic "no such domain" errors.
Per RFC 6761, invalid. and all of its subdomains are synthetic "no such domain" errors.
Per the implication of RFC 6761 and long-standing /etc/hosts convention, reverse lookups of 127.a.b.c IPv4 addresses map to , except that 127.0.0.1 maps to c.b.a.127.localhost.localhost..
Likewise, reverse lookups of the ::1 IPv6 address map to localhost..
In the forward direction, A and AAAA lookups of localhost. map to 127.0.0.1 and ::1 respectively, and A and AAAA lookups of map to 127.c.b.a.127.localhost.a.b.c and ::FFFF:127.a.b.c respectively.
Per RFC 8880, ipv4only.arpa. exists synthetically with the fixed IPv4 addresses 192.0.0.170 and 192.0.0.171, reverse lookups of those IPv4 addresses synthetically map back to it, and all of its subdomains are synthetic "no such domain" errors.
NAT64 prefix discovery is supposed not to be using this DNS client library.
The IP addresses of the one or more proxy DNS servers are determined as follows:
The primary configuration setting is the process environment of whatever tool is running. The values of two environment variables are taken to be a list of whitespace-separated IP addresses and a numeric port number:
DNSCACHEIP_DNSSD and DNSCACHEPORT_DNSSDthe proxy DNS server(s) used for DNS Service Discovery
DNSCACHEIP_HOME and DNSCACHEPORT_HOMEthe proxy DNS server(s) used for homenet
DNSCACHEIP_ONION and DNSCACHEPORT_ONIONthe proxy DNS server(s) used for onion.
DNSCACHEIP_MDNS and DNSCACHEPORT_MDNSthe proxy DNS server(s) used for multicast DNS
DNSCACHEIP and DNSCACHEPORTthe regular proxy DNS server(s) used for everything else
If this environment variable is not found, the library falls back to resolv.conf(5). The values of various directives are likewise taken to be a list of whitespace-separated IP addresses.
nameserver-dnssdthe proxy DNS server(s) used for DNS Service Discovery
nameserver-homethe proxy DNS server(s) used for homenet
nameserver-onionthe proxy DNS server(s) used for onion.
nameserver-mdnsthe proxy DNS server(s) used for multicast DNS
nameserverthe regular proxy DNS server(s) used for everything else
If no other configuration is specified, the library falls back to hardwired default IP address of 127.0.0.1 and ::1 for everything except for mDNS for which the hardwired defaults are 224.0.0.251 and FF02::FB. This is the same fallback as other DNS client libraries, and running an instance of dnscache(1) listening on either 127.0.0.1 or ::1 is a conventional system configuration.
Several tools pass input strings through a name qualification phase before performing lookups with the dns_resolve() function.
The name qualification procedure is the simple iterative application of a set of string matching and replacement rules.
The ruleset is determined as follows:
The primary configuration setting is the process environment of whatever tool is running.
The value of the DNSREWRITEFILE environment variable is taken to be the name of a file from which rewrite rules are read.
If this environment variable is not found, the library falls back to /etc/dnsrewrite as the name of the file.
If the rules file is not found, the library falls back to the LOCALDOMAIN environment variable.
If it exists, its value is used to synthesize rules.
Its value is treated as a whitespace-separated list of domains and is turned into a ?:. rewrite rule with the domain+.domaindomains separated by +.
A final *.: rule is appended.
If neither the rules file nor the LOCALDOMAIN environment variable are found, the library falls back to resolv.conf(5) and synthesizes rules from its directives.
The first search or a domain directive followed by a whitespace-separated list of domains is turned into a ?:. rewrite rule with the domain+.domaindomains separated by +.
A final *.: rule is appended.
If not even a suitable directive in resolv.conf(5) is found, the library falls back on the result of the operating system's gethostname(2) library function.
A ?:. rewrite rule is synthesized where domaindomain is the part of the (dynamic) hostname that follows the first dot.
A final *.: rule is appended.
Rules comprise a single leading character, then a match, a :, and a replacement.
=match:replacement
An exact match rule and complete replacement rule.
If the input string exactly matches match then the replacement is substituted.
-match:replacement
A suffix match and complete replacement rule.
If a suffix of the input string exactly matches match then the replacement is substituted.
*match:replacement
An unconstrained suffix match and suffix replacement rule.
If a suffix of the input string exactly matches match then the replacement is suffixed instead to the non-matching prefix part of the input string.
?match:replacements
A constrained suffix match and suffix replacement rule.
If a suffix of the input string exactly matches match and the remaining prefix does not contain a dot, then the replacement is suffixed instead to the non-matching prefix part of the input string.
If the resultant string, after applying all of the rules, contains + characters, it is deemed to be a search path of the form .
A search path list of names to try looking up is constructed by each prefix+suffix+suffixsuffix in order being appended to prefix
*.:
This simply removes trailing dots. The djbwares DNS client library does not need the trailing dots after qualification, as the output, search path, list of one or more names is taken to be the "fully-qualified" name(s) to actually look up.
?:.example.org
If the input string is curtin, the resultant name is curtin.example.org.
But this rule will not apply if the input string is saint.james or curtin..
LOCALDOMAIN environment variable and search directives in resolv.conf(5) are converted when they have 1 entry, even though it is not actually traditional DNS client library behaviour.
?:.intranet.example.org+.example.org+
If the input string is curtin, the resultant search list is curtin.intranet.example.org then curtin.example.org then curtin.
But this rule will not apply if the input string is saint.james or curtin..
curtin if the input string is curtin, which is one way in which the world often generates bogus query traffic to the "." content DNS servers (or, worse, happens to match one of the lesser-known global top-level domains unexpectedly).
Converting the LOCALDOMAIN environment variable and search directives in resolv.conf(5) when they have more than 1 entry does not append an extra final blank search path beyond the search path that is given.
But, rather, it yields the form:
?:.intranet.example.org+.example.org
If the input string is curtin, the resultant search list is curtin.intranet.example.org then curtin.example.org.
*:.work.example.org+.school.example.org+
If the input string is curtin, the resultant search list is curtin.work.example.org then curtin.school.example.org then curtin.
If the input string is saint.james, the resultant search list is saint.james.work.example.org then saint.james.school.example.org then saint.james.
example.com becoming example.com.work.example.org and example.com.school.example.org here before example.com is finally tried.
saint.james. or curtin., which definitely surprises many users, even the ones that thought that they knew to use trailing dots.
In general, despite being the traditional DNS client library behaviour of yore, this is almost never a good rule.
*.example.org:.example.net
If the input string is saint.james.example.org, the resultant name is saint.james.example.net.
However, if the input string is saint.james.example.org., this rule does not apply, following the usual convention that a trailing dot indicates an already qualified name.
-.example.com:example.com
If the input string is smith.example.com or meyers.example.com, the resultant name is simply example.com.
Again however, if the input string is instead smith.example.com. or meyers.example.com., this rule does not apply, following the usual convention.
The input string example.com sans any subdomain is also not matched by this rule; a separate extra = rule being necessary for that if appropriate.
The most common real world use for this are the subdomains of localhost. and of invalid. all being their equivalents.
-.localhost:localhost -.invalid:invalid
But those are already special-use domain names handled by the DNS client library.
The DNSCACHEIP, DNSREWRITEFILE, and LOCALDOMAIN environment variables and limited compatibililty processing of resolv.conf(5) were originally part of Daniel J. Bernstein's djbdns toolset, begun in 1999.
At the time, many of the additional systems and special-use domain names had not even been invented.
The formal documentation of conventional DNS results for subdomains of localhost. and invalid. did not occur until 2013, for instance.
The additional environment variables and mechanisms for special-use domain names were added in 2025.
The caution against using search paths is also Bernstein's, and remains as true today as it did then.