Quickstart
Five minutes
Nothing here needs root, a config file, or a resolver already configured on the machine. hollow starts at the root servers, so it works on a host with no /etc/resolv.conf at all.
1. Resolve a name
The default path walks from the root servers down to the authoritative nameserver. No upstream resolver is involved, which is why the first lookup for a name costs a few hundred milliseconds and the second costs nothing.
$ hollow resolve example.com; <<>> hollow <<>> example.com. A;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 53913;; flags: qr aa; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1;; OPT PSEUDOSECTION:; EDNS: version: 0, flags:; udp: 1232;; QUESTION SECTION:;example.com. IN A;; ANSWER SECTION:example.com. 300 IN A 172.66.147.243example.com. 300 IN A 104.20.23.154;; Query time: 31 ms;; SERVER: 2803:f800:50::6ca2:c0a2#53 (udp);; MSG SIZE rcvd: 72
2. Ask for a record type
The type is the second argument and defaults to A. Nine types are parsed into fields; anything else is accepted and kept verbatim under RFC 3597, so a type hollow does not model can still be asked for by number as TYPE65535.
$ hollow resolve google.com MX; <<>> hollow <<>> google.com. MX;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 47540;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 5;; OPT PSEUDOSECTION:; EDNS: version: 0, flags:; udp: 512;; QUESTION SECTION:;google.com. IN MX;; ANSWER SECTION:google.com. 300 IN MX 10 smtp.google.com.;; ADDITIONAL SECTION:smtp.google.com. 300 IN A 192.178.158.27smtp.google.com. 300 IN A 192.178.158.26smtp.google.com. 300 IN AAAA 2404:6800:4013:813::1asmtp.google.com. 300 IN AAAA 2404:6800:4013:813::1b;; Query time: 80 ms;; SERVER: 2001:4860:4802:38::a#53 (udp);; MSG SIZE rcvd: 148
3. See the walk
trace resolves through the same code path and draws the delegation chain that walk actually took. Every line comes from a step the resolver emitted as it sent the packet, so a trace and a resolve cannot disagree about what happened.
hollow trace www.github.com4. Run the server
Loopback and port 15353 by default: no privileges, no firewall prompt, and no open resolver on the network. Point dig at it, or set it as your system resolver.
$ hollow serve --addr 127.0.0.1:15399 --control 127.0.0.1:15398 --block hosts.txthollow listening on 127.0.0.1:15399, udp and tcpresolving iteratively from the rootquery names go out with randomised case, and a reply that does not echo it is refusedcache holding 100000 answersblocking 2 names and 1 domains with everything under them, 0 allowed past, answering nxdomainrate limiting responses past 20 a second to one client network, every second one answered truncated so a real client retries over tcp; 127.0.0.0/8, ::1/128 exemptcontrol socket on 127.0.0.1:15398, for hollow stats and hollow dash^Ccache: 0 hits, 1 misses, 0 served stale, 1 entries, 0 evictedqueries: 2 in 4s, 1 blocked, 0 upstream failureslatency: p50 461ms, p99 461mstop names: 1 ads.example.net. 1 example.com.control: 0 clients attached over this runhollow stopped
The startup block is the whole configuration stated back to you, and the block after Ctrl-C is the server's own accounting of what it did. Blocklists load once, at startup, and a line that does not parse is counted and reported rather than silently dropped.
5. Query it
Any DNS client will do. The server answers on UDP and TCP at the same address.
dig @127.0.0.1 -p 15353 example.com