Skip to content

Commit b168482

Browse files
authored
Merge pull request #14 from blinklabs-io/feat/dns-root-behavior
feat: root server DNS behavior
2 parents c8e3439 + 572441f commit b168482

File tree

2 files changed

+53
-79
lines changed

2 files changed

+53
-79
lines changed

internal/dns/dns.go

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package dns
33
import (
44
"fmt"
55
"net"
6+
"strings"
67

78
"github.com/blinklabs-io/chnsd/internal/config"
89
"github.com/blinklabs-io/chnsd/internal/indexer"
@@ -35,40 +36,51 @@ func handleQuery(w dns.ResponseWriter, r *dns.Msg) {
3536
logger := logging.GetLogger()
3637
m := new(dns.Msg)
3738

38-
switch r.Question[0].Qtype {
39-
default:
40-
// Return a SERVFAIL response for unsupported record types
41-
m.SetRcode(r, dns.RcodeServerFailure)
42-
case dns.TypeA, dns.TypeNS:
43-
records := indexer.GetIndexer().LookupRecords(r.Question[0].Name, dns.Type(r.Question[0].Qtype).String())
44-
if len(records) == 0 {
45-
// Send NXDOMAIN
46-
m.SetRcode(r, dns.RcodeNameError)
47-
} else {
48-
// Send response
49-
m.SetReply(r)
50-
for _, record := range records {
51-
switch r.Question[0].Qtype {
52-
case dns.TypeA:
53-
ipAddr := net.ParseIP(record.Value)
54-
a := &dns.A{
55-
Hdr: dns.RR_Header{Name: record.Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 999},
56-
A: ipAddr,
57-
}
58-
m.Answer = append(m.Answer, a)
59-
case dns.TypeNS:
60-
ns := &dns.NS{
61-
Hdr: dns.RR_Header{Name: record.Name, Rrtype: dns.TypeNS, Class: dns.ClassINET, Ttl: 999},
62-
Ns: record.Value,
63-
}
64-
m.Answer = append(m.Answer, ns)
65-
default:
66-
39+
// Split query name into labels and lookup each domain and parent until we get a hit
40+
queryLabels := dns.SplitDomainName(r.Question[0].Name)
41+
for startLabelIdx := 0; startLabelIdx < len(queryLabels); startLabelIdx++ {
42+
lookupDomainName := strings.Join(queryLabels[startLabelIdx:], ".") + `.`
43+
domain := indexer.GetIndexer().LookupDomain(lookupDomainName)
44+
if domain == nil {
45+
continue
46+
}
47+
// Assemble response
48+
m.SetReply(r)
49+
for nameserver, ipAddress := range domain.Nameservers {
50+
// NS record
51+
ns := &dns.NS{
52+
Hdr: dns.RR_Header{Name: domain.Name, Rrtype: dns.TypeNS, Class: dns.ClassINET, Ttl: 999},
53+
Ns: nameserver,
54+
}
55+
m.Ns = append(m.Ns, ns)
56+
// A or AAAA record
57+
ipAddr := net.ParseIP(ipAddress)
58+
if ipAddr.To4() != nil {
59+
// IPv4
60+
a := &dns.A{
61+
Hdr: dns.RR_Header{Name: nameserver, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 999},
62+
A: ipAddr,
6763
}
64+
m.Extra = append(m.Extra, a)
65+
} else {
66+
// IPv6
67+
aaaa := &dns.AAAA{
68+
Hdr: dns.RR_Header{Name: nameserver, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 999},
69+
AAAA: ipAddr,
70+
}
71+
m.Extra = append(m.Extra, aaaa)
6872
}
6973
}
74+
// Send response
75+
if err := w.WriteMsg(m); err != nil {
76+
logger.Errorf("failed to write response: %s", err)
77+
}
78+
// We found our answer, to return from handler
79+
return
7080
}
7181

82+
// Return NXDOMAIN if we have no information about the requested domain or any of its parents
83+
m.SetRcode(r, dns.RcodeNameError)
7284
if err := w.WriteMsg(m); err != nil {
7385
logger.Errorf("failed to write response: %s", err)
7486
}

internal/indexer/indexer.go

Lines changed: 12 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,11 @@ import (
1414
input_chainsync "github.com/blinklabs-io/snek/input/chainsync"
1515
output_embedded "github.com/blinklabs-io/snek/output/embedded"
1616
"github.com/blinklabs-io/snek/pipeline"
17-
"github.com/miekg/dns"
1817
)
1918

2019
type Domain struct {
21-
name string
22-
records map[string]map[string][]DomainRecord
23-
}
24-
25-
type DomainRecord struct {
26-
Name string
27-
Value string
20+
Name string
21+
Nameservers map[string]string
2822
}
2923

3024
type Indexer struct {
@@ -120,63 +114,31 @@ func (i *Indexer) handleEvent(evt event.Event) error {
120114
}
121115
datumFields := datum.Value().(cbor.Constructor).Fields()
122116
domainName := string(datumFields[0].(cbor.ByteString).Bytes()) + `.`
117+
// Create empty domain record
118+
// This will also clobber any previous definition of the domain
119+
i.domains[domainName] = Domain{
120+
Name: domainName,
121+
Nameservers: make(map[string]string),
122+
}
123123
for _, record := range datumFields[1].([]any) {
124124
recordConstructor := record.(cbor.Constructor)
125125
nameServer := string(recordConstructor.Fields()[0].(cbor.ByteString).Bytes()) + `.`
126126
ipAddress := string(recordConstructor.Fields()[1].(cbor.ByteString).Bytes())
127-
// Create NS record for domain
128-
i.addRecord(domainName, domainName, "NS", nameServer)
129-
// Create A record for name server
130-
i.addRecord(domainName, nameServer, "A", ipAddress)
127+
i.domains[domainName].Nameservers[nameServer] = ipAddress
131128
}
132129
logger.Infof("found updated registration for domain: %s", domainName)
133130
}
134131
}
135132
return nil
136133
}
137134

138-
func (i *Indexer) LookupRecords(name string, recordType string) []DomainRecord {
139-
for domainName, domain := range i.domains {
140-
if dns.IsSubDomain(domainName, name) {
141-
if records, ok := domain.records[name]; ok {
142-
if record, ok := records[recordType]; ok {
143-
return record
144-
} else {
145-
return nil
146-
}
147-
} else {
148-
return nil
149-
}
150-
}
135+
func (i *Indexer) LookupDomain(name string) *Domain {
136+
if domain, ok := i.domains[name]; ok {
137+
return &domain
151138
}
152139
return nil
153140
}
154141

155-
func (i *Indexer) addRecord(domainName string, recordName string, recordType string, value string) {
156-
// Create initial domain record
157-
if _, ok := i.domains[domainName]; !ok {
158-
i.domains[domainName] = Domain{
159-
name: domainName,
160-
records: make(map[string]map[string][]DomainRecord),
161-
}
162-
}
163-
// Create initial list for record type
164-
if _, ok := i.domains[domainName].records[recordName]; !ok {
165-
i.domains[domainName].records[recordName] = make(map[string][]DomainRecord)
166-
if _, ok := i.domains[domainName].records[recordName][recordType]; !ok {
167-
i.domains[domainName].records[recordName][recordType] = make([]DomainRecord, 0)
168-
}
169-
}
170-
// Create record
171-
i.domains[domainName].records[recordName][recordType] = append(
172-
i.domains[domainName].records[recordName][recordType],
173-
DomainRecord{
174-
Name: recordName,
175-
Value: value,
176-
},
177-
)
178-
}
179-
180142
// GetIndexer returns the global indexer instance
181143
func GetIndexer() *Indexer {
182144
return globalIndexer

0 commit comments

Comments
 (0)