Most of this site is about the global DNS: one tree, run by thousands of organizations, the same answers for everyone. But the first decisions about a name are made on your own machine and your own network, long before any root server hears of it. How a home router hands out a resolver is covered in networks · DHCP & DNS and networks · Your home network; how a VPN carries your traffic to the office is networks · VPNs, tunnels & overlays. This chapter is about what happens to a name on that local network: the search list that quietly rewrites what you type, the .local names that never touch a DNS server, the printer that announces itself, the company name that works at your desk and vanishes on the VPN, and the old fallback protocols that will answer for a stranger.
Common mix-up: .local is not a private top-level domain you can use for your company's internal DNS. It is reserved for Multicast DNS: a name ending in .local is supposed to be shouted to the local link, not sent to a DNS server. Networks that named their internal zone corp.local years ago now fight every Mac, phone and Linux laptop that joins them. RFC 6762 recommends against it outright. Use a subdomain of a domain you own, such as corp.example.com, or home.arpa at home.
A program that wants an address calls a library function, usually getaddrinfo. That function, the stub resolver and its helpers in the operating system, makes several decisions before a single DNS packet is sent. Get any of them wrong and you see behavior that looks like DNS is lying, when DNS was never asked.
On Linux the order is written down in one line of /etc/nsswitch.conf. A typical desktop has hosts: files mdns4_minimal [NOTFOUND=return] dns: hosts file, then mDNS for .local names (and stop there if mDNS has no answer, so .local never leaks to a DNS server), then DNS. macOS and Windows keep the equivalent logic inside their resolver services, which is why dig and nslookup, which talk to a DNS server directly, can disagree with what your browser sees. A browser goes through the system resolver; dig skips the hosts file, the search list (unless you ask with +search) and mDNS entirely.
At work you type wiki into a browser and the company wiki appears. No server is called just wiki: your laptop was given a search list, often by DHCP, containing something like corp.example, and it quietly tried wiki.corp.example. The search list is a convenience from the era of typing hostnames into terminals, and it is still everywhere: in /etc/resolv.conf as a search line, in Windows as the DNS suffix search list, in every container platform.
Which goes first, the name as typed or the name with a suffix? That is decided by ndots. On Linux, the resolv.conf manual defines it as the number of dots a name must contain before it is tried as an absolute name first. The default is 1:
Every miss costs a round trip, and every candidate is usually asked twice, for IPv4 (A) and IPv6 (AAAA). The settings also decide who sees your internal names. Each wrong guess that leaves your network tells the public DNS, starting with the root servers, that someone asked for git.lab.
Early resolvers built the search list automatically from the host's own domain, walking up it one label at a time. RFC 1535 (1993) describes what happened when someone registered EDU.COM. A host in Tech.ACES.COM looking up UnivHost.University.EDU would try UnivHost.University.EDU.Tech.ACES.COM, then …EDU.ACES.COM, then …EDU.COM, which now existed and belonged to someone else. One wildcard record there could catch every connection from a .com site to a .edu site. The RFC's rule still applies: a search list must stay inside the part of the namespace you administer, and resolvers "SHOULD NOT come pre-configured with explicit search lists that perpetuate this problem." A suffix for a domain you don't control, or one you let expire, turns every short name your users type into a question answered by a stranger.
A name with no dots at all is the worst case. If no suffix matches, it is finally sent as a top-level domain. Years ago that reliably returned NXDOMAIN. Since the expansion of top-level domains, some short words people use as internal hostnames are also real top-level domains, so the last try in the list may go somewhere real. Container platforms make the opposite trade: Kubernetes writes ndots:5 and three cluster suffixes into each pod's resolv.conf, so that short service names resolve, at the cost of trying www.example.com with every cluster suffix before trying it as written.
Set a search list and an ndots value, then type a name. The expander follows the same rules as the Linux stub resolver and lists, in order, every question it would send and where each one goes, until one gets an answer. It flags internal names that leak to the public DNS and short names that resolve somewhere you did not intend. The network is simulated: internal zones corp.example, lab.corp.example, cluster.test and home.arpa live on the company resolver; everything else is "the internet," with a handful of made-up public records.
The algorithm the expander uses is the one in the GNU C library's res_search. Count the dots in the name. If the count is at least ndots, try the name as an absolute name first, then with each search suffix. If it is lower, try each suffix first and the bare name last. A trailing dot turns all of this off. The resolv.conf manual caps ndots at 15; since glibc 2.26 the search list itself is unlimited (it used to be six domains).
Implementations differ at the edges. musl, the C library in many small container images, handles the search list in its own way. Windows has separate settings for the primary suffix, connection-specific suffixes and "devolution," a feature that walks up the computer's own domain name one label at a time, the pattern RFC 1535 warned about, so check how far it is allowed to go. systemd-resolved applies the search list itself and can route each suffix to a different server (see split DNS below). When a lookup surprises you, look at the queries actually sent with a packet capture rather than reasoning from the config file.
Two settings help. Make names in configuration files fully qualified, with a trailing dot where the software allows it. And raise ndots only as far as your shortest internal names need: ndots:2 stops git.lab leaking, without making every external name pay the search-list tax.
A home network has no one to run a DNS zone. Nobody types the printer's address into a server. Yet your phone finds printer.local. That is Multicast DNS (mDNS), published as RFC 6762 in 2013 after more than a decade of use in Apple's Bonjour. The idea is plain: instead of asking a server, ask everyone on the link at once, and let the device that owns the name answer for itself.
The messages are ordinary DNS messages, the same header and sections described in Chapter 2. What changes is where they go. A query for any name ending in .local. is sent to UDP port 5353 at the multicast group 224.0.0.251 (or ff02::fb over IPv6). Every mDNS responder on the link receives it. The one that holds the name answers, normally by multicast too, so every other device can update its own cache from the same packet. Those groups are link-local: routers do not forward them, which is why printer.local on the guest Wi-Fi is invisible from the main network.
; simulated mDNS exchange on one link (documentation addresses) ; 192.0.2.10 → 224.0.0.251:5353 query ;printer.local. IN A ; 192.0.2.40 → 224.0.0.251:5353 response, sent by the printer itself printer.local. 120 IN A 192.0.2.40 ; class has the cache-flush bit set
With no registry, two devices can want the same name. RFC 6762 settles that with a short ritual every responder performs when it starts or joins a network. First it probes: after a random wait of up to 250 ms, it sends a query for the name it wants, three times, 250 ms apart, asking whether anyone already holds it. A device that does answers, and the newcomer must pick another name, for example by adding a number (printer-2.local, or "Office Printer (2)" for a service), then probe again. The RFC notes the window is deliberately short: an existing holder has just 750 ms to defend its name. If no one objects, the device announces: at least two unsolicited responses, one second apart, so every cache on the link learns the name at once.
Records whose name or data is a host name, such as A, AAAA and SRV, get a TTL of 120 seconds; others get 75 minutes. A device leaving the network politely sends its records again with a TTL of 0, a "goodbye," so caches drop them at once rather than waiting.
mDNS reuses the top bit of the 16-bit class field. In a question it is the unicast-response bit ("QU"): please answer me directly rather than to the group, used for the first query after a device wakes up. In an answer it is the cache-flush bit: this record set is complete, so throw away any older records for the same name and type. That is how a device that changed address corrects everyone's cache in one packet.
Because every query reaches every device, mDNS works hard to send fewer packets. A querier lists the answers it already has, with enough TTL left, in the answer section of its question (known-answer suppression), and responders that see their record listed stay silent. Repeated queries back off, the gap at least doubling from one second. Responders delay their replies by 20 to 120 ms when an answer is shared by many devices, so a question about a common service doesn't produce a burst of simultaneous replies. Even so, mDNS was designed for one link, and it grows noisy on large Wi-Fi networks; that is the motivation for the unicast alternatives at the end of the next section.
Knowing that printer.local is at 192.0.2.40 is only useful if you already knew to ask for printer.local. The real question a laptop has is "which printers are there?" DNS-Based Service Discovery (DNS-SD, RFC 6763) answers it with nothing but ordinary record types arranged in a chain. It works over mDNS on a home network and over unicast DNS in a managed one.
; simulated DNS-SD chain for one printer _ipp._tcp.local. 4500 IN PTR Office\032Printer._ipp._tcp.local. Office\032Printer._ipp._tcp.local. 120 IN SRV 0 0 631 printer.local. Office\032Printer._ipp._tcp.local. 4500 IN TXT "txtvers=1" "rp=ipp/print" "ty=Example Laser 400" printer.local. 120 IN A 192.0.2.40 ; \032 is a space written as a decimal escape: the instance name is "Office Printer"
Responders usually put the SRV, TXT and address records in the additional section of the PTR reply, so one round trip does the whole chain. A special query for PTR records at _services._dns-sd._udp.local lists every service type present on the link, which is how network browsers build their menus.
Because DNS-SD is only record types, a network operator can publish the same PTR, SRV and TXT records in a normal unicast zone, say _ipp._tcp.corp.example, and laptops that know to browse corp.example will find printers across a whole campus without any multicast. Two newer standards automate that. The Discovery Proxy (RFC 8766) sits on a link, listens to mDNS, and answers unicast DNS-SD questions about what it hears. The Service Registration Protocol (RFC 9665, 2025) lets devices register their services with a server using DNS UPDATE, protected by a public key each device keeps, so wireless and low-power networks don't have to carry multicast at all.
A simulated home link with four devices. Browse a service type to see the PTR answers arrive from the devices themselves, then pick an instance to follow the SRV, TXT and address records. Plug in a second printer that also wants the name printer.local and watch it probe, lose, rename itself and announce. Packets are drawn four times slower than real time so you can follow them. All names and addresses are simulated.
Some names are carved out of the public tree on purpose. RFC 6761 (2013) set the rules for these special-use domain names and IANA keeps the registry. Each entry says how applications, stub resolvers, recursive resolvers and authoritative servers should treat the name. For local networks, these are the ones that matter:
| Name | Reserved by | What it is for | What a resolver does with it |
|---|---|---|---|
| home.arpa | RFC 8375 (2018) | Names inside a home network, such as nas.home.arpa. Not unique: every home has its own. | Answers from local data; MUST NOT forward the query to the internet (RFC 6303 rules) |
| local | RFC 6762 (2013) | Multicast DNS names on one link | Sends to 224.0.0.251 / ff02::fb port 5353, not to a DNS server |
| localhost | RFC 6761 (2013) | This machine, always the loopback address | Answers locally; never asks a server |
| test, example, invalid | RFC 6761 (2013, from RFC 2606) | Testing, documentation, names that must never resolve | invalid always NXDOMAIN; the others may be used in private test setups |
| alt | RFC 9476 (2023) | Namespaces that are not DNS at all (for example peer-to-peer naming systems) | Never sent into the DNS |
| ipv4only.arpa | RFC 7050, RFC 8880 (2020) | Lets a host discover the NAT64 prefix (see DNS64 below) | Has fixed well-known answers |
| 10.in-addr.arpa etc. | RFC 6761, RFC 6303 | Reverse names for private address ranges | Answered locally; no reason to ask the public reverse tree (Chapter 7) |
home.arpa has a short, telling history. The home-networking working group first used .home, a name that was never delegated but that home routers had used for years, and which an enormous number of misconfigured devices were already leaking to the root servers. RFC 8375 moved it under .arpa, the infrastructure domain the IETF controls, where a reservation could actually be made and enforced: the public arpa zone holds an insecure delegation for home.arpa, so DNSSEC validators accept local answers for it instead of rejecting them.
The practical advice follows. At home, use home.arpa (or just .local names via mDNS). In an organization, use a subdomain of a domain you have registered: corp.example.com or internal.example.com. You can then get certificates for it, delegate it, and never collide with a future top-level domain. RFC 6762's Appendix G lists made-up suffixes people have used, .corp, .lan, .internal, .home among them, and says plainly that it does not recommend unregistered top-level domains at all.
Global DNS gives everyone the same answer. Organizations often want the opposite. The company website www.example.com should resolve, from the internet, to the public address on the firewall; from a desk in the office, it should resolve straight to the server's inside address, without going out to the internet and back. The wiki wiki.corp.example should not exist on the internet at all. Running different answers for the same name depending on who asks is called split horizon or split DNS. Servers implement it with views: the server checks where a query came from and answers it from one of two (or more) copies of the zone.
; the same question to the same server name, from two places (simulated) ; from a desk in the office: the inside view www.example.com. 300 IN A 198.51.100.10 ; from a café: the outside view www.example.com. 300 IN A 203.0.113.10 ; and wiki.corp.example from the café ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN
Why not just give everyone the public address? Because from inside, the public address points at your own firewall, and reaching your own public address from inside your own network needs hairpin NAT (the firewall turning the packet around), which many routers don't do. Split horizon avoids the question by never handing an inside client the outside answer.
The trouble is that a view is chosen by which resolver you ask, and that depends on where you are and what your machine has been told. It works perfectly in the office. It goes wrong when people take laptops home:
The robust fix is split DNS on the client: the VPN tells the operating system which suffixes belong to the office (corp.example, and example.com if it is split), and the system sends only those names to the office resolver and everything else to the normal one. macOS does this with per-domain resolver entries, systemd-resolved with per-link routing domains, Windows with its Name Resolution Policy Table.
Put the laptop in the office, at home, or at home on the VPN; choose how the VPN handles routes and DNS; and switch on the browser's own encrypted DNS. For each of four names the simulator works out which resolver is asked, which view answers, whether the address that comes back is reachable along the route you chose, and so whether the name works. Pick a name to draw its path. The company's inside addresses are shown in 198.51.100.0/24 and its public face in 203.0.113.0/24; everything is simulated.
| Name | Asked | Answer | Route to it | Result |
|---|
In a managed network, you can type a colleague's machine name and reach it, even though nobody edited a zone file when that laptop joined. The DHCP server, or the laptop itself, registered the name with DNS UPDATE, defined in RFC 2136 (1997). An UPDATE message uses the DNS format with a different opcode (5) and renames the four sections: Zone (which zone to change), Prerequisite (conditions that must hold), Update (records to add or delete) and Additional.
The prerequisites are what make it safe for many writers to share one zone. "This name must not exist yet," "this exact record set must be present": the server checks them atomically and refuses the whole update if any fails. A typical DHCP flow is: the client sends its preferred host name (the Client FQDN option, number 81 in DHCPv4 per RFC 4702, 39 in DHCPv6 per RFC 4704), the DHCP server hands out an address, then sends an update that adds the A record and the matching PTR record in the reverse zone.
; a DNS UPDATE as a person would write it with nsupdate (simulated, harmless) zone corp.example. prereq nxdomain laptop-17.corp.example. ; only if nobody has the name update add laptop-17.corp.example. 3600 A 198.51.100.117 send ; server reply: status NOERROR, the zone's SOA serial goes up by one
An open UPDATE port is an open door: anyone who can reach it can rewrite your names. So updates must be authenticated. RFC 3007 defines secure dynamic update; in practice that means TSIG, a shared secret key between the DHCP server and the DNS server that signs every message (RFC 8945, and Chapter 6 on how TSIG also protects zone transfers), or GSS-TSIG with Kerberos in Windows domains. Restrict which keys may change which names, and keep dynamic names in their own subzone so that a compromised key cannot rewrite www.
Some networks, mobile carriers especially, give devices only IPv6. To reach a site that has only an IPv4 address, they pair a NAT64 gateway with a DNS64 resolver (RFC 6147). When a name has an A record but no AAAA, DNS64 synthesizes an AAAA record by embedding the IPv4 address in an IPv6 prefix that routes to the gateway, usually the well-known 64:ff9b::/96 from RFC 6052. Ask for the AAAA of a site at 192.0.2.33 and you get 64:ff9b::c000:221; your packets go to the gateway, which translates them to IPv4. Hosts discover the prefix by asking for ipv4only.arpa, which has only well-known IPv4 addresses. Because the answer is invented by the resolver, DNS64 and DNSSEC validation on the device do not mix: a validating client will see that the synthesized record carries no valid signature.
Windows inherited two older ways to find a name on the local link: NetBIOS name service (NBT-NS, UDP 137, broadcast) and Link-Local Multicast Name Resolution (LLMNR, RFC 4795, UDP 5355 to 224.0.0.252 or ff02::1:3). Both are used as fallbacks when DNS has no answer. Both ask everyone on the link, and both believe whoever answers first. Neither has any way to check that the answer comes from the real owner of the name.
That is the weakness MITRE ATT&CK catalogs as T1557.001, LLMNR/NBT-NS Poisoning and SMB Relay. Someone mistypes a file-share name, DNS says NXDOMAIN, the machine falls back to asking the room, and any device on the same network can reply "that's me." If the name was a file share, the machine then tries to sign in to the impostor, which receives a challenge-response derived from the user's password that it can try to crack offline or relay to another server. Nothing about this needs a flaw in DNS; it needs only a typo and a fallback. MITRE's entry notes the same spoofing applies to mDNS traffic, which is one more reason to keep mDNS to networks that need it.
The fixes are well established and boring, which is what you want:
mDNS has the same basic trust model as LLMNR: anyone on the link can answer. The differences are in how it is used. Browsers and operating systems consult mDNS only for .local names, never as a fallback for a failed company name, so a typo in a share name doesn't wander onto the link. And it is designed for networks where everyone on the link is trusted to about the same degree: a home, not a hotel lobby. On guest and public networks, isolating clients from each other at the access point removes the audience for both mDNS and LLMNR. The Service Registration Protocol mentioned above goes further, binding each registered name to a key so a later impostor cannot take it over.