diff --git a/service.go b/service.go index 7225cdb..5c9104d 100644 --- a/service.go +++ b/service.go @@ -744,6 +744,17 @@ func (service *Service) Resolve(name string) (ResolveAllResult, bool) { return resolveResult(record), true } +// ResolveWithMatch returns matching records and whether the match used a wildcard. +// +// result, found, usedWildcard := service.ResolveWithMatch("node.charon.lthn") +func (service *Service) ResolveWithMatch(name string) (ResolveAllResult, bool, bool) { + record, ok, usedWildcard := service.findRecordWithMatch(name) + if !ok { + return ResolveAllResult{}, false, false + } + return resolveResult(record), true, usedWildcard +} + // ResolveTXT returns only TXT values for a name. // // txt, ok := service.ResolveTXT("gateway.charon.lthn") @@ -946,17 +957,26 @@ func (service *Service) ResolveReverseNames(ip string) (ReverseLookupResult, boo func (service *Service) findRecord(name string) (NameRecords, bool) { service.pruneExpiredRecords() + record, ok, _ := service.findRecordWithMatch(name) + return record, ok +} + +func (service *Service) findRecordWithMatch(name string) (NameRecords, bool, bool) { + service.pruneExpiredRecords() service.mu.RLock() defer service.mu.RUnlock() normalized := normalizeName(name) if record, ok := service.records[normalized]; ok { - return record, true + return record, true, false } match, ok := findWildcardMatch(normalized, service.records) - return match, ok + if !ok { + return NameRecords{}, false, false + } + return match, true, true } func resolveResult(record NameRecords) ResolveAllResult { diff --git a/service_test.go b/service_test.go index aca9374..b721e68 100644 --- a/service_test.go +++ b/service_test.go @@ -73,6 +73,45 @@ func TestServiceResolveUsesExactNameBeforeWildcard(t *testing.T) { } } +func TestServiceResolveWithMatchIndicatesExactMatch(t *testing.T) { + service := NewService(ServiceOptions{ + Records: map[string]NameRecords{ + "*.charon.lthn": { + A: []string{"10.69.69.165"}, + }, + "gateway.charon.lthn": { + A: []string{"10.10.10.10"}, + }, + }, + }) + + result, ok, usedWildcard := service.ResolveWithMatch("gateway.charon.lthn") + if !ok { + t.Fatal("expected exact record to resolve") + } + if usedWildcard { + t.Fatalf("expected exact match to report usedWildcard=false, got %#v", result.A) + } +} + +func TestServiceResolveWithMatchIndicatesWildcardMatch(t *testing.T) { + service := NewService(ServiceOptions{ + Records: map[string]NameRecords{ + "*.charon.lthn": { + A: []string{"10.69.69.165"}, + }, + }, + }) + + result, ok, usedWildcard := service.ResolveWithMatch("gateway.charon.lthn") + if !ok { + t.Fatal("expected wildcard record to resolve") + } + if !usedWildcard { + t.Fatalf("expected wildcard match to report usedWildcard=true, got %#v", result.A) + } +} + func TestServiceOptionsAliasBuildsService(t *testing.T) { service := NewService(Options{ Records: map[string]NameRecords{