> your AI agent picks dependencies from memory; give it dated facts — try starlog.dev ↗ vet your agent's deps ↗ vibe-coding is fine. vibe-importing isn’t. — try starlog.dev ↗ vibe-importing isn’t fine ↗ your agent has never seen your private packages — try starlog.dev ↗ facts for private packages ↗ a linter for the dependencies your AI agent picks — try starlog.dev ↗ a linter for agent deps ↗

Back to Articles

dig8: Crawling DNS from the Root Servers Up

[ View on GitHub ]

dig8: Crawling DNS from the Root Servers Up

Hook

When you run dig example.com, you're trusting a recursive resolver to tell you the truth. But what if you need to see exactly which nameservers were contacted, in what order, and what each one actually said?

Context

Most DNS tools operate in one of two modes: they either query a recursive resolver (like 8.8.8.8) that does all the work for you, or they perform simple authoritative queries against specific nameservers you already know about. This works fine for routine lookups, but it obscures the entire delegation chain that makes DNS work.

When you're debugging why a domain resolves differently from different locations, auditing DNS infrastructure for security research, or simply trying to understand how DNS actually functions beneath the abstraction layer, you need visibility into the full iterative resolution process. You need to see the root servers, the TLD servers, the authoritative nameservers, and every delegation in between. dig8 fills this gap by implementing the same iterative resolution algorithm that recursive DNS servers use internally, but exposing every step to you in a detailed trace format.

Technical Insight

Iterative Resolution

Target Domain

13 Hardcoded Root IPs

TLD Referral

Recursive Query

TLD NS IPs

Auth NS Referral

Recursive Query

Auth NS IPs

Final DNS Record

dig8 CLI

Root Servers Query

Root Server Response

Resolve TLD NS Addresses

Query TLD Servers

Resolve Auth NS Addresses

Query Auth Servers

Complete Trace Output

System architecture — auto-generated

dig8's architecture mirrors the iterative resolution process defined in RFC 1034. Instead of asking a recursive resolver for an answer, it starts at the root servers (the 13 logical root server addresses are hardcoded) and follows referrals downward through the DNS hierarchy until it reaches an authoritative answer or hits an error condition.

The resolution flow works like this: First, dig8 queries a root server for your target domain. The root server responds with a referral to the appropriate TLD nameservers (like the .com servers). dig8 then resolves the IP addresses of those TLD nameservers (which itself may require additional iterative queries), contacts them to ask about your domain, receives another referral to the authoritative nameservers, resolves those nameserver addresses, and finally queries them for the actual record you want.

Here's what using the command-line tool looks like:

# Basic usage - resolve a domain starting from root servers
$ dig8 github.com

# The output shows each step:
# Query root server
[198.41.0.4]:53 <- github.com. A (13.2ms)
  -> delegation to [a-m].gtld-servers.net

# Resolve TLD nameserver addresses
[198.41.0.4]:53 <- a.gtld-servers.net. A (11.8ms)
  -> 192.5.6.30

# Query TLD server
[192.5.6.30]:53 <- github.com. A (45.3ms)
  -> delegation to [ns-1283.awsdns-32.org, ...]

# Resolve authoritative nameserver addresses
[198.41.0.4]:53 <- ns-1283.awsdns-32.org. A (12.1ms)
  -> 205.251.197.3

# Query authoritative server
[205.251.197.3]:53 <- github.com. A (8.7ms)
  -> 140.82.121.4 (ANSWER)

The library interface is equally straightforward. Here's how you'd use it programmatically:

package main

import (
    "fmt"
    "github.com/h8liu/dig8"
)

func main() {
    // Create a crawler instance
    crawler := dig8.NewCrawler()
    
    // Perform iterative resolution
    result, err := crawler.Crawl("example.com", dig8.TypeA)
    if err != nil {
        panic(err)
    }
    
    // Access the trace of all queries made
    for _, step := range result.Trace {
        fmt.Printf("Queried %s: %s\n", step.Server, step.Response)
    }
    
    // Get the final answer
    for _, record := range result.Answers {
        fmt.Printf("Answer: %s\n", record)
    }
}

The dig8s command enables batch processing, which is valuable for DNS research projects. You can feed it a list of domains and it will crawl each one, maintaining detailed logs of the entire resolution path. This is particularly useful when analyzing DNS infrastructure patterns across many domains or detecting anomalies in delegation chains.

What makes dig8 interesting from an architectural perspective is that it doesn't take shortcuts. Many DNS libraries cache aggressively or skip intermediate steps when they can infer answers. dig8 deliberately performs every query, even when resolving nameserver addresses that it might have seen before. This makes it slower but provides complete observability - you see exactly what a cold-start recursive resolver would see, without any cache pollution from previous queries.

The tool also handles the complexity of glue records correctly. When a nameserver is within the domain it's authoritative for (like ns1.example.com being authoritative for example.com), you'd have a circular dependency. The parent zone provides glue records (the IP addresses of these nameservers) in the additional section of the referral response, and dig8 properly extracts and uses these to continue resolution.

Gotcha

The biggest limitation of dig8 is its lack of production-ready features. There's no built-in rate limiting, which means you could easily overwhelm nameservers if you're not careful with batch operations. The error handling is basic - if a nameserver is down or returns malformed responses, you'll get errors but not necessarily graceful fallbacks to alternate nameservers. There's also no retry logic or timeout configuration exposed in the API, so transient network issues can derail your crawls.

The output format, while human-readable, isn't structured for parsing. If you want to analyze crawl results programmatically, you'll need to work with the library directly rather than parsing command output. The library also doesn't provide hooks for progress tracking during batch operations, so running dig8s on thousands of domains gives you no visibility into how far along the process is. Documentation is minimal - you'll be reading the source code to understand nuances of the API or output format. With only 5 GitHub stars, you're also unlikely to find community support or examples beyond what's in the repository.

Verdict

Use dig8 if you're doing DNS research, security auditing, or educational work where you need complete visibility into the DNS delegation chain. It's excellent for understanding exactly how iterative resolution works, debugging complex DNS configurations where you suspect issues at specific levels of the hierarchy, or building datasets of DNS infrastructure patterns. The batch processing capability makes it viable for academic research or one-time analysis projects. Skip dig8 if you need production-grade reliability, high-volume querying with rate limiting and concurrency controls, or extensive documentation and community support. For routine DNS debugging, standard tools like dig +trace provide similar iterative resolution visibility with more polish. For building production DNS services, mature libraries like miekg/dns offer comprehensive features and battle-tested reliability that dig8's focused scope doesn't provide.