Skip to main content

dns_resolver/
main.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use anyhow::{Context as _, Error};
6use dns::async_resolver::{Resolver, Spawner};
7use dns::config::{ServerList, UpdateServersResult};
8use fidl_fuchsia_net as fnet;
9use fidl_fuchsia_net_ext as net_ext;
10use fidl_fuchsia_net_name::{
11    self as fname, LookupAdminRequest, LookupAdminRequestStream, LookupRequest, LookupRequestStream,
12};
13use fidl_fuchsia_net_routes as fnet_routes;
14use fuchsia_async as fasync;
15use fuchsia_component::server::{ServiceFs, ServiceFsDir};
16use fuchsia_sync::RwLock;
17use futures::channel::mpsc;
18use futures::lock::Mutex;
19use futures::{FutureExt as _, SinkExt as _, StreamExt as _, TryFutureExt as _, TryStreamExt as _};
20use log::{debug, error, info, warn};
21use net_declare::fidl_ip_v6;
22use net_types::ip::IpAddress;
23use std::collections::{BTreeMap, HashMap, VecDeque};
24use std::convert::TryFrom as _;
25use std::hash::{Hash, Hasher};
26use std::net::IpAddr;
27use std::num::NonZeroUsize;
28use std::str::FromStr as _;
29use std::sync::Arc;
30use trust_dns_proto::error::ProtoErrorKind;
31use trust_dns_proto::op::ResponseCode;
32use trust_dns_proto::rr::domain::IntoName;
33use trust_dns_proto::rr::{RData, RecordType};
34use trust_dns_resolver::config::{
35    LookupIpStrategy, NameServerConfig, NameServerConfigGroup, Protocol, ResolverConfig,
36    ResolverOpts, ServerOrderingStrategy,
37};
38use trust_dns_resolver::error::{ResolveError, ResolveErrorKind};
39use trust_dns_resolver::{NameServerStats, lookup};
40use unicode_xid::UnicodeXID as _;
41
42#[derive(Debug, Clone)]
43/// A type wrapping the underlying resolver.
44///
45/// The outer `Arc` lets the whole thing be shared between threads. Although
46/// lookup and updating the config happens in a single thread, Inspect may call
47/// from other threads to get resolver information.
48///
49/// The lock is required so the server can be updated when new nameservers are
50/// configured.
51///
52/// The inner `Arc` means users of the nameserver only hold the lock long enough
53/// to clone the `Arc`, or, in the case of updating, long enough to swap out the
54/// resolver. Otherwise, lookups would have to hold the lock the whole time a
55/// query is running, which can be on the order of seconds.
56///
57/// NOTE: The lock is unnecessary since on most architectures you can atomically
58/// swap a pointer, but the lock leads to a simpler implementation and there
59/// isn't much contention here.
60struct SharedResolver<T>(Arc<RwLock<Arc<T>>>);
61
62impl<T> SharedResolver<T> {
63    fn new(resolver: T) -> Self {
64        SharedResolver(Arc::new(RwLock::new(Arc::new(resolver))))
65    }
66
67    fn read(&self) -> Arc<T> {
68        let Self(inner) = self;
69        inner.read().clone()
70    }
71
72    fn write(&self, other: Arc<T>) {
73        let Self(inner) = self;
74        *inner.write() = other;
75    }
76}
77
78const STAT_WINDOW_DURATION: zx::MonotonicDuration = zx::MonotonicDuration::from_seconds(60);
79const STAT_WINDOW_COUNT: usize = 30;
80const RETAINED_ERRORS_PER_NAME_SERVER: usize = 32;
81
82/// Stats about queries during the last `STAT_WINDOW_COUNT` windows of
83/// `STAT_WINDOW_DURATION` time.
84///
85/// For example, if `STAT_WINDOW_DURATION` == 1 minute, and
86/// `STAT_WINDOW_COUNT` == 30, `past_queries` contains information about, at
87/// most, 30 one-minute windows of completed queries.
88///
89/// NB: there is no guarantee that these windows are directly consecutive; only
90/// that each window begins at least `STAT_WINDOW_DURATION` after the previous
91/// window's start time.
92struct QueryStats {
93    inner: Mutex<VecDeque<QueryWindow>>,
94}
95
96/// Relevant info to be recorded about a completed query. The `Ok` variant
97/// contains the number of addresses in the response, and the `Err` variant
98/// contains the kind of error that was encountered.
99type QueryResult<'a> = Result<NonZeroUsize, &'a ResolveErrorKind>;
100
101impl QueryStats {
102    fn new() -> Self {
103        Self { inner: Mutex::new(VecDeque::new()) }
104    }
105
106    async fn finish_query(&self, start_time: fasync::MonotonicInstant, result: QueryResult<'_>) {
107        let end_time = fasync::MonotonicInstant::now();
108        let finish = move |window: &mut QueryWindow| {
109            let elapsed_time = end_time - start_time;
110            match result {
111                Ok(num_addrs) => window.succeed(elapsed_time, num_addrs),
112                Err(e) => window.fail(elapsed_time, e),
113            }
114        };
115
116        let Self { inner } = self;
117        let past_queries = &mut *inner.lock().await;
118
119        let current_window = past_queries.back_mut().and_then(|window| {
120            let QueryWindow { start, .. } = window;
121            (end_time - *start < STAT_WINDOW_DURATION).then_some(window)
122        });
123
124        match current_window {
125            Some(window) => finish(window),
126            None => {
127                if past_queries.len() == STAT_WINDOW_COUNT {
128                    // Remove the oldest window of query stats.
129                    let _: QueryWindow = past_queries
130                        .pop_front()
131                        .expect("there should be at least one element in `past_queries`");
132                }
133                let mut window = QueryWindow::new(end_time);
134                finish(&mut window);
135                past_queries.push_back(window);
136            }
137        }
138    }
139}
140
141#[derive(Debug)]
142struct HashableResponseCode {
143    response_code: ResponseCode,
144}
145
146impl Hash for HashableResponseCode {
147    fn hash<H: Hasher>(&self, state: &mut H) {
148        let HashableResponseCode { response_code } = self;
149        u16::from(*response_code).hash(state)
150    }
151}
152
153// Hand-implemented because of clippy's derive_hash_xor_eq lint.
154impl PartialEq for HashableResponseCode {
155    fn eq(&self, other: &Self) -> bool {
156        let HashableResponseCode { response_code } = self;
157        let HashableResponseCode { response_code: other } = other;
158        response_code.eq(other)
159    }
160}
161
162impl Eq for HashableResponseCode {}
163
164impl From<ResponseCode> for HashableResponseCode {
165    fn from(response_code: ResponseCode) -> Self {
166        HashableResponseCode { response_code }
167    }
168}
169
170#[derive(Default, Debug, PartialEq)]
171struct NoRecordsFoundStats {
172    response_code_counts: HashMap<HashableResponseCode, u64>,
173}
174
175impl NoRecordsFoundStats {
176    fn increment(&mut self, response_code: &ResponseCode) {
177        let NoRecordsFoundStats { response_code_counts } = self;
178        let count = response_code_counts.entry((*response_code).into()).or_insert(0);
179        *count += 1
180    }
181}
182
183/// A type that handles counting the number of occurrences of the name of an
184/// enum variant without any of the contents of that enum. See
185/// [`enum_variant_string`] for more information on that process.
186///
187/// This is for privacy purposes; in some cases it's not possible to know a
188/// priori whether a particular enum variant includes user data such as
189/// hostnames.
190#[derive(Default, Debug, PartialEq)]
191struct GenericErrorKindStats(HashMap<String, u64>);
192
193impl GenericErrorKindStats {
194    /// Increments the counter for the given error kind. Returns a string
195    /// containing the name of that kind so the caller doesn't have to perform
196    /// the processing if they want to log it.
197    fn increment(&mut self, error_kind: &impl std::fmt::Debug) -> String {
198        let Self(counts) = self;
199        let truncated_debug = enum_variant_string(error_kind);
200        let count = counts.entry(truncated_debug.clone()).or_insert(0);
201        *count += 1;
202
203        truncated_debug
204    }
205}
206
207#[derive(Default, Debug, PartialEq)]
208struct IoErrorStats(HashMap<std::io::ErrorKind, u64>);
209
210impl IoErrorStats {
211    /// Increments the counter for the given IO error kind.
212    fn increment(&mut self, error_kind: std::io::ErrorKind) {
213        let Self(counts) = self;
214        *counts.entry(error_kind).or_insert(0) += 1;
215    }
216}
217
218/// Stats about queries that failed due to an internal trust-dns error.
219/// These counters map to variants of
220/// [`trust_dns_resolver::error::ResolveErrorKind`].
221#[derive(Default, Debug, PartialEq)]
222struct FailureStats {
223    message: u64,
224    no_connections: u64,
225    no_records_found: NoRecordsFoundStats,
226    io: IoErrorStats,
227    proto: GenericErrorKindStats,
228    timeout: u64,
229    unhandled_resolve_error_kind: GenericErrorKindStats,
230}
231
232impl FailureStats {
233    fn increment(&mut self, kind: &ResolveErrorKind) {
234        let FailureStats {
235            message,
236            no_connections,
237            no_records_found,
238            io,
239            proto,
240            timeout,
241            unhandled_resolve_error_kind,
242        } = self;
243
244        match kind {
245            ResolveErrorKind::Message(error) => {
246                let _: &str = error;
247                *message += 1
248            }
249            ResolveErrorKind::Msg(error) => {
250                let _: &String = error;
251                *message += 1
252            }
253            ResolveErrorKind::NoConnections => *no_connections += 1,
254            ResolveErrorKind::NoRecordsFound {
255                query: _,
256                soa: _,
257                negative_ttl: _,
258                response_code,
259                trusted: _,
260            } => no_records_found.increment(response_code),
261            ResolveErrorKind::Io(error) => io.increment(error.kind()),
262
263            ResolveErrorKind::Proto(error) => match error.kind() {
264                ProtoErrorKind::Io(error) => io.increment(error.kind()),
265                _ => {
266                    let _ = proto.increment(error.kind());
267                }
268            },
269            ResolveErrorKind::Timeout => *timeout += 1,
270            // ResolveErrorKind is marked #[non_exhaustive] in trust-dns:
271            // https://github.com/hickory-dns/hickory-dns/blob/v0.21.0-alpha.1/crates/resolver/src/error.rs#L29
272            // So we have to include a wildcard match.
273            // TODO(https://github.com/rust-lang/rust/issues/89554): remove once
274            // we're able to apply the non_exhaustive_omitted_patterns lint
275            kind => {
276                let variant = unhandled_resolve_error_kind.increment(kind);
277                error!("unhandled variant: {variant}");
278            }
279        }
280    }
281
282    fn populate_inspect_node(&self, node: &fuchsia_inspect::Node) {
283        let FailureStats {
284            message,
285            no_connections,
286            no_records_found: NoRecordsFoundStats { response_code_counts },
287            io: IoErrorStats(io),
288            proto: GenericErrorKindStats(proto),
289            timeout,
290            unhandled_resolve_error_kind: GenericErrorKindStats(unhandled_resolve_error_kind),
291        } = self;
292
293        node.record_uint("Message", *message);
294        node.record_uint("NoConnections", *no_connections);
295        node.record_uint("Timeout", *timeout);
296
297        let io_error_codes = node.create_child("IoErrorCounts");
298        for (kind, count) in io {
299            io_error_codes.record_uint(format!("{kind:?}"), *count);
300        }
301        node.record(io_error_codes);
302
303        let proto_error_codes = node.create_child("ProtoErrorCounts");
304        for (kind, count) in proto {
305            proto_error_codes.record_uint(kind, *count);
306        }
307        node.record(proto_error_codes);
308
309        let no_records_found_response_codes = node.create_child("NoRecordsFoundResponseCodeCounts");
310        for (HashableResponseCode { response_code }, count) in response_code_counts {
311            no_records_found_response_codes.record_uint(format!("{:?}", response_code), *count);
312        }
313        node.record(no_records_found_response_codes);
314
315        let unhandled_resolve_error_kinds = node.create_child("UnhandledResolveErrorKindCounts");
316        for (error_kind, count) in unhandled_resolve_error_kind {
317            unhandled_resolve_error_kinds.record_uint(error_kind, *count);
318        }
319        node.record(unhandled_resolve_error_kinds);
320    }
321}
322
323struct QueryWindow {
324    start: fasync::MonotonicInstant,
325    success_count: u64,
326    failure_count: u64,
327    success_elapsed_time: zx::MonotonicDuration,
328    failure_elapsed_time: zx::MonotonicDuration,
329    failure_stats: FailureStats,
330    address_counts_histogram: BTreeMap<NonZeroUsize, u64>,
331}
332
333impl QueryWindow {
334    fn new(start: fasync::MonotonicInstant) -> Self {
335        Self {
336            start,
337            success_count: 0,
338            failure_count: 0,
339            success_elapsed_time: zx::MonotonicDuration::from_nanos(0),
340            failure_elapsed_time: zx::MonotonicDuration::from_nanos(0),
341            failure_stats: FailureStats::default(),
342            address_counts_histogram: Default::default(),
343        }
344    }
345
346    fn succeed(&mut self, elapsed_time: zx::MonotonicDuration, num_addrs: NonZeroUsize) {
347        let QueryWindow {
348            success_count,
349            success_elapsed_time,
350            address_counts_histogram: address_counts,
351            start: _,
352            failure_count: _,
353            failure_elapsed_time: _,
354            failure_stats: _,
355        } = self;
356        *success_count += 1;
357        *success_elapsed_time += elapsed_time;
358        *address_counts.entry(num_addrs).or_default() += 1;
359    }
360
361    fn fail(&mut self, elapsed_time: zx::MonotonicDuration, error: &ResolveErrorKind) {
362        let QueryWindow {
363            failure_count,
364            failure_elapsed_time,
365            failure_stats,
366            start: _,
367            success_count: _,
368            success_elapsed_time: _,
369            address_counts_histogram: _,
370        } = self;
371        *failure_count += 1;
372        *failure_elapsed_time += elapsed_time;
373        failure_stats.increment(error)
374    }
375}
376
377/// Returns the name of the enum variant that was set.
378fn enum_variant_string(variant: &impl std::fmt::Debug) -> String {
379    let debug = format!("{:?}", variant);
380    // We just want to keep the part of the debug string that indicates
381    // which enum variant this is.
382    // See https://doc.rust-lang.org/reference/identifiers.html
383    match debug.find(|c: char| !c.is_xid_continue() && !c.is_xid_start()) {
384        Some(i) => debug[..i].to_string(),
385        None => debug,
386    }
387}
388
389fn update_resolver<T: ResolverLookup>(resolver: &SharedResolver<T>, servers: ServerList) {
390    let mut resolver_opts = ResolverOpts::default();
391    // TODO(https://fxbug.dev/42053483): Set ip_strategy once a unified lookup API
392    // exists that respects this setting.
393    resolver_opts.num_concurrent_reqs = 10;
394    // TODO(https://github.com/hickory-dns/hickory-dns/issues/1702): Use the
395    // default server ordering strategy once the algorithm is improved.
396    resolver_opts.server_ordering_strategy = ServerOrderingStrategy::UserProvidedOrder;
397
398    // We're going to add each server twice, once with protocol UDP and
399    // then with protocol TCP.
400    let mut name_servers = NameServerConfigGroup::with_capacity(servers.len() * 2);
401
402    name_servers.extend(servers.into_iter().flat_map(|server| {
403        let net_ext::SocketAddress(socket_addr) = server.into();
404        // Every server config gets UDP and TCP versions with
405        // preference for UDP.
406        std::iter::once(NameServerConfig {
407            socket_addr,
408            protocol: Protocol::Udp,
409            tls_dns_name: None,
410            trust_nx_responses: false,
411            bind_addr: None,
412            num_retained_errors: RETAINED_ERRORS_PER_NAME_SERVER,
413        })
414        .chain(std::iter::once(NameServerConfig {
415            socket_addr,
416            protocol: Protocol::Tcp,
417            tls_dns_name: None,
418            trust_nx_responses: false,
419            bind_addr: None,
420            num_retained_errors: RETAINED_ERRORS_PER_NAME_SERVER,
421        }))
422    }));
423
424    let new_resolver =
425        T::new(ResolverConfig::from_parts(None, Vec::new(), name_servers), resolver_opts);
426    resolver.write(Arc::new(new_resolver));
427}
428
429enum IncomingRequest {
430    Lookup(LookupRequestStream),
431    LookupAdmin(LookupAdminRequestStream),
432}
433
434trait ResolverLookup {
435    fn new(config: ResolverConfig, options: ResolverOpts) -> Self;
436
437    async fn lookup<N: IntoName + Send>(
438        &self,
439        name: N,
440        record_type: RecordType,
441    ) -> Result<lookup::Lookup, ResolveError>;
442
443    async fn reverse_lookup(&self, addr: IpAddr) -> Result<lookup::ReverseLookup, ResolveError>;
444}
445
446impl ResolverLookup for Resolver {
447    fn new(config: ResolverConfig, options: ResolverOpts) -> Self {
448        Resolver::new(config, options, Spawner).expect("failed to create resolver")
449    }
450
451    async fn lookup<N: IntoName + Send>(
452        &self,
453        name: N,
454        record_type: RecordType,
455    ) -> Result<lookup::Lookup, ResolveError> {
456        self.lookup(name, record_type).await
457    }
458
459    async fn reverse_lookup(&self, addr: IpAddr) -> Result<lookup::ReverseLookup, ResolveError> {
460        self.reverse_lookup(addr).await
461    }
462}
463
464trait NameServerStatsProvider {
465    fn name_server_stats(&self) -> Vec<trust_dns_resolver::NameServerStats>;
466}
467
468impl NameServerStatsProvider for Resolver {
469    fn name_server_stats(&self) -> Vec<trust_dns_resolver::NameServerStats> {
470        self.name_server_stats()
471    }
472}
473
474#[derive(Debug)]
475enum LookupIpErrorSource {
476    Ipv4,
477    Ipv6,
478    CanonicalName,
479}
480
481#[derive(Default)]
482struct LookupIpErrorsFromSource {
483    ipv4: Option<ResolveError>,
484    ipv6: Option<ResolveError>,
485    canonical_name: Option<ResolveError>,
486}
487
488impl LookupIpErrorsFromSource {
489    fn any_error(&self) -> Option<&ResolveError> {
490        let Self { ipv4, ipv6, canonical_name } = self;
491        ipv4.as_ref().or(ipv6.as_ref()).or(canonical_name.as_ref())
492    }
493
494    fn accumulate(&mut self, src: LookupIpErrorSource, error: ResolveError) {
495        let Self { ipv4, ipv6, canonical_name } = self;
496        let target = match src {
497            LookupIpErrorSource::Ipv4 => ipv4,
498            LookupIpErrorSource::Ipv6 => ipv6,
499            LookupIpErrorSource::CanonicalName => canonical_name,
500        };
501        debug_assert!(target.is_none(), "multiple errors observed for {src:?}");
502        *target = Some(error)
503    }
504
505    fn handle(self) -> fname::LookupError {
506        let Self { ipv4, ipv6, canonical_name } = self;
507        let mut ret = None;
508        for (src, err) in [
509            ("LookupIp(IPv4)", ipv4),
510            ("LookupIp(IPv6)", ipv6),
511            ("LookupIp(CanonicalName)", canonical_name),
512        ]
513        .into_iter()
514        .filter_map(|(src, err)| err.map(|e| (src, e)))
515        {
516            // We want to log all errors, but only convert one of them to the
517            // return. The fixed order IPv4, IPv6, CanonicalName is chosen to
518            // maximize the likelihood of the reported error being useful.
519            let err = handle_err(src, err);
520            if ret.is_none() {
521                ret = Some(err)
522            }
523        }
524        ret.unwrap_or(fname::LookupError::InternalError)
525    }
526}
527
528fn handle_err(source: &str, err: ResolveError) -> fname::LookupError {
529    use trust_dns_proto::error::ProtoErrorKind;
530
531    let (lookup_err, ioerr): (_, Option<(std::io::ErrorKind, _)>) = match err.kind() {
532        // The following mapping is based on the analysis of `ResolveError` enumerations.
533        // For cases that are not obvious such as `ResolveErrorKind::Msg` and
534        // `ResolveErrorKind::Message`, I (chunyingw) did code searches to have more insights.
535        // `ResolveErrorKind::Msg`: An error with arbitrary message, it could be ex. "lock was
536        // poisoned, this is non-recoverable" and ""DNS Error".
537        // `ResolveErrorKind::Message`: An error with arbitrary message, it is mostly returned when
538        // there is no name in the input vector to look up with "can not lookup for no names".
539        // This is a best-effort mapping.
540        ResolveErrorKind::NoRecordsFound {
541            query: _,
542            soa: _,
543            negative_ttl: _,
544            response_code: _,
545            trusted: _,
546        } => (fname::LookupError::NotFound, None),
547        ResolveErrorKind::Proto(err) => match err.kind() {
548            ProtoErrorKind::DomainNameTooLong(_) | ProtoErrorKind::EdnsNameNotRoot(_) => {
549                (fname::LookupError::InvalidArgs, None)
550            }
551            ProtoErrorKind::Busy | ProtoErrorKind::Canceled(_) | ProtoErrorKind::Timeout => {
552                (fname::LookupError::Transient, None)
553            }
554            ProtoErrorKind::Io(inner) => {
555                (fname::LookupError::Transient, Some((inner.kind(), inner.raw_os_error())))
556            }
557            ProtoErrorKind::BadQueryCount(_)
558            | ProtoErrorKind::CharacterDataTooLong { max: _, len: _ }
559            | ProtoErrorKind::LabelOverlapsWithOther { label: _, other: _ }
560            | ProtoErrorKind::DnsKeyProtocolNot3(_)
561            | ProtoErrorKind::FormError { header: _, error: _ }
562            | ProtoErrorKind::HmacInvalid()
563            | ProtoErrorKind::IncorrectRDataLengthRead { read: _, len: _ }
564            | ProtoErrorKind::LabelBytesTooLong(_)
565            | ProtoErrorKind::PointerNotPriorToLabel { idx: _, ptr: _ }
566            | ProtoErrorKind::MaxBufferSizeExceeded(_)
567            | ProtoErrorKind::Message(_)
568            | ProtoErrorKind::Msg(_)
569            | ProtoErrorKind::NoError
570            | ProtoErrorKind::NotAllRecordsWritten { count: _ }
571            | ProtoErrorKind::RrsigsNotPresent { name: _, record_type: _ }
572            | ProtoErrorKind::UnknownAlgorithmTypeValue(_)
573            | ProtoErrorKind::UnknownDnsClassStr(_)
574            | ProtoErrorKind::UnknownDnsClassValue(_)
575            | ProtoErrorKind::UnknownRecordTypeStr(_)
576            | ProtoErrorKind::UnknownRecordTypeValue(_)
577            | ProtoErrorKind::UnrecognizedLabelCode(_)
578            | ProtoErrorKind::UnrecognizedNsec3Flags(_)
579            | ProtoErrorKind::UnrecognizedCsyncFlags(_)
580            | ProtoErrorKind::Poisoned
581            | ProtoErrorKind::Ring(_)
582            | ProtoErrorKind::SSL(_)
583            | ProtoErrorKind::Timer
584            | ProtoErrorKind::UrlParsing(_)
585            | ProtoErrorKind::Utf8(_)
586            | ProtoErrorKind::FromUtf8(_)
587            | ProtoErrorKind::ParseInt(_) => (fname::LookupError::InternalError, None),
588            // ProtoErrorKind is marked #[non_exhaustive] in trust-dns:
589            // https://github.com/hickory-dns/hickory-dns/blob/v0.21.0-alpha.1/crates/proto/src/error.rs#L66
590            // So we have to include a wildcard match.
591            kind => {
592                error!("unhandled variant {:?}", enum_variant_string(kind));
593                (fname::LookupError::InternalError, None)
594            }
595        },
596        ResolveErrorKind::Io(inner) => {
597            (fname::LookupError::Transient, Some((inner.kind(), inner.raw_os_error())))
598        }
599        ResolveErrorKind::Timeout => (fname::LookupError::Transient, None),
600        ResolveErrorKind::Msg(_)
601        | ResolveErrorKind::Message(_)
602        | ResolveErrorKind::NoConnections => (fname::LookupError::InternalError, None),
603        // ResolveErrorKind is marked #[non_exhaustive] in trust-dns:
604        // https://github.com/hickory-dns/hickory-dns/blob/v0.21.0-alpha.1/crates/resolver/src/error.rs#L29
605        // So we have to include a wildcard match.
606        kind => {
607            error!("unhandled variant {:?}", enum_variant_string(kind));
608            (fname::LookupError::InternalError, None)
609        }
610    };
611
612    if let Some((ioerr, raw_os_error)) = ioerr {
613        match raw_os_error {
614            Some(libc::EHOSTUNREACH | libc::ENETUNREACH) => {
615                debug!("{} error: {:?}; (IO error {:?})", source, lookup_err, ioerr)
616            }
617            _ => warn!("{} error: {:?}; (IO error {:?})", source, lookup_err, ioerr),
618        }
619    } else {
620        warn!("{} error: {:?}", source, lookup_err);
621    }
622
623    lookup_err
624}
625
626async fn sort_preferred_addresses(
627    mut addrs: Vec<fnet::IpAddress>,
628    routes: &fnet_routes::StateProxy,
629) -> Result<Vec<fnet::IpAddress>, fname::LookupError> {
630    let mut addrs_info = futures::future::try_join_all(
631        addrs
632            // Drain addresses from addrs, but keep it alive so we don't need to
633            // reallocate.
634            .drain(..)
635            .map(|addr| async move {
636                let source_addr = match routes.resolve(&addr).await? {
637                    Ok(fnet_routes::Resolved::Direct(fnet_routes::Destination {
638                        source_address,
639                        ..
640                    }))
641                    | Ok(fnet_routes::Resolved::Gateway(fnet_routes::Destination {
642                        source_address,
643                        ..
644                    })) => source_address,
645                    // If resolving routes returns an error treat it as an
646                    // unreachable address.
647                    Err(e) => {
648                        debug!(
649                            "fuchsia.net.routes/State.resolve({}) failed {}",
650                            net_ext::IpAddress::from(addr),
651                            zx::Status::from_raw(e)
652                        );
653                        None
654                    }
655                };
656                Ok((addr, DasCmpInfo::from_addrs(&addr, source_addr.as_ref())))
657            }),
658    )
659    .await
660    .map_err(|e: fidl::Error| {
661        warn!("fuchsia.net.routes/State.resolve FIDL error {:?}", e);
662        fname::LookupError::InternalError
663    })?;
664
665    addrs_info.sort_by(|(_laddr, left), (_raddr, right)| left.cmp(right));
666    // Reinsert the addresses in order from addr_info.
667    addrs.extend(addrs_info.into_iter().map(|(addr, _)| addr));
668    Ok(addrs)
669}
670
671#[derive(Debug)]
672struct Policy {
673    prefix: net_types::ip::Subnet<net_types::ip::Ipv6Addr>,
674    precedence: usize,
675    label: usize,
676}
677
678macro_rules! decl_policy {
679    ($ip:tt/$prefix:expr => $precedence:expr, $label:expr) => {
680        Policy {
681            // Unsafe allows us to declare constant subnets.
682            // We make sure no invalid subnets are created in
683            // test_valid_policy_table.
684            prefix: unsafe {
685                net_types::ip::Subnet::new_unchecked(
686                    net_types::ip::Ipv6Addr::from_bytes(fidl_ip_v6!($ip).addr),
687                    $prefix,
688                )
689            },
690            precedence: $precedence,
691            label: $label,
692        }
693    };
694}
695
696/// Policy table is defined in RFC 6724, section 2.1
697///
698/// A more human-readable version:
699///
700///  Prefix        Precedence Label
701///  ::1/128               50     0
702///  ::/0                  40     1
703///  ::ffff:0:0/96         35     4
704///  2002::/16             30     2
705///  2001::/32              5     5
706///  fc00::/7               3    13
707///  ::/96                  1     3
708///  fec0::/10              1    11
709///  3ffe::/16              1    12
710///
711/// We willingly left out ::/96, fec0::/10, 3ffe::/16 since those prefix
712/// assignments are deprecated.
713///
714/// The table is sorted by prefix length so longest-prefix match can be easily
715/// achieved.
716const POLICY_TABLE: [Policy; 6] = [
717    decl_policy!("::1"/128 => 50, 0),
718    decl_policy!("::ffff:0:0"/96 => 35, 4),
719    decl_policy!("2001::"/32 => 5, 5),
720    decl_policy!("2002::"/16 => 30, 2),
721    decl_policy!("fc00::"/7 => 3, 13),
722    decl_policy!("::"/0 => 40, 1),
723];
724
725fn policy_lookup(addr: &net_types::ip::Ipv6Addr) -> &'static Policy {
726    POLICY_TABLE
727        .iter()
728        .find(|policy| policy.prefix.contains(addr))
729        .expect("policy table MUST contain the all addresses subnet")
730}
731
732/// Destination Address selection information.
733///
734/// `DasCmpInfo` provides an implementation of a subset of Destination Address
735/// Selection according to the sorting rules defined in [RFC 6724 Section 6].
736///
737/// TODO(https://fxbug.dev/42143905): Implement missing rules 3, 4, and 7.
738/// Rules 3, 4, and 7 are omitted for compatibility with the equivalent
739/// implementation in Fuchsia's libc.
740///
741/// `DasCmpInfo` provides an [`std::cmp::Ord`] implementation that will return
742/// preferred addresses as "lesser" values.
743///
744/// [RFC 6724 Section 6]: https://tools.ietf.org/html/rfc6724#section-6
745#[derive(Debug)]
746struct DasCmpInfo {
747    usable: bool,
748    matching_scope: bool,
749    matching_label: bool,
750    precedence: usize,
751    scope: net_types::ip::Ipv6Scope,
752    common_prefix_len: u8,
753}
754
755impl DasCmpInfo {
756    /// Helper function to convert a FIDL IP address into
757    /// [`net_types::ip::Ipv6Addr`], using a mapped IPv4 when that's the case.
758    fn convert_addr(fidl: &fnet::IpAddress) -> net_types::ip::Ipv6Addr {
759        match fidl {
760            fnet::IpAddress::Ipv4(fnet::Ipv4Address { addr }) => {
761                net_types::ip::Ipv6Addr::from(net_types::ip::Ipv4Addr::new(*addr))
762            }
763            fnet::IpAddress::Ipv6(fnet::Ipv6Address { addr }) => {
764                net_types::ip::Ipv6Addr::from_bytes(*addr)
765            }
766        }
767    }
768
769    fn from_addrs(dst_addr: &fnet::IpAddress, src_addr: Option<&fnet::IpAddress>) -> Self {
770        use net_types::ScopeableAddress;
771
772        let dst_addr = Self::convert_addr(dst_addr);
773        let Policy { prefix: _, precedence, label: dst_label } = policy_lookup(&dst_addr);
774        let (usable, matching_scope, matching_label, common_prefix_len) = match src_addr {
775            Some(src_addr) => {
776                let src_addr = Self::convert_addr(src_addr);
777                let Policy { prefix: _, precedence: _, label: src_label } =
778                    policy_lookup(&src_addr);
779                (
780                    true,
781                    dst_addr.scope() == src_addr.scope(),
782                    dst_label == src_label,
783                    dst_addr.common_prefix_len(&src_addr),
784                )
785            }
786            None => (false, false, false, 0),
787        };
788        DasCmpInfo {
789            usable,
790            matching_scope,
791            matching_label,
792            precedence: *precedence,
793            scope: dst_addr.scope(),
794            common_prefix_len,
795        }
796    }
797}
798
799impl std::cmp::Ord for DasCmpInfo {
800    // TODO(https://fxbug.dev/42143905): Implement missing rules 3, 4, and 7.
801    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
802        use std::cmp::Ordering;
803        let DasCmpInfo {
804            usable: self_usable,
805            matching_scope: self_matching_scope,
806            matching_label: self_matching_label,
807            precedence: self_precedence,
808            scope: self_scope,
809            common_prefix_len: self_common_prefix_len,
810        } = self;
811        let DasCmpInfo {
812            usable: other_usable,
813            matching_scope: other_matching_scope,
814            matching_label: other_matching_label,
815            precedence: other_precedence,
816            scope: other_scope,
817            common_prefix_len: other_common_prefix_len,
818        } = other;
819
820        fn prefer_true(left: bool, right: bool) -> Ordering {
821            match (left, right) {
822                (true, false) => Ordering::Less,
823                (false, true) => Ordering::Greater,
824                (false, false) | (true, true) => Ordering::Equal,
825            }
826        }
827
828        // Rule 1: Avoid unusable destinations.
829        prefer_true(*self_usable, *other_usable)
830            .then(
831                // Rule 2: Prefer matching scope.
832                prefer_true(*self_matching_scope, *other_matching_scope),
833            )
834            .then(
835                // Rule 5: Prefer matching label.
836                prefer_true(*self_matching_label, *other_matching_label),
837            )
838            .then(
839                // Rule 6: Prefer higher precedence.
840                self_precedence.cmp(other_precedence).reverse(),
841            )
842            .then(
843                // Rule 8: Prefer smaller scope.
844                self_scope.multicast_scope_id().cmp(&other_scope.multicast_scope_id()),
845            )
846            .then(
847                // Rule 9: Use longest matching prefix.
848                self_common_prefix_len.cmp(other_common_prefix_len).reverse(),
849            )
850        // Rule 10: Otherwise, leave the order unchanged.
851    }
852}
853
854impl std::cmp::PartialOrd for DasCmpInfo {
855    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
856        Some(self.cmp(other))
857    }
858}
859
860impl std::cmp::PartialEq for DasCmpInfo {
861    fn eq(&self, other: &Self) -> bool {
862        self.cmp(other) == std::cmp::Ordering::Equal
863    }
864}
865
866impl std::cmp::Eq for DasCmpInfo {}
867
868async fn handle_lookup_hostname<T: ResolverLookup>(
869    resolver: &SharedResolver<T>,
870    addr: fnet::IpAddress,
871) -> Result<String, fname::LookupError> {
872    let net_ext::IpAddress(addr) = addr.into();
873    let resolver = resolver.read();
874
875    match resolver.reverse_lookup(addr).await {
876        Ok(response) => {
877            response.iter().next().ok_or(fname::LookupError::NotFound).map(ToString::to_string)
878        }
879        Err(error) => Err(handle_err("LookupHostname", error)),
880    }
881}
882
883struct IpLookupRequest {
884    hostname: String,
885    options: fname::LookupIpOptions,
886    responder: fname::LookupLookupIpResponder,
887}
888
889async fn run_lookup<T: ResolverLookup>(
890    resolver: &SharedResolver<T>,
891    stream: LookupRequestStream,
892    sender: mpsc::Sender<IpLookupRequest>,
893) -> Result<(), fidl::Error> {
894    stream
895        .try_for_each_concurrent(None, |request| async {
896            match request {
897                LookupRequest::LookupIp { hostname, options, responder } => {
898                    sender
899                        .clone()
900                        .send(IpLookupRequest { hostname, options, responder })
901                        .await
902                        .expect("receiver should not be closed");
903                    Ok(())
904                }
905                LookupRequest::LookupHostname { addr, responder } => responder
906                    .send(handle_lookup_hostname(&resolver, addr).await.as_deref().map_err(|e| *e)),
907            }
908        })
909        .await
910}
911
912const MAX_PARALLEL_REQUESTS: usize = 256;
913
914fn create_ip_lookup_fut<T: ResolverLookup>(
915    resolver: &SharedResolver<T>,
916    stats: Arc<QueryStats>,
917    routes: fnet_routes::StateProxy,
918    recv: mpsc::Receiver<IpLookupRequest>,
919) -> impl futures::Future<Output = ()> + '_ {
920    recv.for_each_concurrent(
921        MAX_PARALLEL_REQUESTS,
922        move |IpLookupRequest { hostname, options, responder }| {
923            let stats = stats.clone();
924            let routes = routes.clone();
925            async move {
926                let fname::LookupIpOptions {
927                    ipv4_lookup,
928                    ipv6_lookup,
929                    sort_addresses,
930                    canonical_name_lookup,
931                    ..
932                } = options;
933                let ipv4_lookup = ipv4_lookup.unwrap_or(false);
934                let ipv6_lookup = ipv6_lookup.unwrap_or(false);
935                let sort_addresses = sort_addresses.unwrap_or(false);
936                let canonical_name_lookup = canonical_name_lookup.unwrap_or(false);
937                let lookup_result = (|| async {
938                    let hostname = hostname.as_str();
939                    // The [`IntoName`] implementation for &str does not
940                    // properly reject IPv4 addresses in accordance with RFC
941                    // 1123 section 2.1:
942                    //
943                    //   If a dotted-decimal number can be entered without such
944                    //   identifying delimiters, then a full syntactic check must be
945                    //   made, because a segment of a host domain name is now allowed
946                    //   to begin with a digit and could legally be entirely numeric
947                    //   (see Section 6.1.2.4).  However, a valid host name can never
948                    //   have the dotted-decimal form #.#.#.#, since at least the
949                    //   highest-level component label will be alphabetic.
950                    //
951                    // Thus we explicitly reject such input here.
952                    //
953                    // TODO(https://github.com/hickory-dns/hickory-dns/issues/1725):
954                    // Remove this when the implementation is sufficiently
955                    // strict.
956                    match IpAddr::from_str(hostname) {
957                        Ok(addr) => {
958                            let _: IpAddr = addr;
959                            return Err(fname::LookupError::InvalidArgs);
960                        }
961                        Err(std::net::AddrParseError { .. }) => {}
962                    };
963                    let resolver = resolver.read();
964                    let start_time = fasync::MonotonicInstant::now();
965                    let (ret1, ret2, ret3) = futures::future::join3(
966                        futures::future::OptionFuture::from(
967                            ipv4_lookup.then(|| {
968                                resolver
969                                    .lookup(hostname, RecordType::A)
970                                    .map_err(|e| (LookupIpErrorSource::Ipv4, e))
971                            }),
972                        ),
973                        futures::future::OptionFuture::from(
974                            ipv6_lookup.then(|| {
975                                resolver
976                                    .lookup(hostname, RecordType::AAAA)
977                                    .map_err(|e| (LookupIpErrorSource::Ipv6, e))
978                            }),
979                        ),
980                        futures::future::OptionFuture::from(
981                            canonical_name_lookup
982                                .then(|| {
983                                    resolver
984                                        .lookup(hostname, RecordType::CNAME)
985                                        .map_err(|e| (LookupIpErrorSource::CanonicalName, e))
986                                }),
987                        ),
988                    )
989                    .await;
990                    let result = [ret1, ret2, ret3];
991                    if result.iter().all(Option::is_none) {
992                        return Err(fname::LookupError::InvalidArgs);
993                    }
994                    let (addrs, cnames, error) =
995                        result.into_iter().filter_map(std::convert::identity).fold(
996                            (Vec::new(), Vec::new(), LookupIpErrorsFromSource::default()),
997                            |(mut addrs, mut cnames, mut error), result| {
998                                match result {
999                                    Err((src, err)) => {
1000                                        error.accumulate(src, err);
1001                                    },
1002                                    Ok(lookup) => lookup.iter().for_each(|rdata| match rdata {
1003                                        RData::A(addr) if ipv4_lookup => addrs
1004                                            .push(net_ext::IpAddress(IpAddr::V4(*addr)).into()),
1005                                        RData::AAAA(addr) if ipv6_lookup => addrs
1006                                            .push(net_ext::IpAddress(IpAddr::V6(*addr)).into()),
1007                                        RData::CNAME(name) => {
1008                                            // CNAME records are known to be present with other
1009                                            // query types; avoid logging in that case.
1010                                            if canonical_name_lookup {
1011                                                cnames.push(name.to_utf8())
1012                                            }
1013                                        }
1014                                        rdata => {
1015                                            error!(
1016                                                "Lookup(_, {:?}) yielded unexpected record type: {}",
1017                                                options, rdata.to_record_type(),
1018                                            )
1019                                        }
1020                                    }),
1021                                };
1022                            (addrs, cnames, error)
1023                        });
1024                    let count = match NonZeroUsize::try_from(addrs.len() + cnames.len()) {
1025                        Ok(count) => Ok(count),
1026                        Err(std::num::TryFromIntError { .. }) => match error.any_error() {
1027                            None => {
1028                                // TODO(https://fxbug.dev/42062388): Remove this
1029                                // once Trust-DNS enforces that all responses
1030                                // with no records return a `NoRecordsFound`
1031                                // error.
1032                                //
1033                                // Note that returning here means that query
1034                                // stats for inspect will not get logged. This
1035                                // is ok since this case should be rare and is
1036                                // considered to be temporary. Moreover, the
1037                                // failed query counters are based on the
1038                                // `ResolverError::kind`, which isn't applicable
1039                                // here.
1040                                error!("resolver response unexpectedly contained no records \
1041                                        and no error. See https://fxbug.dev/42062388.");
1042                                return Err(fname::LookupError::NotFound);
1043                            },
1044                            Some(any_err) => {
1045                                Err(any_err)
1046                            }
1047                        }
1048                    };
1049                    stats
1050                        .finish_query(
1051                            start_time,
1052                            count.as_ref().copied().map_err(|e| e.kind()),
1053                        )
1054                        .await;
1055                    match count {
1056                        Ok(_) => {},
1057                        Err(_any_err) => {
1058                            // Handle all the errors instead of just the one used for stats.
1059                            return Err(error.handle());
1060                        }
1061                    }
1062                    let addrs = if sort_addresses {
1063                        sort_preferred_addresses(addrs, &routes).await?
1064                    } else {
1065                        addrs
1066                    };
1067                    let addrs = if addrs.len() > fname::MAX_ADDRESSES.into() {
1068                        warn!(
1069                            "Lookup(_, {:?}): {} addresses, truncating to {}",
1070                            options, addrs.len(), fname::MAX_ADDRESSES
1071                        );
1072                        let mut addrs = addrs;
1073                        addrs.truncate(fname::MAX_ADDRESSES.into());
1074                        addrs
1075                    } else {
1076                        addrs
1077                    };
1078                    // Per RFC 1034 section 3.6.2:
1079                    //
1080                    //   If a CNAME RR is present at a node, no other data should be present; this
1081                    //   ensures that the data for a canonical name and its aliases cannot be
1082                    //   different.  This rule also insures that a cached CNAME can be used without
1083                    //   checking with an authoritative server for other RR types.
1084                    if cnames.len() > 1 {
1085                        let cnames =
1086                            cnames.iter().fold(HashMap::<&str, usize>::new(), |mut acc, cname| {
1087                                *acc.entry(cname).or_default() += 1;
1088                                acc
1089                            });
1090                        warn!(
1091                            "Lookup(_, {:?}): multiple CNAMEs: {:?}",
1092                            options, cnames
1093                        )
1094                    }
1095                    let cname = {
1096                        let mut cnames = cnames;
1097                        cnames.pop()
1098                    };
1099                    Ok(fname::LookupResult {
1100                        addresses: Some(addrs),
1101                        canonical_name: cname,
1102                        ..Default::default()
1103                    })
1104                })()
1105                .await;
1106                responder.send(lookup_result.as_ref().map_err(|e| *e)).unwrap_or_else(|e|
1107                    warn!(
1108                        "failed to send IP lookup result due to FIDL error: {}",
1109                        e
1110                    )
1111                )
1112            }
1113        },
1114    )
1115}
1116
1117/// Serves `stream` and forwards received configurations to `sink`.
1118async fn run_lookup_admin<T: ResolverLookup>(
1119    resolver: &SharedResolver<T>,
1120    state: &dns::config::ServerConfigState,
1121    stream: LookupAdminRequestStream,
1122) -> Result<(), fidl::Error> {
1123    stream
1124        .try_for_each(|req| async {
1125            match req {
1126                LookupAdminRequest::SetDnsServers { servers, responder } => {
1127                    let response = match state.update_servers(servers) {
1128                        UpdateServersResult::Updated(servers) => {
1129                            update_resolver(resolver, servers);
1130                            Ok(())
1131                        }
1132                        UpdateServersResult::NoChange => Ok(()),
1133                        UpdateServersResult::InvalidsServers => {
1134                            Err(zx::Status::INVALID_ARGS.into_raw())
1135                        }
1136                    };
1137                    responder.send(response)?;
1138                }
1139                LookupAdminRequest::GetDnsServers { responder } => {
1140                    responder.send(&state.servers())?;
1141                }
1142            }
1143            Ok(())
1144        })
1145        .await
1146}
1147
1148/// Adds a [`dns::policy::ServerConfigState`] inspection child node to
1149/// `parent`.
1150fn add_config_state_inspect(
1151    parent: &fuchsia_inspect::Node,
1152    config_state: Arc<dns::config::ServerConfigState>,
1153) -> fuchsia_inspect::LazyNode {
1154    parent.create_lazy_child("servers", move || {
1155        let config_state = config_state.clone();
1156        async move {
1157            let srv = fuchsia_inspect::Inspector::default();
1158            let server_list = config_state.servers();
1159            for (i, server) in server_list.into_iter().enumerate() {
1160                let child = srv.root().create_child(format!("{}", i));
1161                let net_ext::SocketAddress(addr) = server.into();
1162                child.record_string("address", format!("{}", addr));
1163                srv.root().record(child);
1164            }
1165            Ok(srv)
1166        }
1167        .boxed()
1168    })
1169}
1170
1171/// Adds a nameserver stats child node to `parent`.
1172fn add_name_server_stats_inspect<T>(
1173    parent: &fuchsia_inspect::Node,
1174    shared_resolver: SharedResolver<T>,
1175) -> fuchsia_inspect::LazyNode
1176where
1177    T: NameServerStatsProvider + Send + Sync + 'static,
1178{
1179    parent.create_lazy_child("name_servers", move || {
1180        let shared_resolver = shared_resolver.read();
1181        async move {
1182            let inspector = fuchsia_inspect::Inspector::default();
1183            for (
1184                i,
1185                NameServerStats { addr, proto, failures, successes, recent_errors, success_streak },
1186            ) in shared_resolver.name_server_stats().into_iter().enumerate()
1187            {
1188                let child = inspector.root().create_child(format!("{i}"));
1189                child.record_string("address", format!("{addr}"));
1190                child.record_string("protocol", format!("{proto:?}"));
1191                child.record_uint("successful_queries", successes as u64);
1192                child.record_uint("failed_queries", failures as u64);
1193                child.record_uint("success_streak", success_streak as u64);
1194
1195                let failure_stats =
1196                    recent_errors.iter().fold(FailureStats::default(), |mut stats, error| {
1197                        stats.increment(error);
1198                        stats
1199                    });
1200                let errors = child.create_child("errors");
1201                failure_stats.populate_inspect_node(&errors);
1202                child.record(errors);
1203
1204                inspector.root().record(child);
1205            }
1206            Ok(inspector)
1207        }
1208        .boxed()
1209    })
1210}
1211
1212/// Adds a [`QueryStats`] inspection child node to `parent`.
1213fn add_query_stats_inspect(
1214    parent: &fuchsia_inspect::Node,
1215    stats: Arc<QueryStats>,
1216) -> fuchsia_inspect::LazyNode {
1217    parent.create_lazy_child("query_stats", move || {
1218        let stats = stats.clone();
1219        async move {
1220            let past_queries = &*stats.inner.lock().await;
1221            let node = fuchsia_inspect::Inspector::default();
1222            for (
1223                i,
1224                QueryWindow {
1225                    start,
1226                    success_count,
1227                    failure_count,
1228                    success_elapsed_time,
1229                    failure_elapsed_time,
1230                    failure_stats,
1231                    address_counts_histogram,
1232                },
1233            ) in past_queries.iter().enumerate()
1234            {
1235                let child = node.root().create_child(format!("window {}", i + 1));
1236
1237                match u64::try_from(start.into_nanos()) {
1238                    Ok(nanos) => {
1239                        child.record_uint("start_time_nanos", nanos);
1240                    },
1241                    Err(e) => warn!(
1242                        "error computing `start_time_nanos`: {:?}.into_nanos() from i64 -> u64 failed: {}",
1243                        start, e
1244                    ),
1245                }
1246                child.record_uint("successful_queries", *success_count);
1247                child.record_uint("failed_queries", *failure_count);
1248                let record_average = |name: &str, total: zx::MonotonicDuration, count: u64| {
1249                    // Don't record an average if there are no stats.
1250                    if count == 0 {
1251                        return;
1252                    }
1253                    match u64::try_from(total.into_micros()) {
1254                        Ok(micros) => child.record_uint(name, micros / count),
1255                        Err(e) => warn!(
1256                            "error computing `{}`: {:?}.into_micros() from i64 -> u64 failed: {}",
1257                            name, success_elapsed_time, e
1258                        ),
1259                    }
1260                };
1261                record_average(
1262                    "average_success_duration_micros",
1263                    *success_elapsed_time,
1264                    *success_count,
1265                );
1266                record_average(
1267                    "average_failure_duration_micros",
1268                    *failure_elapsed_time,
1269                    *failure_count,
1270                );
1271
1272                let errors = child.create_child("errors");
1273                failure_stats.populate_inspect_node(&errors);
1274                child.record(errors);
1275
1276                let address_counts_node = child.create_child("address_counts");
1277                for (count, occurrences) in address_counts_histogram {
1278                    address_counts_node.record_uint(count.to_string(), *occurrences);
1279                }
1280                child.record(address_counts_node);
1281
1282                node.root().record(child);
1283            }
1284            Ok(node)
1285        }
1286        .boxed()
1287    })
1288}
1289
1290// NB: We manually set tags so logs from trust-dns crates also get the same
1291// tags as opposed to only the crate path.
1292#[fuchsia::main(logging_tags = ["dns"])]
1293pub async fn main() -> Result<(), Error> {
1294    info!("starting");
1295
1296    let mut resolver_opts = ResolverOpts::default();
1297    // Resolver will query for A and AAAA in parallel for lookup_ip.
1298    resolver_opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6;
1299    let resolver = SharedResolver::new(
1300        Resolver::new(ResolverConfig::default(), resolver_opts, Spawner)
1301            .expect("failed to create resolver"),
1302    );
1303
1304    let config_state = Arc::new(dns::config::ServerConfigState::new());
1305    let stats = Arc::new(QueryStats::new());
1306
1307    let mut fs = ServiceFs::new_local();
1308
1309    let inspector = fuchsia_inspect::component::inspector();
1310    let _state_inspect_node = add_config_state_inspect(inspector.root(), config_state.clone());
1311    let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
1312    let _name_server_stats_inspect_node =
1313        add_name_server_stats_inspect(inspector.root(), resolver.clone());
1314    let _inspect_server_task =
1315        inspect_runtime::publish(inspector, inspect_runtime::PublishOptions::default())
1316            .context("publish Inspect task")?;
1317
1318    let routes = fuchsia_component::client::connect_to_protocol::<fnet_routes::StateMarker>()
1319        .context("failed to connect to fuchsia.net.routes/State")?;
1320
1321    let _: &mut ServiceFsDir<'_, _> = fs
1322        .dir("svc")
1323        .add_fidl_service(IncomingRequest::Lookup)
1324        .add_fidl_service(IncomingRequest::LookupAdmin);
1325    let _: &mut ServiceFs<_> =
1326        fs.take_and_serve_directory_handle().context("failed to serve directory")?;
1327
1328    // Create a channel with buffer size `MAX_PARALLEL_REQUESTS`, which allows
1329    // request processing to always be fully saturated.
1330    let (sender, recv) = mpsc::channel(MAX_PARALLEL_REQUESTS);
1331    let serve_fut = fs.for_each_concurrent(None, |incoming_service| async {
1332        match incoming_service {
1333            IncomingRequest::Lookup(stream) => run_lookup(&resolver, stream, sender.clone())
1334                .await
1335                .unwrap_or_else(|e| warn!("run_lookup finished with error: {}", e)),
1336            IncomingRequest::LookupAdmin(stream) => {
1337                run_lookup_admin(&resolver, &config_state, stream)
1338                    .await
1339                    .unwrap_or_else(|e| error!("run_lookup_admin finished with error: {}", e))
1340            }
1341        }
1342    });
1343    let ip_lookup_fut = create_ip_lookup_fut(&resolver, stats.clone(), routes, recv);
1344
1345    // Failing to apply a scheduling role is not fatal. Issue a warning in case
1346    // DNS latency is important to a product and running at default priority is
1347    // insufficient.
1348    match fuchsia_scheduler::set_role_for_this_thread("fuchsia.networking.dns.resolver.main") {
1349        Ok(_) => info!("Applied scheduling role"),
1350        Err(err) => warn!("Failed to apply scheduling role: {}", err),
1351    };
1352
1353    let ((), ()) = futures::future::join(serve_fut, ip_lookup_fut).await;
1354    Ok(())
1355}
1356
1357#[cfg(test)]
1358mod tests {
1359    use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
1360    use std::pin::pin;
1361    use std::str::FromStr;
1362
1363    use assert_matches::assert_matches;
1364    use diagnostics_assertions::{NonZeroUintProperty, assert_data_tree, tree_assertion};
1365    use dns::DEFAULT_PORT;
1366    use dns::test_util::*;
1367    use futures::future::TryFutureExt as _;
1368    use itertools::Itertools as _;
1369    use net_declare::{fidl_ip, std_ip, std_ip_v4, std_ip_v6, std_socket_addr};
1370    use net_types::ip::Ip as _;
1371    use test_case::test_case;
1372    use trust_dns_proto::op::Query;
1373    use trust_dns_proto::rr::{Name, Record};
1374    use trust_dns_resolver::config::Protocol;
1375    use trust_dns_resolver::lookup::{Lookup, ReverseLookup};
1376
1377    use super::*;
1378
1379    const IPV4_LOOPBACK: fnet::IpAddress = fidl_ip!("127.0.0.1");
1380    const IPV6_LOOPBACK: fnet::IpAddress = fidl_ip!("::1");
1381    const LOCAL_HOST: &str = "localhost.";
1382
1383    // IPv4 address returned by mock lookup.
1384    const IPV4_HOST: Ipv4Addr = std_ip_v4!("240.0.0.2");
1385    // IPv6 address returned by mock lookup.
1386    const IPV6_HOST: Ipv6Addr = std_ip_v6!("abcd::2");
1387
1388    // host which has IPv4 address only.
1389    const REMOTE_IPV4_HOST: &str = "www.foo.com";
1390    // host which has IPv6 address only.
1391    const REMOTE_IPV6_HOST: &str = "www.bar.com";
1392    const REMOTE_IPV4_HOST_ALIAS: &str = "www.alsofoo.com";
1393    const REMOTE_IPV6_HOST_ALIAS: &str = "www.alsobar.com";
1394    // host used in reverse_lookup when multiple hostnames are returned.
1395    const REMOTE_IPV6_HOST_EXTRA: &str = "www.bar2.com";
1396    // host which has IPv4 and IPv6 address if reset name servers.
1397    const REMOTE_IPV4_IPV6_HOST: &str = "www.foobar.com";
1398    // host which has no records and does not result in an error.
1399    const NO_RECORDS_AND_NO_ERROR_HOST: &str = "www.no-records-and-no-error.com";
1400
1401    async fn setup_namelookup_service() -> (fname::LookupProxy, impl futures::Future<Output = ()>) {
1402        let (name_lookup_proxy, stream) =
1403            fidl::endpoints::create_proxy_and_stream::<fname::LookupMarker>();
1404
1405        let mut resolver_opts = ResolverOpts::default();
1406        resolver_opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6;
1407
1408        let resolver = SharedResolver::new(
1409            Resolver::new(ResolverConfig::default(), resolver_opts, Spawner)
1410                .expect("failed to create resolver"),
1411        );
1412        let stats = Arc::new(QueryStats::new());
1413        let (routes_proxy, routes_stream) =
1414            fidl::endpoints::create_proxy_and_stream::<fnet_routes::StateMarker>();
1415        let routes_fut =
1416            routes_stream.try_for_each(|req| -> futures::future::Ready<Result<(), fidl::Error>> {
1417                panic!("Should not call routes/State. Received request {:?}", req)
1418            });
1419        let (sender, recv) = mpsc::channel(MAX_PARALLEL_REQUESTS);
1420
1421        (name_lookup_proxy, async move {
1422            futures::future::try_join3(
1423                run_lookup(&resolver, stream, sender),
1424                routes_fut,
1425                create_ip_lookup_fut(&resolver, stats.clone(), routes_proxy, recv).map(Ok),
1426            )
1427            .map(|r| match r {
1428                Ok(((), (), ())) => (),
1429                Err(e) => panic!("namelookup service error {:?}", e),
1430            })
1431            .await
1432        })
1433    }
1434
1435    #[fasync::run_singlethreaded(test)]
1436    async fn test_lookupip_localhost() {
1437        let (proxy, fut) = setup_namelookup_service().await;
1438        let ((), ()) = futures::future::join(fut, async move {
1439            // IP Lookup IPv4 and IPv6 for localhost.
1440            assert_eq!(
1441                proxy
1442                    .lookup_ip(
1443                        LOCAL_HOST,
1444                        &fname::LookupIpOptions {
1445                            ipv4_lookup: Some(true),
1446                            ipv6_lookup: Some(true),
1447                            ..Default::default()
1448                        }
1449                    )
1450                    .await
1451                    .expect("lookup_ip"),
1452                Ok(fname::LookupResult {
1453                    addresses: Some(vec![IPV4_LOOPBACK, IPV6_LOOPBACK]),
1454                    ..Default::default()
1455                }),
1456            );
1457
1458            // IP Lookup IPv4 only for localhost.
1459            assert_eq!(
1460                proxy
1461                    .lookup_ip(
1462                        LOCAL_HOST,
1463                        &fname::LookupIpOptions { ipv4_lookup: Some(true), ..Default::default() }
1464                    )
1465                    .await
1466                    .expect("lookup_ip"),
1467                Ok(fname::LookupResult {
1468                    addresses: Some(vec![IPV4_LOOPBACK]),
1469                    ..Default::default()
1470                }),
1471            );
1472
1473            // IP Lookup IPv6 only for localhost.
1474            assert_eq!(
1475                proxy
1476                    .lookup_ip(
1477                        LOCAL_HOST,
1478                        &fname::LookupIpOptions { ipv6_lookup: Some(true), ..Default::default() }
1479                    )
1480                    .await
1481                    .expect("lookup_ip"),
1482                Ok(fname::LookupResult {
1483                    addresses: Some(vec![IPV6_LOOPBACK]),
1484                    ..Default::default()
1485                }),
1486            );
1487        })
1488        .await;
1489    }
1490
1491    #[fasync::run_singlethreaded(test)]
1492    async fn test_lookuphostname_localhost() {
1493        let (proxy, fut) = setup_namelookup_service().await;
1494        let ((), ()) = futures::future::join(fut, async move {
1495            let hostname = IPV4_LOOPBACK;
1496            assert_eq!(
1497                proxy.lookup_hostname(&hostname).await.expect("lookup_hostname").as_deref(),
1498                Ok(LOCAL_HOST)
1499            );
1500        })
1501        .await;
1502    }
1503
1504    #[derive(Debug, Clone)]
1505    struct MockResolver {
1506        config: ResolverConfig,
1507        repeat: u16,
1508        name_server_stats: Vec<NameServerStats>,
1509    }
1510
1511    impl ResolverLookup for MockResolver {
1512        fn new(config: ResolverConfig, _options: ResolverOpts) -> Self {
1513            Self { config, repeat: 1, name_server_stats: Vec::new() }
1514        }
1515
1516        async fn lookup<N: IntoName + Send>(
1517            &self,
1518            name: N,
1519            record_type: RecordType,
1520        ) -> Result<lookup::Lookup, ResolveError> {
1521            let Self { config: _, repeat, name_server_stats: _ } = self;
1522
1523            let name = name.into_name()?;
1524            let host_name = name.to_utf8();
1525
1526            if host_name == NO_RECORDS_AND_NO_ERROR_HOST {
1527                return Ok(Lookup::new_with_max_ttl(Query::default(), Arc::new([])));
1528            }
1529            let rdatas = match record_type {
1530                RecordType::A => [REMOTE_IPV4_HOST, REMOTE_IPV4_IPV6_HOST]
1531                    .contains(&host_name.as_str())
1532                    .then_some(RData::A(IPV4_HOST)),
1533                RecordType::AAAA => [REMOTE_IPV6_HOST, REMOTE_IPV4_IPV6_HOST]
1534                    .contains(&host_name.as_str())
1535                    .then_some(RData::AAAA(IPV6_HOST)),
1536                RecordType::CNAME => match host_name.as_str() {
1537                    REMOTE_IPV4_HOST_ALIAS => Some(REMOTE_IPV4_HOST),
1538                    REMOTE_IPV6_HOST_ALIAS => Some(REMOTE_IPV6_HOST),
1539                    _ => None,
1540                }
1541                .map(Name::from_str)
1542                .transpose()
1543                .unwrap()
1544                .map(RData::CNAME),
1545                record_type => {
1546                    panic!("unexpected record type {:?}", record_type)
1547                }
1548            }
1549            .into_iter();
1550
1551            let len = rdatas.len() * usize::from(*repeat);
1552            let records: Vec<Record> = rdatas
1553                .map(|rdata| {
1554                    Record::from_rdata(
1555                        Name::new(),
1556                        // The following ttl value is taken arbitrarily and does not matter in the
1557                        // test.
1558                        60,
1559                        rdata,
1560                    )
1561                })
1562                .cycle()
1563                .take(len)
1564                .collect();
1565
1566            if records.is_empty() {
1567                let mut response = trust_dns_proto::op::Message::new();
1568                let _: &mut trust_dns_proto::op::Message =
1569                    response.set_response_code(ResponseCode::NoError);
1570                let error = ResolveError::from_response(response.into(), false)
1571                    .expect_err("response with no records should be a NoRecordsFound error");
1572                return Err(error);
1573            }
1574
1575            Ok(Lookup::new_with_max_ttl(Query::default(), records.into()))
1576        }
1577
1578        async fn reverse_lookup(
1579            &self,
1580            addr: IpAddr,
1581        ) -> Result<lookup::ReverseLookup, ResolveError> {
1582            let lookup = if addr == IPV4_HOST {
1583                Lookup::from_rdata(
1584                    Query::default(),
1585                    RData::PTR(Name::from_str(REMOTE_IPV4_HOST).unwrap()),
1586                )
1587            } else if addr == IPV6_HOST {
1588                Lookup::new_with_max_ttl(
1589                    Query::default(),
1590                    Arc::new([
1591                        Record::from_rdata(
1592                            Name::new(),
1593                            60, // The value is taken arbitrarily and does not matter
1594                            // in the test.
1595                            RData::PTR(Name::from_str(REMOTE_IPV6_HOST).unwrap()),
1596                        ),
1597                        Record::from_rdata(
1598                            Name::new(),
1599                            60, // The value is taken arbitrarily and does not matter
1600                            // in the test.
1601                            RData::PTR(Name::from_str(REMOTE_IPV6_HOST_EXTRA).unwrap()),
1602                        ),
1603                    ]),
1604                )
1605            } else {
1606                Lookup::new_with_max_ttl(Query::default(), Arc::new([]))
1607            };
1608            Ok(ReverseLookup::from(lookup))
1609        }
1610    }
1611
1612    impl NameServerStatsProvider for MockResolver {
1613        fn name_server_stats(&self) -> Vec<NameServerStats> {
1614            self.name_server_stats.clone()
1615        }
1616    }
1617
1618    struct TestEnvironment {
1619        shared_resolver: SharedResolver<MockResolver>,
1620        config_state: Arc<dns::config::ServerConfigState>,
1621        stats: Arc<QueryStats>,
1622    }
1623
1624    impl Default for TestEnvironment {
1625        fn default() -> Self {
1626            Self::new(1)
1627        }
1628    }
1629
1630    impl TestEnvironment {
1631        fn new(repeat: u16) -> Self {
1632            Self {
1633                shared_resolver: SharedResolver::new(MockResolver {
1634                    config: ResolverConfig::from_parts(
1635                        None,
1636                        vec![],
1637                        // Set name_servers as empty, so it's guaranteed to be different from IPV4_NAMESERVER
1638                        // and IPV6_NAMESERVER.
1639                        NameServerConfigGroup::with_capacity(0),
1640                    ),
1641                    repeat,
1642                    name_server_stats: Vec::new(),
1643                }),
1644                config_state: Arc::new(dns::config::ServerConfigState::new()),
1645                stats: Arc::new(QueryStats::new()),
1646            }
1647        }
1648
1649        async fn run_lookup<F, Fut>(&self, f: F)
1650        where
1651            Fut: futures::Future<Output = ()>,
1652            F: FnOnce(fname::LookupProxy) -> Fut,
1653        {
1654            self.run_lookup_with_routes_handler(f, |req| {
1655                panic!("Should not call routes/State. Received request {:?}", req)
1656            })
1657            .await
1658        }
1659
1660        async fn run_lookup_with_routes_handler<F, Fut, R>(&self, f: F, handle_routes: R)
1661        where
1662            Fut: futures::Future<Output = ()>,
1663            F: FnOnce(fname::LookupProxy) -> Fut,
1664            R: Fn(fnet_routes::StateRequest),
1665        {
1666            let (name_lookup_proxy, name_lookup_stream) =
1667                fidl::endpoints::create_proxy_and_stream::<fname::LookupMarker>();
1668
1669            let (routes_proxy, routes_stream) =
1670                fidl::endpoints::create_proxy_and_stream::<fnet_routes::StateMarker>();
1671
1672            let (sender, recv) = mpsc::channel(MAX_PARALLEL_REQUESTS);
1673            let Self { shared_resolver, config_state: _, stats } = self;
1674            let ((), (), (), ()) = futures::future::try_join4(
1675                run_lookup(shared_resolver, name_lookup_stream, sender),
1676                f(name_lookup_proxy).map(Ok),
1677                routes_stream.try_for_each(|req| futures::future::ok(handle_routes(req))),
1678                create_ip_lookup_fut(shared_resolver, stats.clone(), routes_proxy, recv).map(Ok),
1679            )
1680            .await
1681            .expect("Error running lookup future");
1682        }
1683
1684        async fn run_admin<F, Fut>(&self, f: F)
1685        where
1686            Fut: futures::Future<Output = ()>,
1687            F: FnOnce(fname::LookupAdminProxy) -> Fut,
1688        {
1689            let (lookup_admin_proxy, lookup_admin_stream) =
1690                fidl::endpoints::create_proxy_and_stream::<fname::LookupAdminMarker>();
1691            let Self { shared_resolver, config_state, stats: _ } = self;
1692            let ((), ()) = futures::future::try_join(
1693                run_lookup_admin(shared_resolver, config_state, lookup_admin_stream)
1694                    .map_err(anyhow::Error::from),
1695                f(lookup_admin_proxy).map(Ok),
1696            )
1697            .await
1698            .expect("Error running admin future");
1699        }
1700    }
1701
1702    fn map_ip<T: Into<IpAddr>>(addr: T) -> fnet::IpAddress {
1703        net_ext::IpAddress(addr.into()).into()
1704    }
1705
1706    #[fasync::run_singlethreaded(test)]
1707    async fn test_no_records_and_no_error() {
1708        TestEnvironment::default()
1709            .run_lookup(|proxy| async move {
1710                let proxy = &proxy;
1711                futures::stream::iter([(true, true), (true, false), (false, true)])
1712                    .for_each_concurrent(None, move |(ipv4_lookup, ipv6_lookup)| async move {
1713                        // Verify that the resolver does not panic when the
1714                        // response contains no records and no error. This
1715                        // scenario should theoretically not occur, but
1716                        // currently does. See https://fxbug.dev/42062388.
1717                        assert_eq!(
1718                            proxy
1719                                .lookup_ip(
1720                                    NO_RECORDS_AND_NO_ERROR_HOST,
1721                                    &fname::LookupIpOptions {
1722                                        ipv4_lookup: Some(ipv4_lookup),
1723                                        ipv6_lookup: Some(ipv6_lookup),
1724                                        ..Default::default()
1725                                    }
1726                                )
1727                                .await
1728                                .expect("lookup_ip"),
1729                            Err(fname::LookupError::NotFound),
1730                        );
1731                    })
1732                    .await
1733            })
1734            .await;
1735    }
1736
1737    #[fasync::run_singlethreaded(test)]
1738    async fn test_lookupip_remotehost_overflow() {
1739        // We're returning two addresses, so we need each one to repeat only half as many times.
1740        const REPEAT: u16 = fname::MAX_ADDRESSES / 2 + 1;
1741        let expected = std::iter::empty()
1742            .chain(std::iter::repeat(map_ip(IPV4_HOST)).take(REPEAT.into()))
1743            .chain(std::iter::repeat(map_ip(IPV6_HOST)).take(REPEAT.into()))
1744            .take(fname::MAX_ADDRESSES.into())
1745            .collect::<Vec<_>>();
1746        assert_eq!(expected.len(), usize::from(fname::MAX_ADDRESSES));
1747        TestEnvironment::new(REPEAT)
1748            .run_lookup(|proxy| async move {
1749                assert_eq!(
1750                    proxy
1751                        .lookup_ip(
1752                            REMOTE_IPV4_IPV6_HOST,
1753                            &fname::LookupIpOptions {
1754                                ipv4_lookup: Some(true),
1755                                ipv6_lookup: Some(true),
1756                                ..Default::default()
1757                            }
1758                        )
1759                        .await
1760                        .expect("lookup_ip"),
1761                    Ok(fname::LookupResult { addresses: Some(expected), ..Default::default() })
1762                );
1763            })
1764            .await;
1765    }
1766
1767    #[fasync::run_singlethreaded(test)]
1768    async fn test_lookupip_remotehost_ipv4() {
1769        TestEnvironment::default()
1770            .run_lookup(|proxy| async move {
1771                // IP Lookup IPv4 and IPv6 for REMOTE_IPV4_HOST.
1772                assert_eq!(
1773                    proxy
1774                        .lookup_ip(
1775                            REMOTE_IPV4_HOST,
1776                            &fname::LookupIpOptions {
1777                                ipv4_lookup: Some(true),
1778                                ipv6_lookup: Some(true),
1779                                ..Default::default()
1780                            }
1781                        )
1782                        .await
1783                        .expect("lookup_ip"),
1784                    Ok(fname::LookupResult {
1785                        addresses: Some(vec![map_ip(IPV4_HOST)]),
1786                        ..Default::default()
1787                    }),
1788                );
1789
1790                // IP Lookup IPv4 for REMOTE_IPV4_HOST.
1791                assert_eq!(
1792                    proxy
1793                        .lookup_ip(
1794                            REMOTE_IPV4_HOST,
1795                            &fname::LookupIpOptions {
1796                                ipv4_lookup: Some(true),
1797                                ..Default::default()
1798                            }
1799                        )
1800                        .await
1801                        .expect("lookup_ip"),
1802                    Ok(fname::LookupResult {
1803                        addresses: Some(vec![map_ip(IPV4_HOST)]),
1804                        ..Default::default()
1805                    }),
1806                );
1807
1808                // IP Lookup IPv6 for REMOTE_IPV4_HOST.
1809                assert_eq!(
1810                    proxy
1811                        .lookup_ip(
1812                            REMOTE_IPV4_HOST,
1813                            &fname::LookupIpOptions {
1814                                ipv6_lookup: Some(true),
1815                                ..Default::default()
1816                            }
1817                        )
1818                        .await
1819                        .expect("lookup_ip"),
1820                    Err(fname::LookupError::NotFound),
1821                );
1822            })
1823            .await;
1824    }
1825
1826    #[fasync::run_singlethreaded(test)]
1827    async fn test_lookupip_remotehost_ipv6() {
1828        TestEnvironment::default()
1829            .run_lookup(|proxy| async move {
1830                // IP Lookup IPv4 and IPv6 for REMOTE_IPV6_HOST.
1831                assert_eq!(
1832                    proxy
1833                        .lookup_ip(
1834                            REMOTE_IPV6_HOST,
1835                            &fname::LookupIpOptions {
1836                                ipv4_lookup: Some(true),
1837                                ipv6_lookup: Some(true),
1838                                ..Default::default()
1839                            }
1840                        )
1841                        .await
1842                        .expect("lookup_ip"),
1843                    Ok(fname::LookupResult {
1844                        addresses: Some(vec![map_ip(IPV6_HOST)]),
1845                        ..Default::default()
1846                    }),
1847                );
1848
1849                // IP Lookup IPv4 for REMOTE_IPV6_HOST.
1850                assert_eq!(
1851                    proxy
1852                        .lookup_ip(
1853                            REMOTE_IPV6_HOST,
1854                            &fname::LookupIpOptions {
1855                                ipv4_lookup: Some(true),
1856                                ..Default::default()
1857                            }
1858                        )
1859                        .await
1860                        .expect("lookup_ip"),
1861                    Err(fname::LookupError::NotFound),
1862                );
1863
1864                // IP Lookup IPv6 for REMOTE_IPV4_HOST.
1865                assert_eq!(
1866                    proxy
1867                        .lookup_ip(
1868                            REMOTE_IPV6_HOST,
1869                            &fname::LookupIpOptions {
1870                                ipv6_lookup: Some(true),
1871                                ..Default::default()
1872                            }
1873                        )
1874                        .await
1875                        .expect("lookup_ip"),
1876                    Ok(fname::LookupResult {
1877                        addresses: Some(vec![map_ip(IPV6_HOST)]),
1878                        ..Default::default()
1879                    }),
1880                );
1881            })
1882            .await;
1883    }
1884
1885    #[test_case(REMOTE_IPV4_HOST_ALIAS, REMOTE_IPV4_HOST; "ipv4")]
1886    #[test_case(REMOTE_IPV6_HOST_ALIAS, REMOTE_IPV6_HOST; "ipv6")]
1887    #[fasync::run_singlethreaded(test)]
1888    async fn test_lookupip_remotehost_canonical_name(hostname: &str, expected: &str) {
1889        TestEnvironment::default()
1890            .run_lookup(|proxy| async move {
1891                assert_matches!(
1892                    proxy
1893                        .lookup_ip(
1894                            hostname,
1895                            &fname::LookupIpOptions {
1896                                canonical_name_lookup: Some(true),
1897                                ..Default::default()
1898                            }
1899                        )
1900                        .await,
1901                    Ok(Ok(fname::LookupResult {
1902                        canonical_name: Some(cname),
1903                        ..
1904                    })) => assert_eq!(cname, expected)
1905                );
1906            })
1907            .await;
1908    }
1909
1910    #[fasync::run_singlethreaded(test)]
1911    async fn test_lookupip_ip_literal() {
1912        TestEnvironment::default()
1913            .run_lookup(|proxy| async move {
1914                let proxy = &proxy;
1915
1916                let range = || [true, false].into_iter();
1917
1918                futures::stream::iter(range().cartesian_product(range()))
1919                    .for_each_concurrent(None, move |(ipv4_lookup, ipv6_lookup)| async move {
1920                        assert_eq!(
1921                            proxy
1922                                .lookup_ip(
1923                                    "240.0.0.2",
1924                                    &fname::LookupIpOptions {
1925                                        ipv4_lookup: Some(ipv4_lookup),
1926                                        ipv6_lookup: Some(ipv6_lookup),
1927                                        ..Default::default()
1928                                    }
1929                                )
1930                                .await
1931                                .expect("lookup_ip"),
1932                            Err(fname::LookupError::InvalidArgs),
1933                            "ipv4_lookup={},ipv6_lookup={}",
1934                            ipv4_lookup,
1935                            ipv6_lookup,
1936                        );
1937
1938                        assert_eq!(
1939                            proxy
1940                                .lookup_ip(
1941                                    "abcd::2",
1942                                    &fname::LookupIpOptions {
1943                                        ipv4_lookup: Some(ipv4_lookup),
1944                                        ipv6_lookup: Some(ipv6_lookup),
1945                                        ..Default::default()
1946                                    }
1947                                )
1948                                .await
1949                                .expect("lookup_ip"),
1950                            Err(fname::LookupError::InvalidArgs),
1951                            "ipv4_lookup={},ipv6_lookup={}",
1952                            ipv4_lookup,
1953                            ipv6_lookup,
1954                        );
1955                    })
1956                    .await
1957            })
1958            .await
1959    }
1960
1961    #[fasync::run_singlethreaded(test)]
1962    async fn test_lookup_hostname() {
1963        TestEnvironment::default()
1964            .run_lookup(|proxy| async move {
1965                assert_eq!(
1966                    proxy
1967                        .lookup_hostname(&map_ip(IPV4_HOST))
1968                        .await
1969                        .expect("lookup_hostname")
1970                        .as_deref(),
1971                    Ok(REMOTE_IPV4_HOST)
1972                );
1973            })
1974            .await;
1975    }
1976
1977    // Multiple hostnames returned from trust-dns* APIs, and only the first one will be returned
1978    // by the FIDL.
1979    #[fasync::run_singlethreaded(test)]
1980    async fn test_lookup_hostname_multi() {
1981        TestEnvironment::default()
1982            .run_lookup(|proxy| async move {
1983                assert_eq!(
1984                    proxy
1985                        .lookup_hostname(&map_ip(IPV6_HOST))
1986                        .await
1987                        .expect("lookup_hostname")
1988                        .as_deref(),
1989                    Ok(REMOTE_IPV6_HOST)
1990                );
1991            })
1992            .await;
1993    }
1994
1995    #[fasync::run_singlethreaded(test)]
1996    async fn test_set_server_names() {
1997        let env = TestEnvironment::default();
1998
1999        let to_server_configs = |socket_addr: SocketAddr| -> [NameServerConfig; 2] {
2000            [
2001                NameServerConfig {
2002                    socket_addr,
2003                    protocol: Protocol::Udp,
2004                    tls_dns_name: None,
2005                    trust_nx_responses: false,
2006                    bind_addr: None,
2007                    num_retained_errors: RETAINED_ERRORS_PER_NAME_SERVER,
2008                },
2009                NameServerConfig {
2010                    socket_addr,
2011                    protocol: Protocol::Tcp,
2012                    tls_dns_name: None,
2013                    trust_nx_responses: false,
2014                    bind_addr: None,
2015                    num_retained_errors: RETAINED_ERRORS_PER_NAME_SERVER,
2016                },
2017            ]
2018        };
2019
2020        // Assert that mock config has no servers originally.
2021        assert_eq!(env.shared_resolver.read().config.name_servers().to_vec(), vec![]);
2022
2023        // Set servers.
2024        env.run_admin(|proxy| async move {
2025            proxy
2026                .set_dns_servers(&[DHCP_SERVER, NDP_SERVER, DHCPV6_SERVER])
2027                .await
2028                .expect("Failed to call SetDnsServers")
2029                .expect("SetDnsServers error");
2030        })
2031        .await;
2032        assert_eq!(
2033            env.shared_resolver.read().config.name_servers().to_vec(),
2034            vec![DHCP_SERVER, NDP_SERVER, DHCPV6_SERVER]
2035                .into_iter()
2036                .map(|s| {
2037                    let net_ext::SocketAddress(s) = s.into();
2038                    s
2039                })
2040                .flat_map(|x| to_server_configs(x).to_vec().into_iter())
2041                .collect::<Vec<_>>()
2042        );
2043
2044        // Clear servers.
2045        env.run_admin(|proxy| async move {
2046            proxy
2047                .set_dns_servers(&[])
2048                .await
2049                .expect("Failed to call SetDnsServers")
2050                .expect("SetDnsServers error");
2051        })
2052        .await;
2053        assert_eq!(env.shared_resolver.read().config.name_servers().to_vec(), Vec::new());
2054    }
2055
2056    #[fasync::run_singlethreaded(test)]
2057    async fn test_set_server_names_error() {
2058        let env = TestEnvironment::default();
2059        // Assert that mock config has no servers originally.
2060        assert_eq!(env.shared_resolver.read().config.name_servers().to_vec(), vec![]);
2061
2062        env.run_admin(|proxy| async move {
2063            // Attempt to set bad addresses.
2064
2065            // Multicast not allowed.
2066            let status = proxy
2067                .set_dns_servers(&[fnet::SocketAddress::Ipv4(fnet::Ipv4SocketAddress {
2068                    address: fnet::Ipv4Address { addr: [224, 0, 0, 1] },
2069                    port: DEFAULT_PORT,
2070                })])
2071                .await
2072                .expect("Failed to call SetDnsServers")
2073                .expect_err("SetDnsServers should fail for multicast address");
2074            assert_eq!(zx::Status::from_raw(status), zx::Status::INVALID_ARGS);
2075
2076            // Unspecified not allowed.
2077            let status = proxy
2078                .set_dns_servers(&[fnet::SocketAddress::Ipv6(fnet::Ipv6SocketAddress {
2079                    address: fnet::Ipv6Address { addr: [0; 16] },
2080                    port: DEFAULT_PORT,
2081                    zone_index: 0,
2082                })])
2083                .await
2084                .expect("Failed to call SetDnsServers")
2085                .expect_err("SetDnsServers should fail for unspecified address");
2086            assert_eq!(zx::Status::from_raw(status), zx::Status::INVALID_ARGS);
2087        })
2088        .await;
2089
2090        // Assert that config didn't change.
2091        assert_eq!(env.shared_resolver.read().config.name_servers().to_vec(), vec![]);
2092    }
2093
2094    #[fasync::run_singlethreaded(test)]
2095    async fn test_get_servers() {
2096        let env = TestEnvironment::default();
2097        env.run_admin(|proxy| async move {
2098            let expect = &[NDP_SERVER, DHCP_SERVER, DHCPV6_SERVER, STATIC_SERVER];
2099            proxy.set_dns_servers(expect).await.expect("FIDL error").expect("set_servers failed");
2100            assert_matches!(proxy.get_dns_servers().await, Ok(got) if got == expect);
2101        })
2102        .await;
2103    }
2104
2105    #[fasync::run_singlethreaded(test)]
2106    async fn test_config_inspect() {
2107        let env = TestEnvironment::default();
2108        let inspector = fuchsia_inspect::Inspector::default();
2109        let _config_state_node =
2110            add_config_state_inspect(inspector.root(), env.config_state.clone());
2111        assert_data_tree!(inspector, root:{
2112            servers: {}
2113        });
2114        env.run_admin(|proxy| async move {
2115            let servers = &[NDP_SERVER, DHCP_SERVER, DHCPV6_SERVER, STATIC_SERVER];
2116            proxy.set_dns_servers(servers).await.expect("FIDL error").expect("set_servers failed");
2117        })
2118        .await;
2119        assert_data_tree!(inspector, root:{
2120            servers: {
2121                "0": {
2122                    address: "[2001:4860:4860::4444%2]:53",
2123                },
2124                "1": {
2125                    address: "8.8.4.4:53",
2126                },
2127                "2": {
2128                    address: "[2002:4860:4860::4444%3]:53",
2129                },
2130                "3": {
2131                    address: "8.8.8.8:53",
2132                },
2133            }
2134        });
2135    }
2136
2137    #[fasync::run_singlethreaded(test)]
2138    async fn test_name_server_stats_inspect() {
2139        let env = TestEnvironment::default();
2140        let inspector = fuchsia_inspect::Inspector::default();
2141        let _name_server_stats_node =
2142            add_name_server_stats_inspect(inspector.root(), env.shared_resolver.clone());
2143        assert_data_tree!(inspector, root:{
2144            name_servers: {}
2145        });
2146
2147        let addr = std_socket_addr!("1.2.3.4:53");
2148        let stats = NameServerStats {
2149            addr,
2150            proto: Protocol::Udp,
2151            failures: 1,
2152            successes: 2,
2153            success_streak: 0,
2154            recent_errors: vec![ResolveErrorKind::Timeout.into()],
2155        };
2156        env.shared_resolver.write(Arc::new(MockResolver {
2157            config: ResolverConfig::default(),
2158            repeat: 1,
2159            name_server_stats: vec![stats],
2160        }));
2161
2162        assert_data_tree!(inspector, root:{
2163            name_servers: {
2164                "0": {
2165                    address: "1.2.3.4:53",
2166                    protocol: "Udp",
2167                    successful_queries: 2u64,
2168                    failed_queries: 1u64,
2169                    success_streak: 0u64,
2170                    errors: {
2171                        Timeout: 1u64,
2172                        Message: 0u64,
2173                        NoConnections: 0u64,
2174                        IoErrorCounts: {},
2175                        ProtoErrorCounts: {},
2176                        NoRecordsFoundResponseCodeCounts: {},
2177                        UnhandledResolveErrorKindCounts: {},
2178                    }
2179                }
2180            }
2181        });
2182    }
2183
2184    #[test]
2185    fn test_unhandled_resolve_error_kind_stats() {
2186        use ResolveErrorKind::{Msg, Timeout};
2187        let mut unhandled_resolve_error_kind_stats = GenericErrorKindStats::default();
2188        assert_eq!(
2189            unhandled_resolve_error_kind_stats.increment(&Msg(String::from("abcdefgh"))),
2190            "Msg"
2191        );
2192        assert_eq!(
2193            unhandled_resolve_error_kind_stats.increment(&Msg(String::from("ijklmn"))),
2194            "Msg"
2195        );
2196        assert_eq!(unhandled_resolve_error_kind_stats.increment(&Timeout), "Timeout");
2197        assert_eq!(
2198            unhandled_resolve_error_kind_stats,
2199            GenericErrorKindStats([(String::from("Msg"), 2), (String::from("Timeout"), 1)].into())
2200        )
2201    }
2202
2203    #[fasync::run_singlethreaded(test)]
2204    async fn test_query_stats_updated() {
2205        let env = TestEnvironment::default();
2206        let inspector = fuchsia_inspect::Inspector::default();
2207        let _query_stats_inspect_node =
2208            add_query_stats_inspect(inspector.root(), env.stats.clone());
2209        assert_data_tree!(inspector, root:{
2210            query_stats: {}
2211        });
2212
2213        env.run_lookup(|proxy| async move {
2214            // IP Lookup IPv4 for REMOTE_IPV4_HOST.
2215            assert_eq!(
2216                proxy
2217                    .lookup_ip(
2218                        REMOTE_IPV4_HOST,
2219                        &fname::LookupIpOptions { ipv4_lookup: Some(true), ..Default::default() }
2220                    )
2221                    .await
2222                    .expect("lookup_ip"),
2223                Ok(fname::LookupResult {
2224                    addresses: Some(vec![map_ip(IPV4_HOST)]),
2225                    ..Default::default()
2226                }),
2227            );
2228        })
2229        .await;
2230        env.run_lookup(|proxy| async move {
2231            // IP Lookup IPv6 for REMOTE_IPV4_HOST.
2232            assert_eq!(
2233                proxy
2234                    .lookup_ip(
2235                        REMOTE_IPV4_HOST,
2236                        &fname::LookupIpOptions { ipv6_lookup: Some(true), ..Default::default() }
2237                    )
2238                    .await
2239                    .expect("lookup_ip"),
2240                Err(fname::LookupError::NotFound),
2241            );
2242        })
2243        .await;
2244        assert_data_tree!(inspector, root:{
2245            query_stats: {
2246                "window 1": {
2247                    start_time_nanos: NonZeroUintProperty,
2248                    successful_queries: 1u64,
2249                    failed_queries: 1u64,
2250                    average_success_duration_micros: NonZeroUintProperty,
2251                    average_failure_duration_micros: NonZeroUintProperty,
2252                    errors: {
2253                        Message: 0u64,
2254                        NoConnections: 0u64,
2255                        NoRecordsFoundResponseCodeCounts: {
2256                            NoError: 1u64,
2257                        },
2258                        IoErrorCounts: {},
2259                        ProtoErrorCounts: {},
2260                        Timeout: 0u64,
2261                        UnhandledResolveErrorKindCounts: {},
2262                    },
2263                    address_counts: {
2264                        "1": 1u64,
2265                    },
2266                },
2267            }
2268        });
2269    }
2270
2271    fn run_fake_lookup(
2272        exec: &mut fasync::TestExecutor,
2273        stats: Arc<QueryStats>,
2274        result: QueryResult<'_>,
2275        delay: zx::MonotonicDuration,
2276    ) {
2277        let start_time = fasync::MonotonicInstant::now();
2278        exec.set_fake_time(fasync::MonotonicInstant::after(delay));
2279        let update_stats = stats.finish_query(start_time, result);
2280        let mut update_stats = pin!(update_stats);
2281        assert!(exec.run_until_stalled(&mut update_stats).is_ready());
2282    }
2283
2284    // Safety: This is safe because the initial value is not zero.
2285    const NON_ZERO_USIZE_ONE: NonZeroUsize = NonZeroUsize::new(1).unwrap();
2286
2287    #[test]
2288    fn test_query_stats_inspect_average() {
2289        let mut exec = fasync::TestExecutor::new_with_fake_time();
2290        const START_NANOS: i64 = 1_234_567;
2291        exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2292
2293        let stats = Arc::new(QueryStats::new());
2294        let inspector = fuchsia_inspect::Inspector::default();
2295        let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2296        const SUCCESSFUL_QUERY_COUNT: u64 = 10;
2297        const SUCCESSFUL_QUERY_DURATION: zx::MonotonicDuration =
2298            zx::MonotonicDuration::from_seconds(30);
2299        for _ in 0..SUCCESSFUL_QUERY_COUNT / 2 {
2300            run_fake_lookup(
2301                &mut exec,
2302                stats.clone(),
2303                Ok(/*addresses*/ NON_ZERO_USIZE_ONE),
2304                zx::MonotonicDuration::from_nanos(0),
2305            );
2306            run_fake_lookup(
2307                &mut exec,
2308                stats.clone(),
2309                Ok(/*addresses*/ NON_ZERO_USIZE_ONE),
2310                SUCCESSFUL_QUERY_DURATION,
2311            );
2312            exec.set_fake_time(fasync::MonotonicInstant::after(
2313                STAT_WINDOW_DURATION - SUCCESSFUL_QUERY_DURATION,
2314            ));
2315        }
2316        let mut expected = tree_assertion!(query_stats: {});
2317        for i in 0..SUCCESSFUL_QUERY_COUNT / 2 {
2318            let name = &format!("window {}", i + 1);
2319            let child = tree_assertion!(var name: {
2320                start_time_nanos: u64::try_from(
2321                    START_NANOS + STAT_WINDOW_DURATION.into_nanos() * i64::try_from(i).unwrap()
2322                ).unwrap(),
2323                successful_queries: 2u64,
2324                failed_queries: 0u64,
2325                average_success_duration_micros: u64::try_from(
2326                    SUCCESSFUL_QUERY_DURATION.into_micros()
2327                ).unwrap() / 2,
2328                errors: {
2329                    Message: 0u64,
2330                    NoConnections: 0u64,
2331                    NoRecordsFoundResponseCodeCounts: {},
2332                    IoErrorCounts: {},
2333                    ProtoErrorCounts: {},
2334                    Timeout: 0u64,
2335                    UnhandledResolveErrorKindCounts: {},
2336                },
2337                address_counts: {
2338                    "1": 2u64,
2339                },
2340            });
2341            expected.add_child_assertion(child);
2342        }
2343        assert_data_tree!(@executor exec, inspector, root: {
2344            expected,
2345        });
2346    }
2347
2348    #[test]
2349    fn test_query_stats_inspect_error_counters() {
2350        let mut exec = fasync::TestExecutor::new_with_fake_time();
2351        const START_NANOS: i64 = 1_234_567;
2352        exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2353
2354        let stats = Arc::new(QueryStats::new());
2355        let inspector = fuchsia_inspect::Inspector::default();
2356        let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2357        const FAILED_QUERY_COUNT: u64 = 10;
2358        const FAILED_QUERY_DURATION: zx::MonotonicDuration =
2359            zx::MonotonicDuration::from_millis(500);
2360        for _ in 0..FAILED_QUERY_COUNT {
2361            run_fake_lookup(
2362                &mut exec,
2363                stats.clone(),
2364                Err(&ResolveErrorKind::Timeout),
2365                FAILED_QUERY_DURATION,
2366            );
2367        }
2368        assert_data_tree!(@executor exec, inspector, root:{
2369            query_stats: {
2370                "window 1": {
2371                    start_time_nanos: u64::try_from(
2372                        START_NANOS + FAILED_QUERY_DURATION.into_nanos()
2373                    ).unwrap(),
2374                    successful_queries: 0u64,
2375                    failed_queries: FAILED_QUERY_COUNT,
2376                    average_failure_duration_micros: u64::try_from(
2377                        FAILED_QUERY_DURATION.into_micros()
2378                    ).unwrap(),
2379                    errors: {
2380                        Message: 0u64,
2381                        NoConnections: 0u64,
2382                        NoRecordsFoundResponseCodeCounts: {},
2383                        IoErrorCounts: {},
2384                        ProtoErrorCounts: {},
2385                        Timeout: FAILED_QUERY_COUNT,
2386                        UnhandledResolveErrorKindCounts: {},
2387                    },
2388                    address_counts: {},
2389                },
2390            }
2391        });
2392    }
2393
2394    #[test]
2395    fn test_query_stats_inspect_no_records_found() {
2396        let mut exec = fasync::TestExecutor::new_with_fake_time();
2397        const START_NANOS: i64 = 1_234_567;
2398        exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2399
2400        let stats = Arc::new(QueryStats::new());
2401        let inspector = fuchsia_inspect::Inspector::default();
2402        let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2403        const FAILED_QUERY_COUNT: u64 = 10;
2404        const FAILED_QUERY_DURATION: zx::MonotonicDuration =
2405            zx::MonotonicDuration::from_millis(500);
2406
2407        let mut run_fake_no_records_lookup = |response_code: ResponseCode| {
2408            run_fake_lookup(
2409                &mut exec,
2410                stats.clone(),
2411                Err(&ResolveErrorKind::NoRecordsFound {
2412                    query: Box::new(Query::default()),
2413                    soa: None,
2414                    negative_ttl: None,
2415                    response_code,
2416                    trusted: false,
2417                }),
2418                FAILED_QUERY_DURATION,
2419            )
2420        };
2421
2422        for _ in 0..FAILED_QUERY_COUNT {
2423            run_fake_no_records_lookup(ResponseCode::NXDomain);
2424            run_fake_no_records_lookup(ResponseCode::Refused);
2425            run_fake_no_records_lookup(4096.into());
2426            run_fake_no_records_lookup(4097.into());
2427        }
2428
2429        assert_data_tree!(@executor exec, inspector, root:{
2430            query_stats: {
2431                "window 1": {
2432                    start_time_nanos: u64::try_from(
2433                        START_NANOS + FAILED_QUERY_DURATION.into_nanos()
2434                    ).unwrap(),
2435                    successful_queries: 0u64,
2436                    failed_queries: FAILED_QUERY_COUNT * 4,
2437                    average_failure_duration_micros: u64::try_from(
2438                        FAILED_QUERY_DURATION.into_micros()
2439                    ).unwrap(),
2440                    errors: {
2441                        Message: 0u64,
2442                        NoConnections: 0u64,
2443                        NoRecordsFoundResponseCodeCounts: {
2444                          NXDomain: FAILED_QUERY_COUNT,
2445                          Refused: FAILED_QUERY_COUNT,
2446                          "Unknown(4096)": FAILED_QUERY_COUNT,
2447                          "Unknown(4097)": FAILED_QUERY_COUNT,
2448                        },
2449                        IoErrorCounts: {},
2450                        ProtoErrorCounts: {},
2451                        Timeout: 0u64,
2452                        UnhandledResolveErrorKindCounts: {},
2453                    },
2454                    address_counts: {},
2455                },
2456            }
2457        });
2458    }
2459
2460    #[test]
2461    fn test_query_stats_resolved_address_counts() {
2462        let mut exec = fasync::TestExecutor::new_with_fake_time();
2463        const START_NANOS: i64 = 1_234_567;
2464        exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2465
2466        let stats = Arc::new(QueryStats::new());
2467        let inspector = fuchsia_inspect::Inspector::default();
2468        let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2469
2470        // Create some test data to run fake lookups. Simulate a histogram with:
2471        //  - 99 occurrences of a response with 1 address,
2472        //  - 98 occurrences of a response with 2 addresses,
2473        //  - ...
2474        //  - 1 occurrence of a response with 99 addresses.
2475        let address_counts: HashMap<usize, _> = (1..100).zip((1..100).rev()).collect();
2476        const QUERY_DURATION: zx::MonotonicDuration = zx::MonotonicDuration::from_millis(10);
2477        for (count, occurrences) in address_counts.iter() {
2478            for _ in 0..*occurrences {
2479                run_fake_lookup(
2480                    &mut exec,
2481                    stats.clone(),
2482                    Ok(NonZeroUsize::new(*count).expect("address count must be greater than zero")),
2483                    QUERY_DURATION,
2484                );
2485            }
2486        }
2487
2488        let mut expected_address_counts = tree_assertion!(address_counts: {});
2489        for (count, occurrences) in address_counts.iter() {
2490            expected_address_counts
2491                .add_property_assertion(&count.to_string(), Arc::new(*occurrences));
2492        }
2493        assert_data_tree!(@executor exec, inspector, root: {
2494            query_stats: {
2495                "window 1": {
2496                    start_time_nanos: u64::try_from(
2497                        START_NANOS + QUERY_DURATION.into_nanos()
2498                    ).unwrap(),
2499                    successful_queries: address_counts.values().sum::<u64>(),
2500                    failed_queries: 0u64,
2501                    average_success_duration_micros: u64::try_from(
2502                        QUERY_DURATION.into_micros()
2503                    ).unwrap(),
2504                    errors: {
2505                        Message: 0u64,
2506                        NoConnections: 0u64,
2507                        NoRecordsFoundResponseCodeCounts: {},
2508                        IoErrorCounts: {},
2509                        ProtoErrorCounts: {},
2510                        Timeout: 0u64,
2511                        UnhandledResolveErrorKindCounts: {},
2512                    },
2513                    expected_address_counts,
2514                },
2515            },
2516        });
2517    }
2518
2519    #[test]
2520    fn test_query_stats_inspect_oldest_stats_erased() {
2521        let mut exec = fasync::TestExecutor::new_with_fake_time();
2522        const START_NANOS: i64 = 1_234_567;
2523        exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2524
2525        let stats = Arc::new(QueryStats::new());
2526        let inspector = fuchsia_inspect::Inspector::default();
2527        let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2528        const DELAY: zx::MonotonicDuration = zx::MonotonicDuration::from_millis(100);
2529        for _ in 0..STAT_WINDOW_COUNT {
2530            let () =
2531                run_fake_lookup(&mut exec, stats.clone(), Err(&ResolveErrorKind::Timeout), DELAY);
2532            let () =
2533                exec.set_fake_time(fasync::MonotonicInstant::after(STAT_WINDOW_DURATION - DELAY));
2534        }
2535        for _ in 0..STAT_WINDOW_COUNT {
2536            run_fake_lookup(&mut exec, stats.clone(), Ok(/*addresses*/ NON_ZERO_USIZE_ONE), DELAY);
2537            let () =
2538                exec.set_fake_time(fasync::MonotonicInstant::after(STAT_WINDOW_DURATION - DELAY));
2539        }
2540        // All the failed queries should be erased from the stats as they are
2541        // now out of date.
2542        let mut expected = tree_assertion!(query_stats: {});
2543        let start_offset = START_NANOS
2544            + DELAY.into_nanos()
2545            + STAT_WINDOW_DURATION.into_nanos() * i64::try_from(STAT_WINDOW_COUNT).unwrap();
2546        for i in 0..STAT_WINDOW_COUNT {
2547            let name = &format!("window {}", i + 1);
2548            let child = tree_assertion!(var name: {
2549                start_time_nanos: u64::try_from(
2550                    start_offset + STAT_WINDOW_DURATION.into_nanos() * i64::try_from(i).unwrap()
2551                ).unwrap(),
2552                successful_queries: 1u64,
2553                failed_queries: 0u64,
2554                average_success_duration_micros: u64::try_from(DELAY.into_micros()).unwrap(),
2555                errors: {
2556                    Message: 0u64,
2557                    NoConnections: 0u64,
2558                    NoRecordsFoundResponseCodeCounts: {},
2559                    IoErrorCounts: {},
2560                    ProtoErrorCounts: {},
2561                    Timeout: 0u64,
2562                    UnhandledResolveErrorKindCounts: {},
2563                },
2564                address_counts: {
2565                    "1": 1u64,
2566                },
2567            });
2568            expected.add_child_assertion(child);
2569        }
2570        assert_data_tree!(@executor exec, inspector, root: {
2571            expected,
2572        });
2573    }
2574
2575    struct BlockingResolver {}
2576
2577    impl ResolverLookup for BlockingResolver {
2578        fn new(_config: ResolverConfig, _options: ResolverOpts) -> Self {
2579            BlockingResolver {}
2580        }
2581
2582        async fn lookup<N: IntoName + Send>(
2583            &self,
2584            _name: N,
2585            _record_type: RecordType,
2586        ) -> Result<lookup::Lookup, ResolveError> {
2587            futures::future::pending().await
2588        }
2589
2590        async fn reverse_lookup(
2591            &self,
2592            _addr: IpAddr,
2593        ) -> Result<lookup::ReverseLookup, ResolveError> {
2594            panic!("BlockingResolver does not handle reverse lookup")
2595        }
2596    }
2597
2598    #[fasync::run_singlethreaded(test)]
2599    async fn test_parallel_query_limit() {
2600        // Collect requests by setting up a FIDL proxy and stream for the Lookup
2601        // protocol, because there isn't a good way to directly construct fake
2602        // requests to be used for testing.
2603        let requests = {
2604            let (name_lookup_proxy, name_lookup_stream) =
2605                fidl::endpoints::create_proxy_and_stream::<fname::LookupMarker>();
2606            const NUM_REQUESTS: usize = MAX_PARALLEL_REQUESTS * 2 + 2;
2607            for _ in 0..NUM_REQUESTS {
2608                // Don't await on this future because we are using these
2609                // requests to collect FIDL responders in order to send test
2610                // requests later, and will not respond to these requests.
2611                let _: fidl::client::QueryResponseFut<fname::LookupLookupIpResult> =
2612                    name_lookup_proxy.lookup_ip(
2613                        LOCAL_HOST,
2614                        &fname::LookupIpOptions {
2615                            ipv4_lookup: Some(true),
2616                            ipv6_lookup: Some(true),
2617                            ..Default::default()
2618                        },
2619                    );
2620            }
2621            // Terminate the stream so its items can be collected below.
2622            drop(name_lookup_proxy);
2623            let requests = name_lookup_stream
2624                .map(|request| match request.expect("channel error") {
2625                    LookupRequest::LookupIp { hostname, options, responder } => {
2626                        IpLookupRequest { hostname, options, responder }
2627                    }
2628                    req => panic!("Expected LookupRequest::LookupIp request, found {:?}", req),
2629                })
2630                .collect::<Vec<_>>()
2631                .await;
2632            assert_eq!(requests.len(), NUM_REQUESTS);
2633            requests
2634        };
2635
2636        let (mut sender, recv) = mpsc::channel(MAX_PARALLEL_REQUESTS);
2637
2638        // The channel's capacity is equal to buffer + num-senders. Thus the
2639        // channel has a capacity of `MAX_PARALLEL_REQUESTS` + 1, and the
2640        // `for_each_concurrent` future has a limit of `MAX_PARALLEL_REQUESTS`,
2641        // so the sender should be able to queue `MAX_PARALLEL_REQUESTS` * 2 + 1
2642        // requests before `send` fails.
2643        const BEFORE_LAST_INDEX: usize = MAX_PARALLEL_REQUESTS * 2;
2644        const LAST_INDEX: usize = MAX_PARALLEL_REQUESTS * 2 + 1;
2645        let mut send_fut = pin!(
2646            async {
2647                for (i, req) in requests.into_iter().enumerate() {
2648                    match i {
2649                        BEFORE_LAST_INDEX => assert_matches!(sender.try_send(req), Ok(())),
2650                        LAST_INDEX => assert_matches!(sender.try_send(req), Err(e) if e.is_full()),
2651                        _ => assert_matches!(sender.send(req).await, Ok(())),
2652                    }
2653                }
2654            }
2655            .fuse()
2656        );
2657        let mut recv_fut = pin!({
2658            let resolver = SharedResolver::new(BlockingResolver::new(
2659                ResolverConfig::default(),
2660                ResolverOpts::default(),
2661            ));
2662            let stats = Arc::new(QueryStats::new());
2663            let (routes_proxy, _routes_stream) =
2664                fidl::endpoints::create_proxy_and_stream::<fnet_routes::StateMarker>();
2665            async move { create_ip_lookup_fut(&resolver, stats.clone(), routes_proxy, recv).await }
2666                .fuse()
2667        });
2668        futures::select! {
2669            () = send_fut => {},
2670            () = recv_fut => panic!("recv_fut should never complete"),
2671        };
2672    }
2673
2674    #[test]
2675    fn test_failure_stats() {
2676        use anyhow::anyhow;
2677        use trust_dns_proto::error::ProtoError;
2678        use trust_dns_proto::op::Query;
2679
2680        let mut stats = FailureStats::default();
2681        for (error_kind, expected) in &[
2682            (ResolveErrorKind::Message("foo"), FailureStats { message: 1, ..Default::default() }),
2683            (
2684                ResolveErrorKind::Msg("foo".to_string()),
2685                FailureStats { message: 2, ..Default::default() },
2686            ),
2687            (
2688                ResolveErrorKind::NoRecordsFound {
2689                    query: Box::new(Query::default()),
2690                    soa: None,
2691                    negative_ttl: None,
2692                    response_code: ResponseCode::Refused,
2693                    trusted: false,
2694                },
2695                FailureStats {
2696                    message: 2,
2697                    no_records_found: NoRecordsFoundStats {
2698                        response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2699                    },
2700                    ..Default::default()
2701                },
2702            ),
2703            (
2704                ResolveErrorKind::Io(std::io::Error::new(
2705                    std::io::ErrorKind::NotFound,
2706                    anyhow!("foo"),
2707                )),
2708                FailureStats {
2709                    message: 2,
2710                    no_records_found: NoRecordsFoundStats {
2711                        response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2712                    },
2713                    io: IoErrorStats([(std::io::ErrorKind::NotFound, 1)].into()),
2714                    ..Default::default()
2715                },
2716            ),
2717            (
2718                ResolveErrorKind::Proto(ProtoError::from("foo")),
2719                FailureStats {
2720                    message: 2,
2721                    no_records_found: NoRecordsFoundStats {
2722                        response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2723                    },
2724                    io: IoErrorStats([(std::io::ErrorKind::NotFound, 1)].into()),
2725                    proto: GenericErrorKindStats([(String::from("Message"), 1)].into()),
2726                    ..Default::default()
2727                },
2728            ),
2729            (
2730                ResolveErrorKind::NoConnections,
2731                FailureStats {
2732                    message: 2,
2733                    no_connections: 1,
2734                    no_records_found: NoRecordsFoundStats {
2735                        response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2736                    },
2737                    io: IoErrorStats([(std::io::ErrorKind::NotFound, 1)].into()),
2738                    proto: GenericErrorKindStats([(String::from("Message"), 1)].into()),
2739                    ..Default::default()
2740                },
2741            ),
2742            (
2743                ResolveErrorKind::Timeout,
2744                FailureStats {
2745                    message: 2,
2746                    no_connections: 1,
2747                    no_records_found: NoRecordsFoundStats {
2748                        response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2749                    },
2750                    io: IoErrorStats([(std::io::ErrorKind::NotFound, 1)].into()),
2751                    proto: GenericErrorKindStats([(String::from("Message"), 1)].into()),
2752                    timeout: 1,
2753                    unhandled_resolve_error_kind: Default::default(),
2754                },
2755            ),
2756            (
2757                ResolveErrorKind::NoRecordsFound {
2758                    query: Box::new(Query::default()),
2759                    soa: None,
2760                    negative_ttl: None,
2761                    response_code: ResponseCode::NXDomain,
2762                    trusted: false,
2763                },
2764                FailureStats {
2765                    message: 2,
2766                    no_connections: 1,
2767                    no_records_found: NoRecordsFoundStats {
2768                        response_code_counts: [
2769                            (ResponseCode::NXDomain.into(), 1),
2770                            (ResponseCode::Refused.into(), 1),
2771                        ]
2772                        .into(),
2773                    },
2774                    io: IoErrorStats([(std::io::ErrorKind::NotFound, 1)].into()),
2775                    proto: GenericErrorKindStats([(String::from("Message"), 1)].into()),
2776                    timeout: 1,
2777                    unhandled_resolve_error_kind: Default::default(),
2778                },
2779            ),
2780            (
2781                ResolveErrorKind::NoRecordsFound {
2782                    query: Box::new(Query::default()),
2783                    soa: None,
2784                    negative_ttl: None,
2785                    response_code: ResponseCode::NXDomain,
2786                    trusted: false,
2787                },
2788                FailureStats {
2789                    message: 2,
2790                    no_connections: 1,
2791                    no_records_found: NoRecordsFoundStats {
2792                        response_code_counts: [
2793                            (ResponseCode::NXDomain.into(), 2),
2794                            (ResponseCode::Refused.into(), 1),
2795                        ]
2796                        .into(),
2797                    },
2798                    io: IoErrorStats([(std::io::ErrorKind::NotFound, 1)].into()),
2799                    proto: GenericErrorKindStats([(String::from("Message"), 1)].into()),
2800                    timeout: 1,
2801                    unhandled_resolve_error_kind: Default::default(),
2802                },
2803            ),
2804            (
2805                ResolveErrorKind::Proto(ProtoError::from(std::io::Error::new(
2806                    std::io::ErrorKind::ConnectionAborted,
2807                    anyhow!("foo"),
2808                ))),
2809                FailureStats {
2810                    message: 2,
2811                    no_connections: 1,
2812                    no_records_found: NoRecordsFoundStats {
2813                        response_code_counts: [
2814                            (ResponseCode::NXDomain.into(), 2),
2815                            (ResponseCode::Refused.into(), 1),
2816                        ]
2817                        .into(),
2818                    },
2819                    io: IoErrorStats(
2820                        [
2821                            (std::io::ErrorKind::NotFound, 1),
2822                            (std::io::ErrorKind::ConnectionAborted, 1),
2823                        ]
2824                        .into(),
2825                    ),
2826                    proto: GenericErrorKindStats([(String::from("Message"), 1)].into()),
2827                    timeout: 1,
2828                    unhandled_resolve_error_kind: Default::default(),
2829                },
2830            ),
2831        ][..]
2832        {
2833            stats.increment(error_kind);
2834            assert_eq!(&stats, expected, "invalid stats after incrementing with {:?}", error_kind);
2835        }
2836    }
2837
2838    fn test_das_helper(
2839        l_addr: fnet::IpAddress,
2840        l_src: Option<fnet::IpAddress>,
2841        r_addr: fnet::IpAddress,
2842        r_src: Option<fnet::IpAddress>,
2843        want: std::cmp::Ordering,
2844    ) {
2845        let left = DasCmpInfo::from_addrs(&l_addr, l_src.as_ref());
2846        let right = DasCmpInfo::from_addrs(&r_addr, r_src.as_ref());
2847        assert_eq!(
2848            left.cmp(&right),
2849            want,
2850            "want = {:?}\n left = {:?}({:?}) DAS={:?}\n right = {:?}({:?}) DAS={:?}",
2851            want,
2852            l_addr,
2853            l_src,
2854            left,
2855            r_addr,
2856            r_src,
2857            right
2858        );
2859    }
2860
2861    macro_rules! add_das_test {
2862        ($name:ident, preferred: $pref_dst:expr => $pref_src:expr, other: $other_dst:expr => $other_src:expr) => {
2863            #[test]
2864            fn $name() {
2865                test_das_helper(
2866                    $pref_dst,
2867                    $pref_src,
2868                    $other_dst,
2869                    $other_src,
2870                    std::cmp::Ordering::Less,
2871                )
2872            }
2873        };
2874    }
2875
2876    add_das_test!(
2877        prefer_reachable,
2878        preferred: fidl_ip!("198.51.100.121") => Some(fidl_ip!("198.51.100.117")),
2879        other: fidl_ip!("2001:db8:1::1") => Option::<fnet::IpAddress>::None
2880    );
2881
2882    // These test cases are taken from RFC 6724, section 10.2.
2883
2884    add_das_test!(
2885        prefer_matching_scope,
2886        preferred: fidl_ip!("198.51.100.121") => Some(fidl_ip!("198.51.100.117")),
2887        other: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("fe80::1"))
2888    );
2889
2890    add_das_test!(
2891        prefer_matching_label,
2892        preferred: fidl_ip!("2002:c633:6401::1") => Some(fidl_ip!("2002:c633:6401::2")),
2893        other:  fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2002:c633:6401::2"))
2894    );
2895
2896    add_das_test!(
2897        prefer_higher_precedence_1,
2898        preferred: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2001:db8:1::2")),
2899        other: fidl_ip!("10.1.2.3") => Some(fidl_ip!("10.1.2.4"))
2900    );
2901
2902    add_das_test!(
2903        prefer_higher_precedence_2,
2904        preferred: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2001:db8:1::2")),
2905        other: fidl_ip!("2002:c633:6401::1") => Some(fidl_ip!("2002:c633:6401::2"))
2906    );
2907
2908    add_das_test!(
2909        prefer_smaller_scope,
2910        preferred: fidl_ip!("fe80::1") => Some(fidl_ip!("fe80::2")),
2911        other: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2001:db8:1::2"))
2912    );
2913
2914    add_das_test!(
2915        prefer_longest_matching_prefix,
2916        preferred: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2001:db8:1::2")),
2917        other: fidl_ip!("2001:db8:3ffe::1") => Some(fidl_ip!("2001:db8:3f44::2"))
2918    );
2919
2920    #[test]
2921    fn test_das_equals() {
2922        for (dst, src) in [
2923            (fidl_ip!("192.168.0.1"), fidl_ip!("192.168.0.2")),
2924            (fidl_ip!("2001:db8::1"), fidl_ip!("2001:db8::2")),
2925        ]
2926        .iter()
2927        {
2928            test_das_helper(*dst, None, *dst, None, std::cmp::Ordering::Equal);
2929            test_das_helper(*dst, Some(*src), *dst, Some(*src), std::cmp::Ordering::Equal);
2930        }
2931    }
2932
2933    #[test]
2934    fn test_valid_policy_table() {
2935        // Last element in policy table MUST be ::/0.
2936        assert_eq!(
2937            POLICY_TABLE.iter().last().expect("empty policy table").prefix,
2938            net_types::ip::Subnet::new(net_types::ip::Ipv6::UNSPECIFIED_ADDRESS, 0)
2939                .expect("invalid subnet")
2940        );
2941        // Policy table must be sorted by prefix length.
2942        POLICY_TABLE.array_windows().for_each(|[w0, w1]| {
2943            let Policy { prefix: cur, precedence: _, label: _ } = w0;
2944            let Policy { prefix: nxt, precedence: _, label: _ } = w1;
2945            assert!(
2946                cur.prefix() >= nxt.prefix(),
2947                "bad ordering of prefixes, {} must come after {}",
2948                cur,
2949                nxt
2950            )
2951        });
2952        // Assert that POLICY_TABLE declaration does not use any invalid
2953        // subnets.
2954        for policy in POLICY_TABLE.iter() {
2955            assert!(policy.prefix.prefix() <= 128, "Invalid subnet in policy {:?}", policy);
2956        }
2957    }
2958
2959    #[fasync::run_singlethreaded(test)]
2960    async fn test_sort_preferred_addresses() {
2961        const TEST_IPS: [(fnet::IpAddress, Option<fnet::IpAddress>); 5] = [
2962            (fidl_ip!("127.0.0.1"), Some(fidl_ip!("127.0.0.1"))),
2963            (fidl_ip!("::1"), Some(fidl_ip!("::1"))),
2964            (fidl_ip!("192.168.50.22"), None),
2965            (fidl_ip!("2001::2"), None),
2966            (fidl_ip!("2001:db8:1::1"), Some(fidl_ip!("2001:db8:1::2"))),
2967        ];
2968        // Declared using std types so we get cleaner output when we assert
2969        // expectations.
2970        const SORTED: [IpAddr; 5] = [
2971            std_ip!("::1"),
2972            std_ip!("2001:db8:1::1"),
2973            std_ip!("127.0.0.1"),
2974            std_ip!("192.168.50.22"),
2975            std_ip!("2001::2"),
2976        ];
2977        let (routes_proxy, routes_stream) =
2978            fidl::endpoints::create_proxy_and_stream::<fnet_routes::StateMarker>();
2979        let routes_fut =
2980            routes_stream.map(|r| r.context("stream FIDL error")).try_for_each(|req| {
2981                let (destination, responder) = assert_matches!(
2982                    req,
2983                    fnet_routes::StateRequest::Resolve { destination, responder }
2984                        => (destination, responder)
2985                );
2986                let result = TEST_IPS
2987                    .iter()
2988                    .enumerate()
2989                    .find_map(|(i, (dst, src))| {
2990                        if *dst == destination && src.is_some() {
2991                            let inner = fnet_routes::Destination {
2992                                address: Some(*dst),
2993                                source_address: *src,
2994                                ..Default::default()
2995                            };
2996                            // Send both Direct and Gateway resolved routes to show we
2997                            // don't care about that part.
2998                            if i % 2 == 0 {
2999                                Some(fnet_routes::Resolved::Direct(inner))
3000                            } else {
3001                                Some(fnet_routes::Resolved::Gateway(inner))
3002                            }
3003                        } else {
3004                            None
3005                        }
3006                    })
3007                    .ok_or_else(|| zx::Status::ADDRESS_UNREACHABLE.into_raw());
3008                futures::future::ready(
3009                    responder
3010                        .send(result.as_ref().map_err(|e| *e))
3011                        .context("failed to send Resolve response"),
3012                )
3013            });
3014
3015        let ((), ()) = futures::future::try_join(routes_fut, async move {
3016            let addrs = TEST_IPS.iter().map(|(dst, _src)| *dst).collect();
3017            let addrs = sort_preferred_addresses(addrs, &routes_proxy)
3018                .await
3019                .expect("failed to sort addresses");
3020            let addrs = addrs
3021                .into_iter()
3022                .map(|a| {
3023                    let net_ext::IpAddress(a) = a.into();
3024                    a
3025                })
3026                .collect::<Vec<_>>();
3027            assert_eq!(&addrs[..], &SORTED[..]);
3028            Ok(())
3029        })
3030        .await
3031        .expect("error running futures");
3032    }
3033
3034    #[fasync::run_singlethreaded(test)]
3035    async fn test_lookupip() {
3036        // Routes handler will say that only IPV6_HOST is reachable.
3037        let routes_handler = |req| {
3038            let (destination, responder) = assert_matches!(
3039                req,
3040                fnet_routes::StateRequest::Resolve { destination, responder }
3041                    => (destination, responder)
3042            );
3043            let resolved;
3044            let response = if destination == map_ip(IPV6_HOST) {
3045                resolved = fnet_routes::Resolved::Direct(fnet_routes::Destination {
3046                    address: Some(destination),
3047                    source_address: Some(destination),
3048                    ..Default::default()
3049                });
3050                Ok(&resolved)
3051            } else {
3052                Err(zx::Status::ADDRESS_UNREACHABLE.into_raw())
3053            };
3054            responder.send(response).expect("failed to send Resolve FIDL response");
3055        };
3056        TestEnvironment::default()
3057            .run_lookup_with_routes_handler(
3058                |proxy| async move {
3059                    // All arguments unset.
3060                    assert_eq!(
3061                        proxy
3062                            .lookup_ip(REMOTE_IPV4_HOST, &fname::LookupIpOptions::default())
3063                            .await
3064                            .expect("lookup_ip"),
3065                        Err(fname::LookupError::InvalidArgs)
3066                    );
3067                    // No IP addresses to look.
3068                    assert_eq!(
3069                        proxy
3070                            .lookup_ip(
3071                                REMOTE_IPV4_HOST,
3072                                &fname::LookupIpOptions {
3073                                    ipv4_lookup: Some(false),
3074                                    ipv6_lookup: Some(false),
3075                                    ..Default::default()
3076                                }
3077                            )
3078                            .await
3079                            .expect("lookup_ip"),
3080                        Err(fname::LookupError::InvalidArgs)
3081                    );
3082                    // No results for an IPv4 only host.
3083                    assert_eq!(
3084                        proxy
3085                            .lookup_ip(
3086                                REMOTE_IPV4_HOST,
3087                                &fname::LookupIpOptions {
3088                                    ipv4_lookup: Some(false),
3089                                    ipv6_lookup: Some(true),
3090                                    ..Default::default()
3091                                }
3092                            )
3093                            .await
3094                            .expect("lookup_ip"),
3095                        Err(fname::LookupError::NotFound)
3096                    );
3097                    // Successfully resolve IPv4.
3098                    assert_eq!(
3099                        proxy
3100                            .lookup_ip(
3101                                REMOTE_IPV4_HOST,
3102                                &fname::LookupIpOptions {
3103                                    ipv4_lookup: Some(true),
3104                                    ipv6_lookup: Some(true),
3105                                    ..Default::default()
3106                                }
3107                            )
3108                            .await
3109                            .expect("lookup_ip"),
3110                        Ok(fname::LookupResult {
3111                            addresses: Some(vec![map_ip(IPV4_HOST)]),
3112                            ..Default::default()
3113                        })
3114                    );
3115                    // Successfully resolve IPv4 + IPv6 (no sorting).
3116                    assert_eq!(
3117                        proxy
3118                            .lookup_ip(
3119                                REMOTE_IPV4_IPV6_HOST,
3120                                &fname::LookupIpOptions {
3121                                    ipv4_lookup: Some(true),
3122                                    ipv6_lookup: Some(true),
3123                                    ..Default::default()
3124                                }
3125                            )
3126                            .await
3127                            .expect("lookup_ip"),
3128                        Ok(fname::LookupResult {
3129                            addresses: Some(vec![map_ip(IPV4_HOST), map_ip(IPV6_HOST)]),
3130                            ..Default::default()
3131                        })
3132                    );
3133                    // Successfully resolve IPv4 + IPv6 (with sorting).
3134                    assert_eq!(
3135                        proxy
3136                            .lookup_ip(
3137                                REMOTE_IPV4_IPV6_HOST,
3138                                &fname::LookupIpOptions {
3139                                    ipv4_lookup: Some(true),
3140                                    ipv6_lookup: Some(true),
3141                                    sort_addresses: Some(true),
3142                                    ..Default::default()
3143                                }
3144                            )
3145                            .await
3146                            .expect("lookup_ip"),
3147                        Ok(fname::LookupResult {
3148                            addresses: Some(vec![map_ip(IPV6_HOST), map_ip(IPV4_HOST)]),
3149                            ..Default::default()
3150                        })
3151                    );
3152                },
3153                routes_handler,
3154            )
3155            .await
3156    }
3157}