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 itertools::Itertools as _;
1368    use net_declare::{fidl_ip, std_ip, std_ip_v4, std_ip_v6, std_socket_addr};
1369    use net_types::ip::Ip as _;
1370    use test_case::test_case;
1371    use trust_dns_proto::op::Query;
1372    use trust_dns_proto::rr::{Name, Record};
1373    use trust_dns_resolver::config::Protocol;
1374    use trust_dns_resolver::lookup::{Lookup, ReverseLookup};
1375
1376    use super::*;
1377
1378    const IPV4_LOOPBACK: fnet::IpAddress = fidl_ip!("127.0.0.1");
1379    const IPV6_LOOPBACK: fnet::IpAddress = fidl_ip!("::1");
1380    const LOCAL_HOST: &str = "localhost.";
1381
1382    // IPv4 address returned by mock lookup.
1383    const IPV4_HOST: Ipv4Addr = std_ip_v4!("240.0.0.2");
1384    // IPv6 address returned by mock lookup.
1385    const IPV6_HOST: Ipv6Addr = std_ip_v6!("abcd::2");
1386
1387    // host which has IPv4 address only.
1388    const REMOTE_IPV4_HOST: &str = "www.foo.com";
1389    // host which has IPv6 address only.
1390    const REMOTE_IPV6_HOST: &str = "www.bar.com";
1391    const REMOTE_IPV4_HOST_ALIAS: &str = "www.alsofoo.com";
1392    const REMOTE_IPV6_HOST_ALIAS: &str = "www.alsobar.com";
1393    // host used in reverse_lookup when multiple hostnames are returned.
1394    const REMOTE_IPV6_HOST_EXTRA: &str = "www.bar2.com";
1395    // host which has IPv4 and IPv6 address if reset name servers.
1396    const REMOTE_IPV4_IPV6_HOST: &str = "www.foobar.com";
1397    // host which has no records and does not result in an error.
1398    const NO_RECORDS_AND_NO_ERROR_HOST: &str = "www.no-records-and-no-error.com";
1399
1400    async fn setup_namelookup_service() -> (fname::LookupProxy, impl futures::Future<Output = ()>) {
1401        let (name_lookup_proxy, stream) =
1402            fidl::endpoints::create_proxy_and_stream::<fname::LookupMarker>();
1403
1404        let mut resolver_opts = ResolverOpts::default();
1405        resolver_opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6;
1406
1407        let resolver = SharedResolver::new(
1408            Resolver::new(ResolverConfig::default(), resolver_opts, Spawner)
1409                .expect("failed to create resolver"),
1410        );
1411        let stats = Arc::new(QueryStats::new());
1412        let (routes_proxy, routes_stream) =
1413            fidl::endpoints::create_proxy_and_stream::<fnet_routes::StateMarker>();
1414        let routes_fut =
1415            routes_stream.try_for_each(|req| -> futures::future::Ready<Result<(), fidl::Error>> {
1416                panic!("Should not call routes/State. Received request {:?}", req)
1417            });
1418        let (sender, recv) = mpsc::channel(MAX_PARALLEL_REQUESTS);
1419
1420        (name_lookup_proxy, async move {
1421            futures::future::try_join3(
1422                run_lookup(&resolver, stream, sender),
1423                routes_fut,
1424                create_ip_lookup_fut(&resolver, stats.clone(), routes_proxy, recv).map(Ok),
1425            )
1426            .map(|r| match r {
1427                Ok(((), (), ())) => (),
1428                Err(e) => panic!("namelookup service error {:?}", e),
1429            })
1430            .await
1431        })
1432    }
1433
1434    #[fasync::run_singlethreaded(test)]
1435    async fn test_lookupip_localhost() {
1436        let (proxy, fut) = setup_namelookup_service().await;
1437        let ((), ()) = futures::future::join(fut, async move {
1438            // IP Lookup IPv4 and IPv6 for localhost.
1439            assert_eq!(
1440                proxy
1441                    .lookup_ip(
1442                        LOCAL_HOST,
1443                        &fname::LookupIpOptions {
1444                            ipv4_lookup: Some(true),
1445                            ipv6_lookup: Some(true),
1446                            ..Default::default()
1447                        }
1448                    )
1449                    .await
1450                    .expect("lookup_ip"),
1451                Ok(fname::LookupResult {
1452                    addresses: Some(vec![IPV4_LOOPBACK, IPV6_LOOPBACK]),
1453                    ..Default::default()
1454                }),
1455            );
1456
1457            // IP Lookup IPv4 only for localhost.
1458            assert_eq!(
1459                proxy
1460                    .lookup_ip(
1461                        LOCAL_HOST,
1462                        &fname::LookupIpOptions { ipv4_lookup: Some(true), ..Default::default() }
1463                    )
1464                    .await
1465                    .expect("lookup_ip"),
1466                Ok(fname::LookupResult {
1467                    addresses: Some(vec![IPV4_LOOPBACK]),
1468                    ..Default::default()
1469                }),
1470            );
1471
1472            // IP Lookup IPv6 only for localhost.
1473            assert_eq!(
1474                proxy
1475                    .lookup_ip(
1476                        LOCAL_HOST,
1477                        &fname::LookupIpOptions { ipv6_lookup: Some(true), ..Default::default() }
1478                    )
1479                    .await
1480                    .expect("lookup_ip"),
1481                Ok(fname::LookupResult {
1482                    addresses: Some(vec![IPV6_LOOPBACK]),
1483                    ..Default::default()
1484                }),
1485            );
1486        })
1487        .await;
1488    }
1489
1490    #[fasync::run_singlethreaded(test)]
1491    async fn test_lookuphostname_localhost() {
1492        let (proxy, fut) = setup_namelookup_service().await;
1493        let ((), ()) = futures::future::join(fut, async move {
1494            let hostname = IPV4_LOOPBACK;
1495            assert_eq!(
1496                proxy.lookup_hostname(&hostname).await.expect("lookup_hostname").as_deref(),
1497                Ok(LOCAL_HOST)
1498            );
1499        })
1500        .await;
1501    }
1502
1503    #[derive(Debug, Clone)]
1504    struct MockResolver {
1505        config: ResolverConfig,
1506        repeat: u16,
1507        name_server_stats: Vec<NameServerStats>,
1508    }
1509
1510    impl ResolverLookup for MockResolver {
1511        fn new(config: ResolverConfig, _options: ResolverOpts) -> Self {
1512            Self { config, repeat: 1, name_server_stats: Vec::new() }
1513        }
1514
1515        async fn lookup<N: IntoName + Send>(
1516            &self,
1517            name: N,
1518            record_type: RecordType,
1519        ) -> Result<lookup::Lookup, ResolveError> {
1520            let Self { config: _, repeat, name_server_stats: _ } = self;
1521
1522            let name = name.into_name()?;
1523            let host_name = name.to_utf8();
1524
1525            if host_name == NO_RECORDS_AND_NO_ERROR_HOST {
1526                return Ok(Lookup::new_with_max_ttl(Query::default(), Arc::new([])));
1527            }
1528            let rdatas = match record_type {
1529                RecordType::A => [REMOTE_IPV4_HOST, REMOTE_IPV4_IPV6_HOST]
1530                    .contains(&host_name.as_str())
1531                    .then_some(RData::A(IPV4_HOST)),
1532                RecordType::AAAA => [REMOTE_IPV6_HOST, REMOTE_IPV4_IPV6_HOST]
1533                    .contains(&host_name.as_str())
1534                    .then_some(RData::AAAA(IPV6_HOST)),
1535                RecordType::CNAME => match host_name.as_str() {
1536                    REMOTE_IPV4_HOST_ALIAS => Some(REMOTE_IPV4_HOST),
1537                    REMOTE_IPV6_HOST_ALIAS => Some(REMOTE_IPV6_HOST),
1538                    _ => None,
1539                }
1540                .map(Name::from_str)
1541                .transpose()
1542                .unwrap()
1543                .map(RData::CNAME),
1544                record_type => {
1545                    panic!("unexpected record type {:?}", record_type)
1546                }
1547            }
1548            .into_iter();
1549
1550            let len = rdatas.len() * usize::from(*repeat);
1551            let records: Vec<Record> = rdatas
1552                .map(|rdata| {
1553                    Record::from_rdata(
1554                        Name::new(),
1555                        // The following ttl value is taken arbitrarily and does not matter in the
1556                        // test.
1557                        60,
1558                        rdata,
1559                    )
1560                })
1561                .cycle()
1562                .take(len)
1563                .collect();
1564
1565            if records.is_empty() {
1566                let mut response = trust_dns_proto::op::Message::new();
1567                let _: &mut trust_dns_proto::op::Message =
1568                    response.set_response_code(ResponseCode::NoError);
1569                let error = ResolveError::from_response(response.into(), false)
1570                    .expect_err("response with no records should be a NoRecordsFound error");
1571                return Err(error);
1572            }
1573
1574            Ok(Lookup::new_with_max_ttl(Query::default(), records.into()))
1575        }
1576
1577        async fn reverse_lookup(
1578            &self,
1579            addr: IpAddr,
1580        ) -> Result<lookup::ReverseLookup, ResolveError> {
1581            let lookup = if addr == IPV4_HOST {
1582                Lookup::from_rdata(
1583                    Query::default(),
1584                    RData::PTR(Name::from_str(REMOTE_IPV4_HOST).unwrap()),
1585                )
1586            } else if addr == IPV6_HOST {
1587                Lookup::new_with_max_ttl(
1588                    Query::default(),
1589                    Arc::new([
1590                        Record::from_rdata(
1591                            Name::new(),
1592                            60, // The value is taken arbitrarily and does not matter
1593                            // in the test.
1594                            RData::PTR(Name::from_str(REMOTE_IPV6_HOST).unwrap()),
1595                        ),
1596                        Record::from_rdata(
1597                            Name::new(),
1598                            60, // The value is taken arbitrarily and does not matter
1599                            // in the test.
1600                            RData::PTR(Name::from_str(REMOTE_IPV6_HOST_EXTRA).unwrap()),
1601                        ),
1602                    ]),
1603                )
1604            } else {
1605                Lookup::new_with_max_ttl(Query::default(), Arc::new([]))
1606            };
1607            Ok(ReverseLookup::from(lookup))
1608        }
1609    }
1610
1611    impl NameServerStatsProvider for MockResolver {
1612        fn name_server_stats(&self) -> Vec<NameServerStats> {
1613            self.name_server_stats.clone()
1614        }
1615    }
1616
1617    struct TestEnvironment {
1618        shared_resolver: SharedResolver<MockResolver>,
1619        config_state: Arc<dns::config::ServerConfigState>,
1620        stats: Arc<QueryStats>,
1621    }
1622
1623    impl Default for TestEnvironment {
1624        fn default() -> Self {
1625            Self::new(1)
1626        }
1627    }
1628
1629    impl TestEnvironment {
1630        fn new(repeat: u16) -> Self {
1631            Self {
1632                shared_resolver: SharedResolver::new(MockResolver {
1633                    config: ResolverConfig::from_parts(
1634                        None,
1635                        vec![],
1636                        // Set name_servers as empty, so it's guaranteed to be different from IPV4_NAMESERVER
1637                        // and IPV6_NAMESERVER.
1638                        NameServerConfigGroup::with_capacity(0),
1639                    ),
1640                    repeat,
1641                    name_server_stats: Vec::new(),
1642                }),
1643                config_state: Arc::new(dns::config::ServerConfigState::new()),
1644                stats: Arc::new(QueryStats::new()),
1645            }
1646        }
1647
1648        async fn run_lookup<F, Fut>(&self, f: F)
1649        where
1650            Fut: futures::Future<Output = ()>,
1651            F: FnOnce(fname::LookupProxy) -> Fut,
1652        {
1653            self.run_lookup_with_routes_handler(f, |req| {
1654                panic!("Should not call routes/State. Received request {:?}", req)
1655            })
1656            .await
1657        }
1658
1659        async fn run_lookup_with_routes_handler<F, Fut, R>(&self, f: F, handle_routes: R)
1660        where
1661            Fut: futures::Future<Output = ()>,
1662            F: FnOnce(fname::LookupProxy) -> Fut,
1663            R: Fn(fnet_routes::StateRequest),
1664        {
1665            let (name_lookup_proxy, name_lookup_stream) =
1666                fidl::endpoints::create_proxy_and_stream::<fname::LookupMarker>();
1667
1668            let (routes_proxy, routes_stream) =
1669                fidl::endpoints::create_proxy_and_stream::<fnet_routes::StateMarker>();
1670
1671            let (sender, recv) = mpsc::channel(MAX_PARALLEL_REQUESTS);
1672            let Self { shared_resolver, config_state: _, stats } = self;
1673            let ((), (), (), ()) = futures::future::try_join4(
1674                run_lookup(shared_resolver, name_lookup_stream, sender),
1675                f(name_lookup_proxy).map(Ok),
1676                routes_stream.try_for_each(|req| futures::future::ok(handle_routes(req))),
1677                create_ip_lookup_fut(shared_resolver, stats.clone(), routes_proxy, recv).map(Ok),
1678            )
1679            .await
1680            .expect("Error running lookup future");
1681        }
1682
1683        async fn run_admin<F, Fut>(&self, f: F)
1684        where
1685            Fut: futures::Future<Output = ()>,
1686            F: FnOnce(fname::LookupAdminProxy) -> Fut,
1687        {
1688            let (lookup_admin_proxy, lookup_admin_stream) =
1689                fidl::endpoints::create_proxy_and_stream::<fname::LookupAdminMarker>();
1690            let Self { shared_resolver, config_state, stats: _ } = self;
1691            let ((), ()) = futures::future::try_join(
1692                run_lookup_admin(shared_resolver, config_state, lookup_admin_stream)
1693                    .map_err(anyhow::Error::from),
1694                f(lookup_admin_proxy).map(Ok),
1695            )
1696            .await
1697            .expect("Error running admin future");
1698        }
1699    }
1700
1701    fn map_ip<T: Into<IpAddr>>(addr: T) -> fnet::IpAddress {
1702        net_ext::IpAddress(addr.into()).into()
1703    }
1704
1705    #[fasync::run_singlethreaded(test)]
1706    async fn test_no_records_and_no_error() {
1707        TestEnvironment::default()
1708            .run_lookup(|proxy| async move {
1709                let proxy = &proxy;
1710                futures::stream::iter([(true, true), (true, false), (false, true)])
1711                    .for_each_concurrent(None, move |(ipv4_lookup, ipv6_lookup)| async move {
1712                        // Verify that the resolver does not panic when the
1713                        // response contains no records and no error. This
1714                        // scenario should theoretically not occur, but
1715                        // currently does. See https://fxbug.dev/42062388.
1716                        assert_eq!(
1717                            proxy
1718                                .lookup_ip(
1719                                    NO_RECORDS_AND_NO_ERROR_HOST,
1720                                    &fname::LookupIpOptions {
1721                                        ipv4_lookup: Some(ipv4_lookup),
1722                                        ipv6_lookup: Some(ipv6_lookup),
1723                                        ..Default::default()
1724                                    }
1725                                )
1726                                .await
1727                                .expect("lookup_ip"),
1728                            Err(fname::LookupError::NotFound),
1729                        );
1730                    })
1731                    .await
1732            })
1733            .await;
1734    }
1735
1736    #[fasync::run_singlethreaded(test)]
1737    async fn test_lookupip_remotehost_overflow() {
1738        // We're returning two addresses, so we need each one to repeat only half as many times.
1739        const REPEAT: u16 = fname::MAX_ADDRESSES / 2 + 1;
1740        let expected = std::iter::empty()
1741            .chain(std::iter::repeat(map_ip(IPV4_HOST)).take(REPEAT.into()))
1742            .chain(std::iter::repeat(map_ip(IPV6_HOST)).take(REPEAT.into()))
1743            .take(fname::MAX_ADDRESSES.into())
1744            .collect::<Vec<_>>();
1745        assert_eq!(expected.len(), usize::from(fname::MAX_ADDRESSES));
1746        TestEnvironment::new(REPEAT)
1747            .run_lookup(|proxy| async move {
1748                assert_eq!(
1749                    proxy
1750                        .lookup_ip(
1751                            REMOTE_IPV4_IPV6_HOST,
1752                            &fname::LookupIpOptions {
1753                                ipv4_lookup: Some(true),
1754                                ipv6_lookup: Some(true),
1755                                ..Default::default()
1756                            }
1757                        )
1758                        .await
1759                        .expect("lookup_ip"),
1760                    Ok(fname::LookupResult { addresses: Some(expected), ..Default::default() })
1761                );
1762            })
1763            .await;
1764    }
1765
1766    #[fasync::run_singlethreaded(test)]
1767    async fn test_lookupip_remotehost_ipv4() {
1768        TestEnvironment::default()
1769            .run_lookup(|proxy| async move {
1770                // IP Lookup IPv4 and IPv6 for REMOTE_IPV4_HOST.
1771                assert_eq!(
1772                    proxy
1773                        .lookup_ip(
1774                            REMOTE_IPV4_HOST,
1775                            &fname::LookupIpOptions {
1776                                ipv4_lookup: Some(true),
1777                                ipv6_lookup: Some(true),
1778                                ..Default::default()
1779                            }
1780                        )
1781                        .await
1782                        .expect("lookup_ip"),
1783                    Ok(fname::LookupResult {
1784                        addresses: Some(vec![map_ip(IPV4_HOST)]),
1785                        ..Default::default()
1786                    }),
1787                );
1788
1789                // IP Lookup IPv4 for REMOTE_IPV4_HOST.
1790                assert_eq!(
1791                    proxy
1792                        .lookup_ip(
1793                            REMOTE_IPV4_HOST,
1794                            &fname::LookupIpOptions {
1795                                ipv4_lookup: Some(true),
1796                                ..Default::default()
1797                            }
1798                        )
1799                        .await
1800                        .expect("lookup_ip"),
1801                    Ok(fname::LookupResult {
1802                        addresses: Some(vec![map_ip(IPV4_HOST)]),
1803                        ..Default::default()
1804                    }),
1805                );
1806
1807                // IP Lookup IPv6 for REMOTE_IPV4_HOST.
1808                assert_eq!(
1809                    proxy
1810                        .lookup_ip(
1811                            REMOTE_IPV4_HOST,
1812                            &fname::LookupIpOptions {
1813                                ipv6_lookup: Some(true),
1814                                ..Default::default()
1815                            }
1816                        )
1817                        .await
1818                        .expect("lookup_ip"),
1819                    Err(fname::LookupError::NotFound),
1820                );
1821            })
1822            .await;
1823    }
1824
1825    #[fasync::run_singlethreaded(test)]
1826    async fn test_lookupip_remotehost_ipv6() {
1827        TestEnvironment::default()
1828            .run_lookup(|proxy| async move {
1829                // IP Lookup IPv4 and IPv6 for REMOTE_IPV6_HOST.
1830                assert_eq!(
1831                    proxy
1832                        .lookup_ip(
1833                            REMOTE_IPV6_HOST,
1834                            &fname::LookupIpOptions {
1835                                ipv4_lookup: Some(true),
1836                                ipv6_lookup: Some(true),
1837                                ..Default::default()
1838                            }
1839                        )
1840                        .await
1841                        .expect("lookup_ip"),
1842                    Ok(fname::LookupResult {
1843                        addresses: Some(vec![map_ip(IPV6_HOST)]),
1844                        ..Default::default()
1845                    }),
1846                );
1847
1848                // IP Lookup IPv4 for REMOTE_IPV6_HOST.
1849                assert_eq!(
1850                    proxy
1851                        .lookup_ip(
1852                            REMOTE_IPV6_HOST,
1853                            &fname::LookupIpOptions {
1854                                ipv4_lookup: Some(true),
1855                                ..Default::default()
1856                            }
1857                        )
1858                        .await
1859                        .expect("lookup_ip"),
1860                    Err(fname::LookupError::NotFound),
1861                );
1862
1863                // IP Lookup IPv6 for REMOTE_IPV4_HOST.
1864                assert_eq!(
1865                    proxy
1866                        .lookup_ip(
1867                            REMOTE_IPV6_HOST,
1868                            &fname::LookupIpOptions {
1869                                ipv6_lookup: Some(true),
1870                                ..Default::default()
1871                            }
1872                        )
1873                        .await
1874                        .expect("lookup_ip"),
1875                    Ok(fname::LookupResult {
1876                        addresses: Some(vec![map_ip(IPV6_HOST)]),
1877                        ..Default::default()
1878                    }),
1879                );
1880            })
1881            .await;
1882    }
1883
1884    #[test_case(REMOTE_IPV4_HOST_ALIAS, REMOTE_IPV4_HOST; "ipv4")]
1885    #[test_case(REMOTE_IPV6_HOST_ALIAS, REMOTE_IPV6_HOST; "ipv6")]
1886    #[fasync::run_singlethreaded(test)]
1887    async fn test_lookupip_remotehost_canonical_name(hostname: &str, expected: &str) {
1888        TestEnvironment::default()
1889            .run_lookup(|proxy| async move {
1890                assert_matches!(
1891                    proxy
1892                        .lookup_ip(
1893                            hostname,
1894                            &fname::LookupIpOptions {
1895                                canonical_name_lookup: Some(true),
1896                                ..Default::default()
1897                            }
1898                        )
1899                        .await,
1900                    Ok(Ok(fname::LookupResult {
1901                        canonical_name: Some(cname),
1902                        ..
1903                    })) => assert_eq!(cname, expected)
1904                );
1905            })
1906            .await;
1907    }
1908
1909    #[fasync::run_singlethreaded(test)]
1910    async fn test_lookupip_ip_literal() {
1911        TestEnvironment::default()
1912            .run_lookup(|proxy| async move {
1913                let proxy = &proxy;
1914
1915                let range = || [true, false].into_iter();
1916
1917                futures::stream::iter(range().cartesian_product(range()))
1918                    .for_each_concurrent(None, move |(ipv4_lookup, ipv6_lookup)| async move {
1919                        assert_eq!(
1920                            proxy
1921                                .lookup_ip(
1922                                    "240.0.0.2",
1923                                    &fname::LookupIpOptions {
1924                                        ipv4_lookup: Some(ipv4_lookup),
1925                                        ipv6_lookup: Some(ipv6_lookup),
1926                                        ..Default::default()
1927                                    }
1928                                )
1929                                .await
1930                                .expect("lookup_ip"),
1931                            Err(fname::LookupError::InvalidArgs),
1932                            "ipv4_lookup={},ipv6_lookup={}",
1933                            ipv4_lookup,
1934                            ipv6_lookup,
1935                        );
1936
1937                        assert_eq!(
1938                            proxy
1939                                .lookup_ip(
1940                                    "abcd::2",
1941                                    &fname::LookupIpOptions {
1942                                        ipv4_lookup: Some(ipv4_lookup),
1943                                        ipv6_lookup: Some(ipv6_lookup),
1944                                        ..Default::default()
1945                                    }
1946                                )
1947                                .await
1948                                .expect("lookup_ip"),
1949                            Err(fname::LookupError::InvalidArgs),
1950                            "ipv4_lookup={},ipv6_lookup={}",
1951                            ipv4_lookup,
1952                            ipv6_lookup,
1953                        );
1954                    })
1955                    .await
1956            })
1957            .await
1958    }
1959
1960    #[fasync::run_singlethreaded(test)]
1961    async fn test_lookup_hostname() {
1962        TestEnvironment::default()
1963            .run_lookup(|proxy| async move {
1964                assert_eq!(
1965                    proxy
1966                        .lookup_hostname(&map_ip(IPV4_HOST))
1967                        .await
1968                        .expect("lookup_hostname")
1969                        .as_deref(),
1970                    Ok(REMOTE_IPV4_HOST)
1971                );
1972            })
1973            .await;
1974    }
1975
1976    // Multiple hostnames returned from trust-dns* APIs, and only the first one will be returned
1977    // by the FIDL.
1978    #[fasync::run_singlethreaded(test)]
1979    async fn test_lookup_hostname_multi() {
1980        TestEnvironment::default()
1981            .run_lookup(|proxy| async move {
1982                assert_eq!(
1983                    proxy
1984                        .lookup_hostname(&map_ip(IPV6_HOST))
1985                        .await
1986                        .expect("lookup_hostname")
1987                        .as_deref(),
1988                    Ok(REMOTE_IPV6_HOST)
1989                );
1990            })
1991            .await;
1992    }
1993
1994    #[fasync::run_singlethreaded(test)]
1995    async fn test_set_server_names() {
1996        let env = TestEnvironment::default();
1997
1998        let to_server_configs = |socket_addr: SocketAddr| -> [NameServerConfig; 2] {
1999            [
2000                NameServerConfig {
2001                    socket_addr,
2002                    protocol: Protocol::Udp,
2003                    tls_dns_name: None,
2004                    trust_nx_responses: false,
2005                    bind_addr: None,
2006                    num_retained_errors: RETAINED_ERRORS_PER_NAME_SERVER,
2007                },
2008                NameServerConfig {
2009                    socket_addr,
2010                    protocol: Protocol::Tcp,
2011                    tls_dns_name: None,
2012                    trust_nx_responses: false,
2013                    bind_addr: None,
2014                    num_retained_errors: RETAINED_ERRORS_PER_NAME_SERVER,
2015                },
2016            ]
2017        };
2018
2019        // Assert that mock config has no servers originally.
2020        assert_eq!(env.shared_resolver.read().config.name_servers().to_vec(), vec![]);
2021
2022        // Set servers.
2023        env.run_admin(|proxy| async move {
2024            proxy
2025                .set_dns_servers(&[DHCP_SERVER, NDP_SERVER, DHCPV6_SERVER])
2026                .await
2027                .expect("Failed to call SetDnsServers")
2028                .expect("SetDnsServers error");
2029        })
2030        .await;
2031        assert_eq!(
2032            env.shared_resolver.read().config.name_servers().to_vec(),
2033            vec![DHCP_SERVER, NDP_SERVER, DHCPV6_SERVER]
2034                .into_iter()
2035                .map(|s| {
2036                    let net_ext::SocketAddress(s) = s.into();
2037                    s
2038                })
2039                .flat_map(|x| to_server_configs(x).to_vec().into_iter())
2040                .collect::<Vec<_>>()
2041        );
2042
2043        // Clear servers.
2044        env.run_admin(|proxy| async move {
2045            proxy
2046                .set_dns_servers(&[])
2047                .await
2048                .expect("Failed to call SetDnsServers")
2049                .expect("SetDnsServers error");
2050        })
2051        .await;
2052        assert_eq!(env.shared_resolver.read().config.name_servers().to_vec(), Vec::new());
2053    }
2054
2055    #[fasync::run_singlethreaded(test)]
2056    async fn test_set_server_names_error() {
2057        let env = TestEnvironment::default();
2058        // Assert that mock config has no servers originally.
2059        assert_eq!(env.shared_resolver.read().config.name_servers().to_vec(), vec![]);
2060
2061        env.run_admin(|proxy| async move {
2062            // Attempt to set bad addresses.
2063
2064            // Multicast not allowed.
2065            let status = proxy
2066                .set_dns_servers(&[fnet::SocketAddress::Ipv4(fnet::Ipv4SocketAddress {
2067                    address: fnet::Ipv4Address { addr: [224, 0, 0, 1] },
2068                    port: DEFAULT_PORT,
2069                })])
2070                .await
2071                .expect("Failed to call SetDnsServers")
2072                .expect_err("SetDnsServers should fail for multicast address");
2073            assert_eq!(zx::Status::from_raw(status), zx::Status::INVALID_ARGS);
2074
2075            // Unspecified not allowed.
2076            let status = proxy
2077                .set_dns_servers(&[fnet::SocketAddress::Ipv6(fnet::Ipv6SocketAddress {
2078                    address: fnet::Ipv6Address { addr: [0; 16] },
2079                    port: DEFAULT_PORT,
2080                    zone_index: 0,
2081                })])
2082                .await
2083                .expect("Failed to call SetDnsServers")
2084                .expect_err("SetDnsServers should fail for unspecified address");
2085            assert_eq!(zx::Status::from_raw(status), zx::Status::INVALID_ARGS);
2086        })
2087        .await;
2088
2089        // Assert that config didn't change.
2090        assert_eq!(env.shared_resolver.read().config.name_servers().to_vec(), vec![]);
2091    }
2092
2093    #[fasync::run_singlethreaded(test)]
2094    async fn test_get_servers() {
2095        let env = TestEnvironment::default();
2096        env.run_admin(|proxy| async move {
2097            let expect = &[NDP_SERVER, DHCP_SERVER, DHCPV6_SERVER, STATIC_SERVER];
2098            proxy.set_dns_servers(expect).await.expect("FIDL error").expect("set_servers failed");
2099            assert_matches!(proxy.get_dns_servers().await, Ok(got) if got == expect);
2100        })
2101        .await;
2102    }
2103
2104    #[fasync::run_singlethreaded(test)]
2105    async fn test_config_inspect() {
2106        let env = TestEnvironment::default();
2107        let inspector = fuchsia_inspect::Inspector::default();
2108        let _config_state_node =
2109            add_config_state_inspect(inspector.root(), env.config_state.clone());
2110        assert_data_tree!(inspector, root:{
2111            servers: {}
2112        });
2113        env.run_admin(|proxy| async move {
2114            let servers = &[NDP_SERVER, DHCP_SERVER, DHCPV6_SERVER, STATIC_SERVER];
2115            proxy.set_dns_servers(servers).await.expect("FIDL error").expect("set_servers failed");
2116        })
2117        .await;
2118        assert_data_tree!(inspector, root:{
2119            servers: {
2120                "0": {
2121                    address: "[2001:4860:4860::4444%2]:53",
2122                },
2123                "1": {
2124                    address: "8.8.4.4:53",
2125                },
2126                "2": {
2127                    address: "[2002:4860:4860::4444%3]:53",
2128                },
2129                "3": {
2130                    address: "8.8.8.8:53",
2131                },
2132            }
2133        });
2134    }
2135
2136    #[fasync::run_singlethreaded(test)]
2137    async fn test_name_server_stats_inspect() {
2138        let env = TestEnvironment::default();
2139        let inspector = fuchsia_inspect::Inspector::default();
2140        let _name_server_stats_node =
2141            add_name_server_stats_inspect(inspector.root(), env.shared_resolver.clone());
2142        assert_data_tree!(inspector, root:{
2143            name_servers: {}
2144        });
2145
2146        let addr = std_socket_addr!("1.2.3.4:53");
2147        let stats = NameServerStats {
2148            addr,
2149            proto: Protocol::Udp,
2150            failures: 1,
2151            successes: 2,
2152            success_streak: 0,
2153            recent_errors: vec![ResolveErrorKind::Timeout.into()],
2154        };
2155        env.shared_resolver.write(Arc::new(MockResolver {
2156            config: ResolverConfig::default(),
2157            repeat: 1,
2158            name_server_stats: vec![stats],
2159        }));
2160
2161        assert_data_tree!(inspector, root:{
2162            name_servers: {
2163                "0": {
2164                    address: "1.2.3.4:53",
2165                    protocol: "Udp",
2166                    successful_queries: 2u64,
2167                    failed_queries: 1u64,
2168                    success_streak: 0u64,
2169                    errors: {
2170                        Timeout: 1u64,
2171                        Message: 0u64,
2172                        NoConnections: 0u64,
2173                        IoErrorCounts: {},
2174                        ProtoErrorCounts: {},
2175                        NoRecordsFoundResponseCodeCounts: {},
2176                        UnhandledResolveErrorKindCounts: {},
2177                    }
2178                }
2179            }
2180        });
2181    }
2182
2183    #[test]
2184    fn test_unhandled_resolve_error_kind_stats() {
2185        use ResolveErrorKind::{Msg, Timeout};
2186        let mut unhandled_resolve_error_kind_stats = GenericErrorKindStats::default();
2187        assert_eq!(
2188            unhandled_resolve_error_kind_stats.increment(&Msg(String::from("abcdefgh"))),
2189            "Msg"
2190        );
2191        assert_eq!(
2192            unhandled_resolve_error_kind_stats.increment(&Msg(String::from("ijklmn"))),
2193            "Msg"
2194        );
2195        assert_eq!(unhandled_resolve_error_kind_stats.increment(&Timeout), "Timeout");
2196        assert_eq!(
2197            unhandled_resolve_error_kind_stats,
2198            GenericErrorKindStats([(String::from("Msg"), 2), (String::from("Timeout"), 1)].into())
2199        )
2200    }
2201
2202    #[fasync::run_singlethreaded(test)]
2203    async fn test_query_stats_updated() {
2204        let env = TestEnvironment::default();
2205        let inspector = fuchsia_inspect::Inspector::default();
2206        let _query_stats_inspect_node =
2207            add_query_stats_inspect(inspector.root(), env.stats.clone());
2208        assert_data_tree!(inspector, root:{
2209            query_stats: {}
2210        });
2211
2212        env.run_lookup(|proxy| async move {
2213            // IP Lookup IPv4 for REMOTE_IPV4_HOST.
2214            assert_eq!(
2215                proxy
2216                    .lookup_ip(
2217                        REMOTE_IPV4_HOST,
2218                        &fname::LookupIpOptions { ipv4_lookup: Some(true), ..Default::default() }
2219                    )
2220                    .await
2221                    .expect("lookup_ip"),
2222                Ok(fname::LookupResult {
2223                    addresses: Some(vec![map_ip(IPV4_HOST)]),
2224                    ..Default::default()
2225                }),
2226            );
2227        })
2228        .await;
2229        env.run_lookup(|proxy| async move {
2230            // IP Lookup IPv6 for REMOTE_IPV4_HOST.
2231            assert_eq!(
2232                proxy
2233                    .lookup_ip(
2234                        REMOTE_IPV4_HOST,
2235                        &fname::LookupIpOptions { ipv6_lookup: Some(true), ..Default::default() }
2236                    )
2237                    .await
2238                    .expect("lookup_ip"),
2239                Err(fname::LookupError::NotFound),
2240            );
2241        })
2242        .await;
2243        assert_data_tree!(inspector, root:{
2244            query_stats: {
2245                "window 1": {
2246                    start_time_nanos: NonZeroUintProperty,
2247                    successful_queries: 1u64,
2248                    failed_queries: 1u64,
2249                    average_success_duration_micros: NonZeroUintProperty,
2250                    average_failure_duration_micros: NonZeroUintProperty,
2251                    errors: {
2252                        Message: 0u64,
2253                        NoConnections: 0u64,
2254                        NoRecordsFoundResponseCodeCounts: {
2255                            NoError: 1u64,
2256                        },
2257                        IoErrorCounts: {},
2258                        ProtoErrorCounts: {},
2259                        Timeout: 0u64,
2260                        UnhandledResolveErrorKindCounts: {},
2261                    },
2262                    address_counts: {
2263                        "1": 1u64,
2264                    },
2265                },
2266            }
2267        });
2268    }
2269
2270    fn run_fake_lookup(
2271        exec: &mut fasync::TestExecutor,
2272        stats: Arc<QueryStats>,
2273        result: QueryResult<'_>,
2274        delay: zx::MonotonicDuration,
2275    ) {
2276        let start_time = fasync::MonotonicInstant::now();
2277        exec.set_fake_time(fasync::MonotonicInstant::after(delay));
2278        let update_stats = stats.finish_query(start_time, result);
2279        let mut update_stats = pin!(update_stats);
2280        assert!(exec.run_until_stalled(&mut update_stats).is_ready());
2281    }
2282
2283    // Safety: This is safe because the initial value is not zero.
2284    const NON_ZERO_USIZE_ONE: NonZeroUsize = NonZeroUsize::new(1).unwrap();
2285
2286    #[test]
2287    fn test_query_stats_inspect_average() {
2288        let mut exec = fasync::TestExecutor::new_with_fake_time();
2289        const START_NANOS: i64 = 1_234_567;
2290        exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2291
2292        let stats = Arc::new(QueryStats::new());
2293        let inspector = fuchsia_inspect::Inspector::default();
2294        let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2295        const SUCCESSFUL_QUERY_COUNT: u64 = 10;
2296        const SUCCESSFUL_QUERY_DURATION: zx::MonotonicDuration =
2297            zx::MonotonicDuration::from_seconds(30);
2298        for _ in 0..SUCCESSFUL_QUERY_COUNT / 2 {
2299            run_fake_lookup(
2300                &mut exec,
2301                stats.clone(),
2302                Ok(/*addresses*/ NON_ZERO_USIZE_ONE),
2303                zx::MonotonicDuration::from_nanos(0),
2304            );
2305            run_fake_lookup(
2306                &mut exec,
2307                stats.clone(),
2308                Ok(/*addresses*/ NON_ZERO_USIZE_ONE),
2309                SUCCESSFUL_QUERY_DURATION,
2310            );
2311            exec.set_fake_time(fasync::MonotonicInstant::after(
2312                STAT_WINDOW_DURATION - SUCCESSFUL_QUERY_DURATION,
2313            ));
2314        }
2315        let mut expected = tree_assertion!(query_stats: {});
2316        for i in 0..SUCCESSFUL_QUERY_COUNT / 2 {
2317            let name = &format!("window {}", i + 1);
2318            let child = tree_assertion!(var name: {
2319                start_time_nanos: u64::try_from(
2320                    START_NANOS + STAT_WINDOW_DURATION.into_nanos() * i64::try_from(i).unwrap()
2321                ).unwrap(),
2322                successful_queries: 2u64,
2323                failed_queries: 0u64,
2324                average_success_duration_micros: u64::try_from(
2325                    SUCCESSFUL_QUERY_DURATION.into_micros()
2326                ).unwrap() / 2,
2327                errors: {
2328                    Message: 0u64,
2329                    NoConnections: 0u64,
2330                    NoRecordsFoundResponseCodeCounts: {},
2331                    IoErrorCounts: {},
2332                    ProtoErrorCounts: {},
2333                    Timeout: 0u64,
2334                    UnhandledResolveErrorKindCounts: {},
2335                },
2336                address_counts: {
2337                    "1": 2u64,
2338                },
2339            });
2340            expected.add_child_assertion(child);
2341        }
2342        assert_data_tree!(@executor exec, inspector, root: {
2343            expected,
2344        });
2345    }
2346
2347    #[test]
2348    fn test_query_stats_inspect_error_counters() {
2349        let mut exec = fasync::TestExecutor::new_with_fake_time();
2350        const START_NANOS: i64 = 1_234_567;
2351        exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2352
2353        let stats = Arc::new(QueryStats::new());
2354        let inspector = fuchsia_inspect::Inspector::default();
2355        let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2356        const FAILED_QUERY_COUNT: u64 = 10;
2357        const FAILED_QUERY_DURATION: zx::MonotonicDuration =
2358            zx::MonotonicDuration::from_millis(500);
2359        for _ in 0..FAILED_QUERY_COUNT {
2360            run_fake_lookup(
2361                &mut exec,
2362                stats.clone(),
2363                Err(&ResolveErrorKind::Timeout),
2364                FAILED_QUERY_DURATION,
2365            );
2366        }
2367        assert_data_tree!(@executor exec, inspector, root:{
2368            query_stats: {
2369                "window 1": {
2370                    start_time_nanos: u64::try_from(
2371                        START_NANOS + FAILED_QUERY_DURATION.into_nanos()
2372                    ).unwrap(),
2373                    successful_queries: 0u64,
2374                    failed_queries: FAILED_QUERY_COUNT,
2375                    average_failure_duration_micros: u64::try_from(
2376                        FAILED_QUERY_DURATION.into_micros()
2377                    ).unwrap(),
2378                    errors: {
2379                        Message: 0u64,
2380                        NoConnections: 0u64,
2381                        NoRecordsFoundResponseCodeCounts: {},
2382                        IoErrorCounts: {},
2383                        ProtoErrorCounts: {},
2384                        Timeout: FAILED_QUERY_COUNT,
2385                        UnhandledResolveErrorKindCounts: {},
2386                    },
2387                    address_counts: {},
2388                },
2389            }
2390        });
2391    }
2392
2393    #[test]
2394    fn test_query_stats_inspect_no_records_found() {
2395        let mut exec = fasync::TestExecutor::new_with_fake_time();
2396        const START_NANOS: i64 = 1_234_567;
2397        exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2398
2399        let stats = Arc::new(QueryStats::new());
2400        let inspector = fuchsia_inspect::Inspector::default();
2401        let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2402        const FAILED_QUERY_COUNT: u64 = 10;
2403        const FAILED_QUERY_DURATION: zx::MonotonicDuration =
2404            zx::MonotonicDuration::from_millis(500);
2405
2406        let mut run_fake_no_records_lookup = |response_code: ResponseCode| {
2407            run_fake_lookup(
2408                &mut exec,
2409                stats.clone(),
2410                Err(&ResolveErrorKind::NoRecordsFound {
2411                    query: Box::new(Query::default()),
2412                    soa: None,
2413                    negative_ttl: None,
2414                    response_code,
2415                    trusted: false,
2416                }),
2417                FAILED_QUERY_DURATION,
2418            )
2419        };
2420
2421        for _ in 0..FAILED_QUERY_COUNT {
2422            run_fake_no_records_lookup(ResponseCode::NXDomain);
2423            run_fake_no_records_lookup(ResponseCode::Refused);
2424            run_fake_no_records_lookup(4096.into());
2425            run_fake_no_records_lookup(4097.into());
2426        }
2427
2428        assert_data_tree!(@executor exec, inspector, root:{
2429            query_stats: {
2430                "window 1": {
2431                    start_time_nanos: u64::try_from(
2432                        START_NANOS + FAILED_QUERY_DURATION.into_nanos()
2433                    ).unwrap(),
2434                    successful_queries: 0u64,
2435                    failed_queries: FAILED_QUERY_COUNT * 4,
2436                    average_failure_duration_micros: u64::try_from(
2437                        FAILED_QUERY_DURATION.into_micros()
2438                    ).unwrap(),
2439                    errors: {
2440                        Message: 0u64,
2441                        NoConnections: 0u64,
2442                        NoRecordsFoundResponseCodeCounts: {
2443                          NXDomain: FAILED_QUERY_COUNT,
2444                          Refused: FAILED_QUERY_COUNT,
2445                          "Unknown(4096)": FAILED_QUERY_COUNT,
2446                          "Unknown(4097)": FAILED_QUERY_COUNT,
2447                        },
2448                        IoErrorCounts: {},
2449                        ProtoErrorCounts: {},
2450                        Timeout: 0u64,
2451                        UnhandledResolveErrorKindCounts: {},
2452                    },
2453                    address_counts: {},
2454                },
2455            }
2456        });
2457    }
2458
2459    #[test]
2460    fn test_query_stats_resolved_address_counts() {
2461        let mut exec = fasync::TestExecutor::new_with_fake_time();
2462        const START_NANOS: i64 = 1_234_567;
2463        exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2464
2465        let stats = Arc::new(QueryStats::new());
2466        let inspector = fuchsia_inspect::Inspector::default();
2467        let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2468
2469        // Create some test data to run fake lookups. Simulate a histogram with:
2470        //  - 99 occurrences of a response with 1 address,
2471        //  - 98 occurrences of a response with 2 addresses,
2472        //  - ...
2473        //  - 1 occurrence of a response with 99 addresses.
2474        let address_counts: HashMap<usize, _> = (1..100).zip((1..100).rev()).collect();
2475        const QUERY_DURATION: zx::MonotonicDuration = zx::MonotonicDuration::from_millis(10);
2476        for (count, occurrences) in address_counts.iter() {
2477            for _ in 0..*occurrences {
2478                run_fake_lookup(
2479                    &mut exec,
2480                    stats.clone(),
2481                    Ok(NonZeroUsize::new(*count).expect("address count must be greater than zero")),
2482                    QUERY_DURATION,
2483                );
2484            }
2485        }
2486
2487        let mut expected_address_counts = tree_assertion!(address_counts: {});
2488        for (count, occurrences) in address_counts.iter() {
2489            expected_address_counts
2490                .add_property_assertion(&count.to_string(), Arc::new(*occurrences));
2491        }
2492        assert_data_tree!(@executor exec, inspector, root: {
2493            query_stats: {
2494                "window 1": {
2495                    start_time_nanos: u64::try_from(
2496                        START_NANOS + QUERY_DURATION.into_nanos()
2497                    ).unwrap(),
2498                    successful_queries: address_counts.values().sum::<u64>(),
2499                    failed_queries: 0u64,
2500                    average_success_duration_micros: u64::try_from(
2501                        QUERY_DURATION.into_micros()
2502                    ).unwrap(),
2503                    errors: {
2504                        Message: 0u64,
2505                        NoConnections: 0u64,
2506                        NoRecordsFoundResponseCodeCounts: {},
2507                        IoErrorCounts: {},
2508                        ProtoErrorCounts: {},
2509                        Timeout: 0u64,
2510                        UnhandledResolveErrorKindCounts: {},
2511                    },
2512                    expected_address_counts,
2513                },
2514            },
2515        });
2516    }
2517
2518    #[test]
2519    fn test_query_stats_inspect_oldest_stats_erased() {
2520        let mut exec = fasync::TestExecutor::new_with_fake_time();
2521        const START_NANOS: i64 = 1_234_567;
2522        exec.set_fake_time(fasync::MonotonicInstant::from_nanos(START_NANOS));
2523
2524        let stats = Arc::new(QueryStats::new());
2525        let inspector = fuchsia_inspect::Inspector::default();
2526        let _query_stats_inspect_node = add_query_stats_inspect(inspector.root(), stats.clone());
2527        const DELAY: zx::MonotonicDuration = zx::MonotonicDuration::from_millis(100);
2528        for _ in 0..STAT_WINDOW_COUNT {
2529            let () =
2530                run_fake_lookup(&mut exec, stats.clone(), Err(&ResolveErrorKind::Timeout), DELAY);
2531            let () =
2532                exec.set_fake_time(fasync::MonotonicInstant::after(STAT_WINDOW_DURATION - DELAY));
2533        }
2534        for _ in 0..STAT_WINDOW_COUNT {
2535            run_fake_lookup(&mut exec, stats.clone(), Ok(/*addresses*/ NON_ZERO_USIZE_ONE), DELAY);
2536            let () =
2537                exec.set_fake_time(fasync::MonotonicInstant::after(STAT_WINDOW_DURATION - DELAY));
2538        }
2539        // All the failed queries should be erased from the stats as they are
2540        // now out of date.
2541        let mut expected = tree_assertion!(query_stats: {});
2542        let start_offset = START_NANOS
2543            + DELAY.into_nanos()
2544            + STAT_WINDOW_DURATION.into_nanos() * i64::try_from(STAT_WINDOW_COUNT).unwrap();
2545        for i in 0..STAT_WINDOW_COUNT {
2546            let name = &format!("window {}", i + 1);
2547            let child = tree_assertion!(var name: {
2548                start_time_nanos: u64::try_from(
2549                    start_offset + STAT_WINDOW_DURATION.into_nanos() * i64::try_from(i).unwrap()
2550                ).unwrap(),
2551                successful_queries: 1u64,
2552                failed_queries: 0u64,
2553                average_success_duration_micros: u64::try_from(DELAY.into_micros()).unwrap(),
2554                errors: {
2555                    Message: 0u64,
2556                    NoConnections: 0u64,
2557                    NoRecordsFoundResponseCodeCounts: {},
2558                    IoErrorCounts: {},
2559                    ProtoErrorCounts: {},
2560                    Timeout: 0u64,
2561                    UnhandledResolveErrorKindCounts: {},
2562                },
2563                address_counts: {
2564                    "1": 1u64,
2565                },
2566            });
2567            expected.add_child_assertion(child);
2568        }
2569        assert_data_tree!(@executor exec, inspector, root: {
2570            expected,
2571        });
2572    }
2573
2574    struct BlockingResolver {}
2575
2576    impl ResolverLookup for BlockingResolver {
2577        fn new(_config: ResolverConfig, _options: ResolverOpts) -> Self {
2578            BlockingResolver {}
2579        }
2580
2581        async fn lookup<N: IntoName + Send>(
2582            &self,
2583            _name: N,
2584            _record_type: RecordType,
2585        ) -> Result<lookup::Lookup, ResolveError> {
2586            futures::future::pending().await
2587        }
2588
2589        async fn reverse_lookup(
2590            &self,
2591            _addr: IpAddr,
2592        ) -> Result<lookup::ReverseLookup, ResolveError> {
2593            panic!("BlockingResolver does not handle reverse lookup")
2594        }
2595    }
2596
2597    #[fasync::run_singlethreaded(test)]
2598    async fn test_parallel_query_limit() {
2599        // Collect requests by setting up a FIDL proxy and stream for the Lookup
2600        // protocol, because there isn't a good way to directly construct fake
2601        // requests to be used for testing.
2602        let requests = {
2603            let (name_lookup_proxy, name_lookup_stream) =
2604                fidl::endpoints::create_proxy_and_stream::<fname::LookupMarker>();
2605            const NUM_REQUESTS: usize = MAX_PARALLEL_REQUESTS * 2 + 2;
2606            for _ in 0..NUM_REQUESTS {
2607                // Don't await on this future because we are using these
2608                // requests to collect FIDL responders in order to send test
2609                // requests later, and will not respond to these requests.
2610                let _: fidl::client::QueryResponseFut<fname::LookupLookupIpResult> =
2611                    name_lookup_proxy.lookup_ip(
2612                        LOCAL_HOST,
2613                        &fname::LookupIpOptions {
2614                            ipv4_lookup: Some(true),
2615                            ipv6_lookup: Some(true),
2616                            ..Default::default()
2617                        },
2618                    );
2619            }
2620            // Terminate the stream so its items can be collected below.
2621            drop(name_lookup_proxy);
2622            let requests = name_lookup_stream
2623                .map(|request| match request.expect("channel error") {
2624                    LookupRequest::LookupIp { hostname, options, responder } => {
2625                        IpLookupRequest { hostname, options, responder }
2626                    }
2627                    req => panic!("Expected LookupRequest::LookupIp request, found {:?}", req),
2628                })
2629                .collect::<Vec<_>>()
2630                .await;
2631            assert_eq!(requests.len(), NUM_REQUESTS);
2632            requests
2633        };
2634
2635        let (mut sender, recv) = mpsc::channel(MAX_PARALLEL_REQUESTS);
2636
2637        // The channel's capacity is equal to buffer + num-senders. Thus the
2638        // channel has a capacity of `MAX_PARALLEL_REQUESTS` + 1, and the
2639        // `for_each_concurrent` future has a limit of `MAX_PARALLEL_REQUESTS`,
2640        // so the sender should be able to queue `MAX_PARALLEL_REQUESTS` * 2 + 1
2641        // requests before `send` fails.
2642        const BEFORE_LAST_INDEX: usize = MAX_PARALLEL_REQUESTS * 2;
2643        const LAST_INDEX: usize = MAX_PARALLEL_REQUESTS * 2 + 1;
2644        let mut send_fut = pin!(
2645            async {
2646                for (i, req) in requests.into_iter().enumerate() {
2647                    match i {
2648                        BEFORE_LAST_INDEX => assert_matches!(sender.try_send(req), Ok(())),
2649                        LAST_INDEX => assert_matches!(sender.try_send(req), Err(e) if e.is_full()),
2650                        _ => assert_matches!(sender.send(req).await, Ok(())),
2651                    }
2652                }
2653            }
2654            .fuse()
2655        );
2656        let mut recv_fut = pin!({
2657            let resolver = SharedResolver::new(BlockingResolver::new(
2658                ResolverConfig::default(),
2659                ResolverOpts::default(),
2660            ));
2661            let stats = Arc::new(QueryStats::new());
2662            let (routes_proxy, _routes_stream) =
2663                fidl::endpoints::create_proxy_and_stream::<fnet_routes::StateMarker>();
2664            async move { create_ip_lookup_fut(&resolver, stats.clone(), routes_proxy, recv).await }
2665                .fuse()
2666        });
2667        futures::select! {
2668            () = send_fut => {},
2669            () = recv_fut => panic!("recv_fut should never complete"),
2670        };
2671    }
2672
2673    #[test]
2674    fn test_failure_stats() {
2675        use anyhow::anyhow;
2676        use trust_dns_proto::error::ProtoError;
2677        use trust_dns_proto::op::Query;
2678
2679        let mut stats = FailureStats::default();
2680        for (error_kind, expected) in &[
2681            (ResolveErrorKind::Message("foo"), FailureStats { message: 1, ..Default::default() }),
2682            (
2683                ResolveErrorKind::Msg("foo".to_string()),
2684                FailureStats { message: 2, ..Default::default() },
2685            ),
2686            (
2687                ResolveErrorKind::NoRecordsFound {
2688                    query: Box::new(Query::default()),
2689                    soa: None,
2690                    negative_ttl: None,
2691                    response_code: ResponseCode::Refused,
2692                    trusted: false,
2693                },
2694                FailureStats {
2695                    message: 2,
2696                    no_records_found: NoRecordsFoundStats {
2697                        response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2698                    },
2699                    ..Default::default()
2700                },
2701            ),
2702            (
2703                ResolveErrorKind::Io(std::io::Error::new(
2704                    std::io::ErrorKind::NotFound,
2705                    anyhow!("foo"),
2706                )),
2707                FailureStats {
2708                    message: 2,
2709                    no_records_found: NoRecordsFoundStats {
2710                        response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2711                    },
2712                    io: IoErrorStats([(std::io::ErrorKind::NotFound, 1)].into()),
2713                    ..Default::default()
2714                },
2715            ),
2716            (
2717                ResolveErrorKind::Proto(ProtoError::from("foo")),
2718                FailureStats {
2719                    message: 2,
2720                    no_records_found: NoRecordsFoundStats {
2721                        response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2722                    },
2723                    io: IoErrorStats([(std::io::ErrorKind::NotFound, 1)].into()),
2724                    proto: GenericErrorKindStats([(String::from("Message"), 1)].into()),
2725                    ..Default::default()
2726                },
2727            ),
2728            (
2729                ResolveErrorKind::NoConnections,
2730                FailureStats {
2731                    message: 2,
2732                    no_connections: 1,
2733                    no_records_found: NoRecordsFoundStats {
2734                        response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2735                    },
2736                    io: IoErrorStats([(std::io::ErrorKind::NotFound, 1)].into()),
2737                    proto: GenericErrorKindStats([(String::from("Message"), 1)].into()),
2738                    ..Default::default()
2739                },
2740            ),
2741            (
2742                ResolveErrorKind::Timeout,
2743                FailureStats {
2744                    message: 2,
2745                    no_connections: 1,
2746                    no_records_found: NoRecordsFoundStats {
2747                        response_code_counts: [(ResponseCode::Refused.into(), 1)].into(),
2748                    },
2749                    io: IoErrorStats([(std::io::ErrorKind::NotFound, 1)].into()),
2750                    proto: GenericErrorKindStats([(String::from("Message"), 1)].into()),
2751                    timeout: 1,
2752                    unhandled_resolve_error_kind: Default::default(),
2753                },
2754            ),
2755            (
2756                ResolveErrorKind::NoRecordsFound {
2757                    query: Box::new(Query::default()),
2758                    soa: None,
2759                    negative_ttl: None,
2760                    response_code: ResponseCode::NXDomain,
2761                    trusted: false,
2762                },
2763                FailureStats {
2764                    message: 2,
2765                    no_connections: 1,
2766                    no_records_found: NoRecordsFoundStats {
2767                        response_code_counts: [
2768                            (ResponseCode::NXDomain.into(), 1),
2769                            (ResponseCode::Refused.into(), 1),
2770                        ]
2771                        .into(),
2772                    },
2773                    io: IoErrorStats([(std::io::ErrorKind::NotFound, 1)].into()),
2774                    proto: GenericErrorKindStats([(String::from("Message"), 1)].into()),
2775                    timeout: 1,
2776                    unhandled_resolve_error_kind: Default::default(),
2777                },
2778            ),
2779            (
2780                ResolveErrorKind::NoRecordsFound {
2781                    query: Box::new(Query::default()),
2782                    soa: None,
2783                    negative_ttl: None,
2784                    response_code: ResponseCode::NXDomain,
2785                    trusted: false,
2786                },
2787                FailureStats {
2788                    message: 2,
2789                    no_connections: 1,
2790                    no_records_found: NoRecordsFoundStats {
2791                        response_code_counts: [
2792                            (ResponseCode::NXDomain.into(), 2),
2793                            (ResponseCode::Refused.into(), 1),
2794                        ]
2795                        .into(),
2796                    },
2797                    io: IoErrorStats([(std::io::ErrorKind::NotFound, 1)].into()),
2798                    proto: GenericErrorKindStats([(String::from("Message"), 1)].into()),
2799                    timeout: 1,
2800                    unhandled_resolve_error_kind: Default::default(),
2801                },
2802            ),
2803            (
2804                ResolveErrorKind::Proto(ProtoError::from(std::io::Error::new(
2805                    std::io::ErrorKind::ConnectionAborted,
2806                    anyhow!("foo"),
2807                ))),
2808                FailureStats {
2809                    message: 2,
2810                    no_connections: 1,
2811                    no_records_found: NoRecordsFoundStats {
2812                        response_code_counts: [
2813                            (ResponseCode::NXDomain.into(), 2),
2814                            (ResponseCode::Refused.into(), 1),
2815                        ]
2816                        .into(),
2817                    },
2818                    io: IoErrorStats(
2819                        [
2820                            (std::io::ErrorKind::NotFound, 1),
2821                            (std::io::ErrorKind::ConnectionAborted, 1),
2822                        ]
2823                        .into(),
2824                    ),
2825                    proto: GenericErrorKindStats([(String::from("Message"), 1)].into()),
2826                    timeout: 1,
2827                    unhandled_resolve_error_kind: Default::default(),
2828                },
2829            ),
2830        ][..]
2831        {
2832            stats.increment(error_kind);
2833            assert_eq!(&stats, expected, "invalid stats after incrementing with {:?}", error_kind);
2834        }
2835    }
2836
2837    fn test_das_helper(
2838        l_addr: fnet::IpAddress,
2839        l_src: Option<fnet::IpAddress>,
2840        r_addr: fnet::IpAddress,
2841        r_src: Option<fnet::IpAddress>,
2842        want: std::cmp::Ordering,
2843    ) {
2844        let left = DasCmpInfo::from_addrs(&l_addr, l_src.as_ref());
2845        let right = DasCmpInfo::from_addrs(&r_addr, r_src.as_ref());
2846        assert_eq!(
2847            left.cmp(&right),
2848            want,
2849            "want = {:?}\n left = {:?}({:?}) DAS={:?}\n right = {:?}({:?}) DAS={:?}",
2850            want,
2851            l_addr,
2852            l_src,
2853            left,
2854            r_addr,
2855            r_src,
2856            right
2857        );
2858    }
2859
2860    macro_rules! add_das_test {
2861        ($name:ident, preferred: $pref_dst:expr => $pref_src:expr, other: $other_dst:expr => $other_src:expr) => {
2862            #[test]
2863            fn $name() {
2864                test_das_helper(
2865                    $pref_dst,
2866                    $pref_src,
2867                    $other_dst,
2868                    $other_src,
2869                    std::cmp::Ordering::Less,
2870                )
2871            }
2872        };
2873    }
2874
2875    add_das_test!(
2876        prefer_reachable,
2877        preferred: fidl_ip!("198.51.100.121") => Some(fidl_ip!("198.51.100.117")),
2878        other: fidl_ip!("2001:db8:1::1") => Option::<fnet::IpAddress>::None
2879    );
2880
2881    // These test cases are taken from RFC 6724, section 10.2.
2882
2883    add_das_test!(
2884        prefer_matching_scope,
2885        preferred: fidl_ip!("198.51.100.121") => Some(fidl_ip!("198.51.100.117")),
2886        other: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("fe80::1"))
2887    );
2888
2889    add_das_test!(
2890        prefer_matching_label,
2891        preferred: fidl_ip!("2002:c633:6401::1") => Some(fidl_ip!("2002:c633:6401::2")),
2892        other:  fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2002:c633:6401::2"))
2893    );
2894
2895    add_das_test!(
2896        prefer_higher_precedence_1,
2897        preferred: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2001:db8:1::2")),
2898        other: fidl_ip!("10.1.2.3") => Some(fidl_ip!("10.1.2.4"))
2899    );
2900
2901    add_das_test!(
2902        prefer_higher_precedence_2,
2903        preferred: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2001:db8:1::2")),
2904        other: fidl_ip!("2002:c633:6401::1") => Some(fidl_ip!("2002:c633:6401::2"))
2905    );
2906
2907    add_das_test!(
2908        prefer_smaller_scope,
2909        preferred: fidl_ip!("fe80::1") => Some(fidl_ip!("fe80::2")),
2910        other: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2001:db8:1::2"))
2911    );
2912
2913    add_das_test!(
2914        prefer_longest_matching_prefix,
2915        preferred: fidl_ip!("2001:db8:1::1") => Some(fidl_ip!("2001:db8:1::2")),
2916        other: fidl_ip!("2001:db8:3ffe::1") => Some(fidl_ip!("2001:db8:3f44::2"))
2917    );
2918
2919    #[test]
2920    fn test_das_equals() {
2921        for (dst, src) in [
2922            (fidl_ip!("192.168.0.1"), fidl_ip!("192.168.0.2")),
2923            (fidl_ip!("2001:db8::1"), fidl_ip!("2001:db8::2")),
2924        ]
2925        .iter()
2926        {
2927            test_das_helper(*dst, None, *dst, None, std::cmp::Ordering::Equal);
2928            test_das_helper(*dst, Some(*src), *dst, Some(*src), std::cmp::Ordering::Equal);
2929        }
2930    }
2931
2932    #[test]
2933    fn test_valid_policy_table() {
2934        // Last element in policy table MUST be ::/0.
2935        assert_eq!(
2936            POLICY_TABLE.iter().last().expect("empty policy table").prefix,
2937            net_types::ip::Ipv6::ALL_ADDRS_SUBNET
2938        );
2939        // Policy table must be sorted by prefix length.
2940        POLICY_TABLE.array_windows().for_each(|[w0, w1]| {
2941            let Policy { prefix: cur, precedence: _, label: _ } = w0;
2942            let Policy { prefix: nxt, precedence: _, label: _ } = w1;
2943            assert!(
2944                cur.prefix() >= nxt.prefix(),
2945                "bad ordering of prefixes, {} must come after {}",
2946                cur,
2947                nxt
2948            )
2949        });
2950        // Assert that POLICY_TABLE declaration does not use any invalid
2951        // subnets.
2952        for policy in POLICY_TABLE.iter() {
2953            assert!(policy.prefix.prefix() <= 128, "Invalid subnet in policy {:?}", policy);
2954        }
2955    }
2956
2957    #[fasync::run_singlethreaded(test)]
2958    async fn test_sort_preferred_addresses() {
2959        const TEST_IPS: [(fnet::IpAddress, Option<fnet::IpAddress>); 5] = [
2960            (fidl_ip!("127.0.0.1"), Some(fidl_ip!("127.0.0.1"))),
2961            (fidl_ip!("::1"), Some(fidl_ip!("::1"))),
2962            (fidl_ip!("192.168.50.22"), None),
2963            (fidl_ip!("2001::2"), None),
2964            (fidl_ip!("2001:db8:1::1"), Some(fidl_ip!("2001:db8:1::2"))),
2965        ];
2966        // Declared using std types so we get cleaner output when we assert
2967        // expectations.
2968        const SORTED: [IpAddr; 5] = [
2969            std_ip!("::1"),
2970            std_ip!("2001:db8:1::1"),
2971            std_ip!("127.0.0.1"),
2972            std_ip!("192.168.50.22"),
2973            std_ip!("2001::2"),
2974        ];
2975        let (routes_proxy, routes_stream) =
2976            fidl::endpoints::create_proxy_and_stream::<fnet_routes::StateMarker>();
2977        let routes_fut =
2978            routes_stream.map(|r| r.context("stream FIDL error")).try_for_each(|req| {
2979                let (destination, responder) = assert_matches!(
2980                    req,
2981                    fnet_routes::StateRequest::Resolve { destination, responder }
2982                        => (destination, responder)
2983                );
2984                let result = TEST_IPS
2985                    .iter()
2986                    .enumerate()
2987                    .find_map(|(i, (dst, src))| {
2988                        if *dst == destination && src.is_some() {
2989                            let inner = fnet_routes::Destination {
2990                                address: Some(*dst),
2991                                source_address: *src,
2992                                ..Default::default()
2993                            };
2994                            // Send both Direct and Gateway resolved routes to show we
2995                            // don't care about that part.
2996                            if i % 2 == 0 {
2997                                Some(fnet_routes::Resolved::Direct(inner))
2998                            } else {
2999                                Some(fnet_routes::Resolved::Gateway(inner))
3000                            }
3001                        } else {
3002                            None
3003                        }
3004                    })
3005                    .ok_or_else(|| zx::Status::ADDRESS_UNREACHABLE.into_raw());
3006                futures::future::ready(
3007                    responder
3008                        .send(result.as_ref().map_err(|e| *e))
3009                        .context("failed to send Resolve response"),
3010                )
3011            });
3012
3013        let ((), ()) = futures::future::try_join(routes_fut, async move {
3014            let addrs = TEST_IPS.iter().map(|(dst, _src)| *dst).collect();
3015            let addrs = sort_preferred_addresses(addrs, &routes_proxy)
3016                .await
3017                .expect("failed to sort addresses");
3018            let addrs = addrs
3019                .into_iter()
3020                .map(|a| {
3021                    let net_ext::IpAddress(a) = a.into();
3022                    a
3023                })
3024                .collect::<Vec<_>>();
3025            assert_eq!(&addrs[..], &SORTED[..]);
3026            Ok(())
3027        })
3028        .await
3029        .expect("error running futures");
3030    }
3031
3032    #[fasync::run_singlethreaded(test)]
3033    async fn test_lookupip() {
3034        // Routes handler will say that only IPV6_HOST is reachable.
3035        let routes_handler = |req| {
3036            let (destination, responder) = assert_matches!(
3037                req,
3038                fnet_routes::StateRequest::Resolve { destination, responder }
3039                    => (destination, responder)
3040            );
3041            let resolved;
3042            let response = if destination == map_ip(IPV6_HOST) {
3043                resolved = fnet_routes::Resolved::Direct(fnet_routes::Destination {
3044                    address: Some(destination),
3045                    source_address: Some(destination),
3046                    ..Default::default()
3047                });
3048                Ok(&resolved)
3049            } else {
3050                Err(zx::Status::ADDRESS_UNREACHABLE.into_raw())
3051            };
3052            responder.send(response).expect("failed to send Resolve FIDL response");
3053        };
3054        TestEnvironment::default()
3055            .run_lookup_with_routes_handler(
3056                |proxy| async move {
3057                    // All arguments unset.
3058                    assert_eq!(
3059                        proxy
3060                            .lookup_ip(REMOTE_IPV4_HOST, &fname::LookupIpOptions::default())
3061                            .await
3062                            .expect("lookup_ip"),
3063                        Err(fname::LookupError::InvalidArgs)
3064                    );
3065                    // No IP addresses to look.
3066                    assert_eq!(
3067                        proxy
3068                            .lookup_ip(
3069                                REMOTE_IPV4_HOST,
3070                                &fname::LookupIpOptions {
3071                                    ipv4_lookup: Some(false),
3072                                    ipv6_lookup: Some(false),
3073                                    ..Default::default()
3074                                }
3075                            )
3076                            .await
3077                            .expect("lookup_ip"),
3078                        Err(fname::LookupError::InvalidArgs)
3079                    );
3080                    // No results for an IPv4 only host.
3081                    assert_eq!(
3082                        proxy
3083                            .lookup_ip(
3084                                REMOTE_IPV4_HOST,
3085                                &fname::LookupIpOptions {
3086                                    ipv4_lookup: Some(false),
3087                                    ipv6_lookup: Some(true),
3088                                    ..Default::default()
3089                                }
3090                            )
3091                            .await
3092                            .expect("lookup_ip"),
3093                        Err(fname::LookupError::NotFound)
3094                    );
3095                    // Successfully resolve IPv4.
3096                    assert_eq!(
3097                        proxy
3098                            .lookup_ip(
3099                                REMOTE_IPV4_HOST,
3100                                &fname::LookupIpOptions {
3101                                    ipv4_lookup: Some(true),
3102                                    ipv6_lookup: Some(true),
3103                                    ..Default::default()
3104                                }
3105                            )
3106                            .await
3107                            .expect("lookup_ip"),
3108                        Ok(fname::LookupResult {
3109                            addresses: Some(vec![map_ip(IPV4_HOST)]),
3110                            ..Default::default()
3111                        })
3112                    );
3113                    // Successfully resolve IPv4 + IPv6 (no sorting).
3114                    assert_eq!(
3115                        proxy
3116                            .lookup_ip(
3117                                REMOTE_IPV4_IPV6_HOST,
3118                                &fname::LookupIpOptions {
3119                                    ipv4_lookup: Some(true),
3120                                    ipv6_lookup: Some(true),
3121                                    ..Default::default()
3122                                }
3123                            )
3124                            .await
3125                            .expect("lookup_ip"),
3126                        Ok(fname::LookupResult {
3127                            addresses: Some(vec![map_ip(IPV4_HOST), map_ip(IPV6_HOST)]),
3128                            ..Default::default()
3129                        })
3130                    );
3131                    // Successfully resolve IPv4 + IPv6 (with sorting).
3132                    assert_eq!(
3133                        proxy
3134                            .lookup_ip(
3135                                REMOTE_IPV4_IPV6_HOST,
3136                                &fname::LookupIpOptions {
3137                                    ipv4_lookup: Some(true),
3138                                    ipv6_lookup: Some(true),
3139                                    sort_addresses: Some(true),
3140                                    ..Default::default()
3141                                }
3142                            )
3143                            .await
3144                            .expect("lookup_ip"),
3145                        Ok(fname::LookupResult {
3146                            addresses: Some(vec![map_ip(IPV6_HOST), map_ip(IPV4_HOST)]),
3147                            ..Default::default()
3148                        })
3149                    );
3150                },
3151                routes_handler,
3152            )
3153            .await
3154    }
3155}