Skip to content

Conversation

@agaffney
Copy link
Contributor

@agaffney agaffney commented Dec 11, 2025

Fixes #264


Summary by cubic

Adds ICANN resolver support using embedded root hints so we can perform true recursive resolution from the root servers. Root hints can be overridden via config or env.

  • New Features
    • Embedded named.root via go:embed and loaded at startup.
    • New DnsConfig fields: RootHints and RootHintsFile (env: DNS_ROOT_HINTS, DNS_ROOT_HINTS_FILE).
    • Parses root hints and uses root NS IPs when no nameservers are discovered for a domain.

Written for commit 6e3dd20. Summary will update automatically on new commits.

Summary by CodeRabbit

  • New Features
    • Built-in DNS root hints are included by default.
    • The app loads root hints at startup and uses them as a fallback for root-level DNS resolution.
    • You can provide a custom root hints file at runtime to override the built-in defaults.

✏️ Tip: You can customize this high-level summary in your review settings.

@agaffney agaffney requested a review from a team as a code owner December 11, 2025 22:13
@coderabbitai
Copy link

coderabbitai bot commented Dec 11, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Adds DNS root-hints support. Two new fields (DnsConfig.RootHints and DnsConfig.RootHintsFile) and an embedded default root hints file (internal/config/named.root via //go:embed) are introduced; the global config initializes Dns.RootHints from the embedded data and Load will override it by reading RootHintsFile if provided. The DNS package gains a package-level rootHints cache and loadRootHints(cfg *config.Config) error, which is invoked during Start to parse and populate root hints (startup fails on parse error). findNameserversForDomain now falls back to parsed root-hints (using "." as the domain) when no NS records are found.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Verify internal/config/config.go embed declaration, initialization of defaultRootHints, and precedence of RootHintsFile over embedded data.
  • Inspect internal/dns/dns.go loadRootHints parsing loop, error wrapping, rootHints data structure layout, and Start integration.
  • Check findNameserversForDomain fallback: construction of NS, A, AAAA mappings and use of "." as the fallback domain.
  • Validate formatting and syntactic correctness of internal/config/named.root.

Possibly related PRs

Pre-merge checks and finishing touches

✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: ICANN resolver support' directly corresponds to the main objective of adding ICANN resolver support with embedded root hints.
Linked Issues check ✅ Passed The PR implements the core requirement from issue #264 by adding ICANN resolver support with embedded root zone loading via named.root file and root hints configuration.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing ICANN resolver support: config fields for root hints, embedded root zone file, and root hints loading/fallback logic in DNS resolution.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/icann-resolver

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5c6c7d3 and 6e3dd20.

📒 Files selected for processing (3)
  • internal/config/config.go (5 hunks)
  • internal/config/named.root (1 hunks)
  • internal/dns/dns.go (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/config/config.go
🧰 Additional context used
🧬 Code graph analysis (1)
internal/dns/dns.go (1)
internal/config/config.go (1)
  • Config (19-28)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (go)
🔇 Additional comments (3)
internal/dns/dns.go (3)

33-34: LGTM!

The package-level rootHints variable is properly structured to cache DNS resource records organized by type and name.


45-48: LGTM!

Loading root hints during startup with proper error propagation ensures they're available before handling queries.


85-105: Verify dns.NewRR behavior with comment and empty lines in root hints data.

The code relies on dns.NewRR to handle comment lines (starting with ;) and empty lines gracefully. The current implementation checks for tmpRR == nil on line 92 and returns errors on lines 89-91, which should cover both cases. However, confirm that the library returns (nil, nil) rather than (nil, error) for these cases; if it returns errors, explicit pre-filtering (e.g., strings.TrimSpace and skipping lines starting with ;) would be more defensive and clearer for maintenance.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
internal/config/config.go (1)

180-187: Consider wrapping the error with context.

The error from os.ReadFile is returned directly. Adding context would help users understand what went wrong during configuration loading.

 	// Load root hints
 	if globalConfig.Dns.RootHintsFile != "" {
 		hintsContent, err := os.ReadFile(globalConfig.Dns.RootHintsFile)
 		if err != nil {
-			return nil, err
+			return nil, fmt.Errorf("error reading root hints file: %w", err)
 		}
 		globalConfig.Dns.RootHints = string(hintsContent)
 	}
internal/dns/dns.go (1)

87-87: Consider compiling the regexp at package level.

The regex is compiled on each call to loadRootHints. While this only runs at startup, moving it to a package-level variable is a minor best-practice improvement.

+var commentRe = regexp.MustCompile(`^\s*;`)
+
 func loadRootHints(cfg *config.Config) error {
-	commentRe := regexp.MustCompile(`^\s*;`)
 	rootHints = make(map[uint16]map[string][]dns.RR)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5f98338 and e89f264.

📒 Files selected for processing (3)
  • internal/config/config.go (5 hunks)
  • internal/config/named.root (1 hunks)
  • internal/dns/dns.go (5 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: Analyze (go)
🔇 Additional comments (6)
internal/config/named.root (1)

1-92: LGTM - Standard root hints file.

This is the standard IANA root hints file format containing all 13 root servers with their IPv4 and IPv6 addresses. The data is current (November 2025) and follows the expected BIND zone file format.

internal/config/config.go (3)

10-10: LGTM - Embed import for root hints.

The blank identifier import for embed is required to enable the //go:embed directive.


40-41: LGTM - Root hints configuration fields.

The dual approach (inline content via RootHints or file path via RootHintsFile) provides flexibility for different deployment scenarios.


75-77: LGTM - Embedded default root hints.

Good use of //go:embed to bundle the root hints file at compile time, ensuring the server works out of the box without external dependencies.

internal/dns/dns.go (2)

34-35: LGTM - Root hints cache declaration.

The nested map structure appropriately indexes records by type and name for efficient lookups.


46-49: LGTM - Root hints loading at startup.

Loading root hints before setting up DNS handlers ensures the cache is populated before any queries arrive.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 issues found across 3 files

Prompt for AI agents (all 4 issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="internal/config/config.go">

<violation number="1" location="internal/config/config.go:184">
P2: Error returned without context wrapping. For consistency with other error handling in this function (e.g., `fmt.Errorf(&quot;error reading config file: %w&quot;, err)`), this error should be wrapped with descriptive context.</violation>
</file>

<file name="internal/config/named.root">

<violation number="1" location="internal/config/named.root:92">
P3: File is missing a trailing newline. POSIX text files should end with a newline character. While most modern DNS parsers handle this gracefully, adding a trailing newline ensures compatibility with all tools and follows standard conventions.</violation>
</file>

<file name="internal/dns/dns.go">

<violation number="1" location="internal/dns/dns.go:88">
P0: Variable shadowing: using `:=` creates a local `rootHints` that shadows the package-level variable. The package-level `rootHints` is never populated. Use `=` instead and initialize the package-level variable before assignment.</violation>

<violation number="2" location="internal/dns/dns.go:91">
P1: Empty lines will cause nil pointer panic. `dns.NewRR(&quot;&quot;)` returns `nil, nil`, and calling `tmpRR.Header()` on nil will panic. Add a check for empty lines or verify `tmpRR` is not nil.</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

@agaffney agaffney force-pushed the feat/icann-resolver branch from e89f264 to eb844ff Compare December 14, 2025 18:12
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
internal/config/config.go (1)

180-187: Wrap the error with context.

The error at line 184 should be wrapped with descriptive context for consistency with other error handling in this function (e.g., line 121, line 125).

Apply this diff:

 	if globalConfig.Dns.RootHintsFile != "" {
 		hintsContent, err := os.ReadFile(globalConfig.Dns.RootHintsFile)
 		if err != nil {
-			return nil, err
+			return nil, fmt.Errorf("error reading root hints file: %w", err)
 		}
 		globalConfig.Dns.RootHints = string(hintsContent)
 	}
🧹 Nitpick comments (1)
internal/dns/dns.go (1)

45-48: Consider adding success logging for root hints load.

While treating root hints load failure as fatal is appropriate, logging successful loading would aid debugging and provide visibility into startup initialization.

Apply this diff:

 	// Load root hints
 	if err := loadRootHints(cfg); err != nil {
 		return err
 	}
+	slog.Info("root hints loaded successfully")
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e89f264 and eb844ff.

📒 Files selected for processing (3)
  • internal/config/config.go (5 hunks)
  • internal/config/named.root (1 hunks)
  • internal/dns/dns.go (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/config/named.root
🧰 Additional context used
🧬 Code graph analysis (1)
internal/dns/dns.go (1)
internal/config/config.go (1)
  • Config (19-28)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (go)
🔇 Additional comments (6)
internal/config/config.go (4)

10-10: LGTM! Import added for embed support.

The blank import of the embed package enables the //go:embed directive used at line 75.


40-41: LGTM! New configuration fields for root hints.

The RootHints and RootHintsFile fields are properly configured with YAML tags and environment variable bindings.


75-76: LGTM! Embedded root hints for default configuration.

Using //go:embed to embed the named.root file provides sensible defaults without requiring external files.


87-87: LGTM! Proper initialization from embedded defaults.

Converting the embedded defaultRootHints bytes to a string for the initial RootHints value is appropriate.

internal/dns/dns.go (2)

33-33: LGTM! Package-level root hints cache.

The global rootHints variable provides a centralized cache for parsed root hints, properly typed as a nested map structure.


85-105: LGTM! Root hints loading implementation is correct.

The function properly:

  • Initializes the package-level rootHints variable (no shadowing)
  • Handles nil RRs from empty lines and comments (lines 92-94)
  • Uses correct nested map types with slice values
  • Appends to slices instead of overwriting entries

Previous review concerns have been addressed.

@agaffney agaffney force-pushed the feat/icann-resolver branch from eb844ff to 5c6c7d3 Compare December 16, 2025 20:53
Fixes #264

Signed-off-by: Aurora Gaffney <aurora@blinklabs.io>
@agaffney agaffney force-pushed the feat/icann-resolver branch from 5c6c7d3 to 6e3dd20 Compare December 16, 2025 20:57
@agaffney agaffney merged commit 20434fc into main Dec 16, 2025
11 checks passed
@agaffney agaffney deleted the feat/icann-resolver branch December 16, 2025 23:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create an ICANN resolver

3 participants