diff --git a/pkg/discovery/dns/resolver.go b/pkg/discovery/dns/resolver.go index 84d8d441f8..7d3c593d5f 100644 --- a/pkg/discovery/dns/resolver.go +++ b/pkg/discovery/dns/resolver.go @@ -12,8 +12,9 @@ import ( type QType string const ( - A = QType("dns") - SRV = QType("dnssrv") + A = QType("dns") + SRV = QType("dnssrv") + SRVNoA = QType("dnssrvnoa") ) type Resolver interface { @@ -72,6 +73,26 @@ func (s *dnsSD) Resolve(ctx context.Context, name string, qtype QType) ([]string res = append(res, appendScheme(scheme, net.JoinHostPort(ip.String(), port))) } case SRV: + _, recs, err := s.resolver.LookupSRV(ctx, "", "", host) + if err != nil { + return nil, errors.Wrapf(err, "lookup SRV records %q", host) + } + for _, rec := range recs { + // Only use port from SRV record if no explicit port was specified. + resPort := port + if resPort == "" { + resPort = strconv.Itoa(int(rec.Port)) + } + // Do A lookup for the domain in SRV answer. + resIPs, err := s.resolver.LookupIPAddr(ctx, rec.Target) + if err != nil { + return nil, errors.Wrapf(err, "look IP addresses %q", rec.Target) + } + for _, resIP := range resIPs { + res = append(res, appendScheme(scheme, net.JoinHostPort(resIP.String(), resPort))) + } + } + case SRVNoA: _, recs, err := s.resolver.LookupSRV(ctx, "", "", host) if err != nil { return nil, errors.Wrapf(err, "lookup SRV records %q", host) diff --git a/pkg/discovery/dns/resolver_test.go b/pkg/discovery/dns/resolver_test.go index fe7098f632..11d2986f5e 100644 --- a/pkg/discovery/dns/resolver_test.go +++ b/pkg/discovery/dns/resolver_test.go @@ -77,11 +77,57 @@ var ( resolver: &mockHostnameResolver{}, }, { - testName: "multiple srv records from srv lookup", + testName: "multiple SRV records from SRV lookup", addr: "_test._tcp.mycompany.com", qtype: SRV, expectedResult: []string{"192.168.0.1:8080", "192.168.0.2:8081"}, expectedErr: nil, + resolver: &mockHostnameResolver{ + resultSRVs: map[string][]*net.SRV{ + "_test._tcp.mycompany.com": { + &net.SRV{Target: "alt1.mycompany.com.", Port: 8080}, + &net.SRV{Target: "alt2.mycompany.com.", Port: 8081}, + }, + }, + resultIPs: map[string][]net.IPAddr{ + "alt1.mycompany.com.": {net.IPAddr{IP: net.ParseIP("192.168.0.1")}}, + "alt2.mycompany.com.": {net.IPAddr{IP: net.ParseIP("192.168.0.2")}}, + }, + }, + }, + { + testName: "multiple SRV records from SRV lookup with specified port", + addr: "_test._tcp.mycompany.com:8082", + qtype: SRV, + expectedResult: []string{"192.168.0.1:8082", "192.168.0.2:8082"}, + expectedErr: nil, + resolver: &mockHostnameResolver{ + resultSRVs: map[string][]*net.SRV{ + "_test._tcp.mycompany.com": { + &net.SRV{Target: "alt1.mycompany.com.", Port: 8080}, + &net.SRV{Target: "alt2.mycompany.com.", Port: 8081}, + }, + }, + resultIPs: map[string][]net.IPAddr{ + "alt1.mycompany.com.": {net.IPAddr{IP: net.ParseIP("192.168.0.1")}}, + "alt2.mycompany.com.": {net.IPAddr{IP: net.ParseIP("192.168.0.2")}}, + }, + }, + }, + { + testName: "error from SRV resolver", + addr: "_test._tcp.mycompany.com", + qtype: SRV, + expectedResult: nil, + expectedErr: errors.Wrapf(errorFromResolver, "lookup SRV records \"_test._tcp.mycompany.com\""), + resolver: &mockHostnameResolver{err: errorFromResolver}, + }, + { + testName: "multiple SRV records from SRV no A lookup", + addr: "_test._tcp.mycompany.com", + qtype: SRVNoA, + expectedResult: []string{"192.168.0.1:8080", "192.168.0.2:8081"}, + expectedErr: nil, resolver: &mockHostnameResolver{ resultSRVs: map[string][]*net.SRV{ "_test._tcp.mycompany.com": { @@ -92,9 +138,9 @@ var ( }, }, { - testName: "multiple srv records from srv lookup", + testName: "multiple SRV records from SRV no A lookup with specified port", addr: "_test._tcp.mycompany.com:8082", - qtype: SRV, + qtype: SRVNoA, expectedResult: []string{"192.168.0.1:8082", "192.168.0.2:8082"}, expectedErr: nil, resolver: &mockHostnameResolver{ @@ -107,7 +153,7 @@ var ( }, }, { - testName: "error from SRV resolver", + testName: "error from SRV no A lookup", addr: "_test._tcp.mycompany.com", qtype: SRV, expectedResult: nil,