[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp... #27
3 changed files with 96 additions and 10 deletions
22
serve.go
22
serve.go
|
|
@ -188,16 +188,18 @@ func (handler *dnsRequestHandler) ServeDNS(responseWriter dnsprotocol.ResponseWr
|
|||
if !found {
|
||||
goto noRecord
|
||||
}
|
||||
reply.Answer = append(reply.Answer, &dnsprotocol.SOA{
|
||||
Hdr: dnsprotocol.RR_Header{Name: question.Name, Rrtype: dnsprotocol.TypeSOA, Class: dnsprotocol.ClassINET, Ttl: dnsTTL},
|
||||
Ns: normalizeName("ns."+name) + ".",
|
||||
Mbox: "hostmaster." + name + ".",
|
||||
Serial: uint32(time.Now().Unix()),
|
||||
Refresh: 86400,
|
||||
Retry: 3600,
|
||||
Expire: 3600,
|
||||
Minttl: 300,
|
||||
})
|
||||
if normalizeName(name) == handler.service.ZoneApex() {
|
||||
reply.Answer = append(reply.Answer, &dnsprotocol.SOA{
|
||||
Hdr: dnsprotocol.RR_Header{Name: question.Name, Rrtype: dnsprotocol.TypeSOA, Class: dnsprotocol.ClassINET, Ttl: dnsTTL},
|
||||
Ns: normalizeName("ns."+name) + ".",
|
||||
Mbox: "hostmaster." + name + ".",
|
||||
Serial: uint32(time.Now().Unix()),
|
||||
Refresh: 86400,
|
||||
Retry: 3600,
|
||||
Expire: 3600,
|
||||
Minttl: 300,
|
||||
})
|
||||
}
|
||||
default:
|
||||
reply.SetRcode(request, dnsprotocol.RcodeNotImplemented)
|
||||
_ = responseWriter.WriteMsg(reply)
|
||||
|
|
|
|||
41
service.go
41
service.go
|
|
@ -45,6 +45,7 @@ type Service struct {
|
|||
records map[string]NameRecords
|
||||
reverseIndex map[string][]string
|
||||
treeRoot string
|
||||
zoneApex string
|
||||
hsdClient *HSDClient
|
||||
mainchainAliasClient *MainchainAliasClient
|
||||
discoverer func() (map[string]NameRecords, error)
|
||||
|
|
@ -82,6 +83,7 @@ func NewService(options ServiceOptions) *Service {
|
|||
records: cached,
|
||||
reverseIndex: buildReverseIndex(cached),
|
||||
treeRoot: treeRoot,
|
||||
zoneApex: computeZoneApex(cached),
|
||||
hsdClient: options.HSDClient,
|
||||
mainchainAliasClient: options.MainchainAliasClient,
|
||||
discoverer: options.Discoverer,
|
||||
|
|
@ -353,6 +355,7 @@ func (service *Service) replaceRecords(discovered map[string]NameRecords) {
|
|||
service.records = cached
|
||||
service.reverseIndex = buildReverseIndex(service.records)
|
||||
service.treeRoot = computeTreeRoot(service.records)
|
||||
service.zoneApex = computeZoneApex(service.records)
|
||||
}
|
||||
|
||||
func (service *Service) SetRecord(name string, record NameRecords) {
|
||||
|
|
@ -361,6 +364,7 @@ func (service *Service) SetRecord(name string, record NameRecords) {
|
|||
service.records[normalizeName(name)] = record
|
||||
service.reverseIndex = buildReverseIndex(service.records)
|
||||
service.treeRoot = computeTreeRoot(service.records)
|
||||
service.zoneApex = computeZoneApex(service.records)
|
||||
}
|
||||
|
||||
func (service *Service) RemoveRecord(name string) {
|
||||
|
|
@ -369,6 +373,7 @@ func (service *Service) RemoveRecord(name string) {
|
|||
delete(service.records, normalizeName(name))
|
||||
service.reverseIndex = buildReverseIndex(service.records)
|
||||
service.treeRoot = computeTreeRoot(service.records)
|
||||
service.zoneApex = computeZoneApex(service.records)
|
||||
}
|
||||
|
||||
func (service *Service) Resolve(name string) (ResolveAllResult, bool) {
|
||||
|
|
@ -481,6 +486,12 @@ func (service *Service) Health() map[string]any {
|
|||
}
|
||||
}
|
||||
|
||||
func (service *Service) ZoneApex() string {
|
||||
service.mu.RLock()
|
||||
defer service.mu.RUnlock()
|
||||
return service.zoneApex
|
||||
}
|
||||
|
||||
func (service *Service) ResolveReverseNames(ip string) (ReverseLookupResult, bool) {
|
||||
names, ok := service.ResolveReverse(ip)
|
||||
if !ok {
|
||||
|
|
@ -590,6 +601,36 @@ func computeTreeRoot(records map[string]NameRecords) string {
|
|||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
func computeZoneApex(records map[string]NameRecords) string {
|
||||
names := make([]string, 0, len(records))
|
||||
for name := range records {
|
||||
if strings.HasPrefix(name, "*.") {
|
||||
continue
|
||||
}
|
||||
names = append(names, name)
|
||||
}
|
||||
if len(names) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
commonLabels := strings.Split(names[0], ".")
|
||||
for _, name := range names[1:] {
|
||||
labels := strings.Split(name, ".")
|
||||
commonSuffixLength := 0
|
||||
for commonSuffixLength < len(commonLabels) && commonSuffixLength < len(labels) {
|
||||
if commonLabels[len(commonLabels)-1-commonSuffixLength] != labels[len(labels)-1-commonSuffixLength] {
|
||||
break
|
||||
}
|
||||
commonSuffixLength++
|
||||
}
|
||||
if commonSuffixLength == 0 {
|
||||
return ""
|
||||
}
|
||||
commonLabels = commonLabels[len(commonLabels)-commonSuffixLength:]
|
||||
}
|
||||
return strings.Join(commonLabels, ".")
|
||||
}
|
||||
|
||||
func serializeRecordValues(values []string) string {
|
||||
copied := append([]string(nil), values...)
|
||||
slices.Sort(copied)
|
||||
|
|
|
|||
|
|
@ -957,6 +957,49 @@ func TestServiceServeResolvesWildcardAndPTRRecords(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestServiceServeAnswersSOAOnlyForZoneApex(t *testing.T) {
|
||||
service := NewService(ServiceOptions{
|
||||
Records: map[string]NameRecords{
|
||||
"charon.lthn": {
|
||||
NS: []string{"ns1.charon.lthn"},
|
||||
},
|
||||
"gateway.charon.lthn": {
|
||||
A: []string{"10.10.10.10"},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
srv, err := service.Serve("127.0.0.1", 0)
|
||||
if err != nil {
|
||||
t.Fatalf("expected server to start: %v", err)
|
||||
}
|
||||
defer func() { _ = srv.Close() }()
|
||||
|
||||
client := dnsprotocol.Client{}
|
||||
apexRequest := new(dnsprotocol.Msg)
|
||||
apexRequest.SetQuestion("charon.lthn.", dnsprotocol.TypeSOA)
|
||||
apexResponse := exchangeWithRetry(t, client, apexRequest, srv.Address())
|
||||
if apexResponse.Rcode != dnsprotocol.RcodeSuccess {
|
||||
t.Fatalf("expected SOA query for apex to succeed, got %d", apexResponse.Rcode)
|
||||
}
|
||||
if len(apexResponse.Answer) != 1 {
|
||||
t.Fatalf("expected one SOA answer for apex, got %d", len(apexResponse.Answer))
|
||||
}
|
||||
if _, ok := apexResponse.Answer[0].(*dnsprotocol.SOA); !ok {
|
||||
t.Fatalf("expected SOA answer for apex, got %#v", apexResponse.Answer[0])
|
||||
}
|
||||
|
||||
subdomainRequest := new(dnsprotocol.Msg)
|
||||
subdomainRequest.SetQuestion("gateway.charon.lthn.", dnsprotocol.TypeSOA)
|
||||
subdomainResponse := exchangeWithRetry(t, client, subdomainRequest, srv.Address())
|
||||
if subdomainResponse.Rcode != dnsprotocol.RcodeSuccess {
|
||||
t.Fatalf("expected SOA query for non-apex existing name to succeed, got %d", subdomainResponse.Rcode)
|
||||
}
|
||||
if len(subdomainResponse.Answer) != 0 {
|
||||
t.Fatalf("expected no SOA answer for non-apex name, got %#v", subdomainResponse.Answer)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServiceServeReturnsNXDOMAINWhenMissing(t *testing.T) {
|
||||
service := NewService(ServiceOptions{})
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue