Skip to content

Commit 20434fc

Browse files
authored
feat: ICANN resolver support (#471)
Fixes #264 Signed-off-by: Aurora Gaffney <[email protected]>
1 parent 7fcd97d commit 20434fc

File tree

3 files changed

+153
-2
lines changed

3 files changed

+153
-2
lines changed

internal/config/config.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
package config
88

99
import (
10+
_ "embed"
1011
"fmt"
1112
"os"
1213
"strings"
@@ -36,6 +37,8 @@ type DnsConfig struct {
3637
ListenPort uint `yaml:"port" envconfig:"DNS_LISTEN_PORT"`
3738
ListenTlsPort uint `yaml:"tlsPort" envconfig:"DNS_LISTEN_TLS_PORT"`
3839
RecursionEnabled bool `yaml:"recursionEnabled" envconfig:"DNS_RECURSION"`
40+
RootHints string `yaml:"rootHints" envconfig:"DNS_ROOT_HINTS"`
41+
RootHintsFile string `yaml:"rootHintsFile" envconfig:"DNS_ROOT_HINTS_FILE"`
3942
FallbackServers []string `yaml:"fallbackServers" envconfig:"DNS_FALLBACK_SERVERS"`
4043
}
4144

@@ -69,6 +72,9 @@ type TlsConfig struct {
6972
KeyFilePath string `yaml:"keyFilePath" envconfig:"TLS_KEY_FILE_PATH"`
7073
}
7174

75+
//go:embed named.root
76+
var defaultRootHints []byte
77+
7278
// Singleton config instance with default values
7379
var globalConfig = &Config{
7480
Logging: LoggingConfig{
@@ -78,6 +84,7 @@ var globalConfig = &Config{
7884
ListenAddress: "",
7985
ListenPort: 8053,
8086
ListenTlsPort: 8853,
87+
RootHints: string(defaultRootHints),
8188
// hdns.io
8289
FallbackServers: []string{
8390
"103.196.38.38",
@@ -170,6 +177,14 @@ func Load(configFile string) (*Config, error) {
170177
globalConfig.Indexer.InterceptSlot = interceptSlot
171178
}
172179
}
180+
// Load root hints
181+
if globalConfig.Dns.RootHintsFile != "" {
182+
hintsContent, err := os.ReadFile(globalConfig.Dns.RootHintsFile)
183+
if err != nil {
184+
return nil, err
185+
}
186+
globalConfig.Dns.RootHints = string(hintsContent)
187+
}
173188
return globalConfig, nil
174189
}
175190

internal/config/named.root

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
; This file holds the information on root name servers needed to
2+
; initialize cache of Internet domain name servers
3+
; (e.g. reference this file in the "cache . <file>"
4+
; configuration file of BIND domain name servers).
5+
;
6+
; This file is made available by InterNIC
7+
; under anonymous FTP as
8+
; file /domain/named.cache
9+
; on server FTP.INTERNIC.NET
10+
; -OR- RS.INTERNIC.NET
11+
;
12+
; last update: November 20, 2025
13+
; related version of root zone: 2025112001
14+
;
15+
; FORMERLY NS.INTERNIC.NET
16+
;
17+
. 3600000 NS A.ROOT-SERVERS.NET.
18+
A.ROOT-SERVERS.NET. 3600000 A 198.41.0.4
19+
A.ROOT-SERVERS.NET. 3600000 AAAA 2001:503:ba3e::2:30
20+
;
21+
; FORMERLY NS1.ISI.EDU
22+
;
23+
. 3600000 NS B.ROOT-SERVERS.NET.
24+
B.ROOT-SERVERS.NET. 3600000 A 170.247.170.2
25+
B.ROOT-SERVERS.NET. 3600000 AAAA 2801:1b8:10::b
26+
;
27+
; FORMERLY C.PSI.NET
28+
;
29+
. 3600000 NS C.ROOT-SERVERS.NET.
30+
C.ROOT-SERVERS.NET. 3600000 A 192.33.4.12
31+
C.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:2::c
32+
;
33+
; FORMERLY TERP.UMD.EDU
34+
;
35+
. 3600000 NS D.ROOT-SERVERS.NET.
36+
D.ROOT-SERVERS.NET. 3600000 A 199.7.91.13
37+
D.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:2d::d
38+
;
39+
; FORMERLY NS.NASA.GOV
40+
;
41+
. 3600000 NS E.ROOT-SERVERS.NET.
42+
E.ROOT-SERVERS.NET. 3600000 A 192.203.230.10
43+
E.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:a8::e
44+
;
45+
; FORMERLY NS.ISC.ORG
46+
;
47+
. 3600000 NS F.ROOT-SERVERS.NET.
48+
F.ROOT-SERVERS.NET. 3600000 A 192.5.5.241
49+
F.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:2f::f
50+
;
51+
; FORMERLY NS.NIC.DDN.MIL
52+
;
53+
. 3600000 NS G.ROOT-SERVERS.NET.
54+
G.ROOT-SERVERS.NET. 3600000 A 192.112.36.4
55+
G.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:12::d0d
56+
;
57+
; FORMERLY AOS.ARL.ARMY.MIL
58+
;
59+
. 3600000 NS H.ROOT-SERVERS.NET.
60+
H.ROOT-SERVERS.NET. 3600000 A 198.97.190.53
61+
H.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:1::53
62+
;
63+
; FORMERLY NIC.NORDU.NET
64+
;
65+
. 3600000 NS I.ROOT-SERVERS.NET.
66+
I.ROOT-SERVERS.NET. 3600000 A 192.36.148.17
67+
I.ROOT-SERVERS.NET. 3600000 AAAA 2001:7fe::53
68+
;
69+
; OPERATED BY VERISIGN, INC.
70+
;
71+
. 3600000 NS J.ROOT-SERVERS.NET.
72+
J.ROOT-SERVERS.NET. 3600000 A 192.58.128.30
73+
J.ROOT-SERVERS.NET. 3600000 AAAA 2001:503:c27::2:30
74+
;
75+
; OPERATED BY RIPE NCC
76+
;
77+
. 3600000 NS K.ROOT-SERVERS.NET.
78+
K.ROOT-SERVERS.NET. 3600000 A 193.0.14.129
79+
K.ROOT-SERVERS.NET. 3600000 AAAA 2001:7fd::1
80+
;
81+
; OPERATED BY ICANN
82+
;
83+
. 3600000 NS L.ROOT-SERVERS.NET.
84+
L.ROOT-SERVERS.NET. 3600000 A 199.7.83.42
85+
L.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:9f::42
86+
;
87+
; OPERATED BY WIDE
88+
;
89+
. 3600000 NS M.ROOT-SERVERS.NET.
90+
M.ROOT-SERVERS.NET. 3600000 A 202.12.27.33
91+
M.ROOT-SERVERS.NET. 3600000 AAAA 2001:dc3::35
92+
; End of file

internal/dns/dns.go

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ var metricQueryTotal = promauto.NewCounter(prometheus.CounterOpts{
3030
Help: "total DNS queries handled",
3131
})
3232

33+
var rootHints map[uint16]map[string][]dns.RR
34+
3335
func Start() error {
3436
cfg := config.GetConfig()
3537
listenAddr := fmt.Sprintf(
@@ -40,6 +42,10 @@ func Start() error {
4042
slog.Info(
4143
"starting DNS listener on " + listenAddr,
4244
)
45+
// Load root hints
46+
if err := loadRootHints(cfg); err != nil {
47+
return err
48+
}
4349
// Setup handler
4450
dns.HandleFunc(".", handleQuery)
4551
// UDP listener
@@ -76,6 +82,28 @@ func Start() error {
7682
return nil
7783
}
7884

85+
func loadRootHints(cfg *config.Config) error {
86+
rootHints = make(map[uint16]map[string][]dns.RR)
87+
for line := range strings.SplitSeq(cfg.Dns.RootHints, "\n") {
88+
tmpRR, err := dns.NewRR(line)
89+
if err != nil {
90+
return fmt.Errorf("load root hints: %w", err)
91+
}
92+
if tmpRR == nil {
93+
continue
94+
}
95+
rrType := tmpRR.Header().Rrtype
96+
if _, ok := rootHints[rrType]; !ok {
97+
rootHints[rrType] = make(map[string][]dns.RR)
98+
}
99+
rootHints[rrType][tmpRR.Header().Name] = append(
100+
rootHints[rrType][tmpRR.Header().Name],
101+
tmpRR,
102+
)
103+
}
104+
return nil
105+
}
106+
79107
func startListener(server *dns.Server) {
80108
if err := server.ListenAndServe(); err != nil {
81109
slog.Error(
@@ -492,8 +520,24 @@ func findNameserversForDomain(
492520
return dns.Fqdn(lookupDomainName), ret, nil
493521
}
494522
}
495-
496-
return "", nil, nil
523+
// Return root hints
524+
ret := map[string][]net.IP{}
525+
if rootHints != nil && rootHints[dns.TypeNS] != nil {
526+
for _, tmpRecord := range rootHints[dns.TypeNS][`.`] {
527+
nsRec := tmpRecord.(*dns.NS).Ns
528+
if rootHints[dns.TypeA] != nil {
529+
for _, aRecord := range rootHints[dns.TypeA][nsRec] {
530+
ret[nsRec] = append(ret[nsRec], aRecord.(*dns.A).A)
531+
}
532+
}
533+
if rootHints[dns.TypeAAAA] != nil {
534+
for _, aaaaRecord := range rootHints[dns.TypeAAAA][nsRec] {
535+
ret[nsRec] = append(ret[nsRec], aaaaRecord.(*dns.AAAA).AAAA)
536+
}
537+
}
538+
}
539+
}
540+
return `.`, ret, nil
497541
}
498542

499543
func getNameserversFromResponse(msg *dns.Msg) map[string][]net.IP {

0 commit comments

Comments
 (0)