@@ -6,7 +6,7 @@ use crate::DNS_ZONE;
66use omicron_common:: address:: {
77 Ipv6Subnet , ReservedRackSubnet , AZ_PREFIX , DNS_PORT ,
88} ;
9- use slog:: { debug, info} ;
9+ use slog:: { debug, info, trace } ;
1010use std:: net:: { IpAddr , Ipv6Addr , SocketAddr , SocketAddrV6 } ;
1111use trust_dns_proto:: rr:: record_type:: RecordType ;
1212use trust_dns_resolver:: config:: {
@@ -70,6 +70,15 @@ impl Resolver {
7070 Self :: new_from_subnet ( log, subnet)
7171 }
7272
73+ /// Return a resolver that uses the system configuration (usually
74+ /// /etc/resolv.conf) for the underlying nameservers.
75+ pub fn new_with_resolver (
76+ log : slog:: Logger ,
77+ tokio_resolver : TokioAsyncResolver ,
78+ ) -> Self {
79+ Resolver { log, inner : Box :: new ( tokio_resolver) }
80+ }
81+
7382 // TODO-correctness This function and its callers make assumptions about how
7483 // many internal DNS servers there are on the subnet and where they are. Is
7584 // that okay? It would seem more flexible not to assume this. Instead, we
@@ -121,6 +130,107 @@ impl Resolver {
121130 Ok ( * address)
122131 }
123132
133+ /// Returns the targets of the SRV records for a DNS name
134+ ///
135+ /// The returned values are generally other DNS names that themselves would
136+ /// need to be looked up to find A/AAAA records.
137+ pub async fn lookup_srv (
138+ & self ,
139+ srv : crate :: ServiceName ,
140+ ) -> Result < Vec < String > , ResolveError > {
141+ let name = format ! ( "{}.{}" , srv. dns_name( ) , DNS_ZONE ) ;
142+ trace ! ( self . log, "lookup_srv" ; "dns_name" => & name) ;
143+ let response = self . inner . srv_lookup ( & name) . await ?;
144+ debug ! (
145+ self . log,
146+ "lookup_srv" ;
147+ "dns_name" => & name,
148+ "response" => ?response
149+ ) ;
150+
151+ Ok ( response. into_iter ( ) . map ( |srv| srv. target ( ) . to_string ( ) ) . collect ( ) )
152+ }
153+
154+ pub async fn lookup_all_ipv6 (
155+ & self ,
156+ srv : crate :: ServiceName ,
157+ ) -> Result < Vec < Ipv6Addr > , ResolveError > {
158+ let name = format ! ( "{}.{}" , srv. dns_name( ) , DNS_ZONE ) ;
159+ trace ! ( self . log, "lookup_all_ipv6 srv" ; "dns_name" => & name) ;
160+ let response = self . inner . srv_lookup ( & name) . await ?;
161+ debug ! (
162+ self . log,
163+ "lookup_ipv6 srv" ;
164+ "dns_name" => & name,
165+ "response" => ?response
166+ ) ;
167+
168+ // SRV records have a target, which is itself another DNS name that
169+ // needs to be looked up in order to get to the actual IP addresses.
170+ // Many DNS servers return these IP addresses directly in the response
171+ // to the SRV query as Additional records. Ours does not. See
172+ // omicron#3434. So we need to do another round of lookups separately.
173+ //
174+ // According to the docs` for
175+ // `trust_dns_resolver::lookup::SrvLookup::ip_iter()`, it sounds like
176+ // trust-dns would have done this for us. It doesn't. See
177+ // bluejekyll/trust-dns#1980.
178+ //
179+ // So if we have gotten any IPs, then we assume that one of the above
180+ // issues has been addressed and so we have all the IPs and we're done.
181+ // Otherwise, explicitly do the extra lookups.
182+ let addresses: Vec < Ipv6Addr > = response
183+ . ip_iter ( )
184+ . filter_map ( |addr| match addr {
185+ IpAddr :: V4 ( _) => None ,
186+ IpAddr :: V6 ( addr) => Some ( addr) ,
187+ } )
188+ . collect ( ) ;
189+ if !addresses. is_empty ( ) {
190+ return Ok ( addresses) ;
191+ }
192+
193+ // What do we do if some of these queries succeed while others fail? We
194+ // may have some addresses, but the list might be incomplete. That
195+ // might be okay for some use cases but not others. For now, we do the
196+ // simple thing. In the future, we'll want a more cueball-like resolver
197+ // interface that better deals with these cases.
198+ let log = & self . log ;
199+ let futures = response. iter ( ) . map ( |srv| async {
200+ let target = srv. target ( ) ;
201+ trace ! (
202+ log,
203+ "lookup_all_ipv6: looking up SRV target" ;
204+ "name" => ?target,
205+ ) ;
206+ self . inner . ipv6_lookup ( target. clone ( ) ) . await
207+ } ) ;
208+ let results = futures:: future:: try_join_all ( futures) . await ?;
209+ let results = results
210+ . into_iter ( )
211+ . flat_map ( |ipv6| ipv6. into_iter ( ) )
212+ . collect :: < Vec < _ > > ( ) ;
213+ if results. is_empty ( ) {
214+ Err ( ResolveError :: NotFound ( srv) )
215+ } else {
216+ Ok ( results)
217+ }
218+ }
219+
220+ pub async fn lookup_ip (
221+ & self ,
222+ srv : crate :: ServiceName ,
223+ ) -> Result < IpAddr , ResolveError > {
224+ let name = format ! ( "{}.{}" , srv. dns_name( ) , DNS_ZONE ) ;
225+ debug ! ( self . log, "lookup srv" ; "dns_name" => & name) ;
226+ let response = self . inner . lookup_ip ( & name) . await ?;
227+ let address = response
228+ . iter ( )
229+ . next ( )
230+ . ok_or_else ( || ResolveError :: NotFound ( srv) ) ?;
231+ Ok ( address)
232+ }
233+
124234 /// Looks up a single [`SocketAddrV6`] based on the SRV name
125235 /// Returns an error if the record does not exist.
126236 pub async fn lookup_socket_v6 (
@@ -156,20 +266,6 @@ impl Resolver {
156266 }
157267 } )
158268 }
159-
160- pub async fn lookup_ip (
161- & self ,
162- srv : crate :: ServiceName ,
163- ) -> Result < IpAddr , ResolveError > {
164- let name = format ! ( "{}.{}" , srv. dns_name( ) , DNS_ZONE ) ;
165- debug ! ( self . log, "lookup srv" ; "dns_name" => & name) ;
166- let response = self . inner . lookup_ip ( & name) . await ?;
167- let address = response
168- . iter ( )
169- . next ( )
170- . ok_or_else ( || ResolveError :: NotFound ( srv) ) ?;
171- Ok ( address)
172- }
173269}
174270
175271#[ cfg( test) ]
@@ -427,6 +523,17 @@ mod test {
427523 . expect ( "Should have been able to look up IP address" ) ;
428524 assert ! ( cockroach_addrs. iter( ) . any( |addr| addr. ip( ) == & ip) ) ;
429525
526+ // Look up all the Cockroach addresses.
527+ let mut ips =
528+ resolver. lookup_all_ipv6 ( ServiceName :: Cockroach ) . await . expect (
529+ "Should have been able to look up all CockroachDB addresses" ,
530+ ) ;
531+ ips. sort ( ) ;
532+ assert_eq ! (
533+ ips,
534+ cockroach_addrs. iter( ) . map( |s| * s. ip( ) ) . collect:: <Vec <_>>( )
535+ ) ;
536+
430537 // Look up Clickhouse
431538 let ip = resolver
432539 . lookup_ipv6 ( ServiceName :: Clickhouse )
0 commit comments